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 ); } }