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 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::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 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 InflationCapFloorTest::testConsistency() { BOOST_TEST_MESSAGE("Testing consistency between yoy inflation cap," " floor and collar..."); CommonVars vars; Integer lengths[] = { 1, 2, 3, 5, 7, 10, 15, 20 }; Rate cap_rates[] = { 0.01, 0.025, 0.029, 0.03, 0.031, 0.035, 0.07 }; Rate floor_rates[] = { 0.01, 0.025, 0.029, 0.03, 0.031, 0.035, 0.07 }; Volatility vols[] = { 0.001, 0.005, 0.010, 0.015, 0.020 }; for (Size whichPricer = 0; whichPricer < 3; whichPricer++) { 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.makeYoYLeg(vars.evaluationDate,lengths[i]); ext::shared_ptr<YoYInflationCapFloor> cap = vars.makeYoYCapFloor(YoYInflationCapFloor::Cap, leg, cap_rates[j], vols[l], whichPricer); ext::shared_ptr<YoYInflationCapFloor> floor = vars.makeYoYCapFloor(YoYInflationCapFloor::Floor, leg, floor_rates[k], vols[l], whichPricer); YoYInflationCollar collar(leg,std::vector<Rate>(1,cap_rates[j]), std::vector<Rate>(1,floor_rates[k])); collar.setPricingEngine(vars.makeEngine(vols[l], whichPricer)); if (std::fabs((cap->NPV()-floor->NPV())-collar.NPV()) > 1e-6) { 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. ONE per year Real capletsNPV = 0.0; std::vector<ext::shared_ptr<YoYInflationCapFloor> > caplets; for (Integer m=0; m<lengths[i]*1; m++) { caplets.push_back(cap->optionlet(m)); caplets[m]->setPricingEngine(vars.makeEngine(vols[l], whichPricer)); capletsNPV += caplets[m]->NPV(); } if (std::fabs(cap->NPV() - capletsNPV) > 1e-6) { 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<ext::shared_ptr<YoYInflationCapFloor> > floorlets; for (Integer m=0; m<lengths[i]*1; m++) { floorlets.push_back(floor->optionlet(m)); floorlets[m]->setPricingEngine(vars.makeEngine(vols[l], whichPricer)); floorletsNPV += floorlets[m]->NPV(); } if (std::fabs(floor->NPV() - floorletsNPV) > 1e-6) { 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<ext::shared_ptr<YoYInflationCapFloor> > collarlets; for (Integer m=0; m<lengths[i]*1; m++) { collarlets.push_back(collar.optionlet(m)); collarlets[m]->setPricingEngine(vars.makeEngine(vols[l], whichPricer)); collarletsNPV += collarlets[m]->NPV(); } if (std::fabs(collar.NPV() - collarletsNPV) > 1e-6) { 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" ); } } } } } } } // pricer loop // remove circular refernce vars.hy.linkTo(ext::shared_ptr<YoYInflationTermStructure>()); }