void InflationCPICapFloorTest::cpicapfloorpricer() { CommonVars common; Real nominal = 1.0; boost::shared_ptr<CPICapFloorTermPriceSurface> cpiCFpriceSurf(new InterpolatedCPICapFloorTermPriceSurface <Bilinear>(nominal, common.baseZeroRate, common.observationLag, common.calendar, common.convention, common.dcZCIIS, common.hii, common.nominalUK, common.cStrikesUK, common.fStrikesUK, common.cfMaturitiesUK, *(common.cPriceUK), *(common.fPriceUK))); common.cpiCFsurfUK = cpiCFpriceSurf; // interpolation pricer first // N.B. no new instrument required but we do need a new pricer Date startDate = Settings::instance().evaluationDate(); Date maturity(startDate + Period(3,Years)); Calendar fixCalendar = UnitedKingdom(), payCalendar = UnitedKingdom(); BusinessDayConvention fixConvention(Unadjusted), payConvention(ModifiedFollowing); Rate strike(0.03); Real baseCPI = common.hii->fixing(fixCalendar.adjust(startDate-common.observationLag,fixConvention)); CPI::InterpolationType observationInterpolation = CPI::AsIndex; CPICapFloor aCap(Option::Call, nominal, startDate, // start date of contract (only) baseCPI, maturity, // this is pre-adjustment! fixCalendar, fixConvention, payCalendar, payConvention, strike, common.hii, common.observationLag, observationInterpolation); Handle<CPICapFloorTermPriceSurface> cpiCFsurfUKh(common.cpiCFsurfUK); boost::shared_ptr<PricingEngine>engine(new InterpolatingCPICapFloorEngine(cpiCFsurfUKh)); aCap.setPricingEngine(engine); Date d = common.cpiCFsurfUK->cpiOptionDateFromTenor(Period(3,Years)); Real cached = cpiCFsurfUKh->capPrice(d, strike); QL_REQUIRE(fabs(cached - aCap.NPV())<1e-10,"InterpolatingCPICapFloorEngine does not reproduce cached price: " << cached << " vs " << aCap.NPV()); // remove circular refernce common.hcpi.linkTo(boost::shared_ptr<ZeroInflationTermStructure>()); }
AverageBMALeg::operator Leg() const { QL_REQUIRE(!notionals_.empty(), "no notional given"); Leg cashflows; // the following is not always correct Calendar calendar = schedule_.calendar(); Date refStart, start, refEnd, end; Date paymentDate; Size n = schedule_.size()-1; for (Size i=0; i<n; ++i) { refStart = start = schedule_.date(i); refEnd = end = schedule_.date(i+1); paymentDate = calendar.adjust(end, paymentAdjustment_); if (i == 0 && schedule_.hasIsRegular() && !schedule_.isRegular(i+1) && schedule_.hasTenor()) refStart = calendar.adjust(end - schedule_.tenor(), paymentAdjustment_); if (i == n-1 && schedule_.hasIsRegular() && !schedule_.isRegular(i+1) && schedule_.hasTenor()) refEnd = calendar.adjust(start + schedule_.tenor(), paymentAdjustment_); cashflows.push_back(ext::shared_ptr<CashFlow>(new AverageBMACoupon(paymentDate, detail::get(notionals_, i, notionals_.back()), start, end, index_, detail::get(gearings_, i, 1.0), detail::get(spreads_, i, 0.0), refStart, refEnd, paymentDayCounter_))); } return cashflows; }
CmsSpreadRangeAccrualLeg::operator Leg() const { QL_REQUIRE(!notionals_.empty(), "no notional given"); Size n = schedule_.size()-1; QL_REQUIRE(notionals_.size() <= n, "too many nominals (" << notionals_.size() << "), only " << n << " required"); QL_REQUIRE(fixingDays_.size() <= n, "too many fixingDays (" << fixingDays_.size() << "), only " << n << " required"); QL_REQUIRE(gearings_.size()<=n, "too many gearings (" << gearings_.size() << "), only " << n << " required"); QL_REQUIRE(spreads_.size()<=n, "too many spreads (" << spreads_.size() << "), only " << n << " required"); QL_REQUIRE(lowerTriggers_.size()<=n, "too many lowerTriggers (" << lowerTriggers_.size() << "), only " << n << " required"); QL_REQUIRE(upperTriggers_.size()<=n, "too many upperTriggers (" << upperTriggers_.size() << "), only " << n << " required"); Leg leg(n); // the following is not always correct Calendar calendar = schedule_.calendar(); Date refStart, start, refEnd, end; Date paymentDate; std::vector<boost::shared_ptr<Schedule> > observationsSchedules; for (Size i=0; i<n; ++i) { refStart = start = schedule_.date(i); refEnd = end = schedule_.date(i+1); paymentDate = calendar.adjust(end, paymentAdjustment_); if (i==0 && !schedule_.isRegular(i+1)) { BusinessDayConvention bdc = schedule_.businessDayConvention(); refStart = calendar.adjust(end - schedule_.tenor(), bdc); } if (i==n-1 && !schedule_.isRegular(i+1)) { BusinessDayConvention bdc = schedule_.businessDayConvention(); refEnd = calendar.adjust(start + schedule_.tenor(), bdc); } if (detail::get(gearings_, i, 1.0) == 0.0) { // fixed coupon leg.push_back(boost::shared_ptr<CashFlow>(new FixedRateCoupon(paymentDate, detail::get(notionals_, i, Null<Real>()), detail::get(spreads_, i, 0.0), paymentDayCounter_, start, end, refStart, refEnd))); } else { // floating coupon observationsSchedules.push_back( boost::shared_ptr<Schedule>(new Schedule(start, end, observationTenor_, calendar, observationConvention_, observationConvention_, DateGeneration::Forward, false))); leg.push_back(boost::shared_ptr<CashFlow>(new CmsSpreadRangeAccrualCoupon( paymentDate, detail::get(notionals_, i, Null<Real>()), index_, start, end, detail::get(fixingDays_, i, 2), paymentDayCounter_, detail::get(gearings_, i, 1.0), detail::get(spreads_, i, 0.0), refStart, refEnd, observationsSchedules.back(), detail::get(lowerTriggers_, i, Null<Rate>()), detail::get(upperTriggers_, i, Null<Rate>())))); } } return leg; }
yoyInflationLeg::operator Leg() const { Size n = schedule_.size()-1; QL_REQUIRE(!notionals_.empty(), "no notional given"); QL_REQUIRE(notionals_.size() <= n, "too many nominals (" << notionals_.size() << "), only " << n << " required"); QL_REQUIRE(gearings_.size()<=n, "too many gearings (" << gearings_.size() << "), only " << n << " required"); QL_REQUIRE(spreads_.size()<=n, "too many spreads (" << spreads_.size() << "), only " << n << " required"); QL_REQUIRE(caps_.size()<=n, "too many caps (" << caps_.size() << "), only " << n << " required"); QL_REQUIRE(floors_.size()<=n, "too many floors (" << floors_.size() << "), only " << n << " required"); Leg leg; leg.reserve(n); Calendar calendar = paymentCalendar_; Date refStart, start, refEnd, end; for (Size i=0; i<n; ++i) { refStart = start = schedule_.date(i); refEnd = end = schedule_.date(i+1); Date paymentDate = calendar.adjust(end, paymentAdjustment_); if (i==0 && schedule_.hasIsRegular() && !schedule_.isRegular(i+1)) { BusinessDayConvention bdc = schedule_.businessDayConvention(); refStart = schedule_.calendar().adjust(end - schedule_.tenor(), bdc); } if (i==n-1 && schedule_.hasIsRegular() && !schedule_.isRegular(i+1)) { BusinessDayConvention bdc = schedule_.businessDayConvention(); refEnd = schedule_.calendar().adjust(start + schedule_.tenor(), bdc); } if (detail::get(gearings_, i, 1.0) == 0.0) { // fixed coupon leg.push_back(ext::shared_ptr<CashFlow>(new FixedRateCoupon(paymentDate, detail::get(notionals_, i, 1.0), detail::effectiveFixedRate(spreads_,caps_, floors_,i), paymentDayCounter_, start, end, refStart, refEnd))); } else { // yoy inflation coupon if (detail::noOption(caps_, floors_, i)) { // just swaplet ext::shared_ptr<YoYInflationCoupon> coup(new YoYInflationCoupon( paymentDate, detail::get(notionals_, i, 1.0), start, end, detail::get(fixingDays_, i, 0), index_, observationLag_, paymentDayCounter_, detail::get(gearings_, i, 1.0), detail::get(spreads_, i, 0.0), refStart, refEnd)); // in this case you can set a pricer // straight away because it only provides computation - not data ext::shared_ptr<YoYInflationCouponPricer> pricer( new YoYInflationCouponPricer); coup->setPricer(pricer); leg.push_back(ext::dynamic_pointer_cast<CashFlow>(coup)); } else { // cap/floorlet leg.push_back(ext::shared_ptr<CashFlow>(new CappedFlooredYoYInflationCoupon( paymentDate, detail::get(notionals_, i, 1.0), start, end, detail::get(fixingDays_, i, 0), index_, observationLag_, paymentDayCounter_, detail::get(gearings_, i, 1.0), detail::get(spreads_, i, 0.0), detail::get(caps_, i, Null<Rate>()), detail::get(floors_, i, Null<Rate>()), refStart, refEnd))); } } } return leg; }
int main(int, char* []) { try { boost::timer timer; std::cout << std::endl; Calendar calendar = TARGET(); Date todaysDate(19, March, 2014); // must be a business day todaysDate = calendar.adjust(todaysDate); Settings::instance().evaluationDate() = todaysDate; /* -------------------------------------------------------------- SET UP BASKET PORTFOLIO -------------------------------------------------------------- */ // build curves and issuers into a basket of ten names std::vector<Real> hazardRates; hazardRates += // 0.01, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9; 0.001, 0.01, 0.02, 0.03, 0.04, 0.05, 0.06, 0.07, 0.08, 0.09; // 0.01, 0.01, 0.01, 0.01, 0.01, 0.01, 0.01, 0.01, 0.01, 0.01; std::vector<std::string> names; for(Size i=0; i<hazardRates.size(); i++) names.push_back(std::string("Acme") + boost::lexical_cast<std::string>(i)); std::vector<Handle<DefaultProbabilityTermStructure> > defTS; for(Size i=0; i<hazardRates.size(); i++) { defTS.push_back(Handle<DefaultProbabilityTermStructure>( boost::make_shared<FlatHazardRate>(0, TARGET(), hazardRates[i], Actual365Fixed()))); defTS.back()->enableExtrapolation(); } std::vector<Issuer> issuers; for(Size i=0; i<hazardRates.size(); i++) { std::vector<QuantLib::Issuer::key_curve_pair> curves(1, std::make_pair(NorthAmericaCorpDefaultKey( EURCurrency(), QuantLib::SeniorSec, Period(), 1. // amount threshold ), defTS[i])); issuers.push_back(Issuer(curves)); } boost::shared_ptr<Pool> thePool = boost::make_shared<Pool>(); for(Size i=0; i<hazardRates.size(); i++) thePool->add(names[i], issuers[i], NorthAmericaCorpDefaultKey( EURCurrency(), QuantLib::SeniorSec, Period(), 1.)); std::vector<DefaultProbKey> defaultKeys(hazardRates.size(), NorthAmericaCorpDefaultKey(EURCurrency(), SeniorSec, Period(), 1.)); boost::shared_ptr<Basket> theBskt = boost::make_shared<Basket>( todaysDate, names, std::vector<Real>(hazardRates.size(), 100.), thePool, // 0.0, 0.78); 0.03, .06); /* -------------------------------------------------------------- SET UP DEFAULT LOSS MODELS -------------------------------------------------------------- */ std::vector<Real> recoveries(hazardRates.size(), 0.4); Date calcDate(TARGET().advance(Settings::instance().evaluationDate(), Period(60, Months))); Real factorValue = 0.05; std::vector<std::vector<Real> > fctrsWeights(hazardRates.size(), std::vector<Real>(1, std::sqrt(factorValue))); // --- LHP model -------------------------- boost::shared_ptr<DefaultLossModel> lmGLHP( boost::make_shared<GaussianLHPLossModel>( fctrsWeights[0][0] * fctrsWeights[0][0], recoveries)); theBskt->setLossModel(lmGLHP); std::cout << "GLHP Expected 10-Yr Losses: " << std::endl; std::cout << theBskt->expectedTrancheLoss(calcDate) << std::endl; // --- G Binomial model -------------------- boost::shared_ptr<GaussianConstantLossLM> ktLossLM( boost::make_shared<GaussianConstantLossLM>(fctrsWeights, recoveries, LatentModelIntegrationType::GaussianQuadrature, GaussianCopulaPolicy::initTraits())); boost::shared_ptr<DefaultLossModel> lmBinomial( boost::make_shared<GaussianBinomialLossModel>(ktLossLM)); theBskt->setLossModel(lmBinomial); std::cout << "Gaussian Binomial Expected 10-Yr Losses: " << std::endl; std::cout << theBskt->expectedTrancheLoss(calcDate) << std::endl; // --- T Binomial model -------------------- TCopulaPolicy::initTraits initT; initT.tOrders = std::vector<Integer>(2, 3); boost::shared_ptr<TConstantLossLM> ktTLossLM( boost::make_shared<TConstantLossLM>(fctrsWeights, recoveries, //LatentModelIntegrationType::GaussianQuadrature, LatentModelIntegrationType::Trapezoid, initT)); boost::shared_ptr<DefaultLossModel> lmTBinomial( boost::make_shared<TBinomialLossModel>(ktTLossLM)); theBskt->setLossModel(lmTBinomial); std::cout << "T Binomial Expected 10-Yr Losses: " << std::endl; std::cout << theBskt->expectedTrancheLoss(calcDate) << std::endl; // --- G Inhomogeneous model --------------- boost::shared_ptr<GaussianConstantLossLM> gLM( boost::make_shared<GaussianConstantLossLM>(fctrsWeights, recoveries, LatentModelIntegrationType::GaussianQuadrature, // g++ requires this when using make_shared GaussianCopulaPolicy::initTraits())); Size numBuckets = 100; boost::shared_ptr<DefaultLossModel> inhomogeneousLM( boost::make_shared<IHGaussPoolLossModel>(gLM, numBuckets)); theBskt->setLossModel(inhomogeneousLM); std::cout << "G Inhomogeneous Expected 10-Yr Losses: " << std::endl; std::cout << theBskt->expectedTrancheLoss(calcDate) << std::endl; // --- G Random model --------------------- // Gaussian random joint default model: Size numSimulations = 100000; // Size numCoresUsed = 4; // Sobol, many cores boost::shared_ptr<DefaultLossModel> rdlmG( boost::make_shared<RandomDefaultLM<GaussianCopulaPolicy, RandomSequenceGenerator< BoxMullerGaussianRng<MersenneTwisterUniformRng> > > >(gLM, recoveries, numSimulations, 1.e-6, 2863311530)); //boost::shared_ptr<DefaultLossModel> rdlmG( // boost::make_shared<RandomDefaultLM<GaussianCopulaPolicy> >(gLM, // recoveries, numSimulations, 1.e-6, 2863311530)); theBskt->setLossModel(rdlmG); std::cout << "Random G Expected 10-Yr Losses: " << std::endl; std::cout << theBskt->expectedTrancheLoss(calcDate) << std::endl; // --- StudentT Random model --------------------- // Sobol, many cores boost::shared_ptr<DefaultLossModel> rdlmT( boost::make_shared<RandomDefaultLM<TCopulaPolicy, RandomSequenceGenerator< PolarStudentTRng<MersenneTwisterUniformRng> > > >(ktTLossLM, recoveries, numSimulations, 1.e-6, 2863311530)); //boost::shared_ptr<DefaultLossModel> rdlmT( // boost::make_shared<RandomDefaultLM<TCopulaPolicy> >(ktTLossLM, // recoveries, numSimulations, 1.e-6, 2863311530)); theBskt->setLossModel(rdlmT); std::cout << "Random T Expected 10-Yr Losses: " << std::endl; std::cout << theBskt->expectedTrancheLoss(calcDate) << std::endl; // Spot Loss latent model: std::vector<std::vector<Real> > fctrsWeightsRR(2 * hazardRates.size(), std::vector<Real>(1, std::sqrt(factorValue))); Real modelA = 2.2; boost::shared_ptr<GaussianSpotLossLM> sptLG(new GaussianSpotLossLM( fctrsWeightsRR, recoveries, modelA, LatentModelIntegrationType::GaussianQuadrature, GaussianCopulaPolicy::initTraits())); boost::shared_ptr<TSpotLossLM> sptLT(new TSpotLossLM(fctrsWeightsRR, recoveries, modelA, LatentModelIntegrationType::GaussianQuadrature, initT)); // --- G Random Loss model --------------------- // Gaussian random joint default model: // Sobol, many cores boost::shared_ptr<DefaultLossModel> rdLlmG( boost::make_shared<RandomLossLM<GaussianCopulaPolicy> >(sptLG, numSimulations, 1.e-6, 2863311530)); theBskt->setLossModel(rdLlmG); std::cout << "Random Loss G Expected 10-Yr Losses: " << std::endl; std::cout << theBskt->expectedTrancheLoss(calcDate) << std::endl; // --- T Random Loss model --------------------- // Gaussian random joint default model: // Sobol, many cores boost::shared_ptr<DefaultLossModel> rdLlmT( boost::make_shared<RandomLossLM<TCopulaPolicy> >(sptLT, numSimulations, 1.e-6, 2863311530)); theBskt->setLossModel(rdLlmT); std::cout << "Random Loss T Expected 10-Yr Losses: " << std::endl; std::cout << theBskt->expectedTrancheLoss(calcDate) << std::endl; // Base Correlation model set up to test cocherence with base LHP model std::vector<Period> bcTenors; bcTenors.push_back(Period(1, Years)); bcTenors.push_back(Period(5, Years)); std::vector<Real> bcLossPercentages; bcLossPercentages.push_back(0.03); bcLossPercentages.push_back(0.12); std::vector<std::vector<Handle<Quote> > > correls; // std::vector<Handle<Quote> > corr1Y; // 3% corr1Y.push_back(Handle<Quote>(boost::shared_ptr<Quote>( new SimpleQuote(fctrsWeights[0][0] * fctrsWeights[0][0])))); // 12% corr1Y.push_back(Handle<Quote>(boost::shared_ptr<Quote>( new SimpleQuote(fctrsWeights[0][0] * fctrsWeights[0][0])))); correls.push_back(corr1Y); std::vector<Handle<Quote> > corr2Y; // 3% corr2Y.push_back(Handle<Quote>(boost::shared_ptr<Quote>( new SimpleQuote(fctrsWeights[0][0] * fctrsWeights[0][0])))); // 12% corr2Y.push_back(Handle<Quote>(boost::shared_ptr<Quote>( new SimpleQuote(fctrsWeights[0][0] * fctrsWeights[0][0])))); correls.push_back(corr2Y); boost::shared_ptr<BaseCorrelationTermStructure<BilinearInterpolation> > correlSurface( new BaseCorrelationTermStructure<BilinearInterpolation>( // first one would do, all should be the same. defTS[0]->settlementDays(), defTS[0]->calendar(), Unadjusted, bcTenors, bcLossPercentages, correls, Actual365Fixed() ) ); Handle<BaseCorrelationTermStructure<BilinearInterpolation> > correlHandle(correlSurface); boost::shared_ptr<DefaultLossModel> bcLMG_LHP_Bilin( boost::make_shared<GaussianLHPFlatBCLM>(correlHandle, recoveries, GaussianCopulaPolicy::initTraits())); theBskt->setLossModel(bcLMG_LHP_Bilin); std::cout << "Base Correlation GLHP Expected 10-Yr Losses: " << std::endl; std::cout << theBskt->expectedTrancheLoss(calcDate) << std::endl; Real seconds = timer.elapsed(); Integer hours = Integer(seconds/3600); seconds -= hours * 3600; Integer minutes = Integer(seconds/60); seconds -= minutes * 60; cout << "Run completed in "; if (hours > 0) cout << hours << " h "; if (hours > 0 || minutes > 0) cout << minutes << " m "; cout << fixed << setprecision(0) << seconds << " s" << endl; return 0; } catch (exception& e) { cerr << e.what() << endl; return 1; } catch (...) { cerr << "unknown error" << endl; return 1; } }
void ShortRateModelTest::testSwaps() { BOOST_TEST_MESSAGE("Testing Hull-White swap pricing against known values..."); SavedSettings backup; IndexHistoryCleaner cleaner; Date today = Settings::instance().evaluationDate(); Calendar calendar = TARGET(); today = calendar.adjust(today); Settings::instance().evaluationDate() = today; Date settlement = calendar.advance(today,2,Days); Date dates[] = { settlement, calendar.advance(settlement,1,Weeks), calendar.advance(settlement,1,Months), calendar.advance(settlement,3,Months), calendar.advance(settlement,6,Months), calendar.advance(settlement,9,Months), calendar.advance(settlement,1,Years), calendar.advance(settlement,2,Years), calendar.advance(settlement,3,Years), calendar.advance(settlement,5,Years), calendar.advance(settlement,10,Years), calendar.advance(settlement,15,Years) }; DiscountFactor discounts[] = { 1.0, 0.999258, 0.996704, 0.990809, 0.981798, 0.972570, 0.963430, 0.929532, 0.889267, 0.803693, 0.596903, 0.433022 }; Handle<YieldTermStructure> termStructure( boost::shared_ptr<YieldTermStructure>( new DiscountCurve( std::vector<Date>(dates,dates+LENGTH(dates)), std::vector<DiscountFactor>(discounts, discounts+LENGTH(discounts)), Actual365Fixed()))); boost::shared_ptr<HullWhite> model(new HullWhite(termStructure)); Integer start[] = { -3, 0, 3 }; Integer length[] = { 2, 5, 10 }; Rate rates[] = { 0.02, 0.04, 0.06 }; boost::shared_ptr<IborIndex> euribor(new Euribor6M(termStructure)); boost::shared_ptr<PricingEngine> engine( new TreeVanillaSwapEngine(model,120)); #if defined(QL_USE_INDEXED_COUPON) Real tolerance = 4.0e-3; #else Real tolerance = 1.0e-8; #endif for (Size i=0; i<LENGTH(start); i++) { Date startDate = calendar.advance(settlement,start[i],Months); if (startDate < today) { Date fixingDate = calendar.advance(startDate,-2,Days); TimeSeries<Real> pastFixings; pastFixings[fixingDate] = 0.03; IndexManager::instance().setHistory(euribor->name(), pastFixings); } for (Size j=0; j<LENGTH(length); j++) { Date maturity = calendar.advance(startDate,length[i],Years); Schedule fixedSchedule(startDate, maturity, Period(Annual), calendar, Unadjusted, Unadjusted, DateGeneration::Forward, false); Schedule floatSchedule(startDate, maturity, Period(Semiannual), calendar, Following, Following, DateGeneration::Forward, false); for (Size k=0; k<LENGTH(rates); k++) { VanillaSwap swap(VanillaSwap::Payer, 1000000.0, fixedSchedule, rates[k], Thirty360(), floatSchedule, euribor, 0.0, Actual360()); swap.setPricingEngine(boost::shared_ptr<PricingEngine>( new DiscountingSwapEngine(termStructure))); Real expected = swap.NPV(); swap.setPricingEngine(engine); Real calculated = swap.NPV(); Real error = std::fabs((expected-calculated)/expected); if (error > tolerance) { BOOST_ERROR("Failed to reproduce swap NPV:" << QL_FIXED << std::setprecision(9) << "\n calculated: " << calculated << "\n expected: " << expected << QL_SCIENTIFIC << "\n rel. error: " << error); } } } } }
void CreditDefaultSwapTest::testFairUpfront() { BOOST_TEST_MESSAGE( "Testing fair-upfront calculation for credit-default swaps..."); SavedSettings backup; // Initialize curves Calendar calendar = TARGET(); Date today = calendar.adjust(Date::todaysDate()); Settings::instance().evaluationDate() = today; Handle<Quote> hazardRate = Handle<Quote>( ext::shared_ptr<Quote>(new SimpleQuote(0.01234))); RelinkableHandle<DefaultProbabilityTermStructure> probabilityCurve; probabilityCurve.linkTo( ext::shared_ptr<DefaultProbabilityTermStructure>( new FlatHazardRate(0, calendar, hazardRate, Actual360()))); RelinkableHandle<YieldTermStructure> discountCurve; discountCurve.linkTo(ext::shared_ptr<YieldTermStructure>( new FlatForward(today,0.06,Actual360()))); // Build the schedule Date issueDate = today; Date maturity = calendar.advance(issueDate, 10, Years); BusinessDayConvention convention = Following; Schedule schedule = MakeSchedule().from(issueDate) .to(maturity) .withFrequency(Quarterly) .withCalendar(calendar) .withTerminationDateConvention(convention) .withRule(DateGeneration::TwentiethIMM); // Build the CDS Rate fixedRate = 0.05; Rate upfront = 0.001; DayCounter dayCount = Actual360(); Real notional = 10000.0; Real recoveryRate = 0.4; ext::shared_ptr<PricingEngine> engine( new MidPointCdsEngine(probabilityCurve, recoveryRate, discountCurve, true)); CreditDefaultSwap cds(Protection::Seller, notional, upfront, fixedRate, schedule, convention, dayCount, true, true); cds.setPricingEngine(engine); Rate fairUpfront = cds.fairUpfront(); CreditDefaultSwap fairCds(Protection::Seller, notional, fairUpfront, fixedRate, schedule, convention, dayCount, true, true); fairCds.setPricingEngine(engine); Real fairNPV = fairCds.NPV(); Real tolerance = 1e-10; if (std::fabs(fairNPV) > tolerance) BOOST_ERROR( "Failed to reproduce null NPV with calculated fair upfront\n" << " calculated upfront: " << io::rate(fairUpfront) << "\n" << " calculated NPV: " << fairNPV); // same with null upfront to begin with upfront = 0.0; CreditDefaultSwap cds2(Protection::Seller, notional, upfront, fixedRate, schedule, convention, dayCount, true, true); cds2.setPricingEngine(engine); fairUpfront = cds2.fairUpfront(); CreditDefaultSwap fairCds2(Protection::Seller, notional, fairUpfront, fixedRate, schedule, convention, dayCount, true, true); fairCds2.setPricingEngine(engine); fairNPV = fairCds2.NPV(); if (std::fabs(fairNPV) > tolerance) BOOST_ERROR( "Failed to reproduce null NPV with calculated fair upfront\n" << " calculated upfront: " << io::rate(fairUpfront) << "\n" << " calculated NPV: " << fairNPV); }
void InflationTest::testYYTermStructure() { BOOST_MESSAGE("Testing year-on-year inflation term structure..."); SavedSettings backup; // try the YY UK Calendar calendar = UnitedKingdom(); BusinessDayConvention bdc = ModifiedFollowing; Date evaluationDate(13, August, 2007); evaluationDate = calendar.adjust(evaluationDate); Settings::instance().evaluationDate() = evaluationDate; // fixing data Date from(1, January, 2005); Date to(13, August, 2007); Schedule rpiSchedule = MakeSchedule().from(from).to(to) .withTenor(1*Months) .withCalendar(UnitedKingdom()) .withConvention(ModifiedFollowing); Real fixData[] = { 189.9, 189.9, 189.6, 190.5, 191.6, 192.0, 192.2, 192.2, 192.6, 193.1, 193.3, 193.6, 194.1, 193.4, 194.2, 195.0, 196.5, 197.7, 198.5, 198.5, 199.2, 200.1, 200.4, 201.1, 202.7, 201.6, 203.1, 204.4, 205.4, 206.2, 207.3, -999.0, -999 }; RelinkableHandle<YoYInflationTermStructure> hy; bool interp = false; boost::shared_ptr<YYUKRPIr> iir(new YYUKRPIr(interp, hy)); for (Size i=0; i<rpiSchedule.size();i++) { iir->addFixing(rpiSchedule[i], fixData[i]); } boost::shared_ptr<YieldTermStructure> nominalTS = nominalTermStructure(); // now build the YoY inflation curve Datum yyData[] = { { Date(13, August, 2008), 2.95 }, { Date(13, August, 2009), 2.95 }, { Date(13, August, 2010), 2.93 }, { Date(15, August, 2011), 2.955 }, { Date(13, August, 2012), 2.945 }, { Date(13, August, 2013), 2.985 }, { Date(13, August, 2014), 3.01 }, { Date(13, August, 2015), 3.035 }, { Date(13, August, 2016), 3.055 }, // note that { Date(13, August, 2017), 3.075 }, // some dates will be on { Date(13, August, 2019), 3.105 }, // holidays but the payment { Date(15, August, 2022), 3.135 }, // calendar will roll them { Date(13, August, 2027), 3.155 }, { Date(13, August, 2032), 3.145 }, { Date(13, August, 2037), 3.145 } }; Period observationLag = Period(2,Months); DayCounter dc = Thirty360(); // now build the helpers ... std::vector<boost::shared_ptr<BootstrapHelper<YoYInflationTermStructure> > > helpers = makeHelpers<YoYInflationTermStructure,YearOnYearInflationSwapHelper, YoYInflationIndex>(yyData, LENGTH(yyData), iir, observationLag, calendar, bdc, dc); Rate baseYYRate = yyData[0].rate/100.0; boost::shared_ptr<PiecewiseYoYInflationCurve<Linear> > pYYTS( new PiecewiseYoYInflationCurve<Linear>( evaluationDate, calendar, dc, observationLag, iir->frequency(),iir->interpolated(), baseYYRate, Handle<YieldTermStructure>(nominalTS), helpers)); pYYTS->recalculate(); // validation // yoy swaps should reprice to zero // yy rates should not equal yySwap rates Real eps = 0.000001; // usual swap engine Handle<YieldTermStructure> hTS(nominalTS); boost::shared_ptr<PricingEngine> sppe(new DiscountingSwapEngine(hTS)); // make sure that the index has the latest yoy term structure hy.linkTo(pYYTS); for (Size j = 1; j < LENGTH(yyData); j++) { from = nominalTS->referenceDate(); to = yyData[j].date; Schedule yoySchedule = MakeSchedule().from(from).to(to) .withConvention(Unadjusted) // fixed leg gets calendar from .withCalendar(calendar) // schedule .withTenor(1*Years) .backwards() ; YearOnYearInflationSwap yyS2(YearOnYearInflationSwap::Payer, 1000000.0, yoySchedule,//fixed schedule, but same as yoy yyData[j].rate/100.0, dc, yoySchedule, iir, observationLag, 0.0, //spread on index dc, UnitedKingdom()); yyS2.setPricingEngine(sppe); BOOST_CHECK_MESSAGE(fabs(yyS2.NPV())<eps,"fresh yoy swap NPV!=0 from TS " <<"swap quote for pt " << j << ", is " << yyData[j].rate/100.0 <<" vs YoY rate "<< pYYTS->yoyRate(yyData[j].date-observationLag) <<" at quote date "<<(yyData[j].date-observationLag) <<", NPV of a fresh yoy swap is " << yyS2.NPV() <<"\n fair rate " << yyS2.fairRate() <<" payment "<<yyS2.paymentConvention()); } Size jj=3; for (Size k = 0; k < 14; k++) { from = nominalTS->referenceDate() - k*Months; to = yyData[jj].date - k*Months; Schedule yoySchedule = MakeSchedule().from(from).to(to) .withConvention(Unadjusted) // fixed leg gets calendar from .withCalendar(calendar) // schedule .withTenor(1*Years) .backwards() ; YearOnYearInflationSwap yyS3(YearOnYearInflationSwap::Payer, 1000000.0, yoySchedule,//fixed schedule, but same as yoy yyData[jj].rate/100.0, dc, yoySchedule, iir, observationLag, 0.0, //spread on index dc, UnitedKingdom()); yyS3.setPricingEngine(sppe); BOOST_CHECK_MESSAGE(fabs(yyS3.NPV())< 20000.0, "unexpected size of aged YoY swap, aged " <<k<<" months: YY aged NPV = " << yyS3.NPV() <<", legs "<< yyS3.legNPV(0) << " and " << yyS3.legNPV(1) ); } }
int main(int, char* []) { try { boost::timer timer; std::cout << std::endl; /********************* *** MARKET DATA *** *********************/ Calendar calendar = TARGET(); Date todaysDate(15, May, 2007); // must be a business day todaysDate = calendar.adjust(todaysDate); Settings::instance().evaluationDate() = todaysDate; // dummy curve boost::shared_ptr<Quote> flatRate(new SimpleQuote(0.01)); Handle<YieldTermStructure> tsCurve( boost::shared_ptr<FlatForward>( new FlatForward(todaysDate, Handle<Quote>(flatRate), Actual365Fixed()))); /* In Lehmans Brothers "guide to exotic credit derivatives" p. 32 there's a simple case, zero flat curve with a flat CDS curve with constant market spreads of 150 bp and RR = 50% corresponds to a flat 3% hazard rate. The implied 1-year survival probability is 97.04% and the 2-years is 94.18% */ // market Real recovery_rate = 0.5; Real quoted_spreads[] = { 0.0150, 0.0150, 0.0150, 0.0150 }; vector<Period> tenors; tenors.push_back(3*Months); tenors.push_back(6*Months); tenors.push_back(1*Years); tenors.push_back(2*Years); vector<Date> maturities; for (Size i=0; i<4; i++) { maturities.push_back(calendar.adjust(todaysDate + tenors[i], Following)); } std::vector<boost::shared_ptr<DefaultProbabilityHelper> > instruments; for (Size i=0; i<4; i++) { instruments.push_back(boost::shared_ptr<DefaultProbabilityHelper>( new SpreadCdsHelper( Handle<Quote>(boost::shared_ptr<Quote>( new SimpleQuote(quoted_spreads[i]))), tenors[i], 0, calendar, Quarterly, Following, DateGeneration::TwentiethIMM, Actual365Fixed(), recovery_rate, tsCurve))); } // Bootstrap hazard rates boost::shared_ptr<PiecewiseDefaultCurve<HazardRate, BackwardFlat> > hazardRateStructure( new PiecewiseDefaultCurve<HazardRate, BackwardFlat>( todaysDate, instruments, Actual365Fixed())); vector<pair<Date, Real> > hr_curve_data = hazardRateStructure->nodes(); cout << "Calibrated hazard rate values: " << endl ; for (Size i=0; i<hr_curve_data.size(); i++) { cout << "hazard rate on " << hr_curve_data[i].first << " is " << hr_curve_data[i].second << endl; } cout << endl; cout << "Some survival probability values: " << endl ; cout << "1Y survival probability: " << io::percent(hazardRateStructure->survivalProbability( todaysDate + 1*Years)) << endl << " expected: " << io::percent(0.9704) << endl; cout << "2Y survival probability: " << io::percent(hazardRateStructure->survivalProbability( todaysDate + 2*Years)) << endl << " expected: " << io::percent(0.9418) << endl; cout << endl << endl; // reprice instruments Real nominal = 1000000.0; Handle<DefaultProbabilityTermStructure> probability(hazardRateStructure); boost::shared_ptr<PricingEngine> engine( new MidPointCdsEngine(probability, recovery_rate, tsCurve)); Schedule cdsSchedule = MakeSchedule().from(todaysDate).to(maturities[0]) .withFrequency(Quarterly) .withCalendar(calendar) .withTerminationDateConvention(Unadjusted) .withRule(DateGeneration::TwentiethIMM); CreditDefaultSwap cds_3m(Protection::Seller, nominal, quoted_spreads[0], cdsSchedule, Following, Actual365Fixed()); cdsSchedule = MakeSchedule().from(todaysDate).to(maturities[1]) .withFrequency(Quarterly) .withCalendar(calendar) .withTerminationDateConvention(Unadjusted) .withRule(DateGeneration::TwentiethIMM); CreditDefaultSwap cds_6m(Protection::Seller, nominal, quoted_spreads[1], cdsSchedule, Following, Actual365Fixed()); cdsSchedule = MakeSchedule().from(todaysDate).to(maturities[2]) .withFrequency(Quarterly) .withCalendar(calendar) .withTerminationDateConvention(Unadjusted) .withRule(DateGeneration::TwentiethIMM); CreditDefaultSwap cds_1y(Protection::Seller, nominal, quoted_spreads[2], cdsSchedule, Following, Actual365Fixed()); cdsSchedule = MakeSchedule().from(todaysDate).to(maturities[3]) .withFrequency(Quarterly) .withCalendar(calendar) .withTerminationDateConvention(Unadjusted) .withRule(DateGeneration::TwentiethIMM); CreditDefaultSwap cds_2y(Protection::Seller, nominal, quoted_spreads[3], cdsSchedule, Following, Actual365Fixed()); cds_3m.setPricingEngine(engine); cds_6m.setPricingEngine(engine); cds_1y.setPricingEngine(engine); cds_2y.setPricingEngine(engine); cout << "Repricing of quoted CDSs employed for calibration: " << endl; cout << "3M fair spread: " << io::rate(cds_3m.fairSpread()) << endl << " NPV: " << cds_3m.NPV() << endl << " default leg: " << cds_3m.defaultLegNPV() << endl << " coupon leg: " << cds_3m.couponLegNPV() << endl << endl; cout << "6M fair spread: " << io::rate(cds_6m.fairSpread()) << endl << " NPV: " << cds_6m.NPV() << endl << " default leg: " << cds_6m.defaultLegNPV() << endl << " coupon leg: " << cds_6m.couponLegNPV() << endl << endl; cout << "1Y fair spread: " << io::rate(cds_1y.fairSpread()) << endl << " NPV: " << cds_1y.NPV() << endl << " default leg: " << cds_1y.defaultLegNPV() << endl << " coupon leg: " << cds_1y.couponLegNPV() << endl << endl; cout << "2Y fair spread: " << io::rate(cds_2y.fairSpread()) << endl << " NPV: " << cds_2y.NPV() << endl << " default leg: " << cds_2y.defaultLegNPV() << endl << " coupon leg: " << cds_2y.couponLegNPV() << endl << endl; cout << endl << endl; Real seconds = timer.elapsed(); Integer hours = Integer(seconds/3600); seconds -= hours * 3600; Integer minutes = Integer(seconds/60); seconds -= minutes * 60; cout << "Run completed in "; if (hours > 0) cout << hours << " h "; if (hours > 0 || minutes > 0) cout << minutes << " m "; cout << fixed << setprecision(0) << seconds << " s" << endl; return 0; } catch (exception& e) { cerr << e.what() << endl; return 1; } catch (...) { cerr << "unknown error" << endl; return 1; } }
FixedRateLeg::operator Leg() const { QL_REQUIRE(!couponRates_.empty(), "no coupon rates given"); QL_REQUIRE(!notionals_.empty(), "no notional given"); Leg leg; leg.reserve(schedule_.size()-1); Calendar schCalendar = schedule_.calendar(); // first period might be short or long Date start = schedule_.date(0), end = schedule_.date(1); Date paymentDate = calendar_.adjust(end, paymentAdjustment_); Date exCouponDate; InterestRate rate = couponRates_[0]; Real nominal = notionals_[0]; if (exCouponPeriod_ != Period()) { exCouponDate = exCouponCalendar_.advance(paymentDate, -exCouponPeriod_, exCouponAdjustment_, exCouponEndOfMonth_); } if (schedule_.isRegular(1)) { QL_REQUIRE(firstPeriodDC_.empty() || firstPeriodDC_ == rate.dayCounter(), "regular first coupon " "does not allow a first-period day count"); shared_ptr<CashFlow> temp(new FixedRateCoupon(paymentDate, nominal, rate, start, end, start, end, exCouponDate)); leg.push_back(temp); } else { Date ref = end - schedule_.tenor(); ref = schCalendar.adjust(ref, schedule_.businessDayConvention()); InterestRate r(rate.rate(), firstPeriodDC_.empty() ? rate.dayCounter() : firstPeriodDC_, rate.compounding(), rate.frequency()); leg.push_back(shared_ptr<CashFlow>(new FixedRateCoupon(paymentDate, nominal, r, start, end, ref, end, exCouponDate))); } // regular periods for (Size i=2; i<schedule_.size()-1; ++i) { start = end; end = schedule_.date(i); paymentDate = calendar_.adjust(end, paymentAdjustment_); if (exCouponPeriod_ != Period()) { exCouponDate = exCouponCalendar_.advance(paymentDate, -exCouponPeriod_, exCouponAdjustment_, exCouponEndOfMonth_); } if ((i-1) < couponRates_.size()) rate = couponRates_[i-1]; else rate = couponRates_.back(); if ((i-1) < notionals_.size()) nominal = notionals_[i-1]; else nominal = notionals_.back(); leg.push_back(shared_ptr<CashFlow>(new FixedRateCoupon(paymentDate, nominal, rate, start, end, start, end, exCouponDate))); } if (schedule_.size() > 2) { // last period might be short or long Size N = schedule_.size(); start = end; end = schedule_.date(N-1); paymentDate = calendar_.adjust(end, paymentAdjustment_); if (exCouponPeriod_ != Period()) { exCouponDate = exCouponCalendar_.advance(paymentDate, -exCouponPeriod_, exCouponAdjustment_, exCouponEndOfMonth_); } if ((N-2) < couponRates_.size()) rate = couponRates_[N-2]; else rate = couponRates_.back(); if ((N-2) < notionals_.size()) nominal = notionals_[N-2]; else nominal = notionals_.back(); InterestRate r(rate.rate(), lastPeriodDC_.empty() ? rate.dayCounter() : lastPeriodDC_, rate.compounding(), rate.frequency()); if (schedule_.isRegular(N - 1)) { leg.push_back(shared_ptr<CashFlow>(new FixedRateCoupon(paymentDate, nominal, r, start, end, start, end, exCouponDate))); } else { Date ref = start + schedule_.tenor(); ref = schCalendar.adjust(ref, schedule_.businessDayConvention()); leg.push_back(shared_ptr<CashFlow>(new FixedRateCoupon(paymentDate, nominal, r, start, end, start, ref, exCouponDate))); } } return leg; }
int main(int, char* []) { try { boost::timer timer; std::cout << std::endl; /********************* *** MARKET DATA *** *********************/ Calendar calendar = TARGET(); Date settlementDate(18, September, 2008); // must be a business day settlementDate = calendar.adjust(settlementDate); Integer fixingDays = 3; Natural settlementDays = 3; Date todaysDate = calendar.advance(settlementDate, -fixingDays, Days); // nothing to do with Date::todaysDate Settings::instance().evaluationDate() = todaysDate; std::cout << "Today: " << todaysDate.weekday() << ", " << todaysDate << std::endl; std::cout << "Settlement date: " << settlementDate.weekday() << ", " << settlementDate << std::endl; // Building of the bonds discounting yield curve /********************* *** RATE HELPERS *** *********************/ // RateHelpers are built from the above quotes together with // other instrument dependant infos. Quotes are passed in // relinkable handles which could be relinked to some other // data source later. // Common data // ZC rates for the short end Rate zc3mQuote=0.0096; Rate zc6mQuote=0.0145; Rate zc1yQuote=0.0194; boost::shared_ptr<Quote> zc3mRate(new SimpleQuote(zc3mQuote)); boost::shared_ptr<Quote> zc6mRate(new SimpleQuote(zc6mQuote)); boost::shared_ptr<Quote> zc1yRate(new SimpleQuote(zc1yQuote)); DayCounter zcBondsDayCounter = Actual365Fixed(); boost::shared_ptr<RateHelper> zc3m(new DepositRateHelper( Handle<Quote>(zc3mRate), 3*Months, fixingDays, calendar, ModifiedFollowing, true, zcBondsDayCounter)); boost::shared_ptr<RateHelper> zc6m(new DepositRateHelper( Handle<Quote>(zc6mRate), 6*Months, fixingDays, calendar, ModifiedFollowing, true, zcBondsDayCounter)); boost::shared_ptr<RateHelper> zc1y(new DepositRateHelper( Handle<Quote>(zc1yRate), 1*Years, fixingDays, calendar, ModifiedFollowing, true, zcBondsDayCounter)); // setup bonds Real redemption = 100.0; const Size numberOfBonds = 5; Date issueDates[] = { Date (15, March, 2005), Date (15, June, 2005), Date (30, June, 2006), Date (15, November, 2002), Date (15, May, 1987) }; Date maturities[] = { Date (31, August, 2010), Date (31, August, 2011), Date (31, August, 2013), Date (15, August, 2018), Date (15, May, 2038) }; Real couponRates[] = { 0.02375, 0.04625, 0.03125, 0.04000, 0.04500 }; Real marketQuotes[] = { 100.390625, 106.21875, 100.59375, 101.6875, 102.140625 }; std::vector< boost::shared_ptr<SimpleQuote> > quote; for (Size i=0; i<numberOfBonds; i++) { boost::shared_ptr<SimpleQuote> cp(new SimpleQuote(marketQuotes[i])); quote.push_back(cp); } RelinkableHandle<Quote> quoteHandle[numberOfBonds]; for (Size i=0; i<numberOfBonds; i++) { quoteHandle[i].linkTo(quote[i]); } // Definition of the rate helpers std::vector<boost::shared_ptr<BondHelper> > bondsHelpers; for (Size i=0; i<numberOfBonds; i++) { Schedule schedule(issueDates[i], maturities[i], Period(Semiannual), UnitedStates(UnitedStates::GovernmentBond), Unadjusted, Unadjusted, DateGeneration::Backward, false); boost::shared_ptr<FixedRateBondHelper> bondHelper(new FixedRateBondHelper( quoteHandle[i], settlementDays, 100.0, schedule, std::vector<Rate>(1,couponRates[i]), ActualActual(ActualActual::Bond), Unadjusted, redemption, issueDates[i])); // the above could also be done by creating a // FixedRateBond instance and writing: // // boost::shared_ptr<BondHelper> bondHelper( // new BondHelper(quoteHandle[i], bond)); // // This would also work for bonds that still don't have a // specialized helper, such as floating-rate bonds. bondsHelpers.push_back(bondHelper); } /********************* ** CURVE BUILDING ** *********************/ // Any DayCounter would be fine. // ActualActual::ISDA ensures that 30 years is 30.0 DayCounter termStructureDayCounter = ActualActual(ActualActual::ISDA); double tolerance = 1.0e-15; // A depo-bond curve std::vector<boost::shared_ptr<RateHelper> > bondInstruments; // Adding the ZC bonds to the curve for the short end bondInstruments.push_back(zc3m); bondInstruments.push_back(zc6m); bondInstruments.push_back(zc1y); // Adding the Fixed rate bonds to the curve for the long end for (Size i=0; i<numberOfBonds; i++) { bondInstruments.push_back(bondsHelpers[i]); } boost::shared_ptr<YieldTermStructure> bondDiscountingTermStructure( new PiecewiseYieldCurve<Discount,LogLinear>( settlementDate, bondInstruments, termStructureDayCounter, tolerance)); // Building of the Libor forecasting curve // deposits Rate d1wQuote=0.043375; Rate d1mQuote=0.031875; Rate d3mQuote=0.0320375; Rate d6mQuote=0.03385; Rate d9mQuote=0.0338125; Rate d1yQuote=0.0335125; // swaps Rate s2yQuote=0.0295; Rate s3yQuote=0.0323; Rate s5yQuote=0.0359; Rate s10yQuote=0.0412; Rate s15yQuote=0.0433; /******************** *** QUOTES *** ********************/ // SimpleQuote stores a value which can be manually changed; // other Quote subclasses could read the value from a database // or some kind of data feed. // deposits boost::shared_ptr<Quote> d1wRate(new SimpleQuote(d1wQuote)); boost::shared_ptr<Quote> d1mRate(new SimpleQuote(d1mQuote)); boost::shared_ptr<Quote> d3mRate(new SimpleQuote(d3mQuote)); boost::shared_ptr<Quote> d6mRate(new SimpleQuote(d6mQuote)); boost::shared_ptr<Quote> d9mRate(new SimpleQuote(d9mQuote)); boost::shared_ptr<Quote> d1yRate(new SimpleQuote(d1yQuote)); // swaps boost::shared_ptr<Quote> s2yRate(new SimpleQuote(s2yQuote)); boost::shared_ptr<Quote> s3yRate(new SimpleQuote(s3yQuote)); boost::shared_ptr<Quote> s5yRate(new SimpleQuote(s5yQuote)); boost::shared_ptr<Quote> s10yRate(new SimpleQuote(s10yQuote)); boost::shared_ptr<Quote> s15yRate(new SimpleQuote(s15yQuote)); /********************* *** RATE HELPERS *** *********************/ // RateHelpers are built from the above quotes together with // other instrument dependant infos. Quotes are passed in // relinkable handles which could be relinked to some other // data source later. // deposits DayCounter depositDayCounter = Actual360(); boost::shared_ptr<RateHelper> d1w(new DepositRateHelper( Handle<Quote>(d1wRate), 1*Weeks, fixingDays, calendar, ModifiedFollowing, true, depositDayCounter)); boost::shared_ptr<RateHelper> d1m(new DepositRateHelper( Handle<Quote>(d1mRate), 1*Months, fixingDays, calendar, ModifiedFollowing, true, depositDayCounter)); boost::shared_ptr<RateHelper> d3m(new DepositRateHelper( Handle<Quote>(d3mRate), 3*Months, fixingDays, calendar, ModifiedFollowing, true, depositDayCounter)); boost::shared_ptr<RateHelper> d6m(new DepositRateHelper( Handle<Quote>(d6mRate), 6*Months, fixingDays, calendar, ModifiedFollowing, true, depositDayCounter)); boost::shared_ptr<RateHelper> d9m(new DepositRateHelper( Handle<Quote>(d9mRate), 9*Months, fixingDays, calendar, ModifiedFollowing, true, depositDayCounter)); boost::shared_ptr<RateHelper> d1y(new DepositRateHelper( Handle<Quote>(d1yRate), 1*Years, fixingDays, calendar, ModifiedFollowing, true, depositDayCounter)); // setup swaps Frequency swFixedLegFrequency = Annual; BusinessDayConvention swFixedLegConvention = Unadjusted; DayCounter swFixedLegDayCounter = Thirty360(Thirty360::European); boost::shared_ptr<IborIndex> swFloatingLegIndex(new Euribor6M); const Period forwardStart(1*Days); boost::shared_ptr<RateHelper> s2y(new SwapRateHelper( Handle<Quote>(s2yRate), 2*Years, calendar, swFixedLegFrequency, swFixedLegConvention, swFixedLegDayCounter, swFloatingLegIndex, Handle<Quote>(),forwardStart)); boost::shared_ptr<RateHelper> s3y(new SwapRateHelper( Handle<Quote>(s3yRate), 3*Years, calendar, swFixedLegFrequency, swFixedLegConvention, swFixedLegDayCounter, swFloatingLegIndex, Handle<Quote>(),forwardStart)); boost::shared_ptr<RateHelper> s5y(new SwapRateHelper( Handle<Quote>(s5yRate), 5*Years, calendar, swFixedLegFrequency, swFixedLegConvention, swFixedLegDayCounter, swFloatingLegIndex, Handle<Quote>(),forwardStart)); boost::shared_ptr<RateHelper> s10y(new SwapRateHelper( Handle<Quote>(s10yRate), 10*Years, calendar, swFixedLegFrequency, swFixedLegConvention, swFixedLegDayCounter, swFloatingLegIndex, Handle<Quote>(),forwardStart)); boost::shared_ptr<RateHelper> s15y(new SwapRateHelper( Handle<Quote>(s15yRate), 15*Years, calendar, swFixedLegFrequency, swFixedLegConvention, swFixedLegDayCounter, swFloatingLegIndex, Handle<Quote>(),forwardStart)); /********************* ** CURVE BUILDING ** *********************/ // Any DayCounter would be fine. // ActualActual::ISDA ensures that 30 years is 30.0 // A depo-swap curve std::vector<boost::shared_ptr<RateHelper> > depoSwapInstruments; depoSwapInstruments.push_back(d1w); depoSwapInstruments.push_back(d1m); depoSwapInstruments.push_back(d3m); depoSwapInstruments.push_back(d6m); depoSwapInstruments.push_back(d9m); depoSwapInstruments.push_back(d1y); depoSwapInstruments.push_back(s2y); depoSwapInstruments.push_back(s3y); depoSwapInstruments.push_back(s5y); depoSwapInstruments.push_back(s10y); depoSwapInstruments.push_back(s15y); boost::shared_ptr<YieldTermStructure> depoSwapTermStructure( new PiecewiseYieldCurve<Discount,LogLinear>( settlementDate, depoSwapInstruments, termStructureDayCounter, tolerance)); // Term structures that will be used for pricing: // the one used for discounting cash flows RelinkableHandle<YieldTermStructure> discountingTermStructure; // the one used for forward rate forecasting RelinkableHandle<YieldTermStructure> forecastingTermStructure; /********************* * BONDS TO BE PRICED * **********************/ // Common data Real faceAmount = 100; // Pricing engine boost::shared_ptr<PricingEngine> bondEngine( new DiscountingBondEngine(discountingTermStructure)); // Zero coupon bond ZeroCouponBond zeroCouponBond( settlementDays, UnitedStates(UnitedStates::GovernmentBond), faceAmount, Date(15,August,2013), Following, Real(116.92), Date(15,August,2003)); zeroCouponBond.setPricingEngine(bondEngine); // Fixed 4.5% US Treasury Note Schedule fixedBondSchedule(Date(15, May, 2007), Date(15,May,2017), Period(Semiannual), UnitedStates(UnitedStates::GovernmentBond), Unadjusted, Unadjusted, DateGeneration::Backward, false); FixedRateBond fixedRateBond( settlementDays, faceAmount, fixedBondSchedule, std::vector<Rate>(1, 0.045), ActualActual(ActualActual::Bond), ModifiedFollowing, 100.0, Date(15, May, 2007)); fixedRateBond.setPricingEngine(bondEngine); // Floating rate bond (3M USD Libor + 0.1%) // Should and will be priced on another curve later... RelinkableHandle<YieldTermStructure> liborTermStructure; const boost::shared_ptr<IborIndex> libor3m( new USDLibor(Period(3,Months),liborTermStructure)); libor3m->addFixing(Date(17, July, 2008),0.0278625); Schedule floatingBondSchedule(Date(21, October, 2005), Date(21, October, 2010), Period(Quarterly), UnitedStates(UnitedStates::NYSE), Unadjusted, Unadjusted, DateGeneration::Backward, true); FloatingRateBond floatingRateBond( settlementDays, faceAmount, floatingBondSchedule, libor3m, Actual360(), ModifiedFollowing, Natural(2), // Gearings std::vector<Real>(1, 1.0), // Spreads std::vector<Rate>(1, 0.001), // Caps std::vector<Rate>(), // Floors std::vector<Rate>(), // Fixing in arrears true, Real(100.0), Date(21, October, 2005)); floatingRateBond.setPricingEngine(bondEngine); // Coupon pricers boost::shared_ptr<IborCouponPricer> pricer(new BlackIborCouponPricer); // optionLet volatilities Volatility volatility = 0.0; Handle<OptionletVolatilityStructure> vol; vol = Handle<OptionletVolatilityStructure>( boost::shared_ptr<OptionletVolatilityStructure>(new ConstantOptionletVolatility( settlementDays, calendar, ModifiedFollowing, volatility, Actual365Fixed()))); pricer->setCapletVolatility(vol); setCouponPricer(floatingRateBond.cashflows(),pricer); // Yield curve bootstrapping forecastingTermStructure.linkTo(depoSwapTermStructure); discountingTermStructure.linkTo(bondDiscountingTermStructure); // We are using the depo & swap curve to estimate the future Libor rates liborTermStructure.linkTo(depoSwapTermStructure); /*************** * BOND PRICING * ****************/ std::cout << std::endl; // write column headings Size widths[] = { 18, 10, 10, 10 }; std::cout << std::setw(widths[0]) << " " << std::setw(widths[1]) << "ZC" << std::setw(widths[2]) << "Fixed" << std::setw(widths[3]) << "Floating" << std::endl; std::string separator = " | "; Size width = widths[0] + widths[1] + widths[2] + widths[3]; std::string rule(width, '-'), dblrule(width, '='); std::string tab(8, ' '); std::cout << rule << std::endl; std::cout << std::fixed; std::cout << std::setprecision(2); std::cout << std::setw(widths[0]) << "Net present value" << std::setw(widths[1]) << zeroCouponBond.NPV() << std::setw(widths[2]) << fixedRateBond.NPV() << std::setw(widths[3]) << floatingRateBond.NPV() << std::endl; std::cout << std::setw(widths[0]) << "Clean price" << std::setw(widths[1]) << zeroCouponBond.cleanPrice() << std::setw(widths[2]) << fixedRateBond.cleanPrice() << std::setw(widths[3]) << floatingRateBond.cleanPrice() << std::endl; std::cout << std::setw(widths[0]) << "Dirty price" << std::setw(widths[1]) << zeroCouponBond.dirtyPrice() << std::setw(widths[2]) << fixedRateBond.dirtyPrice() << std::setw(widths[3]) << floatingRateBond.dirtyPrice() << std::endl; std::cout << std::setw(widths[0]) << "Accrued coupon" << std::setw(widths[1]) << zeroCouponBond.accruedAmount() << std::setw(widths[2]) << fixedRateBond.accruedAmount() << std::setw(widths[3]) << floatingRateBond.accruedAmount() << std::endl; std::cout << std::setw(widths[0]) << "Previous coupon" << std::setw(widths[1]) << "N/A" // zeroCouponBond << std::setw(widths[2]) << io::rate(fixedRateBond.previousCouponRate()) << std::setw(widths[3]) << io::rate(floatingRateBond.previousCouponRate()) << std::endl; std::cout << std::setw(widths[0]) << "Next coupon" << std::setw(widths[1]) << "N/A" // zeroCouponBond << std::setw(widths[2]) << io::rate(fixedRateBond.nextCouponRate()) << std::setw(widths[3]) << io::rate(floatingRateBond.nextCouponRate()) << std::endl; std::cout << std::setw(widths[0]) << "Yield" << std::setw(widths[1]) << io::rate(zeroCouponBond.yield(Actual360(),Compounded,Annual)) << std::setw(widths[2]) << io::rate(fixedRateBond.yield(Actual360(),Compounded,Annual)) << std::setw(widths[3]) << io::rate(floatingRateBond.yield(Actual360(),Compounded,Annual)) << std::endl; std::cout << std::endl; // Other computations std::cout << "Sample indirect computations (for the floating rate bond): " << std::endl; std::cout << rule << std::endl; std::cout << "Yield to Clean Price: " << floatingRateBond.cleanPrice(floatingRateBond.yield(Actual360(),Compounded,Annual),Actual360(),Compounded,Annual,settlementDate) << std::endl; std::cout << "Clean Price to Yield: " << io::rate(floatingRateBond.yield(floatingRateBond.cleanPrice(),Actual360(),Compounded,Annual,settlementDate)) << std::endl; /* "Yield to Price" "Price to Yield" */ double seconds = timer.elapsed(); Integer hours = int(seconds/3600); seconds -= hours * 3600; Integer minutes = int(seconds/60); seconds -= minutes * 60; std::cout << " \nRun completed in "; if (hours > 0) std::cout << hours << " h "; if (hours > 0 || minutes > 0) std::cout << minutes << " m "; std::cout << std::fixed << std::setprecision(0) << seconds << " s\n" << std::endl; return 0; } catch (std::exception& e) { std::cerr << e.what() << std::endl; return 1; } catch (...) { std::cerr << "unknown error" << std::endl; return 1; } }
void qlHazardRateFactory::calculate() { try { TiXmlDocument document; document.Parse(instInfo_.c_str(), 0, TIXML_ENCODING_UTF8); TiXmlNode* rootNode = document.FirstChild("defaultCurveCalculation"); Settings::instance().evaluationDate() = strToDate(rootNode->FirstChildElement("evaluationTime")->GetText()); DayCounter daycounter = Actual365Fixed(); TiXmlElement* pricingParametorNode = rootNode->FirstChildElement("pricingParameter"); TiXmlElement* referenceCurveNode = pricingParametorNode->FirstChildElement("referenceCurves"); Calendar calendar = SouthKorea(); Date refereceDate = Settings::instance().evaluationDate(); // must be a business day refereceDate = calendar.adjust(refereceDate); // dummy curve boost::shared_ptr<Quote> flatRate(new SimpleQuote(0.01)); //Handle<YieldTermStructure> tsCurve( // boost::shared_ptr<FlatForward>( // new FlatForward(todaysDate, Handle<Quote>(flatRate), // Actual365Fixed()))); TiXmlNode* pricingParametorNode = document.FirstChild("pricingParameter"); TiXmlElement* referenceCurveNode = pricingParametorNode->FirstChildElement("referenceCurves"); std::vector<boost::shared_ptr<HazardRateStructure>> hazardRateCurves; TiXmlElement* tsNode = referenceCurveNode->FirstChildElement("termstructure"); if(tsNode) { for(tsNode; tsNode; tsNode = tsNode->NextSiblingElement("termstructure")) { hazardRateCurves.push_back(this->hazardRateTS(tsNode)); } } TiXmlNode* yieldNode = document.FirstChild("yieldNode"); qlYieldTermStructureFactory yieldTsFactory = qlYieldTermStructureFactory(); this->tsCurve_ = yieldTsFactory.yieldTSHandle(yieldNode); // outputTenor Build std::vector<Period> outputTenors; Size outputNum = outputTenors.size(); qlTimeUnitFactory tuFactory = qlTimeUnitFactory(); TiXmlElement* outputTenorNode = pricingParametorNode->FirstChildElement("outputCurveTemplate"); TiXmlElement* tenorNode = outputTenorNode->FirstChildElement("tenorList"); if(tenorNode) { for(tenorNode; tenorNode; tenorNode = tenorNode->NextSiblingElement("tenor")) { outputTenors.push_back(tuFactory.timeUnit(tenorNode)); } } //compositeCalculation boost::shared_ptr<DefaultProbabilityTermStructure> copulaHazardTS = boost::shared_ptr<DefaultProbabilityTermStructure>(new CopulaHazardRate<ProbabilityOfAtLeastNEvents,Linear>( dates,hazardRateCurves,0.9,1,50)); //result for (Size i=0 ; i<outputNum ; ++i) { dates_.push_back(calendar.advance(refereceDate,outputTenors[i])); dateTimes_.push_back(daycounter.yearFraction(refereceDate,dates_[i])); data_.push_back(copulaHazardTS->defaultProbability(dateTimes_[i])); } // makeXml this->buildResultXml(); //this->buildResultXml(inst_->additionalResults()); } catch (std::exception& e) { std::cerr << e.what() << std::endl; } catch (...) { std::cerr << "unknown error" << std::endl; } }
PiecewiseYieldCurve<Discount, LogLinear>* create_yield_curve() { Calendar calendar = UnitedStates(); Date settlementDate(4, January, 2012); // must be a business day settlementDate = calendar.adjust(settlementDate); Integer fixingDays = 2; Date todaysDate = calendar.advance(settlementDate, -fixingDays, Days); Settings::instance().evaluationDate() = todaysDate; std::cout << "Today: " << todaysDate.weekday() << ", " << todaysDate << std::endl; std::cout << "Settlement date: " << settlementDate.weekday() << ", " << settlementDate << std::endl; // Data source: Bloomberg as of 12/30/2011 // deposits Rate d1wQuote=0.00208200; Rate d2wQuote=0.00243500; Rate d1mQuote=0.00295300; Rate d2mQuote=0.00427100; Rate d3mQuote=0.00581000; // futures Real fut1Quote=99.2950; Real fut2Quote=99.2650; Real fut3Quote=99.2450; Real fut4Quote=99.2500; Real fut5Quote=99.2350; Real fut6Quote=99.2100; Real fut7Quote=99.1550; Real fut8Quote=99.1550; // swaps Rate s2yQuote=0.00726000; Rate s3yQuote=0.00820400; Rate s4yQuote=0.01006500; Rate s5yQuote=0.01224500; Rate s6yQuote=0.01449100; Rate s7yQuote=0.01643100; Rate s8yQuote=0.01802800; Rate s9yQuote=0.01932400; Rate s10yQuote=0.02027000; Rate s12yQuote=0.02227100; Rate s15yQuote=0.02401600; /* // Data source: Bloomberg as of 01/31/2012 // deposits Rate d1wQuote=0.00196500; Rate d2wQuote=0.00225500; Rate d1mQuote=0.00264750; Rate d2mQuote=0.00394450; Rate d2mQuote=0.00000000; // futures Real fut1Quote=99.5450; Real fut2Quote=99.5400; Real fut3Quote=99.5200; Real fut4Quote=99.5000; Real fut5Quote=99.4950; Real fut6Quote=99.4750; Real fut7Quote=99.4450; Real fut8Quote=99.4000; // swaps Rate s2yQuote=0.00512500000; Rate s3yQuote=0.00590500000; Rate s4yQuote=0.00764500000; Rate s5yQuote=0.00988000000; Rate s6yQuote=0.01228000000; Rate s7yQuote=0.01449000000; Rate s8yQuote=0.01630500000; Rate s9yQuote=0.01782600000; Rate s10yQuote=0.01915000000; Rate s12yQuote=0.02126000000; Rate s15yQuote=0.02340300000; */ /******************** *** QUOTES *** ********************/ // deposits boost::shared_ptr<Quote> d1wRate(new SimpleQuote(d1wQuote)); boost::shared_ptr<Quote> d2wRate(new SimpleQuote(d2wQuote)); boost::shared_ptr<Quote> d1mRate(new SimpleQuote(d1mQuote)); boost::shared_ptr<Quote> d2mRate(new SimpleQuote(d2mQuote)); boost::shared_ptr<Quote> d3mRate(new SimpleQuote(d3mQuote)); // futures boost::shared_ptr<Quote> fut1Price(new SimpleQuote(fut1Quote)); boost::shared_ptr<Quote> fut2Price(new SimpleQuote(fut2Quote)); boost::shared_ptr<Quote> fut3Price(new SimpleQuote(fut3Quote)); boost::shared_ptr<Quote> fut4Price(new SimpleQuote(fut4Quote)); boost::shared_ptr<Quote> fut5Price(new SimpleQuote(fut5Quote)); boost::shared_ptr<Quote> fut6Price(new SimpleQuote(fut6Quote)); boost::shared_ptr<Quote> fut7Price(new SimpleQuote(fut7Quote)); boost::shared_ptr<Quote> fut8Price(new SimpleQuote(fut8Quote)); // swaps boost::shared_ptr<Quote> s2yRate(new SimpleQuote(s2yQuote)); boost::shared_ptr<Quote> s3yRate(new SimpleQuote(s3yQuote)); boost::shared_ptr<Quote> s4yRate(new SimpleQuote(s4yQuote)); boost::shared_ptr<Quote> s5yRate(new SimpleQuote(s5yQuote)); boost::shared_ptr<Quote> s6yRate(new SimpleQuote(s6yQuote)); boost::shared_ptr<Quote> s7yRate(new SimpleQuote(s7yQuote)); boost::shared_ptr<Quote> s8yRate(new SimpleQuote(s8yQuote)); boost::shared_ptr<Quote> s9yRate(new SimpleQuote(s9yQuote)); boost::shared_ptr<Quote> s10yRate(new SimpleQuote(s10yQuote)); boost::shared_ptr<Quote> s12yRate(new SimpleQuote(s12yQuote)); boost::shared_ptr<Quote> s15yRate(new SimpleQuote(s15yQuote)); /********************* *** RATE HELPERS *** *********************/ // deposits DayCounter depositDayCounter = Actual360(); boost::shared_ptr<RateHelper> d1w(new DepositRateHelper( Handle<Quote>(d1wRate), 1*Weeks, fixingDays, calendar, ModifiedFollowing, true, depositDayCounter)); boost::shared_ptr<RateHelper> d2w(new DepositRateHelper( Handle<Quote>(d2wRate), 2*Weeks, fixingDays, calendar, ModifiedFollowing, true, depositDayCounter)); boost::shared_ptr<RateHelper> d1m(new DepositRateHelper( Handle<Quote>(d1mRate), 1*Months, fixingDays, calendar, ModifiedFollowing, true, depositDayCounter)); boost::shared_ptr<RateHelper> d2m(new DepositRateHelper( Handle<Quote>(d2mRate), 2*Months, fixingDays, calendar, ModifiedFollowing, true, depositDayCounter)); boost::shared_ptr<RateHelper> d3m(new DepositRateHelper( Handle<Quote>(d3mRate), 3*Months, fixingDays, calendar, ModifiedFollowing, true, depositDayCounter)); // setup futures // Rate convexityAdjustment = 0.0; Integer futMonths = 3; Date imm = IMM::nextDate(settlementDate); std::cout << "imm1 = " << imm << std::endl; boost::shared_ptr<RateHelper> fut1(new FuturesRateHelper( Handle<Quote>(fut1Price), imm, futMonths, calendar, ModifiedFollowing, true, depositDayCounter)); imm = IMM::nextDate(imm+1); std::cout << "imm2 = " << imm << std::endl; boost::shared_ptr<RateHelper> fut2(new FuturesRateHelper( Handle<Quote>(fut2Price), imm, futMonths, calendar, ModifiedFollowing, true, depositDayCounter)); imm = IMM::nextDate(imm+1); std::cout << "imm3 = " << imm << std::endl; boost::shared_ptr<RateHelper> fut3(new FuturesRateHelper( Handle<Quote>(fut3Price), imm, futMonths, calendar, ModifiedFollowing, true, depositDayCounter)); imm = IMM::nextDate(imm+1); std::cout << "imm4 = " << imm << std::endl; boost::shared_ptr<RateHelper> fut4(new FuturesRateHelper( Handle<Quote>(fut4Price), imm, futMonths, calendar, ModifiedFollowing, true, depositDayCounter)); imm = IMM::nextDate(imm+1); std::cout << "imm5 = " << imm << std::endl; boost::shared_ptr<RateHelper> fut5(new FuturesRateHelper( Handle<Quote>(fut5Price), imm, futMonths, calendar, ModifiedFollowing, true, depositDayCounter)); imm = IMM::nextDate(imm+1); std::cout << "imm6 = " << imm << std::endl; boost::shared_ptr<RateHelper> fut6(new FuturesRateHelper( Handle<Quote>(fut6Price), imm, futMonths, calendar, ModifiedFollowing, true, depositDayCounter)); imm = IMM::nextDate(imm+1); std::cout << "imm7 = " << imm << std::endl; boost::shared_ptr<RateHelper> fut7(new FuturesRateHelper( Handle<Quote>(fut7Price), imm, futMonths, calendar, ModifiedFollowing, true, depositDayCounter)); imm = IMM::nextDate(imm+1); std::cout << "imm8 = " << imm << std::endl; boost::shared_ptr<RateHelper> fut8(new FuturesRateHelper( Handle<Quote>(fut8Price), imm, futMonths, calendar, ModifiedFollowing, true, depositDayCounter)); // setup swaps Frequency swFixedLegFrequency = Annual; BusinessDayConvention swFixedLegConvention = Unadjusted; DayCounter swFixedLegDayCounter = Thirty360(Thirty360::USA); RelinkableHandle<YieldTermStructure> liborTermStructure; boost::shared_ptr<IborIndex> swFloatingLegIndex(new USDLibor(Period(3, Months), liborTermStructure)); boost::shared_ptr<RateHelper> s2y(new SwapRateHelper( Handle<Quote>(s2yRate), 2*Years, calendar, swFixedLegFrequency, swFixedLegConvention, swFixedLegDayCounter, swFloatingLegIndex)); boost::shared_ptr<RateHelper> s3y(new SwapRateHelper( Handle<Quote>(s3yRate), 3*Years, calendar, swFixedLegFrequency, swFixedLegConvention, swFixedLegDayCounter, swFloatingLegIndex)); boost::shared_ptr<RateHelper> s4y(new SwapRateHelper( Handle<Quote>(s4yRate), 4*Years, calendar, swFixedLegFrequency, swFixedLegConvention, swFixedLegDayCounter, swFloatingLegIndex)); boost::shared_ptr<RateHelper> s5y(new SwapRateHelper( Handle<Quote>(s5yRate), 5*Years, calendar, swFixedLegFrequency, swFixedLegConvention, swFixedLegDayCounter, swFloatingLegIndex)); boost::shared_ptr<RateHelper> s6y(new SwapRateHelper( Handle<Quote>(s6yRate), 6*Years, calendar, swFixedLegFrequency, swFixedLegConvention, swFixedLegDayCounter, swFloatingLegIndex)); boost::shared_ptr<RateHelper> s7y(new SwapRateHelper( Handle<Quote>(s7yRate), 7*Years, calendar, swFixedLegFrequency, swFixedLegConvention, swFixedLegDayCounter, swFloatingLegIndex)); boost::shared_ptr<RateHelper> s8y(new SwapRateHelper( Handle<Quote>(s8yRate), 8*Years, calendar, swFixedLegFrequency, swFixedLegConvention, swFixedLegDayCounter, swFloatingLegIndex)); boost::shared_ptr<RateHelper> s9y(new SwapRateHelper( Handle<Quote>(s9yRate), 9*Years, calendar, swFixedLegFrequency, swFixedLegConvention, swFixedLegDayCounter, swFloatingLegIndex)); boost::shared_ptr<RateHelper> s10y(new SwapRateHelper( Handle<Quote>(s10yRate), 10*Years, calendar, swFixedLegFrequency, swFixedLegConvention, swFixedLegDayCounter, swFloatingLegIndex)); boost::shared_ptr<RateHelper> s12y(new SwapRateHelper( Handle<Quote>(s12yRate), 12*Years, calendar, swFixedLegFrequency, swFixedLegConvention, swFixedLegDayCounter, swFloatingLegIndex)); boost::shared_ptr<RateHelper> s15y(new SwapRateHelper( Handle<Quote>(s15yRate), 15*Years, calendar, swFixedLegFrequency, swFixedLegConvention, swFixedLegDayCounter, swFloatingLegIndex)); /********************* ** CURVE BUILDING ** *********************/ DayCounter termStructureDayCounter = ActualActual(ActualActual::ISDA); double tolerance = 1.0e-15; // A depo-futures-swap curve std::vector<boost::shared_ptr<RateHelper> > depoFutSwapInstruments; depoFutSwapInstruments.push_back(d1w); depoFutSwapInstruments.push_back(d2w); depoFutSwapInstruments.push_back(d1m); depoFutSwapInstruments.push_back(d2m); depoFutSwapInstruments.push_back(d3m); depoFutSwapInstruments.push_back(fut1); depoFutSwapInstruments.push_back(fut2); depoFutSwapInstruments.push_back(fut3); depoFutSwapInstruments.push_back(fut4); depoFutSwapInstruments.push_back(fut5); depoFutSwapInstruments.push_back(fut6); depoFutSwapInstruments.push_back(fut7); depoFutSwapInstruments.push_back(fut8); depoFutSwapInstruments.push_back(s3y); depoFutSwapInstruments.push_back(s4y); depoFutSwapInstruments.push_back(s5y); depoFutSwapInstruments.push_back(s6y); depoFutSwapInstruments.push_back(s7y); depoFutSwapInstruments.push_back(s8y); depoFutSwapInstruments.push_back(s9y); depoFutSwapInstruments.push_back(s10y); depoFutSwapInstruments.push_back(s12y); depoFutSwapInstruments.push_back(s15y); /*boost::shared_ptr<YieldTermStructure> depoFutSwapTermStructure( new PiecewiseYieldCurve<Discount, LogLinear>( settlementDate, depoFutSwapInstruments, termStructureDayCounter, tolerance));*/ PiecewiseYieldCurve<Discount, LogLinear>* depoFutSwapTermStructure = new PiecewiseYieldCurve<Discount, LogLinear>( settlementDate, depoFutSwapInstruments, termStructureDayCounter, tolerance); Date date1 = Date(29, May, 2012); Real df1 = depoFutSwapTermStructure->discount(date1); Date date2 = Date(16, Aug, 2013); Real df2 = depoFutSwapTermStructure->discount(date2); Size widths[] = { 35, 14, 14 }; std::cout << std::setw(widths[0]) << std::left << "Date" << std::setw(widths[1]) << std::left << "Disc. Factor" << std::endl; std::cout << std::setw(widths[0]) << std::left << date1 << std::setw(widths[1]) << std::left << df1 << std::endl; std::cout << std::setw(widths[0]) << std::left << date2 << std::setw(widths[1]) << std::left << df2 << std::endl; return depoFutSwapTermStructure; }
void MainWindow::showSwapNPV() { //ui->lineEdit_SwapNPV->setText(tr("1000000000")); //ui->lineEdit_SwapFairRate->setText(QString::number(3.141659)); //ui->lineEdit_SwapFairSpread->setText(QString::number(2.68796451)); /********************* *** MARKET DATA *** *********************/ Calendar calendar = TARGET(); /////■Date settlementDate(22, September, 2004); QDate qtFormSettlementDate = ui->dateEdit_settlementDate->date(); QuantLib::Date settlementDate(22, September, 2004); if(!qtFormSettlementDate.isNull() && qtFormSettlementDate.isValid()) { QuantLib::Date settlementDate_(qtFormSettlementDate.day(), (QuantLib::Month)(qtFormSettlementDate.month()), qtFormSettlementDate.year()); settlementDate = settlementDate_; } // must be a business day settlementDate = calendar.adjust(settlementDate); Integer fixingDays = 2; Date todaysDate = calendar.advance(settlementDate, -fixingDays, Days); // nothing to do with Date::todaysDate Settings::instance().evaluationDate() = todaysDate; todaysDate = Settings::instance().evaluationDate(); std::cout << "Today: " << todaysDate.weekday() << ", " << todaysDate << std::endl; std::cout << "Settlement date: " << settlementDate.weekday() << ", " << settlementDate << std::endl; // deposits Rate d1wQuote=0.0382; d1wQuote = static_cast<Rate>(ui->tableWidget_RatesDeposites->item(-1,1)->text().toDouble()*0.01); Rate d1mQuote=0.0372; d1mQuote = static_cast<Rate>(ui->tableWidget_RatesDeposites->item(0,1)->text().toDouble()*0.01); Rate d3mQuote=0.0363; d3mQuote = static_cast<Rate>(ui->tableWidget_RatesDeposites->item(1,1)->text().toDouble()*0.01); Rate d6mQuote=0.0353; d6mQuote = static_cast<Rate>(ui->tableWidget_RatesDeposites->item(2,1)->text().toDouble()*0.01); Rate d9mQuote=0.0348; d9mQuote = static_cast<Rate>(ui->tableWidget_RatesDeposites->item(3,1)->text().toDouble()*0.01); Rate d1yQuote=0.0345; d1yQuote = static_cast<Rate>(ui->tableWidget_RatesDeposites->item(4,1)->text().toDouble()*0.01); // FRAs Rate fra3x6Quote=0.037125; fra3x6Quote = static_cast<Rate>(ui->tableWidget_RatesFRAs->item(-1,1)->text().toDouble()*0.01); Rate fra6x9Quote=0.037125; fra6x9Quote = static_cast<Rate>(ui->tableWidget_RatesFRAs->item(0,1)->text().toDouble()*0.01); Rate fra6x12Quote=0.037125; fra6x12Quote = static_cast<Rate>(ui->tableWidget_RatesFRAs->item(1,1)->text().toDouble()*0.01); // futures Real fut1Quote=96.2875; fut1Quote = static_cast<Real>(ui->tableWidget_RatesFutures->item(-1,1)->text().toDouble()); Real fut2Quote=96.7875; fut2Quote = static_cast<Real>(ui->tableWidget_RatesFutures->item(0,1)->text().toDouble()); Real fut3Quote=96.9875; fut3Quote = static_cast<Real>(ui->tableWidget_RatesFutures->item(1,1)->text().toDouble()); Real fut4Quote=96.6875; fut4Quote = static_cast<Real>(ui->tableWidget_RatesFutures->item(2,1)->text().toDouble()); Real fut5Quote=96.4875; fut5Quote = static_cast<Real>(ui->tableWidget_RatesFutures->item(3,1)->text().toDouble()); Real fut6Quote=96.3875; fut6Quote = static_cast<Real>(ui->tableWidget_RatesFutures->item(4,1)->text().toDouble()); Real fut7Quote=96.2875; fut7Quote = static_cast<Real>(ui->tableWidget_RatesFutures->item(5,1)->text().toDouble()); Real fut8Quote=96.0875; fut8Quote = static_cast<Real>(ui->tableWidget_RatesFutures->item(6,1)->text().toDouble()); // swaps Rate s2yQuote=0.037125; s2yQuote = static_cast<Rate>(ui->tableWidget_RatesSwaps->item(-1,1)->text().toDouble()*0.01); Rate s3yQuote=0.0398; s3yQuote = static_cast<Rate>(ui->tableWidget_RatesSwaps->item(0,1)->text().toDouble()*0.01); Rate s5yQuote=0.0443; s5yQuote = static_cast<Rate>(ui->tableWidget_RatesSwaps->item(1,1)->text().toDouble()*0.01); Rate s10yQuote=0.05165; s10yQuote = static_cast<Rate>(ui->tableWidget_RatesSwaps->item(2,1)->text().toDouble()*0.01); Rate s15yQuote=0.055175; s15yQuote = static_cast<Rate>(ui->tableWidget_RatesSwaps->item(3,1)->text().toDouble()*0.01); /******************** *** QUOTES *** ********************/ // SimpleQuote stores a value which can be manually changed; // other Quote subclasses could read the value from a database // or some kind of data feed. // deposits boost::shared_ptr<Quote> d1wRate(new SimpleQuote(d1wQuote)); boost::shared_ptr<Quote> d1mRate(new SimpleQuote(d1mQuote)); boost::shared_ptr<Quote> d3mRate(new SimpleQuote(d3mQuote)); boost::shared_ptr<Quote> d6mRate(new SimpleQuote(d6mQuote)); boost::shared_ptr<Quote> d9mRate(new SimpleQuote(d9mQuote)); boost::shared_ptr<Quote> d1yRate(new SimpleQuote(d1yQuote)); // FRAs boost::shared_ptr<Quote> fra3x6Rate(new SimpleQuote(fra3x6Quote)); boost::shared_ptr<Quote> fra6x9Rate(new SimpleQuote(fra6x9Quote)); boost::shared_ptr<Quote> fra6x12Rate(new SimpleQuote(fra6x12Quote)); // futures boost::shared_ptr<Quote> fut1Price(new SimpleQuote(fut1Quote)); boost::shared_ptr<Quote> fut2Price(new SimpleQuote(fut2Quote)); boost::shared_ptr<Quote> fut3Price(new SimpleQuote(fut3Quote)); boost::shared_ptr<Quote> fut4Price(new SimpleQuote(fut4Quote)); boost::shared_ptr<Quote> fut5Price(new SimpleQuote(fut5Quote)); boost::shared_ptr<Quote> fut6Price(new SimpleQuote(fut6Quote)); boost::shared_ptr<Quote> fut7Price(new SimpleQuote(fut7Quote)); boost::shared_ptr<Quote> fut8Price(new SimpleQuote(fut8Quote)); // swaps boost::shared_ptr<Quote> s2yRate(new SimpleQuote(s2yQuote)); boost::shared_ptr<Quote> s3yRate(new SimpleQuote(s3yQuote)); boost::shared_ptr<Quote> s5yRate(new SimpleQuote(s5yQuote)); boost::shared_ptr<Quote> s10yRate(new SimpleQuote(s10yQuote)); boost::shared_ptr<Quote> s15yRate(new SimpleQuote(s15yQuote)); /********************* *** RATE HELPERS *** *********************/ // RateHelpers are built from the above quotes together with // other instrument dependant infos. Quotes are passed in // relinkable handles which could be relinked to some other // data source later. // deposits DayCounter depositDayCounter = Actual360(); boost::shared_ptr<RateHelper> d1w(new DepositRateHelper( Handle<Quote>(d1wRate), 1*Weeks, fixingDays, calendar, ModifiedFollowing, true, depositDayCounter)); boost::shared_ptr<RateHelper> d1m(new DepositRateHelper( Handle<Quote>(d1mRate), 1*Months, fixingDays, calendar, ModifiedFollowing, true, depositDayCounter)); boost::shared_ptr<RateHelper> d3m(new DepositRateHelper( Handle<Quote>(d3mRate), 3*Months, fixingDays, calendar, ModifiedFollowing, true, depositDayCounter)); boost::shared_ptr<RateHelper> d6m(new DepositRateHelper( Handle<Quote>(d6mRate), 6*Months, fixingDays, calendar, ModifiedFollowing, true, depositDayCounter)); boost::shared_ptr<RateHelper> d9m(new DepositRateHelper( Handle<Quote>(d9mRate), 9*Months, fixingDays, calendar, ModifiedFollowing, true, depositDayCounter)); boost::shared_ptr<RateHelper> d1y(new DepositRateHelper( Handle<Quote>(d1yRate), 1*Years, fixingDays, calendar, ModifiedFollowing, true, depositDayCounter)); // setup FRAs boost::shared_ptr<RateHelper> fra3x6(new FraRateHelper( Handle<Quote>(fra3x6Rate), 3, 6, fixingDays, calendar, ModifiedFollowing, true, depositDayCounter)); boost::shared_ptr<RateHelper> fra6x9(new FraRateHelper( Handle<Quote>(fra6x9Rate), 6, 9, fixingDays, calendar, ModifiedFollowing, true, depositDayCounter)); boost::shared_ptr<RateHelper> fra6x12(new FraRateHelper( Handle<Quote>(fra6x12Rate), 6, 12, fixingDays, calendar, ModifiedFollowing, true, depositDayCounter)); // setup futures // Rate convexityAdjustment = 0.0; Integer futMonths = 3; Date imm = IMM::nextDate(settlementDate); boost::shared_ptr<RateHelper> fut1(new FuturesRateHelper( Handle<Quote>(fut1Price), imm, futMonths, calendar, ModifiedFollowing, true, depositDayCounter)); imm = IMM::nextDate(imm+1); boost::shared_ptr<RateHelper> fut2(new FuturesRateHelper( Handle<Quote>(fut2Price), imm, futMonths, calendar, ModifiedFollowing, true, depositDayCounter)); imm = IMM::nextDate(imm+1); boost::shared_ptr<RateHelper> fut3(new FuturesRateHelper( Handle<Quote>(fut3Price), imm, futMonths, calendar, ModifiedFollowing, true, depositDayCounter)); imm = IMM::nextDate(imm+1); boost::shared_ptr<RateHelper> fut4(new FuturesRateHelper( Handle<Quote>(fut4Price), imm, futMonths, calendar, ModifiedFollowing, true, depositDayCounter)); imm = IMM::nextDate(imm+1); boost::shared_ptr<RateHelper> fut5(new FuturesRateHelper( Handle<Quote>(fut5Price), imm, futMonths, calendar, ModifiedFollowing, true, depositDayCounter)); imm = IMM::nextDate(imm+1); boost::shared_ptr<RateHelper> fut6(new FuturesRateHelper( Handle<Quote>(fut6Price), imm, futMonths, calendar, ModifiedFollowing, true, depositDayCounter)); imm = IMM::nextDate(imm+1); boost::shared_ptr<RateHelper> fut7(new FuturesRateHelper( Handle<Quote>(fut7Price), imm, futMonths, calendar, ModifiedFollowing, true, depositDayCounter)); imm = IMM::nextDate(imm+1); boost::shared_ptr<RateHelper> fut8(new FuturesRateHelper( Handle<Quote>(fut8Price), imm, futMonths, calendar, ModifiedFollowing, true, depositDayCounter)); // setup swaps Frequency swFixedLegFrequency = Annual; BusinessDayConvention swFixedLegConvention = Unadjusted; DayCounter swFixedLegDayCounter = Thirty360(Thirty360::European); boost::shared_ptr<IborIndex> swFloatingLegIndex(new Euribor6M); boost::shared_ptr<RateHelper> s2y(new SwapRateHelper( Handle<Quote>(s2yRate), 2*Years, calendar, swFixedLegFrequency, swFixedLegConvention, swFixedLegDayCounter, swFloatingLegIndex)); boost::shared_ptr<RateHelper> s3y(new SwapRateHelper( Handle<Quote>(s3yRate), 3*Years, calendar, swFixedLegFrequency, swFixedLegConvention, swFixedLegDayCounter, swFloatingLegIndex)); boost::shared_ptr<RateHelper> s5y(new SwapRateHelper( Handle<Quote>(s5yRate), 5*Years, calendar, swFixedLegFrequency, swFixedLegConvention, swFixedLegDayCounter, swFloatingLegIndex)); boost::shared_ptr<RateHelper> s10y(new SwapRateHelper( Handle<Quote>(s10yRate), 10*Years, calendar, swFixedLegFrequency, swFixedLegConvention, swFixedLegDayCounter, swFloatingLegIndex)); boost::shared_ptr<RateHelper> s15y(new SwapRateHelper( Handle<Quote>(s15yRate), 15*Years, calendar, swFixedLegFrequency, swFixedLegConvention, swFixedLegDayCounter, swFloatingLegIndex)); /********************* ** CURVE BUILDING ** *********************/ // Any DayCounter would be fine. // ActualActual::ISDA ensures that 30 years is 30.0 DayCounter termStructureDayCounter = ActualActual(ActualActual::ISDA); double tolerance = 1.0e-15; // A depo-swap curve std::vector<boost::shared_ptr<RateHelper> > depoSwapInstruments; depoSwapInstruments.push_back(d1w); depoSwapInstruments.push_back(d1m); depoSwapInstruments.push_back(d3m); depoSwapInstruments.push_back(d6m); depoSwapInstruments.push_back(d9m); depoSwapInstruments.push_back(d1y); depoSwapInstruments.push_back(s2y); depoSwapInstruments.push_back(s3y); depoSwapInstruments.push_back(s5y); depoSwapInstruments.push_back(s10y); depoSwapInstruments.push_back(s15y); boost::shared_ptr<YieldTermStructure> depoSwapTermStructure( new PiecewiseYieldCurve<Discount,LogLinear>( settlementDate, depoSwapInstruments, termStructureDayCounter, tolerance)); // A depo-futures-swap curve std::vector<boost::shared_ptr<RateHelper> > depoFutSwapInstruments; depoFutSwapInstruments.push_back(d1w); depoFutSwapInstruments.push_back(d1m); depoFutSwapInstruments.push_back(fut1); depoFutSwapInstruments.push_back(fut2); depoFutSwapInstruments.push_back(fut3); depoFutSwapInstruments.push_back(fut4); depoFutSwapInstruments.push_back(fut5); depoFutSwapInstruments.push_back(fut6); depoFutSwapInstruments.push_back(fut7); depoFutSwapInstruments.push_back(fut8); depoFutSwapInstruments.push_back(s3y); depoFutSwapInstruments.push_back(s5y); depoFutSwapInstruments.push_back(s10y); depoFutSwapInstruments.push_back(s15y); boost::shared_ptr<YieldTermStructure> depoFutSwapTermStructure( new PiecewiseYieldCurve<Discount,LogLinear>( settlementDate, depoFutSwapInstruments, termStructureDayCounter, tolerance)); // A depo-FRA-swap curve std::vector<boost::shared_ptr<RateHelper> > depoFRASwapInstruments; depoFRASwapInstruments.push_back(d1w); depoFRASwapInstruments.push_back(d1m); depoFRASwapInstruments.push_back(d3m); depoFRASwapInstruments.push_back(fra3x6); depoFRASwapInstruments.push_back(fra6x9); depoFRASwapInstruments.push_back(fra6x12); depoFRASwapInstruments.push_back(s2y); depoFRASwapInstruments.push_back(s3y); depoFRASwapInstruments.push_back(s5y); depoFRASwapInstruments.push_back(s10y); depoFRASwapInstruments.push_back(s15y); boost::shared_ptr<YieldTermStructure> depoFRASwapTermStructure( new PiecewiseYieldCurve<Discount,LogLinear>( settlementDate, depoFRASwapInstruments, termStructureDayCounter, tolerance)); // Term structures that will be used for pricing: // the one used for discounting cash flows RelinkableHandle<YieldTermStructure> discountingTermStructure; // the one used for forward rate forecasting RelinkableHandle<YieldTermStructure> forecastingTermStructure; /********************* * SWAPS TO BE PRICED * **********************/ // constant nominal 1,000,000 Euro /////■Real nominal = 1000000.0; Real nominal = static_cast<Real>(ui->doubleSpinBox_Notional->value()); // fixed leg /////■Frequency fixedLegFrequency = Annual; Frequency fixedLegFrequency = Annual; /////■BusinessDayConvention fixedLegConvention = Unadjusted; BusinessDayConvention fixedLegConvention = Unadjusted; /////■DayCounter fixedLegDayCounter = Thirty360(Thirty360::European); DayCounter fixedLegDayCounter = Thirty360(Thirty360::European); /////■Rate fixedRate = 0.04; Rate fixedRate = static_cast<Real>((ui->doubleSpinBox_fixedRate->value())*0.01); // floating leg /////■ Frequency floatingLegFrequency = Semiannual; Frequency floatingLegFrequency = Semiannual; int comboBoxFloatingLegFrequency = ui->comboBox_FloatingLegFrequency->currentIndex(); switch(comboBoxFloatingLegFrequency) { case 0: // Semiannual floatingLegFrequency = Semiannual; break; case 1: // Annual floatingLegFrequency = Annual; break; case 2: // Quarterly floatingLegFrequency = Quarterly; break; } /////■BusinessDayConvention floatingLegConvention = ModifiedFollowing; BusinessDayConvention floatingLegConvention = ModifiedFollowing; /////■DayCounter floatingLegDayCounter = Actual360(); DayCounter floatingLegDayCounter = Actual360(); boost::shared_ptr<IborIndex> euriborIndex( new Euribor6M(forecastingTermStructure)); /////■ Spread spread = 0.0; Spread spread = static_cast<Spread>((ui->doubleSpinBox_spread->value())*0.01); /////■ Integer lenghtInYears = 5; Integer lenghtInYears = (Integer)(ui->doubleSpinBox_lenghtInYears->value()); /////■ VanillaSwap::Type swapType = VanillaSwap::Payer; int comboBoxPayerReceiver = ui->comboBox_swapType->currentIndex(); VanillaSwap::Type swapType = VanillaSwap::Receiver; switch(comboBoxPayerReceiver) { case 0: // Fix Payer swapType = VanillaSwap::Payer; break; // VanillaSwap::Payer=1 case 1: // Fix Receiver swapType = VanillaSwap::Receiver; break; // VanillaSwap::Receiver=-1 } Date maturity = settlementDate + lenghtInYears*Years; Schedule fixedSchedule(settlementDate, maturity, Period(fixedLegFrequency), calendar, fixedLegConvention, fixedLegConvention, DateGeneration::Forward, false); Schedule floatSchedule(settlementDate, maturity, Period(floatingLegFrequency), calendar, floatingLegConvention, floatingLegConvention, DateGeneration::Forward, false); VanillaSwap spot5YearSwap(swapType, nominal, fixedSchedule, fixedRate, fixedLegDayCounter, floatSchedule, euriborIndex, spread, floatingLegDayCounter); Date fwdStart = calendar.advance(settlementDate, 1, Years); Date fwdMaturity = fwdStart + lenghtInYears*Years; Schedule fwdFixedSchedule(fwdStart, fwdMaturity, Period(fixedLegFrequency), calendar, fixedLegConvention, fixedLegConvention, DateGeneration::Forward, false); Schedule fwdFloatSchedule(fwdStart, fwdMaturity, Period(floatingLegFrequency), calendar, floatingLegConvention, floatingLegConvention, DateGeneration::Forward, false); VanillaSwap oneYearForward5YearSwap(swapType, nominal, fwdFixedSchedule, fixedRate, fixedLegDayCounter, fwdFloatSchedule, euriborIndex, spread, floatingLegDayCounter); /*************** * SWAP PRICING * ****************/ Real NPV; Rate fairRate; Spread fairSpread; boost::shared_ptr<PricingEngine> swapEngine( new DiscountingSwapEngine(discountingTermStructure)); spot5YearSwap.setPricingEngine(swapEngine); oneYearForward5YearSwap.setPricingEngine(swapEngine); int swapTermStructureType(0); swapTermStructureType = ui->comboBox_TermStructureType->currentIndex(); //QMessageBox msgBoxTmp(this); msgBoxTmp.setText(QString::number(swapTermStructureType)); msgBoxTmp.exec(); switch(swapTermStructureType) { case 0: forecastingTermStructure.linkTo(depoSwapTermStructure); discountingTermStructure.linkTo(depoSwapTermStructure); break; case 1: forecastingTermStructure.linkTo(depoFutSwapTermStructure); discountingTermStructure.linkTo(depoFutSwapTermStructure); break; case 2: forecastingTermStructure.linkTo(depoFRASwapTermStructure); discountingTermStructure.linkTo(depoFRASwapTermStructure); break; } NPV = spot5YearSwap.NPV(); fairRate = spot5YearSwap.fairRate(); fairSpread = spot5YearSwap.fairSpread(); ui->lineEdit_SwapNPV->setText(QString::number(static_cast<double>(NPV),'f')); ui->lineEdit_SwapFairRate->setText(QString::number(static_cast<double>(fairRate*100.0),'f')); ui->lineEdit_SwapFairSpread->setText(QString::number(static_cast<double>(fairSpread*100.0),'f')); }
int main(int, char* []) { try { boost::timer timer; std::cout << std::endl; Option::Type type(Option::Put); Real underlying = 36.0; Real spreadRate = 0.005; Spread dividendYield = 0.02; Rate riskFreeRate = 0.06; Volatility volatility = 0.20; Integer settlementDays = 3; Integer length = 5; Real redemption = 100.0; Real conversionRatio = redemption/underlying; // at the money // set up dates/schedules Calendar calendar = TARGET(); Date today = calendar.adjust(Date::todaysDate()); Settings::instance().evaluationDate() = today; Date settlementDate = calendar.advance(today, settlementDays, Days); Date exerciseDate = calendar.advance(settlementDate, length, Years); Date issueDate = calendar.advance(exerciseDate, -length, Years); BusinessDayConvention convention = ModifiedFollowing; Frequency frequency = Annual; Schedule schedule(issueDate, exerciseDate, Period(frequency), calendar, convention, convention, DateGeneration::Backward, false); DividendSchedule dividends; CallabilitySchedule callability; std::vector<Real> coupons(1, 0.05); DayCounter bondDayCount = Thirty360(); Integer callLength[] = { 2, 4 }; // Call dates, years 2, 4. Integer putLength[] = { 3 }; // Put dates year 3 Real callPrices[] = { 101.5, 100.85 }; Real putPrices[]= { 105.0 }; // Load call schedules for (Size i=0; i<LENGTH(callLength); i++) { callability.push_back( boost::shared_ptr<Callability>( new SoftCallability(Callability::Price( callPrices[i], Callability::Price::Clean), schedule.date(callLength[i]), 1.20))); } for (Size j=0; j<LENGTH(putLength); j++) { callability.push_back( boost::shared_ptr<Callability>( new Callability(Callability::Price( putPrices[j], Callability::Price::Clean), Callability::Put, schedule.date(putLength[j])))); } // Assume dividends are paid every 6 months. for (Date d = today + 6*Months; d < exerciseDate; d += 6*Months) { dividends.push_back( boost::shared_ptr<Dividend>(new FixedDividend(1.0, d))); } DayCounter dayCounter = Actual365Fixed(); Time maturity = dayCounter.yearFraction(settlementDate, exerciseDate); std::cout << "option type = " << type << std::endl; std::cout << "Time to maturity = " << maturity << std::endl; std::cout << "Underlying price = " << underlying << std::endl; std::cout << "Risk-free interest rate = " << io::rate(riskFreeRate) << std::endl; std::cout << "Dividend yield = " << io::rate(dividendYield) << std::endl; std::cout << "Volatility = " << io::volatility(volatility) << std::endl; std::cout << std::endl; std::string method; std::cout << std::endl ; // write column headings Size widths[] = { 35, 14, 14 }; Size totalWidth = widths[0] + widths[1] + widths[2]; std::string rule(totalWidth, '-'), dblrule(totalWidth, '='); std::cout << dblrule << std::endl; std::cout << "Tsiveriotis-Fernandes method" << std::endl; std::cout << dblrule << std::endl; std::cout << std::setw(widths[0]) << std::left << "Tree type" << std::setw(widths[1]) << std::left << "European" << std::setw(widths[1]) << std::left << "American" << std::endl; std::cout << rule << std::endl; boost::shared_ptr<Exercise> exercise( new EuropeanExercise(exerciseDate)); boost::shared_ptr<Exercise> amExercise( new AmericanExercise(settlementDate, exerciseDate)); Handle<Quote> underlyingH( boost::shared_ptr<Quote>(new SimpleQuote(underlying))); Handle<YieldTermStructure> flatTermStructure( boost::shared_ptr<YieldTermStructure>( new FlatForward(settlementDate, riskFreeRate, dayCounter))); Handle<YieldTermStructure> flatDividendTS( boost::shared_ptr<YieldTermStructure>( new FlatForward(settlementDate, dividendYield, dayCounter))); Handle<BlackVolTermStructure> flatVolTS( boost::shared_ptr<BlackVolTermStructure>( new BlackConstantVol(settlementDate, calendar, volatility, dayCounter))); boost::shared_ptr<BlackScholesMertonProcess> stochasticProcess( new BlackScholesMertonProcess(underlyingH, flatDividendTS, flatTermStructure, flatVolTS)); Size timeSteps = 801; Handle<Quote> creditSpread( boost::shared_ptr<Quote>(new SimpleQuote(spreadRate))); boost::shared_ptr<Quote> rate(new SimpleQuote(riskFreeRate)); Handle<YieldTermStructure> discountCurve( boost::shared_ptr<YieldTermStructure>( new FlatForward(today, Handle<Quote>(rate), dayCounter))); boost::shared_ptr<PricingEngine> engine( new BinomialConvertibleEngine<JarrowRudd>(stochasticProcess, timeSteps)); ConvertibleFixedCouponBond europeanBond( exercise, conversionRatio, dividends, callability, creditSpread, issueDate, settlementDays, coupons, bondDayCount, schedule, redemption); europeanBond.setPricingEngine(engine); ConvertibleFixedCouponBond americanBond( amExercise, conversionRatio, dividends, callability, creditSpread, issueDate, settlementDays, coupons, bondDayCount, schedule, redemption); americanBond.setPricingEngine(engine); method = "Jarrow-Rudd"; europeanBond.setPricingEngine(boost::shared_ptr<PricingEngine>( new BinomialConvertibleEngine<JarrowRudd>(stochasticProcess, timeSteps))); americanBond.setPricingEngine(boost::shared_ptr<PricingEngine>( new BinomialConvertibleEngine<JarrowRudd>(stochasticProcess, timeSteps))); std::cout << std::setw(widths[0]) << std::left << method << std::fixed << std::setw(widths[1]) << std::left << europeanBond.NPV() << std::setw(widths[2]) << std::left << americanBond.NPV() << std::endl; method = "Cox-Ross-Rubinstein"; europeanBond.setPricingEngine(boost::shared_ptr<PricingEngine>( new BinomialConvertibleEngine<CoxRossRubinstein>(stochasticProcess, timeSteps))); americanBond.setPricingEngine(boost::shared_ptr<PricingEngine>( new BinomialConvertibleEngine<CoxRossRubinstein>(stochasticProcess, timeSteps))); std::cout << std::setw(widths[0]) << std::left << method << std::fixed << std::setw(widths[1]) << std::left << europeanBond.NPV() << std::setw(widths[2]) << std::left << americanBond.NPV() << std::endl; method = "Additive equiprobabilities"; europeanBond.setPricingEngine(boost::shared_ptr<PricingEngine>( new BinomialConvertibleEngine<AdditiveEQPBinomialTree>( stochasticProcess, timeSteps))); americanBond.setPricingEngine(boost::shared_ptr<PricingEngine>( new BinomialConvertibleEngine<AdditiveEQPBinomialTree>( stochasticProcess, timeSteps))); std::cout << std::setw(widths[0]) << std::left << method << std::fixed << std::setw(widths[1]) << std::left << europeanBond.NPV() << std::setw(widths[2]) << std::left << americanBond.NPV() << std::endl; method = "Trigeorgis"; europeanBond.setPricingEngine(boost::shared_ptr<PricingEngine>( new BinomialConvertibleEngine<Trigeorgis>(stochasticProcess, timeSteps))); americanBond.setPricingEngine(boost::shared_ptr<PricingEngine>( new BinomialConvertibleEngine<Trigeorgis>(stochasticProcess, timeSteps))); std::cout << std::setw(widths[0]) << std::left << method << std::fixed << std::setw(widths[1]) << std::left << europeanBond.NPV() << std::setw(widths[2]) << std::left << americanBond.NPV() << std::endl; method = "Tian"; europeanBond.setPricingEngine(boost::shared_ptr<PricingEngine>( new BinomialConvertibleEngine<Tian>(stochasticProcess, timeSteps))); americanBond.setPricingEngine(boost::shared_ptr<PricingEngine>( new BinomialConvertibleEngine<Tian>(stochasticProcess, timeSteps))); std::cout << std::setw(widths[0]) << std::left << method << std::fixed << std::setw(widths[1]) << std::left << europeanBond.NPV() << std::setw(widths[2]) << std::left << americanBond.NPV() << std::endl; method = "Leisen-Reimer"; europeanBond.setPricingEngine(boost::shared_ptr<PricingEngine>( new BinomialConvertibleEngine<LeisenReimer>(stochasticProcess, timeSteps))); americanBond.setPricingEngine(boost::shared_ptr<PricingEngine>( new BinomialConvertibleEngine<LeisenReimer>(stochasticProcess, timeSteps))); std::cout << std::setw(widths[0]) << std::left << method << std::fixed << std::setw(widths[1]) << std::left << europeanBond.NPV() << std::setw(widths[2]) << std::left << americanBond.NPV() << std::endl; method = "Joshi"; europeanBond.setPricingEngine(boost::shared_ptr<PricingEngine>( new BinomialConvertibleEngine<Joshi4>(stochasticProcess, timeSteps))); americanBond.setPricingEngine(boost::shared_ptr<PricingEngine>( new BinomialConvertibleEngine<Joshi4>(stochasticProcess, timeSteps))); std::cout << std::setw(widths[0]) << std::left << method << std::fixed << std::setw(widths[1]) << std::left << europeanBond.NPV() << std::setw(widths[2]) << std::left << americanBond.NPV() << std::endl; std::cout << dblrule << std::endl; Real seconds = timer.elapsed(); Integer hours = int(seconds/3600); seconds -= hours * 3600; Integer minutes = int(seconds/60); seconds -= minutes * 60; std::cout << " \nRun completed in "; if (hours > 0) std::cout << hours << " h "; if (hours > 0 || minutes > 0) std::cout << minutes << " m "; std::cout << std::fixed << std::setprecision(0) << seconds << " s\n" << std::endl; return 0; } catch (std::exception& e) { std::cerr << e.what() << std::endl; return 1; } catch (...) { std::cerr << "unknown error" << std::endl; return 1; } }
void QtSwap::qtswap() { try { boost::timer timer; std::cout << std::endl; /********************* *** MARKET DATA *** *********************/ Calendar calendar = TARGET(); Date settlementDate(22, September, 2004); // must be a business day settlementDate = calendar.adjust(settlementDate); Integer fixingDays = 2; Date todaysDate = calendar.advance(settlementDate, -fixingDays, Days); // nothing to do with Date::todaysDate Settings::instance().evaluationDate() = todaysDate; todaysDate = Settings::instance().evaluationDate(); std::cout << "Today: " << todaysDate.weekday() << ", " << todaysDate << std::endl; std::cout << "Settlement date: " << settlementDate.weekday() << ", " << settlementDate << std::endl; // deposits Rate d1wQuote=0.0382; Rate d1mQuote=0.0372; Rate d3mQuote=0.0363; Rate d6mQuote=0.0353; Rate d9mQuote=0.0348; Rate d1yQuote=0.0345; // FRAs Rate fra3x6Quote=0.037125; Rate fra6x9Quote=0.037125; Rate fra6x12Quote=0.037125; // futures Real fut1Quote=96.2875; Real fut2Quote=96.7875; Real fut3Quote=96.9875; Real fut4Quote=96.6875; Real fut5Quote=96.4875; Real fut6Quote=96.3875; Real fut7Quote=96.2875; Real fut8Quote=96.0875; // swaps Rate s2yQuote=0.037125; Rate s3yQuote=0.0398; Rate s5yQuote=0.0443; Rate s10yQuote=0.05165; Rate s15yQuote=0.055175; /******************** *** QUOTES *** ********************/ // SimpleQuote stores a value which can be manually changed; // other Quote subclasses could read the value from a database // or some kind of data feed. // deposits boost::shared_ptr<Quote> d1wRate(new SimpleQuote(d1wQuote)); boost::shared_ptr<Quote> d1mRate(new SimpleQuote(d1mQuote)); boost::shared_ptr<Quote> d3mRate(new SimpleQuote(d3mQuote)); boost::shared_ptr<Quote> d6mRate(new SimpleQuote(d6mQuote)); boost::shared_ptr<Quote> d9mRate(new SimpleQuote(d9mQuote)); boost::shared_ptr<Quote> d1yRate(new SimpleQuote(d1yQuote)); // FRAs boost::shared_ptr<Quote> fra3x6Rate(new SimpleQuote(fra3x6Quote)); boost::shared_ptr<Quote> fra6x9Rate(new SimpleQuote(fra6x9Quote)); boost::shared_ptr<Quote> fra6x12Rate(new SimpleQuote(fra6x12Quote)); // futures boost::shared_ptr<Quote> fut1Price(new SimpleQuote(fut1Quote)); boost::shared_ptr<Quote> fut2Price(new SimpleQuote(fut2Quote)); boost::shared_ptr<Quote> fut3Price(new SimpleQuote(fut3Quote)); boost::shared_ptr<Quote> fut4Price(new SimpleQuote(fut4Quote)); boost::shared_ptr<Quote> fut5Price(new SimpleQuote(fut5Quote)); boost::shared_ptr<Quote> fut6Price(new SimpleQuote(fut6Quote)); boost::shared_ptr<Quote> fut7Price(new SimpleQuote(fut7Quote)); boost::shared_ptr<Quote> fut8Price(new SimpleQuote(fut8Quote)); // swaps boost::shared_ptr<Quote> s2yRate(new SimpleQuote(s2yQuote)); boost::shared_ptr<Quote> s3yRate(new SimpleQuote(s3yQuote)); boost::shared_ptr<Quote> s5yRate(new SimpleQuote(s5yQuote)); boost::shared_ptr<Quote> s10yRate(new SimpleQuote(s10yQuote)); boost::shared_ptr<Quote> s15yRate(new SimpleQuote(s15yQuote)); /********************* *** RATE HELPERS *** *********************/ // RateHelpers are built from the above quotes together with // other instrument dependant infos. Quotes are passed in // relinkable handles which could be relinked to some other // data source later. // deposits DayCounter depositDayCounter = Actual360(); boost::shared_ptr<RateHelper> d1w(new DepositRateHelper( Handle<Quote>(d1wRate), 1*Weeks, fixingDays, calendar, ModifiedFollowing, true, depositDayCounter)); boost::shared_ptr<RateHelper> d1m(new DepositRateHelper( Handle<Quote>(d1mRate), 1*Months, fixingDays, calendar, ModifiedFollowing, true, depositDayCounter)); boost::shared_ptr<RateHelper> d3m(new DepositRateHelper( Handle<Quote>(d3mRate), 3*Months, fixingDays, calendar, ModifiedFollowing, true, depositDayCounter)); boost::shared_ptr<RateHelper> d6m(new DepositRateHelper( Handle<Quote>(d6mRate), 6*Months, fixingDays, calendar, ModifiedFollowing, true, depositDayCounter)); boost::shared_ptr<RateHelper> d9m(new DepositRateHelper( Handle<Quote>(d9mRate), 9*Months, fixingDays, calendar, ModifiedFollowing, true, depositDayCounter)); boost::shared_ptr<RateHelper> d1y(new DepositRateHelper( Handle<Quote>(d1yRate), 1*Years, fixingDays, calendar, ModifiedFollowing, true, depositDayCounter)); // setup FRAs boost::shared_ptr<RateHelper> fra3x6(new FraRateHelper( Handle<Quote>(fra3x6Rate), 3, 6, fixingDays, calendar, ModifiedFollowing, true, depositDayCounter)); boost::shared_ptr<RateHelper> fra6x9(new FraRateHelper( Handle<Quote>(fra6x9Rate), 6, 9, fixingDays, calendar, ModifiedFollowing, true, depositDayCounter)); boost::shared_ptr<RateHelper> fra6x12(new FraRateHelper( Handle<Quote>(fra6x12Rate), 6, 12, fixingDays, calendar, ModifiedFollowing, true, depositDayCounter)); // setup futures // Rate convexityAdjustment = 0.0; Integer futMonths = 3; Date imm = IMM::nextDate(settlementDate); boost::shared_ptr<RateHelper> fut1(new FuturesRateHelper( Handle<Quote>(fut1Price), imm, futMonths, calendar, ModifiedFollowing, true, depositDayCounter)); imm = IMM::nextDate(imm+1); boost::shared_ptr<RateHelper> fut2(new FuturesRateHelper( Handle<Quote>(fut2Price), imm, futMonths, calendar, ModifiedFollowing, true, depositDayCounter)); imm = IMM::nextDate(imm+1); boost::shared_ptr<RateHelper> fut3(new FuturesRateHelper( Handle<Quote>(fut3Price), imm, futMonths, calendar, ModifiedFollowing, true, depositDayCounter)); imm = IMM::nextDate(imm+1); boost::shared_ptr<RateHelper> fut4(new FuturesRateHelper( Handle<Quote>(fut4Price), imm, futMonths, calendar, ModifiedFollowing, true, depositDayCounter)); imm = IMM::nextDate(imm+1); boost::shared_ptr<RateHelper> fut5(new FuturesRateHelper( Handle<Quote>(fut5Price), imm, futMonths, calendar, ModifiedFollowing, true, depositDayCounter)); imm = IMM::nextDate(imm+1); boost::shared_ptr<RateHelper> fut6(new FuturesRateHelper( Handle<Quote>(fut6Price), imm, futMonths, calendar, ModifiedFollowing, true, depositDayCounter)); imm = IMM::nextDate(imm+1); boost::shared_ptr<RateHelper> fut7(new FuturesRateHelper( Handle<Quote>(fut7Price), imm, futMonths, calendar, ModifiedFollowing, true, depositDayCounter)); imm = IMM::nextDate(imm+1); boost::shared_ptr<RateHelper> fut8(new FuturesRateHelper( Handle<Quote>(fut8Price), imm, futMonths, calendar, ModifiedFollowing, true, depositDayCounter)); // setup swaps Frequency swFixedLegFrequency = Annual; BusinessDayConvention swFixedLegConvention = Unadjusted; DayCounter swFixedLegDayCounter = Thirty360(Thirty360::European); boost::shared_ptr<IborIndex> swFloatingLegIndex(new Euribor6M); boost::shared_ptr<RateHelper> s2y(new SwapRateHelper( Handle<Quote>(s2yRate), 2*Years, calendar, swFixedLegFrequency, swFixedLegConvention, swFixedLegDayCounter, swFloatingLegIndex)); boost::shared_ptr<RateHelper> s3y(new SwapRateHelper( Handle<Quote>(s3yRate), 3*Years, calendar, swFixedLegFrequency, swFixedLegConvention, swFixedLegDayCounter, swFloatingLegIndex)); boost::shared_ptr<RateHelper> s5y(new SwapRateHelper( Handle<Quote>(s5yRate), 5*Years, calendar, swFixedLegFrequency, swFixedLegConvention, swFixedLegDayCounter, swFloatingLegIndex)); boost::shared_ptr<RateHelper> s10y(new SwapRateHelper( Handle<Quote>(s10yRate), 10*Years, calendar, swFixedLegFrequency, swFixedLegConvention, swFixedLegDayCounter, swFloatingLegIndex)); boost::shared_ptr<RateHelper> s15y(new SwapRateHelper( Handle<Quote>(s15yRate), 15*Years, calendar, swFixedLegFrequency, swFixedLegConvention, swFixedLegDayCounter, swFloatingLegIndex)); /********************* ** CURVE BUILDING ** *********************/ // Any DayCounter would be fine. // ActualActual::ISDA ensures that 30 years is 30.0 DayCounter termStructureDayCounter = ActualActual(ActualActual::ISDA); double tolerance = 1.0e-15; // A depo-swap curve std::vector<boost::shared_ptr<RateHelper> > depoSwapInstruments; depoSwapInstruments.push_back(d1w); depoSwapInstruments.push_back(d1m); depoSwapInstruments.push_back(d3m); depoSwapInstruments.push_back(d6m); depoSwapInstruments.push_back(d9m); depoSwapInstruments.push_back(d1y); depoSwapInstruments.push_back(s2y); depoSwapInstruments.push_back(s3y); depoSwapInstruments.push_back(s5y); depoSwapInstruments.push_back(s10y); depoSwapInstruments.push_back(s15y); boost::shared_ptr<YieldTermStructure> depoSwapTermStructure( new PiecewiseYieldCurve<Discount,LogLinear>( settlementDate, depoSwapInstruments, termStructureDayCounter, tolerance)); // A depo-futures-swap curve std::vector<boost::shared_ptr<RateHelper> > depoFutSwapInstruments; depoFutSwapInstruments.push_back(d1w); depoFutSwapInstruments.push_back(d1m); depoFutSwapInstruments.push_back(fut1); depoFutSwapInstruments.push_back(fut2); depoFutSwapInstruments.push_back(fut3); depoFutSwapInstruments.push_back(fut4); depoFutSwapInstruments.push_back(fut5); depoFutSwapInstruments.push_back(fut6); depoFutSwapInstruments.push_back(fut7); depoFutSwapInstruments.push_back(fut8); depoFutSwapInstruments.push_back(s3y); depoFutSwapInstruments.push_back(s5y); depoFutSwapInstruments.push_back(s10y); depoFutSwapInstruments.push_back(s15y); boost::shared_ptr<YieldTermStructure> depoFutSwapTermStructure( new PiecewiseYieldCurve<Discount,LogLinear>( settlementDate, depoFutSwapInstruments, termStructureDayCounter, tolerance)); // A depo-FRA-swap curve std::vector<boost::shared_ptr<RateHelper> > depoFRASwapInstruments; depoFRASwapInstruments.push_back(d1w); depoFRASwapInstruments.push_back(d1m); depoFRASwapInstruments.push_back(d3m); depoFRASwapInstruments.push_back(fra3x6); depoFRASwapInstruments.push_back(fra6x9); depoFRASwapInstruments.push_back(fra6x12); depoFRASwapInstruments.push_back(s2y); depoFRASwapInstruments.push_back(s3y); depoFRASwapInstruments.push_back(s5y); depoFRASwapInstruments.push_back(s10y); depoFRASwapInstruments.push_back(s15y); boost::shared_ptr<YieldTermStructure> depoFRASwapTermStructure( new PiecewiseYieldCurve<Discount,LogLinear>( settlementDate, depoFRASwapInstruments, termStructureDayCounter, tolerance)); // Term structures that will be used for pricing: // the one used for discounting cash flows RelinkableHandle<YieldTermStructure> discountingTermStructure; // the one used for forward rate forecasting RelinkableHandle<YieldTermStructure> forecastingTermStructure; /********************* * SWAPS TO BE PRICED * **********************/ // constant nominal 1,000,000 Euro Real nominal = 1000000.0; // fixed leg Frequency fixedLegFrequency = Annual; BusinessDayConvention fixedLegConvention = Unadjusted; BusinessDayConvention floatingLegConvention = ModifiedFollowing; DayCounter fixedLegDayCounter = Thirty360(Thirty360::European); Rate fixedRate = 0.04; DayCounter floatingLegDayCounter = Actual360(); // floating leg Frequency floatingLegFrequency = Semiannual; boost::shared_ptr<IborIndex> euriborIndex( new Euribor6M(forecastingTermStructure)); Spread spread = 0.0; Integer lenghtInYears = 5; VanillaSwap::Type swapType = VanillaSwap::Payer; Date maturity = settlementDate + lenghtInYears*Years; Schedule fixedSchedule(settlementDate, maturity, Period(fixedLegFrequency), calendar, fixedLegConvention, fixedLegConvention, DateGeneration::Forward, false); Schedule floatSchedule(settlementDate, maturity, Period(floatingLegFrequency), calendar, floatingLegConvention, floatingLegConvention, DateGeneration::Forward, false); VanillaSwap spot5YearSwap(swapType, nominal, fixedSchedule, fixedRate, fixedLegDayCounter, floatSchedule, euriborIndex, spread, floatingLegDayCounter); Date fwdStart = calendar.advance(settlementDate, 1, Years); Date fwdMaturity = fwdStart + lenghtInYears*Years; Schedule fwdFixedSchedule(fwdStart, fwdMaturity, Period(fixedLegFrequency), calendar, fixedLegConvention, fixedLegConvention, DateGeneration::Forward, false); Schedule fwdFloatSchedule(fwdStart, fwdMaturity, Period(floatingLegFrequency), calendar, floatingLegConvention, floatingLegConvention, DateGeneration::Forward, false); VanillaSwap oneYearForward5YearSwap(swapType, nominal, fwdFixedSchedule, fixedRate, fixedLegDayCounter, fwdFloatSchedule, euriborIndex, spread, floatingLegDayCounter); /*************** * SWAP PRICING * ****************/ // utilities for reporting std::vector<std::string> headers(4); headers[0] = "term structure"; headers[1] = "net present value"; headers[2] = "fair spread"; headers[3] = "fair fixed rate"; std::string separator = " | "; Size width = headers[0].size() + separator.size() + headers[1].size() + separator.size() + headers[2].size() + separator.size() + headers[3].size() + separator.size() - 1; std::string rule(width, '-'), dblrule(width, '='); std::string tab(8, ' '); // calculations std::cout << dblrule << std::endl; std::cout << "5-year market swap-rate = " << std::setprecision(2) << io::rate(s5yRate->value()) << std::endl; std::cout << dblrule << std::endl; std::cout << tab << "5-years swap paying " << io::rate(fixedRate) << std::endl; std::cout << headers[0] << separator << headers[1] << separator << headers[2] << separator << headers[3] << separator << std::endl; std::cout << rule << std::endl; Real NPV; Rate fairRate; Spread fairSpread; boost::shared_ptr<PricingEngine> swapEngine( new DiscountingSwapEngine(discountingTermStructure)); spot5YearSwap.setPricingEngine(swapEngine); oneYearForward5YearSwap.setPricingEngine(swapEngine); // Of course, you're not forced to really use different curves forecastingTermStructure.linkTo(depoSwapTermStructure); discountingTermStructure.linkTo(depoSwapTermStructure); NPV = spot5YearSwap.NPV(); fairSpread = spot5YearSwap.fairSpread(); fairRate = spot5YearSwap.fairRate(); std::cout << std::setw(headers[0].size()) << "depo-swap" << separator; std::cout << std::setw(headers[1].size()) << std::fixed << std::setprecision(2) << NPV << separator; std::cout << std::setw(headers[2].size()) << io::rate(fairSpread) << separator; std::cout << std::setw(headers[3].size()) << io::rate(fairRate) << separator; std::cout << std::endl; // let's check that the 5 years swap has been correctly re-priced QL_REQUIRE(std::fabs(fairRate-s5yQuote)<1e-8, "5-years swap mispriced by " << io::rate(std::fabs(fairRate-s5yQuote))); forecastingTermStructure.linkTo(depoFutSwapTermStructure); discountingTermStructure.linkTo(depoFutSwapTermStructure); NPV = spot5YearSwap.NPV(); fairSpread = spot5YearSwap.fairSpread(); fairRate = spot5YearSwap.fairRate(); std::cout << std::setw(headers[0].size()) << "depo-fut-swap" << separator; std::cout << std::setw(headers[1].size()) << std::fixed << std::setprecision(2) << NPV << separator; std::cout << std::setw(headers[2].size()) << io::rate(fairSpread) << separator; std::cout << std::setw(headers[3].size()) << io::rate(fairRate) << separator; std::cout << std::endl; QL_REQUIRE(std::fabs(fairRate-s5yQuote)<1e-8, "5-years swap mispriced!"); forecastingTermStructure.linkTo(depoFRASwapTermStructure); discountingTermStructure.linkTo(depoFRASwapTermStructure); NPV = spot5YearSwap.NPV(); fairSpread = spot5YearSwap.fairSpread(); fairRate = spot5YearSwap.fairRate(); std::cout << std::setw(headers[0].size()) << "depo-FRA-swap" << separator; std::cout << std::setw(headers[1].size()) << std::fixed << std::setprecision(2) << NPV << separator; std::cout << std::setw(headers[2].size()) << io::rate(fairSpread) << separator; std::cout << std::setw(headers[3].size()) << io::rate(fairRate) << separator; std::cout << std::endl; QL_REQUIRE(std::fabs(fairRate-s5yQuote)<1e-8, "5-years swap mispriced!"); std::cout << rule << std::endl; // now let's price the 1Y forward 5Y swap std::cout << tab << "5-years, 1-year forward swap paying " << io::rate(fixedRate) << std::endl; std::cout << headers[0] << separator << headers[1] << separator << headers[2] << separator << headers[3] << separator << std::endl; std::cout << rule << std::endl; forecastingTermStructure.linkTo(depoSwapTermStructure); discountingTermStructure.linkTo(depoSwapTermStructure); NPV = oneYearForward5YearSwap.NPV(); fairSpread = oneYearForward5YearSwap.fairSpread(); fairRate = oneYearForward5YearSwap.fairRate(); std::cout << std::setw(headers[0].size()) << "depo-swap" << separator; std::cout << std::setw(headers[1].size()) << std::fixed << std::setprecision(2) << NPV << separator; std::cout << std::setw(headers[2].size()) << io::rate(fairSpread) << separator; std::cout << std::setw(headers[3].size()) << io::rate(fairRate) << separator; std::cout << std::endl; forecastingTermStructure.linkTo(depoFutSwapTermStructure); discountingTermStructure.linkTo(depoFutSwapTermStructure); NPV = oneYearForward5YearSwap.NPV(); fairSpread = oneYearForward5YearSwap.fairSpread(); fairRate = oneYearForward5YearSwap.fairRate(); std::cout << std::setw(headers[0].size()) << "depo-fut-swap" << separator; std::cout << std::setw(headers[1].size()) << std::fixed << std::setprecision(2) << NPV << separator; std::cout << std::setw(headers[2].size()) << io::rate(fairSpread) << separator; std::cout << std::setw(headers[3].size()) << io::rate(fairRate) << separator; std::cout << std::endl; forecastingTermStructure.linkTo(depoFRASwapTermStructure); discountingTermStructure.linkTo(depoFRASwapTermStructure); NPV = oneYearForward5YearSwap.NPV(); fairSpread = oneYearForward5YearSwap.fairSpread(); fairRate = oneYearForward5YearSwap.fairRate(); std::cout << std::setw(headers[0].size()) << "depo-FRA-swap" << separator; std::cout << std::setw(headers[1].size()) << std::fixed << std::setprecision(2) << NPV << separator; std::cout << std::setw(headers[2].size()) << io::rate(fairSpread) << separator; std::cout << std::setw(headers[3].size()) << io::rate(fairRate) << separator; std::cout << std::endl; // now let's say that the 5-years swap rate goes up to 4.60%. // A smarter market element--say, connected to a data source-- would // notice the change itself. Since we're using SimpleQuotes, // we'll have to change the value manually--which forces us to // downcast the handle and use the SimpleQuote // interface. In any case, the point here is that a change in the // value contained in the Quote triggers a new bootstrapping // of the curve and a repricing of the swap. boost::shared_ptr<SimpleQuote> fiveYearsRate = boost::dynamic_pointer_cast<SimpleQuote>(s5yRate); fiveYearsRate->setValue(0.0460); std::cout << dblrule << std::endl; std::cout << "5-year market swap-rate = " << io::rate(s5yRate->value()) << std::endl; std::cout << dblrule << std::endl; std::cout << tab << "5-years swap paying " << io::rate(fixedRate) << std::endl; std::cout << headers[0] << separator << headers[1] << separator << headers[2] << separator << headers[3] << separator << std::endl; std::cout << rule << std::endl; // now get the updated results forecastingTermStructure.linkTo(depoSwapTermStructure); discountingTermStructure.linkTo(depoSwapTermStructure); NPV = spot5YearSwap.NPV(); fairSpread = spot5YearSwap.fairSpread(); fairRate = spot5YearSwap.fairRate(); std::cout << std::setw(headers[0].size()) << "depo-swap" << separator; std::cout << std::setw(headers[1].size()) << std::fixed << std::setprecision(2) << NPV << separator; std::cout << std::setw(headers[2].size()) << io::rate(fairSpread) << separator; std::cout << std::setw(headers[3].size()) << io::rate(fairRate) << separator; std::cout << std::endl; QL_REQUIRE(std::fabs(fairRate-s5yRate->value())<1e-8, "5-years swap mispriced!"); forecastingTermStructure.linkTo(depoFutSwapTermStructure); discountingTermStructure.linkTo(depoFutSwapTermStructure); NPV = spot5YearSwap.NPV(); fairSpread = spot5YearSwap.fairSpread(); fairRate = spot5YearSwap.fairRate(); std::cout << std::setw(headers[0].size()) << "depo-fut-swap" << separator; std::cout << std::setw(headers[1].size()) << std::fixed << std::setprecision(2) << NPV << separator; std::cout << std::setw(headers[2].size()) << io::rate(fairSpread) << separator; std::cout << std::setw(headers[3].size()) << io::rate(fairRate) << separator; std::cout << std::endl; QL_REQUIRE(std::fabs(fairRate-s5yRate->value())<1e-8, "5-years swap mispriced!"); forecastingTermStructure.linkTo(depoFRASwapTermStructure); discountingTermStructure.linkTo(depoFRASwapTermStructure); NPV = spot5YearSwap.NPV(); fairSpread = spot5YearSwap.fairSpread(); fairRate = spot5YearSwap.fairRate(); std::cout << std::setw(headers[0].size()) << "depo-FRA-swap" << separator; std::cout << std::setw(headers[1].size()) << std::fixed << std::setprecision(2) << NPV << separator; std::cout << std::setw(headers[2].size()) << io::rate(fairSpread) << separator; std::cout << std::setw(headers[3].size()) << io::rate(fairRate) << separator; std::cout << std::endl; QL_REQUIRE(std::fabs(fairRate-s5yRate->value())<1e-8, "5-years swap mispriced!"); std::cout << rule << std::endl; // the 1Y forward 5Y swap changes as well std::cout << tab << "5-years, 1-year forward swap paying " << io::rate(fixedRate) << std::endl; std::cout << headers[0] << separator << headers[1] << separator << headers[2] << separator << headers[3] << separator << std::endl; std::cout << rule << std::endl; forecastingTermStructure.linkTo(depoSwapTermStructure); discountingTermStructure.linkTo(depoSwapTermStructure); NPV = oneYearForward5YearSwap.NPV(); fairSpread = oneYearForward5YearSwap.fairSpread(); fairRate = oneYearForward5YearSwap.fairRate(); std::cout << std::setw(headers[0].size()) << "depo-swap" << separator; std::cout << std::setw(headers[1].size()) << std::fixed << std::setprecision(2) << NPV << separator; std::cout << std::setw(headers[2].size()) << io::rate(fairSpread) << separator; std::cout << std::setw(headers[3].size()) << io::rate(fairRate) << separator; std::cout << std::endl; forecastingTermStructure.linkTo(depoFutSwapTermStructure); discountingTermStructure.linkTo(depoFutSwapTermStructure); NPV = oneYearForward5YearSwap.NPV(); fairSpread = oneYearForward5YearSwap.fairSpread(); fairRate = oneYearForward5YearSwap.fairRate(); std::cout << std::setw(headers[0].size()) << "depo-fut-swap" << separator; std::cout << std::setw(headers[1].size()) << std::fixed << std::setprecision(2) << NPV << separator; std::cout << std::setw(headers[2].size()) << io::rate(fairSpread) << separator; std::cout << std::setw(headers[3].size()) << io::rate(fairRate) << separator; std::cout << std::endl; forecastingTermStructure.linkTo(depoFRASwapTermStructure); discountingTermStructure.linkTo(depoFRASwapTermStructure); NPV = oneYearForward5YearSwap.NPV(); fairSpread = oneYearForward5YearSwap.fairSpread(); fairRate = oneYearForward5YearSwap.fairRate(); std::cout << std::setw(headers[0].size()) << "depo-FRA-swap" << separator; std::cout << std::setw(headers[1].size()) << std::fixed << std::setprecision(2) << NPV << separator; std::cout << std::setw(headers[2].size()) << io::rate(fairSpread) << separator; std::cout << std::setw(headers[3].size()) << io::rate(fairRate) << separator; std::cout << std::endl; Real seconds = timer.elapsed(); Integer hours = int(seconds/3600); seconds -= hours * 3600; Integer minutes = int(seconds/60); seconds -= minutes * 60; std::cout << " \nRun completed in "; if (hours > 0) std::cout << hours << " h "; if (hours > 0 || minutes > 0) std::cout << minutes << " m "; std::cout << std::fixed << std::setprecision(0) << seconds << " s\n" << std::endl; } catch (std::exception& e) { std::cerr << e.what() << std::endl; } catch (...) { std::cerr << "unknown error" << std::endl; } }
void InflationTest::testZeroTermStructure() { BOOST_MESSAGE("Testing zero inflation term structure..."); SavedSettings backup; // try the Zero UK Calendar calendar = UnitedKingdom(); BusinessDayConvention bdc = ModifiedFollowing; Date evaluationDate(13, August, 2007); evaluationDate = calendar.adjust(evaluationDate); Settings::instance().evaluationDate() = evaluationDate; // fixing data Date from(1, January, 2005); Date to(13, August, 2007); Schedule rpiSchedule = MakeSchedule().from(from).to(to) .withTenor(1*Months) .withCalendar(UnitedKingdom()) .withConvention(ModifiedFollowing); Real fixData[] = { 189.9, 189.9, 189.6, 190.5, 191.6, 192.0, 192.2, 192.2, 192.6, 193.1, 193.3, 193.6, 194.1, 193.4, 194.2, 195.0, 196.5, 197.7, 198.5, 198.5, 199.2, 200.1, 200.4, 201.1, 202.7, 201.6, 203.1, 204.4, 205.4, 206.2, 207.3, 206.1, -999.0 }; RelinkableHandle<ZeroInflationTermStructure> hz; bool interp = false; boost::shared_ptr<UKRPI> iiUKRPI(new UKRPI(interp, hz)); for (Size i=0; i<rpiSchedule.size();i++) { iiUKRPI->addFixing(rpiSchedule[i], fixData[i]); } boost::shared_ptr<ZeroInflationIndex> ii = boost::dynamic_pointer_cast<ZeroInflationIndex>(iiUKRPI); boost::shared_ptr<YieldTermStructure> nominalTS = nominalTermStructure(); // now build the zero inflation curve Datum zcData[] = { { Date(13, August, 2008), 2.93 }, { Date(13, August, 2009), 2.95 }, { Date(13, August, 2010), 2.965 }, { Date(15, August, 2011), 2.98 }, { Date(13, August, 2012), 3.0 }, { Date(13, August, 2014), 3.06 }, { Date(13, August, 2017), 3.175 }, { Date(13, August, 2019), 3.243 }, { Date(15, August, 2022), 3.293 }, { Date(14, August, 2027), 3.338 }, { Date(13, August, 2032), 3.348 }, { Date(15, August, 2037), 3.348 }, { Date(13, August, 2047), 3.308 }, { Date(13, August, 2057), 3.228 } }; Period observationLag = Period(2,Months); DayCounter dc = Thirty360(); Frequency frequency = Monthly; std::vector<boost::shared_ptr<BootstrapHelper<ZeroInflationTermStructure> > > helpers = makeHelpers<ZeroInflationTermStructure,ZeroCouponInflationSwapHelper, ZeroInflationIndex>(zcData, LENGTH(zcData), ii, observationLag, calendar, bdc, dc); Rate baseZeroRate = zcData[0].rate/100.0; boost::shared_ptr<PiecewiseZeroInflationCurve<Linear> > pZITS( new PiecewiseZeroInflationCurve<Linear>( evaluationDate, calendar, dc, observationLag, frequency, ii->interpolated(), baseZeroRate, Handle<YieldTermStructure>(nominalTS), helpers)); pZITS->recalculate(); // first check that the zero rates on the curve match the data // and that the helpers give the correct impled rates const Real eps = 0.00000001; bool forceLinearInterpolation = false; for (Size i=0; i<LENGTH(zcData); i++) { BOOST_REQUIRE_MESSAGE(std::fabs(zcData[i].rate/100.0 - pZITS->zeroRate(zcData[i].date, observationLag, forceLinearInterpolation)) < eps, "ZITS zeroRate != instrument " << pZITS->zeroRate(zcData[i].date, observationLag, forceLinearInterpolation) << " vs " << zcData[i].rate/100.0 << " interpolation: " << ii->interpolated() << " forceLinearInterpolation " << forceLinearInterpolation); BOOST_REQUIRE_MESSAGE(std::fabs(helpers[i]->impliedQuote() - zcData[i].rate/100.0) < eps, "ZITS implied quote != instrument " << helpers[i]->impliedQuote() << " vs " << zcData[i].rate/100.0); } // now test the forecasting capability of the index. hz.linkTo(pZITS); from = hz->baseDate(); to = hz->maxDate()-1*Months; // a bit of margin for adjustments Schedule testIndex = MakeSchedule().from(from).to(to) .withTenor(1*Months) .withCalendar(UnitedKingdom()) .withConvention(ModifiedFollowing); // we are testing UKRPI which is not interpolated Date bd = hz->baseDate(); Real bf = ii->fixing(bd); for (Size i=0; i<testIndex.size();i++) { Date d = testIndex[i]; Real z = hz->zeroRate(d, Period(0,Days)); Real t = hz->dayCounter().yearFraction(bd, d); if(!ii->interpolated()) // because fixing constant over period t = hz->dayCounter().yearFraction(bd, inflationPeriod(d, ii->frequency()).first); Real calc = bf * pow( 1+z, t); if (t<=0) calc = ii->fixing(d,false); // still historical if (std::fabs(calc - ii->fixing(d,true))/10000.0 > eps) BOOST_ERROR("ZC index does not forecast correctly for date " << d << " from base date " << bd << " with fixing " << bf << ", correct: " << calc << ", fix: " << ii->fixing(d,true) << ", t " << t); } //=========================================================================================== // Test zero-inflation-indexed (i.e. cpi ratio) cashflow // just ordinary indexed cashflow with a zero inflation index Date baseDate(1, January, 2006); Date fixDate(1, August, 2014); Date payDate=UnitedKingdom().adjust(fixDate+Period(3,Months),ModifiedFollowing); boost::shared_ptr<Index> ind = boost::dynamic_pointer_cast<Index>(ii); BOOST_REQUIRE_MESSAGE(ind,"dynamic_pointer_cast to Index from InflationIndex failed"); Real notional = 1000000.0;//1m IndexedCashFlow iicf(notional,ind,baseDate,fixDate,payDate); Real correctIndexed = ii->fixing(iicf.fixingDate())/ii->fixing(iicf.baseDate()); Real calculatedIndexed = iicf.amount()/iicf.notional(); BOOST_REQUIRE_MESSAGE(std::fabs(correctIndexed - calculatedIndexed) < eps, "IndexedCashFlow indexing wrong: " << calculatedIndexed << " vs correct = " << correctIndexed); //=========================================================================================== // Test zero coupon swap // first make one ... boost::shared_ptr<ZeroInflationIndex> zii = boost::dynamic_pointer_cast<ZeroInflationIndex>(ii); BOOST_REQUIRE_MESSAGE(zii,"dynamic_pointer_cast to ZeroInflationIndex from UKRPI failed"); ZeroCouponInflationSwap nzcis(ZeroCouponInflationSwap::Payer, 1000000.0, evaluationDate, zcData[6].date, // end date = maturity calendar, bdc, dc, zcData[6].rate/100.0, // fixed rate zii, observationLag); // N.B. no coupon pricer because it is not a coupon, effect of inflation curve via // inflation curve attached to the inflation index. Handle<YieldTermStructure> hTS(nominalTS); boost::shared_ptr<PricingEngine> sppe(new DiscountingSwapEngine(hTS)); nzcis.setPricingEngine(sppe); // ... and price it, should be zero BOOST_CHECK_MESSAGE(fabs(nzcis.NPV())<0.00001,"ZCIS does not reprice to zero " << nzcis.NPV() << evaluationDate << " to " << zcData[6].date << " becoming " << nzcis.maturityDate() << " rate " << zcData[6].rate << " fixed leg " << nzcis.legNPV(0) << " indexed-predicted inflated leg " << nzcis.legNPV(1) << " discount " << nominalTS->discount(nzcis.maturityDate()) ); //=========================================================================================== // Test multiplicative seasonality in price // //Seasonality factors NOT normalized //and UKRPI is not interpolated Date trueBaseDate = inflationPeriod(hz->baseDate(), ii->frequency()).second; Date seasonallityBaseDate(31,January,trueBaseDate.year()); std::vector<Rate> seasonalityFactors(12); seasonalityFactors[0] = 1.003245; seasonalityFactors[1] = 1.000000; seasonalityFactors[2] = 0.999715; seasonalityFactors[3] = 1.000495; seasonalityFactors[4] = 1.000929; seasonalityFactors[5] = 0.998687; seasonalityFactors[6] = 0.995949; seasonalityFactors[7] = 0.994682; seasonalityFactors[8] = 0.995949; seasonalityFactors[9] = 1.000519; seasonalityFactors[10] = 1.003705; seasonalityFactors[11] = 1.004186; //Creating two different seasonality objects // boost::shared_ptr<MultiplicativePriceSeasonality> seasonality_1(new MultiplicativePriceSeasonality()); std::vector<Rate> seasonalityFactors_1(12, 1.0); seasonality_1->set(seasonallityBaseDate,Monthly,seasonalityFactors_1); boost::shared_ptr<MultiplicativePriceSeasonality> seasonality_real( new MultiplicativePriceSeasonality(seasonallityBaseDate,Monthly,seasonalityFactors)); //Testing seasonality correction when seasonality factors are = 1 // Rate fixing[] = { ii->fixing(Date(14,January ,2013),true), ii->fixing(Date(14,February ,2013),true), ii->fixing(Date(14,March ,2013),true), ii->fixing(Date(14,April ,2013),true), ii->fixing(Date(14,May ,2013),true), ii->fixing(Date(14,June ,2013),true), ii->fixing(Date(14,July ,2013),true), ii->fixing(Date(14,August ,2013),true), ii->fixing(Date(14,September,2013),true), ii->fixing(Date(14,October ,2013),true), ii->fixing(Date(14,November ,2013),true), ii->fixing(Date(14,December ,2013),true) }; hz->setSeasonality(seasonality_1); QL_REQUIRE(hz->hasSeasonality(),"[44] incorrectly believes NO seasonality correction"); Rate seasonalityFixing_1[] = { ii->fixing(Date(14,January ,2013),true), ii->fixing(Date(14,February ,2013),true), ii->fixing(Date(14,March ,2013),true), ii->fixing(Date(14,April ,2013),true), ii->fixing(Date(14,May ,2013),true), ii->fixing(Date(14,June ,2013),true), ii->fixing(Date(14,July ,2013),true), ii->fixing(Date(14,August ,2013),true), ii->fixing(Date(14,September,2013),true), ii->fixing(Date(14,October ,2013),true), ii->fixing(Date(14,November ,2013),true), ii->fixing(Date(14,December ,2013),true) }; for(int i=0;i<12;i++){ if(std::fabs(fixing[i] - seasonalityFixing_1[i]) > eps) { BOOST_ERROR("Seasonality doesn't work correctly when seasonality factors are set = 1"); } } //Testing seasonality correction when seasonality factors are different from 1 // //0.998687 is the seasonality factor corresponding to June (the base CPI curve month) // Rate expectedFixing[] = { ii->fixing(Date(14,January ,2013),true) * 1.003245/0.998687, ii->fixing(Date(14,February ,2013),true) * 1.000000/0.998687, ii->fixing(Date(14,March ,2013),true) * 0.999715/0.998687, ii->fixing(Date(14,April ,2013),true) * 1.000495/0.998687, ii->fixing(Date(14,May ,2013),true) * 1.000929/0.998687, ii->fixing(Date(14,June ,2013),true) * 0.998687/0.998687, ii->fixing(Date(14,July ,2013),true) * 0.995949/0.998687, ii->fixing(Date(14,August ,2013),true) * 0.994682/0.998687, ii->fixing(Date(14,September,2013),true) * 0.995949/0.998687, ii->fixing(Date(14,October ,2013),true) * 1.000519/0.998687, ii->fixing(Date(14,November ,2013),true) * 1.003705/0.998687, ii->fixing(Date(14,December ,2013),true) * 1.004186/0.998687 }; hz->setSeasonality(seasonality_real); Rate seasonalityFixing_real[] = { ii->fixing(Date(14,January ,2013),true), ii->fixing(Date(14,February ,2013),true), ii->fixing(Date(14,March ,2013),true), ii->fixing(Date(14,April ,2013),true), ii->fixing(Date(14,May ,2013),true), ii->fixing(Date(14,June ,2013),true), ii->fixing(Date(14,July ,2013),true), ii->fixing(Date(14,August ,2013),true), ii->fixing(Date(14,September,2013),true), ii->fixing(Date(14,October ,2013),true), ii->fixing(Date(14,November ,2013),true), ii->fixing(Date(14,December ,2013),true) }; for(int i=0;i<12;i++){ if(std::fabs(expectedFixing[i] - seasonalityFixing_real[i]) > 0.01) { BOOST_ERROR("Seasonality doesn't work correctly when considering seasonality factors != 1 " << expectedFixing[i] << " vs " << seasonalityFixing_real[i]); } } //Testing Unset function // QL_REQUIRE(hz->hasSeasonality(),"[4] incorrectly believes NO seasonality correction"); hz->setSeasonality(); QL_REQUIRE(!hz->hasSeasonality(),"[5] incorrectly believes HAS seasonality correction"); Rate seasonalityFixing_unset[] = { ii->fixing(Date(14,January ,2013),true), ii->fixing(Date(14,February ,2013),true), ii->fixing(Date(14,March ,2013),true), ii->fixing(Date(14,April ,2013),true), ii->fixing(Date(14,May ,2013),true), ii->fixing(Date(14,June ,2013),true), ii->fixing(Date(14,July ,2013),true), ii->fixing(Date(14,August ,2013),true), ii->fixing(Date(14,September,2013),true), ii->fixing(Date(14,October ,2013),true), ii->fixing(Date(14,November ,2013),true), ii->fixing(Date(14,December ,2013),true) }; for(int i=0;i<12;i++){ if(std::fabs(seasonalityFixing_unset[i] - seasonalityFixing_1[i]) > eps) { BOOST_ERROR("UnsetSeasonality doesn't work correctly " << seasonalityFixing_unset[i] << " vs " << seasonalityFixing_1[i]); } } //============================================================================== // now do an INTERPOLATED index, i.e. repeat everything on a fake version of // UKRPI (to save making another term structure) bool interpYES = true; boost::shared_ptr<UKRPI> iiUKRPIyes(new UKRPI(interpYES, hz)); for (Size i=0; i<rpiSchedule.size();i++) { iiUKRPIyes->addFixing(rpiSchedule[i], fixData[i]); } boost::shared_ptr<ZeroInflationIndex> iiyes = boost::dynamic_pointer_cast<ZeroInflationIndex>(iiUKRPIyes); // now build the zero inflation curve // same data, bigger lag or it will be a self-contradiction Period observationLagyes = Period(3,Months); std::vector<boost::shared_ptr<BootstrapHelper<ZeroInflationTermStructure> > > helpersyes = makeHelpers<ZeroInflationTermStructure,ZeroCouponInflationSwapHelper, ZeroInflationIndex>(zcData, LENGTH(zcData), iiyes, observationLagyes, calendar, bdc, dc); boost::shared_ptr<PiecewiseZeroInflationCurve<Linear> > pZITSyes( new PiecewiseZeroInflationCurve<Linear>( evaluationDate, calendar, dc, observationLagyes, frequency, iiyes->interpolated(), baseZeroRate, Handle<YieldTermStructure>(nominalTS), helpersyes)); pZITSyes->recalculate(); // first check that the zero rates on the curve match the data // and that the helpers give the correct impled rates forceLinearInterpolation = false; // still for (Size i=0; i<LENGTH(zcData); i++) { BOOST_CHECK_MESSAGE(std::fabs(zcData[i].rate/100.0 - pZITSyes->zeroRate(zcData[i].date, observationLagyes, forceLinearInterpolation)) < eps, "ZITS INTERPOLATED zeroRate != instrument " << pZITSyes->zeroRate(zcData[i].date, observationLagyes, forceLinearInterpolation) << " date " << zcData[i].date << " observationLagyes " << observationLagyes << " vs " << zcData[i].rate/100.0 << " interpolation: " << iiyes->interpolated() << " forceLinearInterpolation " << forceLinearInterpolation); BOOST_CHECK_MESSAGE(std::fabs(helpersyes[i]->impliedQuote() - zcData[i].rate/100.0) < eps, "ZITS INTERPOLATED implied quote != instrument " << helpersyes[i]->impliedQuote() << " vs " << zcData[i].rate/100.0); } //====================================================================================== // now test the forecasting capability of the index. hz.linkTo(pZITSyes); from = hz->baseDate()+1*Months; // to avoid historical linear bit for rest of base month to = hz->maxDate()-1*Months; // a bit of margin for adjustments testIndex = MakeSchedule().from(from).to(to) .withTenor(1*Months) .withCalendar(UnitedKingdom()) .withConvention(ModifiedFollowing); // we are testing UKRPI which is FAKE interpolated for testing here bd = hz->baseDate(); bf = iiyes->fixing(bd); for (Size i=0; i<testIndex.size();i++) { Date d = testIndex[i]; Real z = hz->zeroRate(d, Period(0,Days)); Real t = hz->dayCounter().yearFraction(bd, d); Real calc = bf * pow( 1+z, t); if (t<=0) calc = iiyes->fixing(d); // still historical if (std::fabs(calc - iiyes->fixing(d)) > eps) BOOST_ERROR("ZC INTERPOLATED index does not forecast correctly for date " << d << " from base date " << bd << " with fixing " << bf << ", correct: " << calc << ", fix: " << iiyes->fixing(d) << ", t " << t << ", zero " << z); } //=========================================================================================== // Test zero coupon swap boost::shared_ptr<ZeroInflationIndex> ziiyes = boost::dynamic_pointer_cast<ZeroInflationIndex>(iiyes); BOOST_REQUIRE_MESSAGE(ziiyes,"dynamic_pointer_cast to ZeroInflationIndex from UKRPI-I failed"); ZeroCouponInflationSwap nzcisyes(ZeroCouponInflationSwap::Payer, 1000000.0, evaluationDate, zcData[6].date, // end date = maturity calendar, bdc, dc, zcData[6].rate/100.0, // fixed rate ziiyes, observationLagyes); // N.B. no coupon pricer because it is not a coupon, effect of inflation curve via // inflation curve attached to the inflation index. nzcisyes.setPricingEngine(sppe); // ... and price it, should be zero BOOST_CHECK_MESSAGE(fabs(nzcisyes.NPV())<0.00001,"ZCIS-I does not reprice to zero " << nzcisyes.NPV() << evaluationDate << " to " << zcData[6].date << " becoming " << nzcisyes.maturityDate() << " rate " << zcData[6].rate << " fixed leg " << nzcisyes.legNPV(0) << " indexed-predicted inflated leg " << nzcisyes.legNPV(1) << " discount " << nominalTS->discount(nzcisyes.maturityDate()) ); }
/* Generally inflation indices are available with a lag of 1month and then observed with a lag of 2-3 months depending whether they use an interpolated fixing or not. Here, we make the swap use the interpolation of the index to avoid incompatibilities. */ ZeroCouponInflationSwap::ZeroCouponInflationSwap( Type type, Real nominal, const Date& startDate, // start date of contract (only) const Date& maturity, // this is pre-adjustment! const Calendar& fixCalendar, BusinessDayConvention fixConvention, const DayCounter& dayCounter, Rate fixedRate, const boost::shared_ptr<ZeroInflationIndex> &infIndex, const Period& observationLag, bool adjustInfObsDates, Calendar infCalendar, BusinessDayConvention infConvention) : Swap(2), type_(type), nominal_(nominal), fixedRate_(fixedRate), infIndex_(infIndex), observationLag_(observationLag), dayCounter_(dayCounter) { // first check compatibility of index and swap definitions if (infIndex_->interpolated()) { Period pShift(infIndex_->frequency()); QL_REQUIRE(observationLag_ - pShift > infIndex_->availabilityLag(), "inconsistency between swap observation of index " << observationLag_ << " index availability " << infIndex_->availabilityLag() << " interpolated index period " << pShift << " and index availability " << infIndex_->availabilityLag() << " need (obsLag-index period) > availLag"); } else { QL_REQUIRE(infIndex_->availabilityLag() < observationLag_, "index tries to observe inflation fixings that do not yet exist: " << " availability lag " << infIndex_->availabilityLag() << " versus obs lag = " << observationLag_); } if (infCalendar==Calendar()) infCalendar = fixCalendar; if (infConvention==BusinessDayConvention()) infConvention = fixConvention; if (adjustInfObsDates) { baseDate_ = infCalendar.adjust(startDate - observationLag_, infConvention); obsDate_ = infCalendar.adjust(maturity - observationLag_, infConvention); } else { baseDate_ = startDate - observationLag_; obsDate_ = maturity - observationLag_; } Date infPayDate = infCalendar.adjust(maturity, infConvention); Date fixedPayDate = fixCalendar.adjust(maturity, fixConvention); // At this point the index may not be able to forecast // i.e. do not want to force the existence of an inflation // term structure before allowing users to create instruments. Real T = inflationYearFraction(infIndex_->frequency(), infIndex_->interpolated(), dayCounter_, baseDate_, obsDate_); // N.B. the -1.0 is because swaps only exchange growth, not notionals as well Real fixedAmount = nominal * ( std::pow(1.0 + fixedRate, T) - 1.0 ); legs_[0].push_back(boost::shared_ptr<CashFlow>( new SimpleCashFlow(fixedAmount, fixedPayDate))); bool growthOnly = true; legs_[1].push_back(boost::shared_ptr<CashFlow>( new IndexedCashFlow(nominal,infIndex,baseDate_,obsDate_,infPayDate,growthOnly))); for (Size j=0; j<2; ++j) { for (Leg::iterator i = legs_[j].begin(); i!= legs_[j].end(); ++i) registerWith(*i); } switch (type_) { case Payer: payer_[0] = +1.0; payer_[1] = -1.0; break; case Receiver: payer_[0] = -1.0; payer_[1] = +1.0; break; default: QL_FAIL("Unknown zero-inflation-swap type"); } }
CPILeg::operator Leg() const { QL_REQUIRE(!notionals_.empty(), "no notional given"); Size n = schedule_.size()-1; Calendar calendar = schedule_.calendar(); Leg leg; leg.reserve(n+1); // +1 for notional, we always have some sort ... if (n>0) { QL_REQUIRE(!fixedRates_.empty() || !spreads_.empty(), "no fixedRates or spreads given"); Date refStart, start, refEnd, end; for (Size i=0; i<n; ++i) { refStart = start = schedule_.date(i); refEnd = end = schedule_.date(i+1); Date paymentDate = calendar.adjust(end, paymentAdjustment_); if (i==0 && !schedule_.isRegular(i+1)) { BusinessDayConvention bdc = schedule_.businessDayConvention(); refStart = schedule_.calendar().adjust(end - schedule_.tenor(), bdc); } if (i==n-1 && !schedule_.isRegular(i+1)) { BusinessDayConvention bdc = schedule_.businessDayConvention(); refEnd = schedule_.calendar().adjust(start + schedule_.tenor(), bdc); } if (detail::get(fixedRates_, i, 1.0) == 0.0) { // fixed coupon leg.push_back(boost::shared_ptr<CashFlow> (new FixedRateCoupon (paymentDate, detail::get(notionals_, i, 0.0), detail::effectiveFixedRate(spreads_,caps_,floors_,i), paymentDayCounter_, start, end, refStart, refEnd))); } else { // zero inflation coupon if (detail::noOption(caps_, floors_, i)) { // just swaplet boost::shared_ptr<CPICoupon> coup; coup = boost::shared_ptr<CPICoupon> (new CPICoupon(baseCPI_, // all have same base for ratio paymentDate, detail::get(notionals_, i, 0.0), start, end, detail::get(fixingDays_, i, 0.0), index_, observationLag_, observationInterpolation_, paymentDayCounter_, detail::get(fixedRates_, i, 0.0), detail::get(spreads_, i, 0.0), refStart, refEnd)); // in this case you can set a pricer // straight away because it only provides computation - not data boost::shared_ptr<CPICouponPricer> pricer (new CPICouponPricer); coup->setPricer(pricer); leg.push_back(boost::dynamic_pointer_cast<CashFlow>(coup)); } else { // cap/floorlet QL_FAIL("caps/floors on CPI coupons not implemented."); } } } } // in CPI legs you always have a notional flow of some sort Date paymentDate = calendar.adjust(schedule_.date(n), paymentAdjustment_); Date fixingDate = paymentDate - observationLag_; boost::shared_ptr<CashFlow> xnl(new CPICashFlow (detail::get(notionals_, n, 0.0), index_, Date(), // is fake, i.e. you do not have one baseCPI_, fixingDate, paymentDate, subtractInflationNominal_, observationInterpolation_, index_->frequency()) ); leg.push_back(xnl); return leg; }
void CreditDefaultSwapTest::testImpliedHazardRate() { BOOST_TEST_MESSAGE("Testing implied hazard-rate for credit-default swaps..."); SavedSettings backup; // Initialize curves Calendar calendar = TARGET(); Date today = calendar.adjust(Date::todaysDate()); Settings::instance().evaluationDate() = today; Rate h1 = 0.30, h2 = 0.40; DayCounter dayCounter = Actual365Fixed(); std::vector<Date> dates(3); std::vector<Real> hazardRates(3); dates[0] = today; hazardRates[0] = h1; dates[1] = today + 5*Years; hazardRates[1] = h1; dates[2] = today + 10*Years; hazardRates[2] = h2; RelinkableHandle<DefaultProbabilityTermStructure> probabilityCurve; probabilityCurve.linkTo(ext::shared_ptr<DefaultProbabilityTermStructure>( new InterpolatedHazardRateCurve<BackwardFlat>(dates, hazardRates, dayCounter))); RelinkableHandle<YieldTermStructure> discountCurve; discountCurve.linkTo(ext::shared_ptr<YieldTermStructure>( new FlatForward(today,0.03,Actual360()))); Frequency frequency = Semiannual; BusinessDayConvention convention = ModifiedFollowing; Date issueDate = calendar.advance(today, -6, Months); Rate fixedRate = 0.0120; DayCounter cdsDayCount = Actual360(); Real notional = 10000.0; Real recoveryRate = 0.4; Rate latestRate = Null<Rate>(); for (Integer n=6; n<=10; ++n) { Date maturity = calendar.advance(issueDate, n, Years); Schedule schedule(issueDate, maturity, Period(frequency), calendar, convention, convention, DateGeneration::Forward, false); CreditDefaultSwap cds(Protection::Seller, notional, fixedRate, schedule, convention, cdsDayCount, true, true); cds.setPricingEngine(ext::shared_ptr<PricingEngine>( new MidPointCdsEngine(probabilityCurve, recoveryRate, discountCurve))); Real NPV = cds.NPV(); Rate flatRate = cds.impliedHazardRate(NPV, discountCurve, dayCounter, recoveryRate); if (flatRate < h1 || flatRate > h2) { BOOST_ERROR("implied hazard rate outside expected range\n" << " maturity: " << n << " years\n" << " expected minimum: " << h1 << "\n" << " expected maximum: " << h2 << "\n" << " implied rate: " << flatRate); } if (n > 6 && flatRate < latestRate) { BOOST_ERROR("implied hazard rate decreasing with swap maturity\n" << " maturity: " << n << " years\n" << " previous rate: " << latestRate << "\n" << " implied rate: " << flatRate); } latestRate = flatRate; RelinkableHandle<DefaultProbabilityTermStructure> probability; probability.linkTo(ext::shared_ptr<DefaultProbabilityTermStructure>( new FlatHazardRate( today, Handle<Quote>(ext::shared_ptr<Quote>(new SimpleQuote(flatRate))), dayCounter))); CreditDefaultSwap cds2(Protection::Seller, notional, fixedRate, schedule, convention, cdsDayCount, true, true); cds2.setPricingEngine(ext::shared_ptr<PricingEngine>( new MidPointCdsEngine(probability,recoveryRate, discountCurve))); Real NPV2 = cds2.NPV(); Real tolerance = 1.0; if (std::fabs(NPV-NPV2) > tolerance) { BOOST_ERROR("failed to reproduce NPV with implied rate\n" << " expected: " << NPV << "\n" << " calculated: " << NPV2); } } }