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 );
    }
}
Example #3
0
// Test inflation cap/floor parity, i.e. that cap-floor = swap, note that this
// is different from nominal because in nominal world standard cap/floors do
// not have the first optionlet.  This is because they set in advance so
// there is no point.  However, yoy inflation generally sets in arrears,
// (actually in arrears with a lag of a few months) thus the first optionlet
// is relevant.  Hence we can do a parity test without a special definition
// of the YoY cap/floor instrument.
void InflationCapFloorTest::testParity() {

    BOOST_TEST_MESSAGE("Testing yoy inflation cap/floor parity...");

    CommonVars vars;

    Integer lengths[] = { 1, 2, 3, 5, 7, 10, 15, 20 };
    // vol is low ...
    Rate strikes[] = { 0., 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 };

    // cap-floor-swap parity is model-independent
    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]);

                    ext::shared_ptr<Instrument> cap
                    = vars.makeYoYCapFloor(YoYInflationCapFloor::Cap,
                                       leg, strikes[j], vols[k], whichPricer);

                    ext::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
                                                strikes[j],
                                                vars.dc,
                                                yoySchedule,
                                                vars.iir,
                                                vars.observationLag,
                                                0.0,        //spread on index
                                                vars.dc,
                                                UnitedKingdom());

                    Handle<YieldTermStructure> hTS(vars.nominalTS);
                    ext::shared_ptr<PricingEngine> sppe(new DiscountingSwapEngine(hTS));
                    swap.setPricingEngine(sppe);

                    // N.B. nominals are 10e6
                    if (std::fabs((cap->NPV()-floor->NPV()) - swap.NPV()) > 1.0e-6) {
                        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());
                    }
                }
            }
        }
    }
    // remove circular refernce
    vars.hy.linkTo(ext::shared_ptr<YoYInflationTermStructure>());
}
Example #4
0
void InflationCapFloorTest::testCachedValue() {

    BOOST_TEST_MESSAGE("Testing Black yoy inflation cap/floor price"
                       " against cached values...");

    CommonVars vars;

    Size whichPricer = 0; // black

    Real K = 0.0295; // one centi-point is fair rate error i.e. < 1 cp
    Size j = 2;
    Leg leg = vars.makeYoYLeg(vars.evaluationDate,j);
    ext::shared_ptr<Instrument> cap
        = vars.makeYoYCapFloor(YoYInflationCapFloor::Cap,leg, K, 0.01, whichPricer);

    ext::shared_ptr<Instrument> floor
        = vars.makeYoYCapFloor(YoYInflationCapFloor::Floor,leg, K, 0.01, whichPricer);


    // close to atm prices
    Real cachedCapNPVblack   = 219.452;
    Real cachedFloorNPVblack =  314.641;
    // N.B. notionals are 10e6.
    BOOST_CHECK_MESSAGE(fabs(cap->NPV()-cachedCapNPVblack)<0.02,"yoy cap cached NPV wrong "
                        <<cap->NPV()<<" should be "<<cachedCapNPVblack<<" Black pricer"
                        <<" diff was "<<(fabs(cap->NPV()-cachedCapNPVblack)));
    BOOST_CHECK_MESSAGE(fabs(floor->NPV()-cachedFloorNPVblack)<0.02,"yoy floor cached NPV wrong "
                        <<floor->NPV()<<" should be "<<cachedFloorNPVblack<<" Black pricer"
                        <<" diff was "<<(fabs(floor->NPV()-cachedFloorNPVblack)));

    whichPricer = 1; // dd

    cap
    = vars.makeYoYCapFloor(YoYInflationCapFloor::Cap,leg, K, 0.01, whichPricer);

    floor
    = vars.makeYoYCapFloor(YoYInflationCapFloor::Floor,leg, K, 0.01, whichPricer);

    // close to atm prices
    Real cachedCapNPVdd   = 9114.61;
    Real cachedFloorNPVdd =  9209.8;
    // N.B. notionals are 10e6.
    BOOST_CHECK_MESSAGE(fabs(cap->NPV()-cachedCapNPVdd)<0.22,"yoy cap cached NPV wrong "
                        <<cap->NPV()<<" should be "<<cachedCapNPVdd<<" dd Black pricer"
                        <<" diff was "<<(fabs(cap->NPV()-cachedCapNPVdd)));
    BOOST_CHECK_MESSAGE(fabs(floor->NPV()-cachedFloorNPVdd)<0.22,"yoy floor cached NPV wrong "
                        <<floor->NPV()<<" should be "<<cachedFloorNPVdd<<" dd Black pricer"
                        <<" diff was "<<(fabs(floor->NPV()-cachedFloorNPVdd)));

    whichPricer = 2; // bachelier

    cap
    = vars.makeYoYCapFloor(YoYInflationCapFloor::Cap,leg, K, 0.01, whichPricer);

    floor
    = vars.makeYoYCapFloor(YoYInflationCapFloor::Floor,leg, K, 0.01, whichPricer);

    // close to atm prices
    Real cachedCapNPVbac   = 8852.4;
    Real cachedFloorNPVbac =  8947.59;
    // N.B. notionals are 10e6.
    BOOST_CHECK_MESSAGE(fabs(cap->NPV()-cachedCapNPVbac)<0.22,"yoy cap cached NPV wrong "
                        <<cap->NPV()<<" should be "<<cachedCapNPVbac<<" bac Black pricer"
                        <<" diff was "<<(fabs(cap->NPV()-cachedCapNPVbac)));
    BOOST_CHECK_MESSAGE(fabs(floor->NPV()-cachedFloorNPVbac)<0.22,"yoy floor cached NPV wrong "
                        <<floor->NPV()<<" should be "<<cachedFloorNPVbac<<" bac Black pricer"
                        <<" diff was "<<(fabs(floor->NPV()-cachedFloorNPVbac)));

    // remove circular refernce
    vars.hy.linkTo(ext::shared_ptr<YoYInflationTermStructure>());
}
Example #5
0
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>());
}