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()) ); }
int main(int, char* []) { try { boost::timer timer; std::cout << std::endl; /********************* *** MARKET DATA *** *********************/ RelinkableHandle<YieldTermStructure> euriborTermStructure; boost::shared_ptr<IborIndex> euribor3m( new Euribor3M(euriborTermStructure)); Date todaysDate = Date(23, May, 2006); Settings::instance().evaluationDate() = todaysDate; Calendar calendar = euribor3m->fixingCalendar(); Integer fixingDays = euribor3m->fixingDays(); Date settlementDate = calendar.advance(todaysDate, fixingDays, Days); std::cout << "Today: " << todaysDate.weekday() << ", " << todaysDate << std::endl; std::cout << "Settlement date: " << settlementDate.weekday() << ", " << settlementDate << std::endl; // 3 month term FRA quotes (index refers to monthsToStart) Rate threeMonthFraQuote[10]; threeMonthFraQuote[1]=0.030; threeMonthFraQuote[2]=0.031; threeMonthFraQuote[3]=0.032; threeMonthFraQuote[6]=0.033; threeMonthFraQuote[9]=0.034; /******************** *** 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. // FRAs boost::shared_ptr<SimpleQuote> fra1x4Rate( new SimpleQuote(threeMonthFraQuote[1])); boost::shared_ptr<SimpleQuote> fra2x5Rate( new SimpleQuote(threeMonthFraQuote[2])); boost::shared_ptr<SimpleQuote> fra3x6Rate( new SimpleQuote(threeMonthFraQuote[3])); boost::shared_ptr<SimpleQuote> fra6x9Rate( new SimpleQuote(threeMonthFraQuote[6])); boost::shared_ptr<SimpleQuote> fra9x12Rate( new SimpleQuote(threeMonthFraQuote[9])); RelinkableHandle<Quote> h1x4; h1x4.linkTo(fra1x4Rate); RelinkableHandle<Quote> h2x5; h2x5.linkTo(fra2x5Rate); RelinkableHandle<Quote> h3x6; h3x6.linkTo(fra3x6Rate); RelinkableHandle<Quote> h6x9; h6x9.linkTo(fra6x9Rate); RelinkableHandle<Quote> h9x12; h9x12.linkTo(fra9x12Rate); /********************* *** 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. DayCounter fraDayCounter = euribor3m->dayCounter(); BusinessDayConvention convention = euribor3m->businessDayConvention(); bool endOfMonth = euribor3m->endOfMonth(); boost::shared_ptr<RateHelper> fra1x4( new FraRateHelper(h1x4, 1, 4, fixingDays, calendar, convention, endOfMonth, fraDayCounter)); boost::shared_ptr<RateHelper> fra2x5( new FraRateHelper(h2x5, 2, 5, fixingDays, calendar, convention, endOfMonth, fraDayCounter)); boost::shared_ptr<RateHelper> fra3x6( new FraRateHelper(h3x6, 3, 6, fixingDays, calendar, convention, endOfMonth, fraDayCounter)); boost::shared_ptr<RateHelper> fra6x9( new FraRateHelper(h6x9, 6, 9, fixingDays, calendar, convention, endOfMonth, fraDayCounter)); boost::shared_ptr<RateHelper> fra9x12( new FraRateHelper(h9x12, 9, 12, fixingDays, calendar, convention, endOfMonth, fraDayCounter)); /********************* ** 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 FRA curve std::vector<boost::shared_ptr<RateHelper> > fraInstruments; fraInstruments.push_back(fra1x4); fraInstruments.push_back(fra2x5); fraInstruments.push_back(fra3x6); fraInstruments.push_back(fra6x9); fraInstruments.push_back(fra9x12); boost::shared_ptr<YieldTermStructure> fraTermStructure( new PiecewiseYieldCurve<Discount,LogLinear>( settlementDate, fraInstruments, termStructureDayCounter, tolerance)); // Term structures used for pricing/discounting RelinkableHandle<YieldTermStructure> discountingTermStructure; discountingTermStructure.linkTo(fraTermStructure); /*********************** *** construct FRA's *** ***********************/ Calendar fraCalendar = euribor3m->fixingCalendar(); BusinessDayConvention fraBusinessDayConvention = euribor3m->businessDayConvention(); Position::Type fraFwdType = Position::Long; Real fraNotional = 100.0; const Integer FraTermMonths = 3; Integer monthsToStart[] = { 1, 2, 3, 6, 9 }; euriborTermStructure.linkTo(fraTermStructure); cout << endl; cout << "Test FRA construction, NPV calculation, and FRA purchase" << endl << endl; Size i; for (i=0; i<LENGTH(monthsToStart); i++) { Date fraValueDate = fraCalendar.advance( settlementDate,monthsToStart[i],Months, fraBusinessDayConvention); Date fraMaturityDate = fraCalendar.advance( fraValueDate,FraTermMonths,Months, fraBusinessDayConvention); Rate fraStrikeRate = threeMonthFraQuote[monthsToStart[i]]; ForwardRateAgreement myFRA(fraValueDate, fraMaturityDate, fraFwdType,fraStrikeRate, fraNotional, euribor3m, discountingTermStructure); cout << "3m Term FRA, Months to Start: " << monthsToStart[i] << endl; cout << "strike FRA rate: " << io::rate(fraStrikeRate) << endl; cout << "FRA 3m forward rate: " << myFRA.forwardRate() << endl; cout << "FRA market quote: " << io::rate(threeMonthFraQuote[monthsToStart[i]]) << endl; cout << "FRA spot value: " << myFRA.spotValue() << endl; cout << "FRA forward value: " << myFRA.forwardValue() << endl; cout << "FRA implied Yield: " << myFRA.impliedYield(myFRA.spotValue(), myFRA.forwardValue(), settlementDate, Simple, fraDayCounter) << endl; cout << "market Zero Rate: " << discountingTermStructure->zeroRate(fraMaturityDate, fraDayCounter, Simple) << endl; cout << "FRA NPV [should be zero]: " << myFRA.NPV() << endl << endl; } cout << endl << endl; cout << "Now take a 100 basis-point upward shift in FRA quotes " << "and examine NPV" << endl << endl; const Real BpsShift = 0.01; threeMonthFraQuote[1]=0.030+BpsShift; threeMonthFraQuote[2]=0.031+BpsShift; threeMonthFraQuote[3]=0.032+BpsShift; threeMonthFraQuote[6]=0.033+BpsShift; threeMonthFraQuote[9]=0.034+BpsShift; fra1x4Rate->setValue(threeMonthFraQuote[1]); fra2x5Rate->setValue(threeMonthFraQuote[2]); fra3x6Rate->setValue(threeMonthFraQuote[3]); fra6x9Rate->setValue(threeMonthFraQuote[6]); fra9x12Rate->setValue(threeMonthFraQuote[9]); for (i=0; i<LENGTH(monthsToStart); i++) { Date fraValueDate = fraCalendar.advance( settlementDate,monthsToStart[i],Months, fraBusinessDayConvention); Date fraMaturityDate = fraCalendar.advance( fraValueDate,FraTermMonths,Months, fraBusinessDayConvention); Rate fraStrikeRate = threeMonthFraQuote[monthsToStart[i]] - BpsShift; ForwardRateAgreement myFRA(fraValueDate, fraMaturityDate, fraFwdType, fraStrikeRate, fraNotional, euribor3m, discountingTermStructure); cout << "3m Term FRA, 100 notional, Months to Start = " << monthsToStart[i] << endl; cout << "strike FRA rate: " << io::rate(fraStrikeRate) << endl; cout << "FRA 3m forward rate: " << myFRA.forwardRate() << endl; cout << "FRA market quote: " << io::rate(threeMonthFraQuote[monthsToStart[i]]) << endl; cout << "FRA spot value: " << myFRA.spotValue() << endl; cout << "FRA forward value: " << myFRA.forwardValue() << endl; cout << "FRA implied Yield: " << myFRA.impliedYield(myFRA.spotValue(), myFRA.forwardValue(), settlementDate, Simple, fraDayCounter) << endl; cout << "market Zero Rate: " << discountingTermStructure->zeroRate(fraMaturityDate, fraDayCounter, Simple) << endl; cout << "FRA NPV [should be positive]: " << myFRA.NPV() << endl << endl; } double seconds = timer.elapsed(); Integer hours = int(seconds/3600); seconds -= hours * 3600; Integer minutes = int(seconds/60); seconds -= minutes * 60; cout << " \nRun completed in "; if (hours > 0) cout << hours << " h "; if (hours > 0 || minutes > 0) cout << minutes << " m "; cout << fixed << setprecision(0) << seconds << " s\n" << endl; return 0; } catch (exception& e) { cerr << e.what() << endl; return 1; } catch (...) { cerr << "unknown error" << endl; return 1; } }
/*Market Data -> FRA Quote -> FRA rate helper -> Curve Building -> Construct FRA*/ int main(int, char* []) { try { boost::timer timer; std::cout << std::endl; std::ifstream file("data.csv"); CSVRow data_row; file >> data_row; const size_t num_data = data_row.size()/2; /********************* *** MARKET DATA *** *********************/ RelinkableHandle<YieldTermStructure> euriborTermStructure; boost::shared_ptr<IborIndex> euribor3m( new Euribor3M(euriborTermStructure)); Date todaysDate = Date(23, May, 2006); Settings::instance().evaluationDate() = todaysDate; Calendar calendar = euribor3m->fixingCalendar(); Integer fixingDays = euribor3m->fixingDays(); Date settlementDate = calendar.advance(todaysDate, fixingDays, Days); std::cout << "Today: " << todaysDate.weekday() << ", " << todaysDate << std::endl; std::cout << "Settlement date: " << settlementDate.weekday() << ", " << settlementDate << std::endl; // 3 month term FRA quotes (index refers to monthsToStart) vector <stFRAMarketData> vecMarketData; for(size_t i = 0 ; i < num_data; i++) { stFRAMarketData element_data; convertFromString(element_data.iMonthToStart, data_row[i]); element_data.iTermDuration = 3; //3 month term convertFromString(element_data.rateFraQuote, data_row[i + num_data]); vecMarketData.push_back(element_data); } /******************** *** 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. // FRAs std::vector <boost::shared_ptr<SimpleQuote> > vecFRAQuote; for(size_t i = 0 ; i < num_data; i++) { boost::shared_ptr<SimpleQuote> fraRate(new SimpleQuote(vecMarketData[i].rateFraQuote)); vecFRAQuote.push_back(fraRate); } std::vector <RelinkableHandle<Quote> > vecQuoteHandle; for(size_t i = 0 ; i < num_data; i++) { RelinkableHandle<Quote> hQuote; hQuote.linkTo(vecFRAQuote[i]); vecQuoteHandle.push_back(hQuote); } /********************* *** 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. DayCounter fraDayCounter = euribor3m->dayCounter(); BusinessDayConvention convention = euribor3m->businessDayConvention(); bool endOfMonth = euribor3m->endOfMonth(); std::vector < boost::shared_ptr<RateHelper> > vecRateHelper; for(size_t i = 0 ; i < num_data; i++) { boost::shared_ptr<RateHelper> fra_helper( new FraRateHelper(vecQuoteHandle[i],vecMarketData[i].iMonthToStart, vecMarketData[i].iMonthToStart + vecMarketData[i].iTermDuration, fixingDays, calendar, convention, endOfMonth, fraDayCounter)); vecRateHelper.push_back(fra_helper); } /********************* ** 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 FRA curve boost::shared_ptr<YieldTermStructure> fraTermStructure( new PiecewiseYieldCurve<Discount,LogLinear>( settlementDate, vecRateHelper, termStructureDayCounter, tolerance)); // Term structures used for pricing/discounting RelinkableHandle<YieldTermStructure> discountingTermStructure; discountingTermStructure.linkTo(fraTermStructure); /*********************** *** construct FRA's *** ***********************/ Calendar fraCalendar = euribor3m->fixingCalendar(); BusinessDayConvention fraBusinessDayConvention = euribor3m->businessDayConvention(); Position::Type fraFwdType = Position::Long; Real fraNotional = 100.0; //const Integer FraTermMonths = 3; //Integer monthsToStart[] = { 1, 2, 3, 6, 9 }; euriborTermStructure.linkTo(fraTermStructure); cout << endl; cout << "Test FRA construction, NPV calculation, and FRA purchase" << endl << endl; Size i; for (i=0; i < num_data; i++) { Date fraValueDate = fraCalendar.advance( settlementDate,vecMarketData[i].iMonthToStart, Months, fraBusinessDayConvention); Date fraMaturityDate = fraCalendar.advance( fraValueDate,vecMarketData[i].iTermDuration,Months, fraBusinessDayConvention); Rate fraStrikeRate = vecMarketData[i].rateFraQuote; ForwardRateAgreement myFRA(fraValueDate, fraMaturityDate, fraFwdType,fraStrikeRate, fraNotional, euribor3m, discountingTermStructure); cout << vecMarketData[i].iTermDuration<<"m Term FRA, Months to Start: " << vecMarketData[i].iMonthToStart << endl; cout << "strike FRA rate: " << io::rate(fraStrikeRate) << endl; cout << "FRA 3m forward rate: " << myFRA.forwardRate() << endl; cout << "FRA market quote: " << io::rate(vecMarketData[i].rateFraQuote) << endl; cout << "FRA spot value: " << myFRA.spotValue() << endl; cout << "FRA forward value: " << myFRA.forwardValue() << endl; cout << "FRA implied Yield: " << myFRA.impliedYield(myFRA.spotValue(), myFRA.forwardValue(), settlementDate, Simple, fraDayCounter) << endl; cout << "market Zero Rate: " << discountingTermStructure->zeroRate(fraMaturityDate, fraDayCounter, Simple) << endl; cout << "FRA NPV [should be zero]: " << myFRA.NPV() << endl << endl; } cout << endl << endl; cout << "Now take a 100 basis-point upward shift in FRA quotes " << "and examine NPV" << endl << endl; const Real BpsShift = 0.01; for(size_t i = 0; i< num_data; i++ ) { vecMarketData[i].rateFraQuote += BpsShift; vecFRAQuote[i]->setValue(vecMarketData[i].rateFraQuote); } for (i=0; i<num_data; i++) { Date fraValueDate = fraCalendar.advance( settlementDate,vecMarketData[i].iMonthToStart,Months, fraBusinessDayConvention); Date fraMaturityDate = fraCalendar.advance( fraValueDate,vecMarketData[i].iTermDuration,Months, fraBusinessDayConvention); Rate fraStrikeRate = vecMarketData[i].rateFraQuote - BpsShift; ForwardRateAgreement myFRA(fraValueDate, fraMaturityDate, fraFwdType, fraStrikeRate, fraNotional, euribor3m, discountingTermStructure); cout << vecMarketData[i].iTermDuration <<"m Term FRA, 100 notional, Months to Start = " << vecMarketData[i].iMonthToStart << endl; cout << "strike FRA rate: " << io::rate(fraStrikeRate) << endl; cout << "FRA 3m forward rate: " << myFRA.forwardRate() << endl; cout << "FRA market quote: " << io::rate(vecMarketData[i].rateFraQuote) << endl; cout << "FRA spot value: " << myFRA.spotValue() << endl; cout << "FRA forward value: " << myFRA.forwardValue() << endl; cout << "FRA implied Yield: " << myFRA.impliedYield(myFRA.spotValue(), myFRA.forwardValue(), settlementDate, Simple, fraDayCounter) << endl; cout << "market Zero Rate: " << discountingTermStructure->zeroRate(fraMaturityDate, fraDayCounter, Simple) << endl; cout << "FRA NPV [should be positive]: " << myFRA.NPV() << endl << endl; } Real seconds = timer.elapsed(); Integer hours = int(seconds/3600); seconds -= hours * 3600; Integer minutes = int(seconds/60); seconds -= minutes * 60; cout << " \nRun completed in "; if (hours > 0) cout << hours << " h "; if (hours > 0 || minutes > 0) cout << minutes << " m "; cout << fixed << setprecision(0) << seconds << " s\n" << endl; return 0; } catch (exception& e) { cerr << e.what() << endl; return 1; } catch (...) { cerr << "unknown error" << endl; return 1; } }
int main(int, char* []) { try { boost::timer timer; std::cout << std::endl; Date repoSettlementDate(14,February,2000);; Date repoDeliveryDate(15,August,2000); Rate repoRate = 0.05; DayCounter repoDayCountConvention = Actual360(); Integer repoSettlementDays = 0; Compounding repoCompounding = Simple; Frequency repoCompoundFreq = Annual; // assume a ten year bond- this is irrelevant Date bondIssueDate(15,September,1995); Date bondDatedDate(15,September,1995); Date bondMaturityDate(15,September,2005); Real bondCoupon = 0.08; Frequency bondCouponFrequency = Semiannual; // unknown what calendar fincad is using Calendar bondCalendar = NullCalendar(); DayCounter bondDayCountConvention = Thirty360(Thirty360::BondBasis); // unknown what fincad is using. this may affect accrued calculation Integer bondSettlementDays = 0; BusinessDayConvention bondBusinessDayConvention = Unadjusted; Real bondCleanPrice = 89.97693786; Real bondRedemption = 100.0; Real faceAmount = 100.0; Settings::instance().evaluationDate() = repoSettlementDate; RelinkableHandle<YieldTermStructure> bondCurve; bondCurve.linkTo(boost::shared_ptr<YieldTermStructure>( new FlatForward(repoSettlementDate, .01, // dummy rate bondDayCountConvention, Compounded, bondCouponFrequency))); /* boost::shared_ptr<FixedRateBond> bond( new FixedRateBond(faceAmount, bondIssueDate, bondDatedDate, bondMaturityDate, bondSettlementDays, std::vector<Rate>(1,bondCoupon), bondCouponFrequency, bondCalendar, bondDayCountConvention, bondBusinessDayConvention, bondBusinessDayConvention, bondRedemption, bondCurve)); */ Schedule bondSchedule(bondDatedDate, bondMaturityDate, Period(bondCouponFrequency), bondCalendar,bondBusinessDayConvention, bondBusinessDayConvention, DateGeneration::Backward,false); boost::shared_ptr<FixedRateBond> bond( new FixedRateBond(bondSettlementDays, faceAmount, bondSchedule, std::vector<Rate>(1,bondCoupon), bondDayCountConvention, bondBusinessDayConvention, bondRedemption, bondIssueDate)); bond->setPricingEngine(boost::shared_ptr<PricingEngine>( new DiscountingBondEngine(bondCurve))); bondCurve.linkTo(boost::shared_ptr<YieldTermStructure> ( new FlatForward(repoSettlementDate, bond->yield(bondCleanPrice, bondDayCountConvention, Compounded, bondCouponFrequency), bondDayCountConvention, Compounded, bondCouponFrequency))); Position::Type fwdType = Position::Long; double dummyStrike = 91.5745; RelinkableHandle<YieldTermStructure> repoCurve; repoCurve.linkTo(boost::shared_ptr<YieldTermStructure> ( new FlatForward(repoSettlementDate, repoRate, repoDayCountConvention, repoCompounding, repoCompoundFreq))); FixedRateBondForward bondFwd(repoSettlementDate, repoDeliveryDate, fwdType, dummyStrike, repoSettlementDays, repoDayCountConvention, bondCalendar, bondBusinessDayConvention, bond, repoCurve, repoCurve); cout << "Underlying bond clean price: " << bond->cleanPrice() << endl; cout << "Underlying bond dirty price: " << bond->dirtyPrice() << endl; cout << "Underlying bond accrued at settlement: " << bond->accruedAmount(repoSettlementDate) << endl; cout << "Underlying bond accrued at delivery: " << bond->accruedAmount(repoDeliveryDate) << endl; cout << "Underlying bond spot income: " << bondFwd.spotIncome(repoCurve) << endl; cout << "Underlying bond fwd income: " << bondFwd.spotIncome(repoCurve)/ repoCurve->discount(repoDeliveryDate) << endl; cout << "Repo strike: " << dummyStrike << endl; cout << "Repo NPV: " << bondFwd.NPV() << endl; cout << "Repo clean forward price: " << bondFwd.cleanForwardPrice() << endl; cout << "Repo dirty forward price: " << bondFwd.forwardPrice() << endl; cout << "Repo implied yield: " << bondFwd.impliedYield(bond->dirtyPrice(), dummyStrike, repoSettlementDate, repoCompounding, repoDayCountConvention) << endl; cout << "Market repo rate: " << repoCurve->zeroRate(repoDeliveryDate, repoDayCountConvention, repoCompounding, repoCompoundFreq) << endl << endl; cout << "Compare with example given at \n" << "http://www.fincad.com/support/developerFunc/mathref/BFWD.htm" << endl; cout << "Clean forward price = 88.2408" << endl << endl; cout << "In that example, it is unknown what bond calendar they are\n" << "using, as well as settlement Days. For that reason, I have\n" << "made the simplest possible assumptions here: NullCalendar\n" << "and 0 settlement days." << endl; double seconds = timer.elapsed(); Integer hours = int(seconds/3600); seconds -= hours * 3600; Integer minutes = int(seconds/60); seconds -= minutes * 60; cout << " \nRun completed in "; if (hours > 0) cout << hours << " h "; if (hours > 0 || minutes > 0) cout << minutes << " m "; cout << fixed << setprecision(0) << seconds << " s\n" << endl; return 0; } catch (exception& e) { cerr << e.what() << endl; return 1; } catch (...) { cerr << "unknown error" << endl; return 1; } }