void SwapTest::testCachedValue() { BOOST_TEST_MESSAGE("Testing vanilla-swap calculation against cached value..."); CommonVars vars; vars.today = Date(17,June,2002); Settings::instance().evaluationDate() = vars.today; vars.settlement = vars.calendar.advance(vars.today,vars.settlementDays,Days); vars.termStructure.linkTo(flatRate(vars.settlement,0.05,Actual365Fixed())); boost::shared_ptr<VanillaSwap> swap = vars.makeSwap(10, 0.06, 0.001); #ifndef QL_USE_INDEXED_COUPON Real cachedNPV = -5.872863313209; #else Real cachedNPV = -5.872342992212; #endif if (std::fabs(swap->NPV()-cachedNPV) > 1.0e-11) BOOST_ERROR("failed to reproduce cached swap value:\n" << QL_FIXED << std::setprecision(12) << " calculated: " << swap->NPV() << "\n" << " expected: " << cachedNPV); }
void OvernightIndexedSwapTest::testSeasonedSwaps() { BOOST_TEST_MESSAGE("Testing seasoned Eonia-swap calculation..."); CommonVars vars; Period lengths[] = { 1*Years, 2*Years, 5*Years, 10*Years, 20*Years }; Spread spreads[] = { -0.001, -0.01, 0.0, 0.01, 0.001 }; Date effectiveDate = Date(2, February, 2009); vars.eoniaIndex->addFixing(Date(2,February,2009), 0.0010); // fake fixing values vars.eoniaIndex->addFixing(Date(3,February,2009), 0.0011); vars.eoniaIndex->addFixing(Date(4,February,2009), 0.0012); vars.eoniaIndex->addFixing(Date(5,February,2009), 0.0013); for (Size i=0; i<LENGTH(lengths); i++) { for (Size j=0; j<LENGTH(spreads); j++) { shared_ptr<OvernightIndexedSwap> swap = vars.makeSwap(lengths[i],0.0,spreads[j],false,effectiveDate); shared_ptr<OvernightIndexedSwap> swap2 = vars.makeSwap(lengths[i],0.0,spreads[j],true,effectiveDate); if (std::fabs(swap->NPV() - swap2->NPV()) > 1.0e-10) { BOOST_ERROR("swap npv is different:\n" << std::setprecision(2) << " length: " << lengths[i] << " \n" << " floating spread: " << io::rate(spreads[j]) << "\n" << " swap value (non telescopic value dates): " << swap->NPV() << "\n swap value (telescopic value dates ): " << swap2->NPV()); } } } }
void SwapTest::testFairSpread() { BOOST_TEST_MESSAGE("Testing vanilla-swap calculation of " "fair floating spread..."); CommonVars vars; Integer lengths[] = { 1, 2, 5, 10, 20 }; Rate rates[] = { 0.04, 0.05, 0.06, 0.07 }; for (Size i=0; i<LENGTH(lengths); i++) { for (Size j=0; j<LENGTH(rates); j++) { boost::shared_ptr<VanillaSwap> swap = vars.makeSwap(lengths[i],rates[j],0.0); swap = vars.makeSwap(lengths[i],rates[j],swap->fairSpread()); if (std::fabs(swap->NPV()) > 1.0e-10) { BOOST_ERROR("recalculating with implied spread:\n" << std::setprecision(2) << " length: " << lengths[i] << " years\n" << " fixed rate: " << io::rate(rates[j]) << "\n" << " swap value: " << swap->NPV()); } } } }
void CapFloorTest::testATMRate() { BOOST_TEST_MESSAGE("Testing cap/floor ATM rate..."); CommonVars vars; Integer lengths[] = { 1, 2, 3, 5, 7, 10, 15, 20 }; Rate strikes[] = { 0., 0.03, 0.04, 0.05, 0.06, 0.07 }; Volatility vols[] = { 0.01, 0.05, 0.10, 0.15, 0.20 }; Date startDate = vars.termStructure->referenceDate(); for (Size i=0; i<LENGTH(lengths); i++) { Leg leg = vars.makeLeg(startDate,lengths[i]); Date maturity = vars.calendar.advance(startDate,lengths[i],Years, vars.convention); Schedule schedule(startDate,maturity, Period(vars.frequency),vars.calendar, vars.convention,vars.convention, DateGeneration::Forward,false); for (Size j=0; j<LENGTH(strikes); j++) { for (Size k=0; k<LENGTH(vols); k++) { boost::shared_ptr<CapFloor> cap = vars.makeCapFloor(CapFloor::Cap, leg, strikes[j],vols[k]); boost::shared_ptr<CapFloor> floor = vars.makeCapFloor(CapFloor::Floor, leg, strikes[j],vols[k]); Rate capATMRate = cap->atmRate(**vars.termStructure); Rate floorATMRate = floor->atmRate(**vars.termStructure); if (!checkAbsError(floorATMRate, capATMRate, 1.0e-10)) BOOST_FAIL( "Cap ATM Rate and floor ATM Rate should be equal :\n" << " length: " << lengths[i] << " years\n" << " volatility: " << io::volatility(vols[k]) << "\n" << " strike: " << io::rate(strikes[j]) << "\n" << " cap ATM rate: " << capATMRate << "\n" << " floor ATM rate:" << floorATMRate << "\n" << " relative Error:" << relativeError(capATMRate, floorATMRate, capATMRate)*100 << "%" ); VanillaSwap swap(VanillaSwap::Payer, vars.nominals[0], schedule, floorATMRate, vars.index->dayCounter(), schedule, vars.index, 0.0, vars.index->dayCounter()); swap.setPricingEngine(boost::shared_ptr<PricingEngine>( new DiscountingSwapEngine(vars.termStructure))); Real swapNPV = swap.NPV(); if (!checkAbsError(swapNPV, 0, 1.0e-10)) BOOST_FAIL( "the NPV of a Swap struck at ATM rate " "should be equal to 0:\n" << " length: " << lengths[i] << " years\n" << " volatility: " << io::volatility(vols[k]) << "\n" << " ATM rate: " << io::rate(floorATMRate) << "\n" << " swap NPV: " << swapNPV); } } } }
void OptionletStripperTest::testFlatTermVolatilityStripping1() { BOOST_TEST_MESSAGE( "Testing forward/forward vol stripping from flat term vol " "surface using OptionletStripper1 class..."); CommonVars vars; Settings::instance().evaluationDate() = Date(28, October, 2013); vars.setFlatTermVolSurface(); shared_ptr<IborIndex> iborIndex(new Euribor6M(vars.yieldTermStructure)); boost::shared_ptr<OptionletStripper> optionletStripper1(new OptionletStripper1(vars.flatTermVolSurface, iborIndex, Null<Rate>(), vars.accuracy)); boost::shared_ptr<StrippedOptionletAdapter> strippedOptionletAdapter(new StrippedOptionletAdapter(optionletStripper1)); Handle<OptionletVolatilityStructure> vol(strippedOptionletAdapter); vol->enableExtrapolation(); boost::shared_ptr<BlackCapFloorEngine> strippedVolEngine(new BlackCapFloorEngine(vars.yieldTermStructure, vol)); boost::shared_ptr<CapFloor> cap; for (Size tenorIndex=0; tenorIndex<vars.optionTenors.size(); ++tenorIndex) { for (Size strikeIndex=0; strikeIndex<vars.strikes.size(); ++strikeIndex) { cap = MakeCapFloor(CapFloor::Cap, vars.optionTenors[tenorIndex], iborIndex, vars.strikes[strikeIndex], 0*Days) .withPricingEngine(strippedVolEngine); Real priceFromStrippedVolatility = cap->NPV(); boost::shared_ptr<PricingEngine> blackCapFloorEngineConstantVolatility(new BlackCapFloorEngine(vars.yieldTermStructure, vars.termV[tenorIndex][strikeIndex])); cap->setPricingEngine(blackCapFloorEngineConstantVolatility); Real priceFromConstantVolatility = cap->NPV(); Real error = std::fabs(priceFromStrippedVolatility - priceFromConstantVolatility); if (error>vars.tolerance) BOOST_FAIL("\noption tenor: " << vars.optionTenors[tenorIndex] << "\nstrike: " << io::rate(vars.strikes[strikeIndex]) << "\nstripped vol price: " << io::rate(priceFromStrippedVolatility) << "\nconstant vol price: " << io::rate(priceFromConstantVolatility) << "\nerror: " << io::rate(error) << "\ntolerance: " << io::rate(vars.tolerance)); } } }
void OvernightIndexedSwapTest::testFairSpread() { BOOST_TEST_MESSAGE("Testing Eonia-swap calculation of " "fair floating spread..."); CommonVars vars; Period lengths[] = { 1*Years, 2*Years, 5*Years, 10*Years, 20*Years }; Rate rates[] = { 0.04, 0.05, 0.06, 0.07 }; for (Size i=0; i<LENGTH(lengths); i++) { for (Size j=0; j<LENGTH(rates); j++) { shared_ptr<OvernightIndexedSwap> swap = vars.makeSwap(lengths[i], rates[j], 0.0); Spread fairSpread = swap->fairSpread(); swap = vars.makeSwap(lengths[i], rates[j], fairSpread); if (std::fabs(swap->NPV()) > 1.0e-10) { BOOST_ERROR("\nrecalculating with implied spread:" << std::setprecision(2) << "\n length: " << lengths[i] << "\n fixed rate: " << io::rate(rates[j]) << "\nfair spread: " << io::rate(fairSpread) << "\n swap value: " << swap->NPV()); } } } }
void OvernightIndexedSwapTest::testFairRate() { BOOST_TEST_MESSAGE("Testing Eonia-swap calculation of fair fixed rate..."); CommonVars vars; Period lengths[] = { 1*Years, 2*Years, 5*Years, 10*Years, 20*Years }; Spread spreads[] = { -0.001, -0.01, 0.0, 0.01, 0.001 }; for (Size i=0; i<LENGTH(lengths); i++) { for (Size j=0; j<LENGTH(spreads); j++) { shared_ptr<OvernightIndexedSwap> swap = vars.makeSwap(lengths[i],0.0,spreads[j]); swap = vars.makeSwap(lengths[i],swap->fairRate(),spreads[j]); if (std::fabs(swap->NPV()) > 1.0e-10) { BOOST_ERROR("recalculating with implied rate:\n" << std::setprecision(2) << " length: " << lengths[i] << " \n" << " floating spread: " << io::rate(spreads[j]) << "\n" << " swap value: " << swap->NPV()); } } } }
void OvernightIndexedSwapTest::testCachedValue() { BOOST_TEST_MESSAGE("Testing Eonia-swap calculation against cached value..."); CommonVars vars; Settings::instance().evaluationDate() = vars.today; vars.settlement = vars.calendar.advance(vars.today,vars.settlementDays,Days); Real flat = 0.05; vars.eoniaTermStructure.linkTo(flatRate(vars.settlement,flat,Actual360())); Real fixedRate = exp(flat) - 1; shared_ptr<OvernightIndexedSwap> swap = vars.makeSwap(1*Years, fixedRate, 0.0,false); shared_ptr<OvernightIndexedSwap> swap2 = vars.makeSwap(1*Years, fixedRate, 0.0,true); Real cachedNPV = 0.001730450147; Real tolerance = 1.0e-11; if (std::fabs(swap->NPV()-cachedNPV) > tolerance) BOOST_ERROR("\nfailed to reproduce cached swap value (non telescopic value dates):" << std::fixed << std::setprecision(12) << "\ncalculated: " << swap->NPV() << "\n expected: " << cachedNPV << "\n tolerance:" << tolerance); if (std::fabs(swap2->NPV()-cachedNPV) > tolerance) BOOST_ERROR("\nfailed to reproduce cached swap value (telescopic value dates):" << std::fixed << std::setprecision(12) << "\ncalculated: " << swap->NPV() << "\n expected: " << cachedNPV << "\n tolerance:" << tolerance); }
void SwaptionTest::testCachedValue() { BOOST_MESSAGE("Testing swaption value against cached value..."); CommonVars vars; vars.today = Date(13, March, 2002); vars.settlement = Date(15, March, 2002); Settings::instance().evaluationDate() = vars.today; vars.termStructure.linkTo(flatRate(vars.settlement, 0.05, Actual365Fixed())); Date exerciseDate = vars.calendar.advance(vars.settlement, 5*Years); Date startDate = vars.calendar.advance(exerciseDate, vars.settlementDays, Days); boost::shared_ptr<VanillaSwap> swap = MakeVanillaSwap(10*Years, vars.index, 0.06) .withEffectiveDate(startDate); boost::shared_ptr<Swaption> swaption = vars.makeSwaption(swap, exerciseDate, 0.20); #ifndef QL_USE_INDEXED_COUPON Real cachedNPV = 0.036418158579; #else Real cachedNPV = 0.036421429684; #endif // FLOATING_POINT_EXCEPTION if (std::fabs(swaption->NPV()-cachedNPV) > 1.0e-12) BOOST_ERROR("failed to reproduce cached swaption value:\n" << QL_FIXED << std::setprecision(12) << "\ncalculated: " << swaption->NPV() << "\nexpected: " << cachedNPV); }
void SwaptionVolatilityMatrixTest::testSwaptionVolMatrixCoherence() { BOOST_TEST_MESSAGE("Testing swaption volatility matrix..."); CommonVars vars; boost::shared_ptr<SwaptionVolatilityMatrix> vol; std::string description; //floating reference date, floating market data description = "floating reference date, floating market data"; vol = boost::shared_ptr<SwaptionVolatilityMatrix>(new SwaptionVolatilityMatrix(vars.conventions.calendar, vars.conventions.optionBdc, vars.atm.tenors.options, vars.atm.tenors.swaps, vars.atm.volsHandle, vars.conventions.dayCounter)); vars.makeCoherenceTest(description, vol); //fixed reference date, floating market data description = "fixed reference date, floating market data"; vol = boost::shared_ptr<SwaptionVolatilityMatrix>(new SwaptionVolatilityMatrix(Settings::instance().evaluationDate(), vars.conventions.calendar, vars.conventions.optionBdc, vars.atm.tenors.options, vars.atm.tenors.swaps, vars.atm.volsHandle, vars.conventions.dayCounter)); vars.makeCoherenceTest(description, vol); // floating reference date, fixed market data description = "floating reference date, fixed market data"; vol = boost::shared_ptr<SwaptionVolatilityMatrix>(new SwaptionVolatilityMatrix(vars.conventions.calendar, vars.conventions.optionBdc, vars.atm.tenors.options, vars.atm.tenors.swaps, vars.atm.volsHandle, vars.conventions.dayCounter)); vars.makeCoherenceTest(description, vol); // fixed reference date, fixed market data description = "fixed reference date, fixed market data"; vol = boost::shared_ptr<SwaptionVolatilityMatrix>(new SwaptionVolatilityMatrix(Settings::instance().evaluationDate(), vars.conventions.calendar, vars.conventions.optionBdc, vars.atm.tenors.options, vars.atm.tenors.swaps, vars.atm.volsHandle, vars.conventions.dayCounter)); vars.makeCoherenceTest(description, vol); }
void CapFloorTest::testVega() { BOOST_TEST_MESSAGE("Testing cap/floor vega..."); CommonVars vars; Integer lengths[] = { 1, 2, 3, 4, 5, 6, 7, 10, 15, 20, 30 }; Volatility vols[] = { 0.01, 0.05, 0.10, 0.15, 0.20 }; Rate strikes[] = { 0.01, 0.02, 0.03, 0.04, 0.05, 0.06, 0.07, 0.08, 0.09 }; CapFloor::Type types[] = { CapFloor::Cap, CapFloor::Floor}; Date startDate = vars.termStructure->referenceDate(); static const Real shift = 1e-8; static const Real tolerance = 0.005; for (Size i=0; i<LENGTH(lengths); i++) { for (Size j=0; j<LENGTH(vols); j++) { for (Size k=0; k<LENGTH(strikes); k++) { for (Size h=0; h<LENGTH(types); h++) { Leg leg = vars.makeLeg(startDate, lengths[i]); boost::shared_ptr<CapFloor> capFloor = vars.makeCapFloor(types[h],leg, strikes[k],vols[j]); boost::shared_ptr<CapFloor> shiftedCapFloor2 = vars.makeCapFloor(types[h],leg, strikes[k],vols[j]+shift); boost::shared_ptr<CapFloor> shiftedCapFloor1 = vars.makeCapFloor(types[h],leg, strikes[k],vols[j]-shift); Real value1 = shiftedCapFloor1->NPV(); Real value2 = shiftedCapFloor2->NPV(); Real numericalVega = (value2 - value1) / (2*shift); if (numericalVega>1.0e-4) { Real analyticalVega = capFloor->result<Real>("vega"); Real discrepancy = std::fabs(numericalVega - analyticalVega); discrepancy /= numericalVega; if (discrepancy > tolerance) BOOST_FAIL( "failed to compute cap/floor vega:" << "\n lengths: " << lengths[j]*Years << "\n strike: " << io::rate(strikes[k]) << //"\n types: " << types[h] << QL_FIXED << std::setprecision(12) << "\n calculated: " << analyticalVega << "\n expected: " << numericalVega << "\n discrepancy: " << io::rate(discrepancy) << "\n tolerance: " << io::rate(tolerance)); } } } } } }
void OptionletStripperTest::testSwitchStrike() { BOOST_TEST_MESSAGE("Testing switch strike level and recalibration of level " "in case of curve relinking..."); CommonVars vars; Settings::instance().evaluationDate() = Date(28, October, 2013); vars.setCapFloorTermVolSurface(); RelinkableHandle< YieldTermStructure > yieldTermStructure; yieldTermStructure.linkTo(boost::shared_ptr< FlatForward >( new FlatForward(0, vars.calendar, 0.03, vars.dayCounter))); shared_ptr< IborIndex > iborIndex(new Euribor6M(yieldTermStructure)); boost::shared_ptr< OptionletStripper1 > optionletStripper1( new OptionletStripper1(vars.capFloorVolSurface, iborIndex, Null< Rate >(), vars.accuracy)); #if defined(QL_USE_INDEXED_COUPON) Real expected = 0.02981258; #else Real expected = 0.02981223; #endif Real error = std::fabs(optionletStripper1->switchStrike() - expected); if (error > vars.tolerance) BOOST_FAIL("\nSwitchstrike not correctly computed: " << "\nexpected switch strike: " << io::rate(expected) << "\ncomputed switch strike: " << io::rate(optionletStripper1->switchStrike()) << "\nerror: " << io::rate(error) << "\ntolerance: " << io::rate(vars.tolerance)); yieldTermStructure.linkTo(boost::shared_ptr< FlatForward >( new FlatForward(0, vars.calendar, 0.05, vars.dayCounter))); #if defined(QL_USE_INDEXED_COUPON) expected = 0.0499381; #else expected = 0.0499371; #endif error = std::fabs(optionletStripper1->switchStrike() - expected); if (error > vars.tolerance) BOOST_FAIL("\nSwitchstrike not correctly computed: " << "\nexpected switch strike: " << io::rate(expected) << "\ncomputed switch strike: " << io::rate(optionletStripper1->switchStrike()) << "\nerror: " << io::rate(error) << "\ntolerance: " << io::rate(vars.tolerance)); }
void CapFloorTest::testParity() { BOOST_TEST_MESSAGE("Testing cap/floor parity..."); CommonVars vars; Integer lengths[] = { 1, 2, 3, 5, 7, 10, 15, 20 }; Rate strikes[] = { 0., 0.03, 0.04, 0.05, 0.06, 0.07 }; Volatility vols[] = { 0.01, 0.05, 0.10, 0.15, 0.20 }; Date startDate = vars.termStructure->referenceDate(); for (Size i=0; i<LENGTH(lengths); i++) { for (Size j=0; j<LENGTH(strikes); j++) { for (Size k=0; k<LENGTH(vols); k++) { Leg leg = vars.makeLeg(startDate,lengths[i]); boost::shared_ptr<Instrument> cap = vars.makeCapFloor(CapFloor::Cap,leg, strikes[j],vols[k]); boost::shared_ptr<Instrument> floor = vars.makeCapFloor(CapFloor::Floor,leg, strikes[j],vols[k]); Date maturity = vars.calendar.advance(startDate,lengths[i],Years, vars.convention); Schedule schedule(startDate,maturity, Period(vars.frequency),vars.calendar, vars.convention,vars.convention, DateGeneration::Forward,false); VanillaSwap swap(VanillaSwap::Payer, vars.nominals[0], schedule, strikes[j], vars.index->dayCounter(), schedule, vars.index, 0.0, vars.index->dayCounter()); swap.setPricingEngine(boost::shared_ptr<PricingEngine>( new DiscountingSwapEngine(vars.termStructure))); // FLOATING_POINT_EXCEPTION if (std::fabs((cap->NPV()-floor->NPV()) - swap.NPV()) > 1.0e-10) { BOOST_FAIL( "put/call parity violated:\n" << " length: " << lengths[i] << " years\n" << " volatility: " << io::volatility(vols[k]) << "\n" << " strike: " << io::rate(strikes[j]) << "\n" << " cap value: " << cap->NPV() << "\n" << " floor value: " << floor->NPV() << "\n" << " swap value: " << swap.NPV()); } } } } }
void CapFloorTest::testCachedValue() { BOOST_TEST_MESSAGE("Testing Black cap/floor price against cached values..."); CommonVars vars; Date cachedToday(14,March,2002), cachedSettlement(18,March,2002); Settings::instance().evaluationDate() = cachedToday; vars.termStructure.linkTo(flatRate(cachedSettlement, 0.05, Actual360())); Date startDate = vars.termStructure->referenceDate(); Leg leg = vars.makeLeg(startDate,20); boost::shared_ptr<Instrument> cap = vars.makeCapFloor(CapFloor::Cap,leg, 0.07,0.20); boost::shared_ptr<Instrument> floor = vars.makeCapFloor(CapFloor::Floor,leg, 0.03,0.20); #ifndef QL_USE_INDEXED_COUPON // par coupon price Real cachedCapNPV = 6.87570026732, cachedFloorNPV = 2.65812927959; #else // index fixing price Real cachedCapNPV = 6.87630307745, cachedFloorNPV = 2.65796764715; #endif // test Black cap price against cached value if (std::fabs(cap->NPV()-cachedCapNPV) > 1.0e-11) BOOST_ERROR( "failed to reproduce cached cap value:\n" << std::setprecision(12) << " calculated: " << cap->NPV() << "\n" << " expected: " << cachedCapNPV); // test Black floor price against cached value if (std::fabs(floor->NPV()-cachedFloorNPV) > 1.0e-11) BOOST_ERROR( "failed to reproduce cached floor value:\n" << std::setprecision(12) << " calculated: " << floor->NPV() << "\n" << " expected: " << cachedFloorNPV); }
void SwapTest::testSpreadDependency() { BOOST_TEST_MESSAGE("Testing vanilla-swap dependency on floating spread..."); CommonVars vars; Integer lengths[] = { 1, 2, 5, 10, 20 }; Rate rates[] = { 0.04, 0.05, 0.06, 0.07 }; Spread spreads[] = { -0.01, -0.002, -0.001, 0.0, 0.001, 0.002, 0.01 }; for (Size i=0; i<LENGTH(lengths); i++) { for (Size j=0; j<LENGTH(rates); j++) { // store the results for different spreads... std::vector<Real> swap_values; for (Size k=0; k<LENGTH(spreads); k++) { boost::shared_ptr<VanillaSwap> swap = vars.makeSwap(lengths[i],rates[j],spreads[k]); swap_values.push_back(swap->NPV()); } // and check that they go the right way std::vector<Real>::iterator it = std::adjacent_find(swap_values.begin(),swap_values.end(), std::greater<Real>()); if (it != swap_values.end()) { Size n = it - swap_values.begin(); BOOST_ERROR( "NPV is decreasing with the floating spread in a swap: \n" << " length: " << lengths[i] << " years\n" << " value: " << swap_values[n] << " receiving spread: " << io::rate(spreads[n]) << "\n" << " value: " << swap_values[n+1] << " receiving spread: " << io::rate(spreads[n+1])); } } } }
double in_arrear_floorlet(const Date& todaysDate_, double start, double length, Rate spot_, Rate strike, Volatility volatility ) { CommonVars vars; try{ vars.makeIndex(todaysDate_,spot_); Leg leg = vars.makeLeg(start,length); boost::shared_ptr<Instrument> floor = vars.makeCapFloor(CapFloor::Floor, leg, strike, volatility); return floor->NPV(); } catch (std::exception& e) { std::cerr << e.what() << std::endl; return 1; } }
void CapFloorTest::testImpliedVolatility() { BOOST_TEST_MESSAGE("Testing implied term volatility for cap and floor..."); CommonVars vars; Size maxEvaluations = 100; Real tolerance = 1.0e-8; CapFloor::Type types[] = { CapFloor::Cap, CapFloor::Floor }; Rate strikes[] = { 0.02, 0.03, 0.04 }; Integer lengths[] = { 1, 5, 10 }; // test data Rate rRates[] = { 0.02, 0.03, 0.04, 0.05, 0.06, 0.07 }; Volatility vols[] = { 0.01, 0.05, 0.10, 0.20, 0.30, 0.70, 0.90 }; for (Size k=0; k<LENGTH(lengths); k++) { Leg leg = vars.makeLeg(vars.settlement, lengths[k]); for (Size i=0; i<LENGTH(types); i++) { for (Size j=0; j<LENGTH(strikes); j++) { boost::shared_ptr<CapFloor> capfloor = vars.makeCapFloor(types[i], leg, strikes[j], 0.0); for (Size n=0; n<LENGTH(rRates); n++) { for (Size m=0; m<LENGTH(vols); m++) { Rate r = rRates[n]; Volatility v = vols[m]; vars.termStructure.linkTo(flatRate(vars.settlement,r, Actual360())); capfloor->setPricingEngine(vars.makeEngine(v)); Real value = capfloor->NPV(); Volatility implVol = 0.0; try { implVol = capfloor->impliedVolatility(value, vars.termStructure, 0.10, tolerance, maxEvaluations, 10.0e-7, 4.0, 0.0); } catch (std::exception& e) { // couldn't bracket? capfloor->setPricingEngine(vars.makeEngine(0.0)); Real value2 = capfloor->NPV(); if (std::fabs(value-value2) < tolerance) { // ok, just skip: continue; } // otherwise, report error BOOST_ERROR("implied vol failure: " << typeToString(types[i]) << "\n strike: " << io::rate(strikes[j]) << "\n risk-free: " << io::rate(r) << "\n length: " << lengths[k] << "Y" << "\n volatility: " << io::volatility(v) << "\n price: " << value << "\n" << e.what()); } if (std::fabs(implVol-v) > tolerance) { // the difference might not matter capfloor->setPricingEngine( vars.makeEngine(implVol)); Real value2 = capfloor->NPV(); if (std::fabs(value-value2) > tolerance) { BOOST_FAIL("implied vol failure: " << typeToString(types[i]) << "\n strike: " << io::rate(strikes[j]) << "\n risk-free: " << io::rate(r) << "\n length: " << lengths[k] << "Y" << "\n volatility: " << io::volatility(v) << "\n price: " << value << "\n implied vol: " << io::volatility(implVol) << "\n implied price: " << value2); } } } } } } } }
void SwaptionTest::testCashSettledSwaptions() { BOOST_MESSAGE("Testing cash settled swaptions modified annuity..."); CommonVars vars; Rate strike = 0.05; for (Size i=0; i<LENGTH(exercises); i++) { for (Size j=0; j<LENGTH(lengths); j++) { Date exerciseDate = vars.calendar.advance(vars.today,exercises[i]); Date startDate = vars.calendar.advance(exerciseDate, vars.settlementDays,Days); Date maturity = vars.calendar.advance(startDate,lengths[j], vars.floatingConvention); Schedule floatSchedule(startDate, maturity, vars.floatingTenor, vars.calendar,vars.floatingConvention, vars.floatingConvention, DateGeneration::Forward, false); // Swap with fixed leg conventions: Business Days = Unadjusted, DayCount = 30/360 Schedule fixedSchedule_u(startDate, maturity, Period(vars.fixedFrequency), vars.calendar, Unadjusted, Unadjusted, DateGeneration::Forward, true); boost::shared_ptr<VanillaSwap> swap_u360( new VanillaSwap(type[0], vars.nominal, fixedSchedule_u,strike,Thirty360(), floatSchedule,vars.index,0.0, vars.index->dayCounter())); // Swap with fixed leg conventions: Business Days = Unadjusted, DayCount = Act/365 boost::shared_ptr<VanillaSwap> swap_u365( new VanillaSwap(type[0],vars.nominal, fixedSchedule_u,strike,Actual365Fixed(), floatSchedule,vars.index,0.0, vars.index->dayCounter())); // Swap with fixed leg conventions: Business Days = Modified Following, DayCount = 30/360 Schedule fixedSchedule_a(startDate,maturity, Period(vars.fixedFrequency), vars.calendar,ModifiedFollowing, ModifiedFollowing, DateGeneration::Forward, true); boost::shared_ptr<VanillaSwap> swap_a360( new VanillaSwap(type[0],vars.nominal, fixedSchedule_a,strike,Thirty360(), floatSchedule,vars.index,0.0, vars.index->dayCounter())); // Swap with fixed leg conventions: Business Days = Modified Following, DayCount = Act/365 boost::shared_ptr<VanillaSwap> swap_a365( new VanillaSwap(type[0],vars.nominal, fixedSchedule_a,strike,Actual365Fixed(), floatSchedule,vars.index,0.0, vars.index->dayCounter())); boost::shared_ptr<PricingEngine> swapEngine( new DiscountingSwapEngine(vars.termStructure)); swap_u360->setPricingEngine(swapEngine); swap_a360->setPricingEngine(swapEngine); swap_u365->setPricingEngine(swapEngine); swap_a365->setPricingEngine(swapEngine); const Leg& swapFixedLeg_u360 = swap_u360->fixedLeg(); const Leg& swapFixedLeg_a360 = swap_a360->fixedLeg(); const Leg& swapFixedLeg_u365 = swap_u365->fixedLeg(); const Leg& swapFixedLeg_a365 = swap_a365->fixedLeg(); // FlatForward curves // FLOATING_POINT_EXCEPTION Handle<YieldTermStructure> termStructure_u360( boost::shared_ptr<YieldTermStructure>( new FlatForward(vars.settlement,swap_u360->fairRate(), Thirty360(),Compounded, vars.fixedFrequency))); Handle<YieldTermStructure> termStructure_a360( boost::shared_ptr<YieldTermStructure>( new FlatForward(vars.settlement,swap_a360->fairRate(), Thirty360(),Compounded, vars.fixedFrequency))); Handle<YieldTermStructure> termStructure_u365( boost::shared_ptr<YieldTermStructure>( new FlatForward(vars.settlement,swap_u365->fairRate(), Actual365Fixed(),Compounded, vars.fixedFrequency))); Handle<YieldTermStructure> termStructure_a365( boost::shared_ptr<YieldTermStructure>( new FlatForward(vars.settlement,swap_a365->fairRate(), Actual365Fixed(),Compounded, vars.fixedFrequency))); // Annuity calculated by swap method fixedLegBPS(). // Fixed leg conventions: Unadjusted, 30/360 Real annuity_u360 = swap_u360->fixedLegBPS() / 0.0001; annuity_u360 = swap_u360->type()==VanillaSwap::Payer ? -annuity_u360 : annuity_u360; // Fixed leg conventions: ModifiedFollowing, act/365 Real annuity_a365 = swap_a365->fixedLegBPS() / 0.0001; annuity_a365 = swap_a365->type()==VanillaSwap::Payer ? -annuity_a365 : annuity_a365; // Fixed leg conventions: ModifiedFollowing, 30/360 Real annuity_a360 = swap_a360->fixedLegBPS() / 0.0001; annuity_a360 = swap_a360->type()==VanillaSwap::Payer ? -annuity_a360 : annuity_a360; // Fixed leg conventions: Unadjusted, act/365 Real annuity_u365 = swap_u365->fixedLegBPS() / 0.0001; annuity_u365 = swap_u365->type()==VanillaSwap::Payer ? -annuity_u365 : annuity_u365; // Calculation of Modified Annuity (cash settlement) // Fixed leg conventions of swap: unadjusted, 30/360 Real cashannuity_u360 = 0.; Size i; for (i=0; i<swapFixedLeg_u360.size(); i++) { cashannuity_u360 += swapFixedLeg_u360[i]->amount()/strike * termStructure_u360->discount( swapFixedLeg_u360[i]->date()); } // Fixed leg conventions of swap: unadjusted, act/365 Real cashannuity_u365 = 0.; for (i=0; i<swapFixedLeg_u365.size(); i++) { cashannuity_u365 += swapFixedLeg_u365[i]->amount()/strike * termStructure_u365->discount( swapFixedLeg_u365[i]->date()); } // Fixed leg conventions of swap: modified following, 30/360 Real cashannuity_a360 = 0.; for (i=0; i<swapFixedLeg_a360.size(); i++) { cashannuity_a360 += swapFixedLeg_a360[i]->amount()/strike * termStructure_a360->discount( swapFixedLeg_a360[i]->date()); } // Fixed leg conventions of swap: modified following, act/365 Real cashannuity_a365 = 0.; for (i=0; i<swapFixedLeg_a365.size(); i++) { cashannuity_a365 += swapFixedLeg_a365[i]->amount()/strike * termStructure_a365->discount( swapFixedLeg_a365[i]->date()); } // Swaptions: underlying swap fixed leg conventions: // unadjusted, 30/360 // Physical settled swaption boost::shared_ptr<Swaption> swaption_p_u360 = vars.makeSwaption(swap_u360,exerciseDate,0.20); Real value_p_u360 = swaption_p_u360->NPV(); // Cash settled swaption boost::shared_ptr<Swaption> swaption_c_u360 = vars.makeSwaption(swap_u360,exerciseDate,0.20, Settlement::Cash); Real value_c_u360 = swaption_c_u360->NPV(); // the NPV's ratio must be equal to annuities ratio Real npv_ratio_u360 = value_c_u360 / value_p_u360; Real annuity_ratio_u360 = cashannuity_u360 / annuity_u360; // Swaptions: underlying swap fixed leg conventions: // modified following, act/365 // Physical settled swaption boost::shared_ptr<Swaption> swaption_p_a365 = vars.makeSwaption(swap_a365,exerciseDate,0.20); Real value_p_a365 = swaption_p_a365->NPV(); // Cash settled swaption boost::shared_ptr<Swaption> swaption_c_a365 = vars.makeSwaption(swap_a365,exerciseDate,0.20, Settlement::Cash); Real value_c_a365 = swaption_c_a365->NPV(); // the NPV's ratio must be equal to annuities ratio Real npv_ratio_a365 = value_c_a365 / value_p_a365; Real annuity_ratio_a365 = cashannuity_a365 / annuity_a365; // Swaptions: underlying swap fixed leg conventions: // modified following, 30/360 // Physical settled swaption boost::shared_ptr<Swaption> swaption_p_a360 = vars.makeSwaption(swap_a360,exerciseDate,0.20); Real value_p_a360 = swaption_p_a360->NPV(); // Cash settled swaption boost::shared_ptr<Swaption> swaption_c_a360 = vars.makeSwaption(swap_a360,exerciseDate,0.20, Settlement::Cash); Real value_c_a360 = swaption_c_a360->NPV(); // the NPV's ratio must be equal to annuities ratio Real npv_ratio_a360 = value_c_a360 / value_p_a360; Real annuity_ratio_a360 = cashannuity_a360 / annuity_a360; // Swaptions: underlying swap fixed leg conventions: // unadjusted, act/365 // Physical settled swaption boost::shared_ptr<Swaption> swaption_p_u365 = vars.makeSwaption(swap_u365,exerciseDate,0.20); Real value_p_u365 = swaption_p_u365->NPV(); // Cash settled swaption boost::shared_ptr<Swaption> swaption_c_u365 = vars.makeSwaption(swap_u365,exerciseDate,0.20, Settlement::Cash); Real value_c_u365 = swaption_c_u365->NPV(); // the NPV's ratio must be equal to annuities ratio Real npv_ratio_u365 = value_c_u365 / value_p_u365; Real annuity_ratio_u365 = cashannuity_u365 / annuity_u365; if (std::fabs(annuity_ratio_u360-npv_ratio_u360)>1e-10 ) { BOOST_ERROR("\n" << " The npv's ratio must be equal to " << " annuities ratio" << "\n" " Swaption " << exercises[i].units() << "y x " << lengths[j].units() << "y" << " (underlying swap fixed leg Unadjusted, 30/360)" << "\n" << " Today : " << vars.today << "\n" << " Settlement date : " << vars.settlement << "\n" << " Exercise date : " << exerciseDate << "\n" << " Swap start date : " << startDate << "\n" << " Swap end date : " << maturity << "\n" << " physical delivered swaption npv : " << value_p_u360 << "\t\t\t" << " annuity : " << annuity_u360 << "\n" << " cash delivered swaption npv : " << value_c_u360 << "\t\t\t" << " annuity : " << cashannuity_u360 << "\n" << " npv ratio : " << npv_ratio_u360 << "\n" << " annuity ratio : " << annuity_ratio_u360 << "\n" << " difference : " << (annuity_ratio_u360-npv_ratio_u360) ); } if (std::fabs(annuity_ratio_a365-npv_ratio_a365)>1e-10) { BOOST_ERROR("\n" << " The npv's ratio must be equal to " << " annuities ratio" << "\n" " Swaption " << exercises[i].units() << "y x " << lengths[j].units() << "y" << " (underlying swap fixed leg Modified Following, act/365" << "\n" << " Today : " << vars.today << "\n" << " Settlement date : " << vars.settlement << "\n" << " Exercise date : " << exerciseDate << "\n" << " Swap start date : " << startDate << "\n" << " Swap end date : " << maturity << "\n" << " physical delivered swaption npv : " << value_p_a365 << "\t\t\t" << " annuity : " << annuity_a365 << "\n" << " cash delivered swaption npv : " << value_c_a365 << "\t\t\t" << " annuity : " << cashannuity_a365 << "\n" << " npv ratio : " << npv_ratio_a365 << "\n" << " annuity ratio : " << annuity_ratio_a365 << "\n" << " difference : " << (annuity_ratio_a365-npv_ratio_a365) ); } if (std::fabs(annuity_ratio_a360-npv_ratio_a360)>1e-10) { BOOST_ERROR("\n" << " The npv's ratio must be equal to " << " annuities ratio" << "\n" " Swaption " << exercises[i].units() << "y x " << lengths[j].units() << "y" << " (underlying swap fixed leg Unadjusted, 30/360)" << "\n" << " Today : " << vars.today << "\n" << " Settlement date : " << vars.settlement << "\n" << " Exercise date : " << exerciseDate << "\n" << " Swap start date : " << startDate << "\n" << " Swap end date : " << maturity << "\n" << " physical delivered swaption npv : " << value_p_a360 << "\t\t\t" << " annuity : " << annuity_a360 << "\n" << " cash delivered swaption npv : " << value_c_a360 << "\t\t\t" << " annuity : " << cashannuity_a360 << "\n" << " npv ratio : " << npv_ratio_a360 << "\n" << " annuity ratio : " << annuity_ratio_a360 << "\n" << " difference : " << (annuity_ratio_a360-npv_ratio_a360) ); } if (std::fabs(annuity_ratio_u365-npv_ratio_u365)>1e-10) { BOOST_ERROR("\n" << " The npv's ratio must be equal to " << " annuities ratio" << "\n" " Swaption " << exercises[i].units() << "y x " << lengths[j].units() << "y" << " (underlying swap fixed leg Unadjusted, act/365)" << "\n" << " Today : " << vars.today << "\n" << " Settlement date : " << vars.settlement << "\n" << " Exercise date : " << exerciseDate << "\n" << " Swap start date : " << startDate << "\n" << " Swap end date : " << maturity << "\n" << " physical delivered swaption npv : " << value_p_u365 << "\t\t\t" << " annuity : " << annuity_u365 << "\n" << " cash delivered swaption npv : " << value_c_u365 << "\t\t\t" << " annuity : " << cashannuity_u365 << "\n" << " npv ratio : " << npv_ratio_u365 << "\n" << " annuity ratio : " << annuity_ratio_u365 << "\n" << " difference : " << (annuity_ratio_u365-npv_ratio_u365) ); } } } }
void SwaptionTest::testVega() { BOOST_MESSAGE("Testing swaption vega..."); CommonVars vars; Settlement::Type types[] = { Settlement::Physical, Settlement::Cash }; Rate strikes[] = { 0.03, 0.04, 0.05, 0.06, 0.07 }; Volatility vols[] = { 0.01, 0.20, 0.30, 0.70, 0.90 }; Volatility shift = 1e-8; for (Size i=0; i<LENGTH(exercises); i++) { Date exerciseDate = vars.calendar.advance(vars.today, exercises[i]); Date startDate = vars.calendar.advance(exerciseDate, vars.settlementDays*Days); for (Size j=0; j<LENGTH(lengths); j++) { for (Size t=0; t<LENGTH(strikes); t++) { for (Size h=0; h<LENGTH(type); h++) { boost::shared_ptr<VanillaSwap> swap = MakeVanillaSwap(lengths[j], vars.index, strikes[t]) .withEffectiveDate(startDate) .withFloatingLegSpread(0.0) .withType(type[h]); for (Size u=0; u<LENGTH(vols); u++) { boost::shared_ptr<Swaption> swaption = vars.makeSwaption(swap, exerciseDate, vols[u], types[h]); // FLOATING_POINT_EXCEPTION boost::shared_ptr<Swaption> swaption1 = vars.makeSwaption(swap, exerciseDate, vols[u]-shift, types[h]); boost::shared_ptr<Swaption> swaption2 = vars.makeSwaption(swap, exerciseDate, vols[u]+shift, types[h]); Real swaptionNPV = swaption->NPV(); Real numericalVegaPerPoint = (swaption2->NPV()-swaption1->NPV())/(200.0*shift); // check only relevant vega if (numericalVegaPerPoint/swaptionNPV>1.0e-7) { Real analyticalVegaPerPoint = swaption->result<Real>("vega")/100.0; Real discrepancy = std::fabs(analyticalVegaPerPoint - numericalVegaPerPoint); discrepancy /= numericalVegaPerPoint; Real tolerance = 0.015; if (discrepancy > tolerance) BOOST_FAIL("failed to compute swaption vega:" << "\n option tenor: " << exercises[i] << "\n volatility: " << io::rate(vols[u]) << "\n option type: " << swaption->type() << "\n swap tenor: " << lengths[j] << "\n strike: " << io::rate(strikes[t]) << "\n settlement: " << types[h] << "\n nominal: " << swaption->underlyingSwap()->nominal() << "\n npv: " << swaptionNPV << "\n calculated vega: " << analyticalVegaPerPoint << "\n expected vega: " << numericalVegaPerPoint << "\n discrepancy: " << io::rate(discrepancy) << "\n tolerance: " << io::rate(tolerance)); } } } } } } }
void OvernightIndexedSwapTest::testBootstrap() { BOOST_TEST_MESSAGE("Testing Eonia-swap curve building..."); CommonVars vars; std::vector<shared_ptr<RateHelper> > eoniaHelpers; std::vector<shared_ptr<RateHelper> > swap3mHelpers; shared_ptr<IborIndex> euribor3m(new Euribor3M); shared_ptr<Eonia> eonia(new Eonia); for (Size i = 0; i < LENGTH(depositData); i++) { Real rate = 0.01 * depositData[i].rate; shared_ptr<SimpleQuote> simple = shared_ptr<SimpleQuote>(new SimpleQuote(rate)); shared_ptr<Quote> quote (simple); Period term = depositData[i].n * depositData[i].unit; shared_ptr<RateHelper> helper(new DepositRateHelper(Handle<Quote>(quote), term, depositData[i].settlementDays, euribor3m->fixingCalendar(), euribor3m->businessDayConvention(), euribor3m->endOfMonth(), euribor3m->dayCounter())); if (term <= 2*Days) eoniaHelpers.push_back(helper); if (term <= 3*Months) swap3mHelpers.push_back(helper); } for (Size i = 0; i < LENGTH(fraData); i++) { Real rate = 0.01 * fraData[i].rate; shared_ptr<SimpleQuote> simple = shared_ptr<SimpleQuote>(new SimpleQuote(rate)); shared_ptr<Quote> quote (simple); shared_ptr<RateHelper> helper(new FraRateHelper(Handle<Quote>(quote), fraData[i].nExpiry, fraData[i].nMaturity, fraData[i].settlementDays, euribor3m->fixingCalendar(), euribor3m->businessDayConvention(), euribor3m->endOfMonth(), euribor3m->dayCounter())); swap3mHelpers.push_back(helper); } for (Size i = 0; i < LENGTH(eoniaSwapData); i++) { Real rate = 0.01 * eoniaSwapData[i].rate; shared_ptr<SimpleQuote> simple = shared_ptr<SimpleQuote>(new SimpleQuote(rate)); shared_ptr<Quote> quote (simple); Period term = eoniaSwapData[i].n * eoniaSwapData[i].unit; shared_ptr<RateHelper> helper(new OISRateHelper(eoniaSwapData[i].settlementDays, term, Handle<Quote>(quote), eonia)); eoniaHelpers.push_back(helper); } for (Size i = 0; i < LENGTH(swapData); i++) { Real rate = 0.01 * swapData[i].rate; shared_ptr<SimpleQuote> simple = shared_ptr<SimpleQuote>(new SimpleQuote(rate)); shared_ptr<Quote> quote (simple); Period tenor = swapData[i].nIndexUnits * swapData[i].indexUnit; Period term = swapData[i].nTermUnits * swapData[i].termUnit; shared_ptr<RateHelper> helper(new SwapRateHelper( Handle<Quote>(quote), term, vars.calendar, vars.fixedSwapFrequency, vars.fixedSwapConvention, vars.fixedSwapDayCount, euribor3m)); if (tenor == 3*Months) swap3mHelpers.push_back(helper); } shared_ptr<PiecewiseFlatForward> eoniaTS(new PiecewiseFlatForward (vars.today, eoniaHelpers, Actual365Fixed())); shared_ptr<PiecewiseFlatForward> swapTS(new PiecewiseFlatForward (vars.today, swap3mHelpers, Actual365Fixed())); vars.eoniaTermStructure.linkTo(eoniaTS); /* std::cout.setf (std::ios::fixed, std::ios::floatfield); std::cout.setf (std::ios::showpoint); */ // test curve consistency Real tolerance = 1.0e-8; for (Size i = 0; i < LENGTH(eoniaSwapData); i++) { Rate expected = eoniaSwapData[i].rate; Period term = eoniaSwapData[i].n * eoniaSwapData[i].unit; shared_ptr<OvernightIndexedSwap> swap = vars.makeSwap(term, 0.0, 0.0); Rate calculated = 100.0 * swap->fairRate(); Rate error = std::fabs(expected-calculated); if (error>tolerance) BOOST_FAIL("curve inconsistency:" << std::setprecision(10) << "\n swap length: " << term << "\n quoted rate: " << expected << "\n calculated rate: " << calculated << "\n error: " << error << "\n tolerance: " << tolerance); } // zero spread /* std::cout << "zero spread:" << std::endl; std::cout << "years date zero3m/% zero1d/% spread/bp" << std::endl; DayCounter dc = Actual365Fixed(); for (Size i = 1; i <= 10; i++) { Date d = vars.today + i*Years; Rate zero1d = eoniaTS->zeroRate(d, dc, Continuous, Annual,false).rate(); Rate zero3m = swapTS->zeroRate(d, dc, Continuous, Annual,false).rate(); std::cout << std::setw(2) << i << "y " << io::iso_date(d) << " " << std::setprecision(3) << zero3m * 100.0 << " " << zero1d * 100.0 << " " << std::setprecision(1) << (zero3m - zero1d) * 10000.0 << std::endl; } */ }
void SwaptionVolatilityMatrixTest::testSwaptionVolMatrixObservability() { BOOST_TEST_MESSAGE("Testing swaption volatility matrix observability..."); CommonVars vars; boost::shared_ptr<SwaptionVolatilityMatrix> vol; std::string description; //floating reference date, floating market data description = "floating reference date, floating market data"; vol = boost::shared_ptr<SwaptionVolatilityMatrix>(new SwaptionVolatilityMatrix(vars.conventions.calendar, vars.conventions.optionBdc, vars.atm.tenors.options, vars.atm.tenors.swaps, vars.atm.volsHandle, vars.conventions.dayCounter)); vars.makeObservabilityTest(description, vol, true, true); //fixed reference date, floating market data description = "fixed reference date, floating market data"; vol = boost::shared_ptr<SwaptionVolatilityMatrix>(new SwaptionVolatilityMatrix(Settings::instance().evaluationDate(), vars.conventions.calendar, vars.conventions.optionBdc, vars.atm.tenors.options, vars.atm.tenors.swaps, vars.atm.volsHandle, vars.conventions.dayCounter)); vars.makeObservabilityTest(description, vol, true, false); // floating reference date, fixed market data description = "floating reference date, fixed market data"; vol = boost::shared_ptr<SwaptionVolatilityMatrix>(new SwaptionVolatilityMatrix(vars.conventions.calendar, vars.conventions.optionBdc, vars.atm.tenors.options, vars.atm.tenors.swaps, vars.atm.volsHandle, vars.conventions.dayCounter)); vars.makeObservabilityTest(description, vol, false, true); // fixed reference date, fixed market data description = "fixed reference date, fixed market data"; vol = boost::shared_ptr<SwaptionVolatilityMatrix>(new SwaptionVolatilityMatrix(Settings::instance().evaluationDate(), vars.conventions.calendar, vars.conventions.optionBdc, vars.atm.tenors.options, vars.atm.tenors.swaps, vars.atm.volsHandle, vars.conventions.dayCounter)); vars.makeObservabilityTest(description, vol, false, false); // fixed reference date and fixed market data, option dates //SwaptionVolatilityMatrix(const Date& referenceDate, // const std::vector<Date>& exerciseDates, // const std::vector<Period>& swapTenors, // const Matrix& volatilities, // const DayCounter& dayCounter); }
void BermudanSwaptionTest::testCachedValues() { BOOST_MESSAGE("Testing Bermudan swaption against cached values..."); CommonVars vars; vars.today = Date(15, February, 2002); Settings::instance().evaluationDate() = vars.today; vars.settlement = Date(19, February, 2002); // flat yield term structure impling 1x5 swap at 5% vars.termStructure.linkTo(flatRate(vars.settlement, 0.04875825, Actual365Fixed())); Rate atmRate = vars.makeSwap(0.0)->fairRate(); boost::shared_ptr<VanillaSwap> itmSwap = vars.makeSwap(0.8*atmRate); boost::shared_ptr<VanillaSwap> atmSwap = vars.makeSwap(atmRate); boost::shared_ptr<VanillaSwap> otmSwap = vars.makeSwap(1.2*atmRate); Real a = 0.048696, sigma = 0.0058904; boost::shared_ptr<ShortRateModel> model(new HullWhite(vars.termStructure, a, sigma)); std::vector<Date> exerciseDates; const Leg& leg = atmSwap->fixedLeg(); for (Size i=0; i<leg.size(); i++) { boost::shared_ptr<Coupon> coupon = boost::dynamic_pointer_cast<Coupon>(leg[i]); exerciseDates.push_back(coupon->accrualStartDate()); } boost::shared_ptr<Exercise> exercise(new BermudanExercise(exerciseDates)); boost::shared_ptr<PricingEngine> engine(new TreeSwaptionEngine(model, 50)); #if defined(QL_USE_INDEXED_COUPON) Real itmValue = 42.2413, atmValue = 12.8789, otmValue = 2.4759; #else Real itmValue = 42.2470, atmValue = 12.8826, otmValue = 2.4769; #endif Real tolerance = 1.0e-4; Swaption swaption(itmSwap, exercise); swaption.setPricingEngine(engine); if (std::fabs(swaption.NPV()-itmValue) > tolerance) BOOST_ERROR("failed to reproduce cached in-the-money swaption value:\n" << "calculated: " << swaption.NPV() << "\n" << "expected: " << itmValue); swaption = Swaption(atmSwap, exercise); swaption.setPricingEngine(engine); if (std::fabs(swaption.NPV()-atmValue) > tolerance) BOOST_ERROR("failed to reproduce cached at-the-money swaption value:\n" << "calculated: " << swaption.NPV() << "\n" << "expected: " << atmValue); swaption = Swaption(otmSwap, exercise); swaption.setPricingEngine(engine); if (std::fabs(swaption.NPV()-otmValue) > tolerance) BOOST_ERROR("failed to reproduce cached out-of-the-money " << "swaption value:\n" << "calculated: " << swaption.NPV() << "\n" << "expected: " << otmValue); for (Size j=0; j<exerciseDates.size(); j++) exerciseDates[j] = vars.calendar.adjust(exerciseDates[j]-10); exercise = boost::shared_ptr<Exercise>(new BermudanExercise(exerciseDates)); #if defined(QL_USE_INDEXED_COUPON) itmValue = 42.1917; atmValue = 12.7788; otmValue = 2.4388; #else itmValue = 42.1974; atmValue = 12.7825; otmValue = 2.4399; #endif swaption = Swaption(itmSwap, exercise); swaption.setPricingEngine(engine); if (std::fabs(swaption.NPV()-itmValue) > tolerance) BOOST_ERROR("failed to reproduce cached in-the-money swaption value:\n" << "calculated: " << swaption.NPV() << "\n" << "expected: " << itmValue); swaption = Swaption(atmSwap, exercise); swaption.setPricingEngine(engine); if (std::fabs(swaption.NPV()-atmValue) > tolerance) BOOST_ERROR("failed to reproduce cached at-the-money swaption value:\n" << "calculated: " << swaption.NPV() << "\n" << "expected: " << atmValue); swaption = Swaption(otmSwap, exercise); swaption.setPricingEngine(engine); if (std::fabs(swaption.NPV()-otmValue) > tolerance) BOOST_ERROR("failed to reproduce cached out-of-the-money " << "swaption value:\n" << "calculated: " << swaption.NPV() << "\n" << "expected: " << otmValue); }
void SwaptionTest::testImpliedVolatility() { BOOST_MESSAGE("Testing implied volatility for swaptions..."); CommonVars vars; Size maxEvaluations = 100; Real tolerance = 1.0e-08; Settlement::Type types[] = { Settlement::Physical, Settlement::Cash }; // test data Rate strikes[] = { 0.02, 0.03, 0.04, 0.05, 0.06, 0.07 }; Volatility vols[] = { 0.01, 0.05, 0.10, 0.20, 0.30, 0.70, 0.90 }; for (Size i=0; i<LENGTH(exercises); i++) { for (Size j=0; j<LENGTH(lengths); j++) { Date exerciseDate = vars.calendar.advance(vars.today,exercises[i]); Date startDate = vars.calendar.advance(exerciseDate, vars.settlementDays, Days); for (Size t=0; t<LENGTH(strikes); t++) { for (Size k=0; k<LENGTH(type); k++) { boost::shared_ptr<VanillaSwap> swap = MakeVanillaSwap(lengths[j], vars.index, strikes[t]) .withEffectiveDate(startDate) .withFloatingLegSpread(0.0) .withType(type[k]); for (Size h=0; h<LENGTH(types); h++) { for (Size u=0; u<LENGTH(vols); u++) { boost::shared_ptr<Swaption> swaption = vars.makeSwaption(swap, exerciseDate, vols[u], types[h]); // Black price Real value = swaption->NPV(); Volatility implVol = 0.0; try { implVol = swaption->impliedVolatility(value, vars.termStructure, 0.10, tolerance, maxEvaluations); } catch (std::exception& e) { // couldn't bracket? swaption->setPricingEngine(vars.makeEngine(0.0)); Real value2 = swaption->NPV(); if (std::fabs(value-value2) < tolerance) { // ok, just skip: continue; } // otherwise, report error BOOST_ERROR("implied vol failure: " << exercises[i] << "x" << lengths[j] << " " << type[k] << "\nsettlement: " << types[h] << "\nstrike " << strikes[t] << "\natm level: " << io::rate(swap->fairRate()) << "\nvol: " << io::volatility(vols[u]) << "\nprice: " << value << "\n" << e.what()); } if (std::fabs(implVol-vols[u]) > tolerance) { // the difference might not matter swaption->setPricingEngine(vars.makeEngine(implVol)); Real value2 = swaption->NPV(); if (std::fabs(value-value2) > tolerance) { BOOST_ERROR("implied vol failure: " << exercises[i] << "x" << lengths[j] << " " << type[k] << "\nsettlement: " << types[h] << "\nstrike " << strikes[t] << "\natm level: " << io::rate(swap->fairRate()) << "\nvol: " << io::volatility(vols[u]) << "\nprice: " << value << "\nimplied vol: " << io::volatility(implVol) << "\nimplied price: " << value2); } } } } } } } } }
void OptionletStripperTest::testTermVolatilityStripping2() { BOOST_TEST_MESSAGE( "Testing forward/forward vol stripping from non-flat term vol " "surface using OptionletStripper2 class..."); CommonVars vars; Settings::instance().evaluationDate() = Date::todaysDate(); vars.setCapFloorTermVolCurve(); vars.setCapFloorTermVolSurface(); shared_ptr<IborIndex> iborIndex(new Euribor6M(vars.yieldTermStructure)); // optionletstripper1 boost::shared_ptr<OptionletStripper1> optionletStripper1(new OptionletStripper1(vars.capFloorVolSurface, iborIndex, Null<Rate>(), vars.accuracy)); boost::shared_ptr<StrippedOptionletAdapter> strippedOptionletAdapter1 = boost::shared_ptr<StrippedOptionletAdapter>(new StrippedOptionletAdapter(optionletStripper1)); Handle<OptionletVolatilityStructure> vol1(strippedOptionletAdapter1); vol1->enableExtrapolation(); // optionletstripper2 boost::shared_ptr<OptionletStripper> optionletStripper2(new OptionletStripper2(optionletStripper1, vars.capFloorVolCurve)); boost::shared_ptr<StrippedOptionletAdapter> strippedOptionletAdapter2(new StrippedOptionletAdapter(optionletStripper2)); Handle<OptionletVolatilityStructure> vol2(strippedOptionletAdapter2); vol2->enableExtrapolation(); // consistency check: diff(stripped vol1-stripped vol2) for (Size strikeIndex=0; strikeIndex<vars.strikes.size(); ++strikeIndex) { for (Size tenorIndex=0; tenorIndex<vars.optionTenors.size(); ++tenorIndex) { Volatility strippedVol1 = vol1->volatility(vars.optionTenors[tenorIndex], vars.strikes[strikeIndex], true); Volatility strippedVol2 = vol2->volatility(vars.optionTenors[tenorIndex], vars.strikes[strikeIndex], true); // vol from flat vol surface (for comparison only) Volatility flatVol = vars.capFloorVolSurface->volatility(vars.optionTenors[tenorIndex], vars.strikes[strikeIndex], true); Real error = std::fabs(strippedVol1-strippedVol2); if (error>vars.tolerance) BOOST_FAIL("\noption tenor: " << vars.optionTenors[tenorIndex] << "\nstrike: " << io::rate(vars.strikes[strikeIndex]) << "\nstripped vol1: " << io::rate(strippedVol1) << "\nstripped vol2: " << io::rate(strippedVol2) << "\nflat vol: " << io::rate(flatVol) << "\nerror: " << io::rate(error) << "\ntolerance: " << io::rate(vars.tolerance)); } } }
void CapFloorTest::testStrikeDependency() { BOOST_TEST_MESSAGE("Testing cap/floor dependency on strike..."); CommonVars vars; Integer lengths[] = { 1, 2, 3, 5, 7, 10, 15, 20 }; Volatility vols[] = { 0.01, 0.05, 0.10, 0.15, 0.20 }; Rate strikes[] = { 0.03, 0.04, 0.05, 0.06, 0.07 }; Date startDate = vars.termStructure->referenceDate(); for (Size i=0; i<LENGTH(lengths); i++) { for (Size j=0; j<LENGTH(vols); j++) { // store the results for different strikes... std::vector<Real> cap_values, floor_values; for (Size k=0; k<LENGTH(strikes); k++) { Leg leg = vars.makeLeg(startDate,lengths[i]); boost::shared_ptr<Instrument> cap = vars.makeCapFloor(CapFloor::Cap,leg, strikes[k],vols[j]); cap_values.push_back(cap->NPV()); boost::shared_ptr<Instrument> floor = vars.makeCapFloor(CapFloor::Floor,leg, strikes[k],vols[j]); floor_values.push_back(floor->NPV()); } // and check that they go the right way std::vector<Real>::iterator it = std::adjacent_find(cap_values.begin(),cap_values.end(), std::less<Real>()); if (it != cap_values.end()) { Size n = it - cap_values.begin(); BOOST_FAIL( "NPV is increasing with the strike in a cap: \n" << std::setprecision(2) << " length: " << lengths[i] << " years\n" << " volatility: " << io::volatility(vols[j]) << "\n" << " value: " << cap_values[n] << " at strike: " << io::rate(strikes[n]) << "\n" << " value: " << cap_values[n+1] << " at strike: " << io::rate(strikes[n+1])); } // same for floors it = std::adjacent_find(floor_values.begin(),floor_values.end(), std::greater<Real>()); if (it != floor_values.end()) { Size n = it - floor_values.begin(); BOOST_FAIL( "NPV is decreasing with the strike in a floor: \n" << std::setprecision(2) << " length: " << lengths[i] << " years\n" << " volatility: " << io::volatility(vols[j]) << "\n" << " value: " << floor_values[n] << " at strike: " << io::rate(strikes[n]) << "\n" << " value: " << floor_values[n+1] << " at strike: " << io::rate(strikes[n+1])); } } } }
void OptionletStripperTest::testTermVolatilityStrippingShiftedLogNormalVol() { BOOST_TEST_MESSAGE( "Testing forward/forward vol stripping from non-flat normal vol term " "vol surface for normal vol setup using OptionletStripper1 class..."); CommonVars vars; Real shift = 0.03; Settings::instance().evaluationDate() = Date(30, April, 2015); vars.setRealCapFloorTermVolSurface(); shared_ptr< IborIndex > iborIndex(new Euribor6M(vars.forwardingYTS)); boost::shared_ptr< OptionletStripper > optionletStripper1( new OptionletStripper1(vars.capFloorVolRealSurface, iborIndex, Null< Rate >(), vars.accuracy, 100, vars.discountingYTS, ShiftedLognormal, shift, true)); boost::shared_ptr< StrippedOptionletAdapter > strippedOptionletAdapter = boost::shared_ptr< StrippedOptionletAdapter >( new StrippedOptionletAdapter(optionletStripper1)); Handle< OptionletVolatilityStructure > vol(strippedOptionletAdapter); vol->enableExtrapolation(); boost::shared_ptr< BlackCapFloorEngine > strippedVolEngine( new BlackCapFloorEngine(vars.discountingYTS, vol)); boost::shared_ptr< CapFloor > cap; for (Size strikeIndex = 0; strikeIndex < vars.strikes.size(); ++strikeIndex) { for (Size tenorIndex = 0; tenorIndex < vars.optionTenors.size(); ++tenorIndex) { cap = MakeCapFloor(CapFloor::Cap, vars.optionTenors[tenorIndex], iborIndex, vars.strikes[strikeIndex], 0 * Days).withPricingEngine(strippedVolEngine); Real priceFromStrippedVolatility = cap->NPV(); boost::shared_ptr< PricingEngine > blackCapFloorEngineConstantVolatility(new BlackCapFloorEngine( vars.discountingYTS, vars.termV[tenorIndex][strikeIndex], vars.capFloorVolRealSurface->dayCounter(), shift)); cap->setPricingEngine(blackCapFloorEngineConstantVolatility); Real priceFromConstantVolatility = cap->NPV(); Real error = std::fabs(priceFromStrippedVolatility - priceFromConstantVolatility); if (error > vars.tolerance) BOOST_FAIL( "\noption tenor: " << vars.optionTenors[tenorIndex] << "\nstrike: " << io::rate(vars.strikes[strikeIndex]) << "\nstripped vol price: " << io::rate(priceFromStrippedVolatility) << "\nconstant vol price: " << io::rate(priceFromConstantVolatility) << "\nerror: " << io::rate(error) << "\ntolerance: " << io::rate(vars.tolerance)); } } }
void CapFloorTest::testConsistency() { BOOST_TEST_MESSAGE("Testing consistency between cap, floor and collar..."); CommonVars vars; Integer lengths[] = { 1, 2, 3, 5, 7, 10, 15, 20 }; Rate cap_rates[] = { 0.03, 0.04, 0.05, 0.06, 0.07 }; Rate floor_rates[] = { 0.03, 0.04, 0.05, 0.06, 0.07 }; Volatility vols[] = { 0.01, 0.05, 0.10, 0.15, 0.20 }; Date startDate = vars.termStructure->referenceDate(); for (Size i=0; i<LENGTH(lengths); i++) { for (Size j=0; j<LENGTH(cap_rates); j++) { for (Size k=0; k<LENGTH(floor_rates); k++) { for (Size l=0; l<LENGTH(vols); l++) { Leg leg = vars.makeLeg(startDate,lengths[i]); boost::shared_ptr<CapFloor> cap = vars.makeCapFloor(CapFloor::Cap,leg, cap_rates[j],vols[l]); boost::shared_ptr<CapFloor> floor = vars.makeCapFloor(CapFloor::Floor,leg, floor_rates[k],vols[l]); Collar collar(leg,std::vector<Rate>(1,cap_rates[j]), std::vector<Rate>(1,floor_rates[k])); collar.setPricingEngine(vars.makeEngine(vols[l])); if (std::fabs((cap->NPV()-floor->NPV())-collar.NPV()) > 1e-10) { BOOST_FAIL( "inconsistency between cap, floor and collar:\n" << " length: " << lengths[i] << " years\n" << " volatility: " << io::volatility(vols[l]) << "\n" << " cap value: " << cap->NPV() << " at strike: " << io::rate(cap_rates[j]) << "\n" << " floor value: " << floor->NPV() << " at strike: " << io::rate(floor_rates[k]) << "\n" << " collar value: " << collar.NPV()); // test re-composition by optionlets, N.B. two per year Real capletsNPV = 0.0; std::vector<boost::shared_ptr<CapFloor> > caplets; for (Integer m=0; m<lengths[i]*2; m++) { caplets.push_back(cap->optionlet(m)); caplets[m]->setPricingEngine(vars.makeEngine(vols[l])); capletsNPV += caplets[m]->NPV(); } if (std::fabs(cap->NPV() - capletsNPV) > 1e-10) { BOOST_FAIL( "sum of caplet NPVs does not equal cap NPV:\n" << " length: " << lengths[i] << " years\n" << " volatility: " << io::volatility(vols[l]) << "\n" << " cap value: " << cap->NPV() << " at strike: " << io::rate(cap_rates[j]) << "\n" << " sum of caplets value: " << capletsNPV << " at strike (first): " << io::rate(caplets[0]->capRates()[0]) << "\n" ); } Real floorletsNPV = 0.0; std::vector<boost::shared_ptr<CapFloor> > floorlets; for (Integer m=0; m<lengths[i]*2; m++) { floorlets.push_back(floor->optionlet(m)); floorlets[m]->setPricingEngine(vars.makeEngine(vols[l])); floorletsNPV += floorlets[m]->NPV(); } if (std::fabs(floor->NPV() - floorletsNPV) > 1e-10) { BOOST_FAIL( "sum of floorlet NPVs does not equal floor NPV:\n" << " length: " << lengths[i] << " years\n" << " volatility: " << io::volatility(vols[l]) << "\n" << " cap value: " << floor->NPV() << " at strike: " << io::rate(floor_rates[j]) << "\n" << " sum of floorlets value: " << floorletsNPV << " at strike (first): " << io::rate(floorlets[0]->floorRates()[0]) << "\n" ); } Real collarletsNPV = 0.0; std::vector<boost::shared_ptr<CapFloor> > collarlets; for (Integer m=0; m<lengths[i]*2; m++) { collarlets.push_back(collar.optionlet(m)); collarlets[m]->setPricingEngine(vars.makeEngine(vols[l])); collarletsNPV += collarlets[m]->NPV(); } if (std::fabs(collar.NPV() - collarletsNPV) > 1e-10) { BOOST_FAIL( "sum of collarlet NPVs does not equal floor NPV:\n" << " length: " << lengths[i] << " years\n" << " volatility: " << io::volatility(vols[l]) << "\n" << " cap value: " << collar.NPV() << " at strike floor: " << io::rate(floor_rates[j]) << " at strike cap: " << io::rate(cap_rates[j]) << "\n" << " sum of collarlets value: " << collarletsNPV << " at strike floor (first): " << io::rate(collarlets[0]->floorRates()[0]) << " at strike cap (first): " << io::rate(collarlets[0]->capRates()[0]) << "\n" ); } } } } } } }
void InflationCapFlooredCouponTest::testInstrumentEquality() { BOOST_MESSAGE("Testing inflation capped/floored coupon against inflation capfloor instrument..."); CommonVars vars; Integer lengths[] = { 1, 2, 3, 5, 7, 10, 15, 20 }; // vol is low ... Rate strikes[] = { 0.01, 0.025, 0.029, 0.03, 0.031, 0.035, 0.07 }; // yoy inflation vol is generally very low Volatility vols[] = { 0.001, 0.005, 0.010, 0.015, 0.020 }; // this is model independent // capped coupon = fwd - cap, and fwd = swap(0) // floored coupon = fwd + floor for (Size whichPricer = 0; whichPricer < 3; whichPricer++) { for (Size i=0; i<LENGTH(lengths); i++) { for (Size j=0; j<LENGTH(strikes); j++) { for (Size k=0; k<LENGTH(vols); k++) { Leg leg = vars.makeYoYLeg(vars.evaluationDate,lengths[i]); boost::shared_ptr<Instrument> cap = vars.makeYoYCapFloor(YoYInflationCapFloor::Cap, leg, strikes[j], vols[k], whichPricer); boost::shared_ptr<Instrument> floor = vars.makeYoYCapFloor(YoYInflationCapFloor::Floor, leg, strikes[j], vols[k], whichPricer); Date from = vars.nominalTS->referenceDate(); Date to = from+lengths[i]*Years; Schedule yoySchedule = MakeSchedule().from(from).to(to) .withTenor(1*Years) .withCalendar(UnitedKingdom()) .withConvention(Unadjusted) .backwards() ; YearOnYearInflationSwap swap(YearOnYearInflationSwap::Payer, 1000000.0, yoySchedule,//fixed schedule, but same as yoy 0.0,//strikes[j], vars.dc, yoySchedule, vars.iir, vars.observationLag, 0.0, //spread on index vars.dc, UnitedKingdom()); Handle<YieldTermStructure> hTS(vars.nominalTS); boost::shared_ptr<PricingEngine> sppe(new DiscountingSwapEngine(hTS)); swap.setPricingEngine(sppe); Leg leg2 = vars.makeYoYCapFlooredLeg(whichPricer, from, lengths[i], std::vector<Rate>(lengths[i],strikes[j]),//cap std::vector<Rate>(),//floor vols[k], 1.0, // gearing 0.0);// spread Leg leg3 = vars.makeYoYCapFlooredLeg(whichPricer, from, lengths[i], std::vector<Rate>(),// cap std::vector<Rate>(lengths[i],strikes[j]),//floor vols[k], 1.0, // gearing 0.0);// spread // N.B. nominals are 10e6 Real capped = CashFlows::npv(leg2,(**vars.nominalTS),false); if ( fabs(capped - (swap.NPV() - cap->NPV())) > 1.0e-6) { BOOST_FAIL( "capped coupon != swap(0) - cap:\n" << " length: " << lengths[i] << " years\n" << " volatility: " << io::volatility(vols[k]) << "\n" << " strike: " << io::rate(strikes[j]) << "\n" << " cap value: " << cap->NPV() << "\n" << " swap value: " << swap.NPV() << "\n" << " capped coupon " << capped); } // N.B. nominals are 10e6 Real floored = CashFlows::npv(leg3,(**vars.nominalTS),false); if ( fabs(floored - (swap.NPV() + floor->NPV())) > 1.0e-6) { BOOST_FAIL( "floored coupon != swap(0) + floor :\n" << " length: " << lengths[i] << " years\n" << " volatility: " << io::volatility(vols[k]) << "\n" << " strike: " << io::rate(strikes[j]) << "\n" << " floor value: " << floor->NPV() << "\n" << " swap value: " << swap.NPV() << "\n" << " floored coupon " << floored); } } } } } }
void InflationCapFlooredCouponTest::testDecomposition() { BOOST_MESSAGE("Testing collared coupon against its decomposition..."); CommonVars vars; Real tolerance = 1e-10; Real npvVanilla,npvCappedLeg,npvFlooredLeg,npvCollaredLeg,npvCap,npvFloor,npvCollar; Real error; Rate floorstrike = 0.05; Rate capstrike = 0.10; std::vector<Rate> caps(vars.length,capstrike); std::vector<Rate> caps0 = std::vector<Rate>(); std::vector<Rate> floors(vars.length,floorstrike); std::vector<Rate> floors0 = std::vector<Rate>(); Rate gearing_p = Rate(0.5); Spread spread_p = Spread(0.002); Rate gearing_n = Rate(-1.5); Spread spread_n = Spread(0.12); // fixed leg with zero rate Leg fixedLeg = vars.makeFixedLeg(vars.startDate,vars.length); // floating leg with gearing=1 and spread=0 Leg floatLeg = vars.makeYoYLeg(vars.startDate,vars.length); // floating leg with positive gearing (gearing_p) and spread<>0 Leg floatLeg_p = vars.makeYoYLeg(vars.startDate,vars.length,gearing_p,spread_p); // floating leg with negative gearing (gearing_n) and spread<>0 Leg floatLeg_n = vars.makeYoYLeg(vars.startDate,vars.length,gearing_n,spread_n); // Swap with null fixed leg and floating leg with gearing=1 and spread=0 Swap vanillaLeg(fixedLeg,floatLeg); // Swap with null fixed leg and floating leg with positive gearing and spread<>0 Swap vanillaLeg_p(fixedLeg,floatLeg_p); // Swap with null fixed leg and floating leg with negative gearing and spread<>0 Swap vanillaLeg_n(fixedLeg,floatLeg_n); boost::shared_ptr<PricingEngine> engine( new DiscountingSwapEngine(vars.nominalTS)); vanillaLeg.setPricingEngine(engine); // here use the autoset feature vanillaLeg_p.setPricingEngine(engine); vanillaLeg_n.setPricingEngine(engine); // CAPPED coupon - Decomposition of payoff // Payoff = Nom * Min(rate,strike) * accrualperiod = // = Nom * [rate + Min(0,strike-rate)] * accrualperiod = // = Nom * rate * accrualperiod - Nom * Max(rate-strike,0) * accrualperiod = // = VanillaFloatingLeg - Call // Size whichPricer = 0; // Case gearing = 1 and spread = 0 Leg cappedLeg = vars.makeYoYCapFlooredLeg(whichPricer,vars.startDate,vars.length, caps,floors0,vars.volatility); Swap capLeg(fixedLeg,cappedLeg); capLeg.setPricingEngine(engine); YoYInflationCap cap(floatLeg, std::vector<Rate>(1, capstrike)); cap.setPricingEngine(vars.makeEngine(vars.volatility,whichPricer)); npvVanilla = vanillaLeg.NPV(); npvCappedLeg = capLeg.NPV(); npvCap = cap.NPV(); error = std::abs(npvCappedLeg - (npvVanilla-npvCap)); if (error>tolerance) { BOOST_ERROR("\nYoY Capped Leg: gearing=1, spread=0%, strike=" << capstrike*100 << "%\n" << " Capped Floating Leg NPV: " << npvCappedLeg << "\n" << " Floating Leg NPV - Cap NPV: " << npvVanilla - npvCap << "\n" << " Diff: " << error ); } // gearing = 1 and spread = 0 // FLOORED coupon - Decomposition of payoff // Payoff = Nom * Max(rate,strike) * accrualperiod = // = Nom * [rate + Max(0,strike-rate)] * accrualperiod = // = Nom * rate * accrualperiod + Nom * Max(strike-rate,0) * accrualperiod = // = VanillaFloatingLeg + Put // Leg flooredLeg = vars.makeYoYCapFlooredLeg(whichPricer,vars.startDate,vars.length, caps0,floors,vars.volatility); Swap floorLeg(fixedLeg,flooredLeg); floorLeg.setPricingEngine(engine); YoYInflationFloor floor(floatLeg, std::vector<Rate>(1, floorstrike)); floor.setPricingEngine(vars.makeEngine(vars.volatility,whichPricer)); npvFlooredLeg = floorLeg.NPV(); npvFloor = floor.NPV(); error = std::abs(npvFlooredLeg-(npvVanilla + npvFloor)); if (error>tolerance) { BOOST_ERROR("YoY Floored Leg: gearing=1, spread=0%, strike=" << floorstrike *100 << "%\n" << " Floored Floating Leg NPV: " << npvFlooredLeg << "\n" << " Floating Leg NPV + Floor NPV: " << npvVanilla + npvFloor << "\n" << " Diff: " << error ); } // gearing = 1 and spread = 0 // COLLARED coupon - Decomposition of payoff // Payoff = Nom * Min(strikem,Max(rate,strikeM)) * accrualperiod = // = VanillaFloatingLeg - Collar // Leg collaredLeg = vars.makeYoYCapFlooredLeg(whichPricer,vars.startDate,vars.length, caps,floors,vars.volatility); Swap collarLeg(fixedLeg,collaredLeg); collarLeg.setPricingEngine(engine); YoYInflationCollar collar(floatLeg, std::vector<Rate>(1, capstrike), std::vector<Rate>(1, floorstrike)); collar.setPricingEngine(vars.makeEngine(vars.volatility,whichPricer)); npvCollaredLeg = collarLeg.NPV(); npvCollar = collar.NPV(); error = std::abs(npvCollaredLeg -(npvVanilla - npvCollar)); if (error>tolerance) { BOOST_ERROR("\nYoY Collared Leg: gearing=1, spread=0%, strike=" << floorstrike*100 << "% and " << capstrike*100 << "%\n" << " Collared Floating Leg NPV: " << npvCollaredLeg << "\n" << " Floating Leg NPV - Collar NPV: " << npvVanilla - npvCollar << "\n" << " Diff: " << error ); } // gearing = a and spread = b // CAPPED coupon - Decomposition of payoff // Payoff // = Nom * Min(a*rate+b,strike) * accrualperiod = // = Nom * [a*rate+b + Min(0,strike-a*rate-b)] * accrualperiod = // = Nom * a*rate+b * accrualperiod + Nom * Min(strike-b-a*rate,0) * accrualperiod // --> If a>0 (assuming positive effective strike): // Payoff = VanillaFloatingLeg - Call(a*rate+b,strike) // --> If a<0 (assuming positive effective strike): // Payoff = VanillaFloatingLeg + Nom * Min(strike-b+|a|*rate+,0) * accrualperiod = // = VanillaFloatingLeg + Put(|a|*rate+b,strike) // // Positive gearing Leg cappedLeg_p = vars.makeYoYCapFlooredLeg(whichPricer,vars.startDate,vars.length,caps,floors0, vars.volatility,gearing_p,spread_p); Swap capLeg_p(fixedLeg,cappedLeg_p); capLeg_p.setPricingEngine(engine); YoYInflationCap cap_p(floatLeg_p,std::vector<Rate>(1,capstrike)); cap_p.setPricingEngine(vars.makeEngine(vars.volatility,whichPricer)); npvVanilla = vanillaLeg_p.NPV(); npvCappedLeg = capLeg_p.NPV(); npvCap = cap_p.NPV(); error = std::abs(npvCappedLeg - (npvVanilla-npvCap)); if (error>tolerance) { BOOST_ERROR("\nYoY Capped Leg: gearing=" << gearing_p << ", " << "spread= " << spread_p *100 << "%, strike=" << capstrike*100 << "%, " << "effective strike= " << (capstrike-spread_p)/gearing_p*100 << "%\n" << " Capped Floating Leg NPV: " << npvCappedLeg << "\n" << " Vanilla Leg NPV: " << npvVanilla << "\n" << " Cap NPV: " << npvCap << "\n" << " Floating Leg NPV - Cap NPV: " << npvVanilla - npvCap << "\n" << " Diff: " << error ); } // Negative gearing Leg cappedLeg_n = vars.makeYoYCapFlooredLeg(whichPricer,vars.startDate,vars.length,caps,floors0, vars.volatility,gearing_n,spread_n); Swap capLeg_n(fixedLeg,cappedLeg_n); capLeg_n.setPricingEngine(engine); YoYInflationFloor floor_n(floatLeg,std::vector<Rate>(1,(capstrike-spread_n)/gearing_n)); floor_n.setPricingEngine(vars.makeEngine(vars.volatility,whichPricer)); npvVanilla = vanillaLeg_n.NPV(); npvCappedLeg = capLeg_n.NPV(); npvFloor = floor_n.NPV(); error = std::abs(npvCappedLeg - (npvVanilla+ gearing_n*npvFloor)); if (error>tolerance) { BOOST_ERROR("\nYoY Capped Leg: gearing=" << gearing_n << ", " << "spread= " << spread_n *100 << "%, strike=" << capstrike*100 << "%, " << "effective strike= " << (capstrike-spread_n)/gearing_n*100 << "%\n" << " Capped Floating Leg NPV: " << npvCappedLeg << "\n" << " npv Vanilla: " << npvVanilla << "\n" << " npvFloor: " << npvFloor << "\n" << " Floating Leg NPV - Cap NPV: " << npvVanilla + gearing_n*npvFloor << "\n" << " Diff: " << error ); } // gearing = a and spread = b // FLOORED coupon - Decomposition of payoff // Payoff // = Nom * Max(a*rate+b,strike) * accrualperiod = // = Nom * [a*rate+b + Max(0,strike-a*rate-b)] * accrualperiod = // = Nom * a*rate+b * accrualperiod + Nom * Max(strike-b-a*rate,0) * accrualperiod // --> If a>0 (assuming positive effective strike): // Payoff = VanillaFloatingLeg + Put(a*rate+b,strike) // --> If a<0 (assuming positive effective strike): // Payoff = VanillaFloatingLeg + Nom * Max(strike-b+|a|*rate+,0) * accrualperiod = // = VanillaFloatingLeg - Call(|a|*rate+b,strike) // // Positive gearing Leg flooredLeg_p1 = vars.makeYoYCapFlooredLeg(whichPricer,vars.startDate,vars.length,caps0,floors, vars.volatility,gearing_p,spread_p); Swap floorLeg_p1(fixedLeg,flooredLeg_p1); floorLeg_p1.setPricingEngine(engine); YoYInflationFloor floor_p1(floatLeg_p,std::vector<Rate>(1,floorstrike)); floor_p1.setPricingEngine(vars.makeEngine(vars.volatility,whichPricer)); npvVanilla = vanillaLeg_p.NPV(); npvFlooredLeg = floorLeg_p1.NPV(); npvFloor = floor_p1.NPV(); error = std::abs(npvFlooredLeg - (npvVanilla+npvFloor)); if (error>tolerance) { BOOST_ERROR("\nYoY Floored Leg: gearing=" << gearing_p << ", " << "spread= " << spread_p *100<< "%, strike=" << floorstrike *100 << "%, " << "effective strike= " << (floorstrike-spread_p)/gearing_p*100 << "%\n" << " Floored Floating Leg NPV: " << npvFlooredLeg << "\n" << " Floating Leg NPV + Floor NPV: " << npvVanilla + npvFloor << "\n" << " Diff: " << error ); } // Negative gearing Leg flooredLeg_n = vars.makeYoYCapFlooredLeg(whichPricer,vars.startDate,vars.length,caps0,floors, vars.volatility,gearing_n,spread_n); Swap floorLeg_n(fixedLeg,flooredLeg_n); floorLeg_n.setPricingEngine(engine); YoYInflationCap cap_n(floatLeg,std::vector<Rate>(1,(floorstrike-spread_n)/gearing_n)); cap_n.setPricingEngine(vars.makeEngine(vars.volatility,whichPricer)); npvVanilla = vanillaLeg_n.NPV(); npvFlooredLeg = floorLeg_n.NPV(); npvCap = cap_n.NPV(); error = std::abs(npvFlooredLeg - (npvVanilla - gearing_n*npvCap)); if (error>tolerance) { BOOST_ERROR("\nYoY Capped Leg: gearing=" << gearing_n << ", " << "spread= " << spread_n *100 << "%, strike=" << floorstrike*100 << "%, " << "effective strike= " << (floorstrike-spread_n)/gearing_n*100 << "%\n" << " Capped Floating Leg NPV: " << npvFlooredLeg << "\n" << " Floating Leg NPV - Cap NPV: " << npvVanilla - gearing_n*npvCap << "\n" << " Diff: " << error ); } // gearing = a and spread = b // COLLARED coupon - Decomposition of payoff // Payoff = Nom * Min(caprate,Max(a*rate+b,floorrate)) * accrualperiod // --> If a>0 (assuming positive effective strike): // Payoff = VanillaFloatingLeg - Collar(a*rate+b, floorrate, caprate) // --> If a<0 (assuming positive effective strike): // Payoff = VanillaFloatingLeg + Collar(|a|*rate+b, caprate, floorrate) // // Positive gearing Leg collaredLeg_p = vars.makeYoYCapFlooredLeg(whichPricer,vars.startDate,vars.length,caps,floors, vars.volatility,gearing_p,spread_p); Swap collarLeg_p1(fixedLeg,collaredLeg_p); collarLeg_p1.setPricingEngine(engine); YoYInflationCollar collar_p(floatLeg_p, std::vector<Rate>(1,capstrike), std::vector<Rate>(1,floorstrike)); collar_p.setPricingEngine(vars.makeEngine(vars.volatility,whichPricer)); npvVanilla = vanillaLeg_p.NPV(); npvCollaredLeg = collarLeg_p1.NPV(); npvCollar = collar_p.NPV(); error = std::abs(npvCollaredLeg - (npvVanilla - npvCollar)); if (error>tolerance) { BOOST_ERROR("\nYoY Collared Leg: gearing=" << gearing_p << ", " << "spread= " << spread_p*100 << "%, strike=" << floorstrike*100 << "% and " << capstrike*100 << "%, " << "effective strike=" << (floorstrike-spread_p)/gearing_p*100 << "% and " << (capstrike-spread_p)/gearing_p*100 << "%\n" << " Collared Floating Leg NPV: " << npvCollaredLeg << "\n" << " Floating Leg NPV - Collar NPV: " << npvVanilla - npvCollar << "\n" << " Diff: " << error ); } // Negative gearing Leg collaredLeg_n = vars.makeYoYCapFlooredLeg(whichPricer,vars.startDate,vars.length,caps,floors, vars.volatility,gearing_n,spread_n); Swap collarLeg_n1(fixedLeg,collaredLeg_n); collarLeg_n1.setPricingEngine(engine); YoYInflationCollar collar_n(floatLeg, std::vector<Rate>(1,(floorstrike-spread_n)/gearing_n), std::vector<Rate>(1,(capstrike-spread_n)/gearing_n)); collar_n.setPricingEngine(vars.makeEngine(vars.volatility,whichPricer)); npvVanilla = vanillaLeg_n.NPV(); npvCollaredLeg = collarLeg_n1.NPV(); npvCollar = collar_n.NPV(); error = std::abs(npvCollaredLeg - (npvVanilla - gearing_n*npvCollar)); if (error>tolerance) { BOOST_ERROR("\nYoY Collared Leg: gearing=" << gearing_n << ", " << "spread= " << spread_n*100 << "%, strike=" << floorstrike*100 << "% and " << capstrike*100 << "%, " << "effective strike=" << (floorstrike-spread_n)/gearing_n*100 << "% and " << (capstrike-spread_n)/gearing_n*100 << "%\n" << " Collared Floating Leg NPV: " << npvCollaredLeg << "\n" << " Floating Leg NPV - Collar NPV: " << npvVanilla - gearing_n*npvCollar << "\n" << " Diff: " << error ); } }
void SwaptionTest::testSpreadTreatment() { BOOST_MESSAGE("Testing swaption treatment of spread..."); CommonVars vars; Spread spreads[] = { -0.002, -0.001, 0.0, 0.001, 0.002 }; for (Size i=0; i<LENGTH(exercises); i++) { for (Size j=0; j<LENGTH(lengths); j++) { for (Size k=0; k<LENGTH(type); k++) { Date exerciseDate = vars.calendar.advance(vars.today, exercises[i]); Date startDate = vars.calendar.advance(exerciseDate, vars.settlementDays,Days); for (Size l=0; l<LENGTH(spreads); l++) { boost::shared_ptr<VanillaSwap> swap = MakeVanillaSwap(lengths[j], vars.index, 0.06) .withEffectiveDate(startDate) .withFloatingLegSpread(spreads[l]) .withType(type[k]); // FLOATING_POINT_EXCEPTION Spread correction = spreads[l] * swap->floatingLegBPS() / swap->fixedLegBPS(); boost::shared_ptr<VanillaSwap> equivalentSwap = MakeVanillaSwap(lengths[j], vars.index, 0.06+correction) .withEffectiveDate(startDate) .withFloatingLegSpread(0.0) .withType(type[k]); boost::shared_ptr<Swaption> swaption1 = vars.makeSwaption(swap,exerciseDate,0.20); boost::shared_ptr<Swaption> swaption2 = vars.makeSwaption(equivalentSwap,exerciseDate,0.20); boost::shared_ptr<Swaption> swaption1_cash = vars.makeSwaption(swap,exerciseDate,0.20, Settlement::Cash); boost::shared_ptr<Swaption> swaption2_cash = vars.makeSwaption(equivalentSwap,exerciseDate,0.20, Settlement::Cash); if (std::fabs(swaption1->NPV()-swaption2->NPV()) > 1.0e-6) BOOST_ERROR("wrong spread treatment:" << "\nexercise: " << exerciseDate << "\nlength: " << lengths[j] << "\ntype " << type[k] << "\nspread: " << io::rate(spreads[l]) << "\noriginal swaption value: " << swaption1->NPV() << "\nequivalent swaption value: " << swaption2->NPV()); if (std::fabs(swaption1_cash->NPV()-swaption2_cash->NPV()) > 1.0e-6) BOOST_ERROR("wrong spread treatment:" << "\nexercise date: " << exerciseDate << "\nlength: " << lengths[j] << "\npay " << (type[k] ? "fixed" : "floating") << "\nspread: " << io::rate(spreads[l]) << "\nvalue of original swaption: " << swaption1_cash->NPV() << "\nvalue of equivalent swaption: " << swaption2_cash->NPV()); } } } } }