예제 #1
0
        public void testCachedValue()
        {
            // Testing Black yoy inflation cap/floor price  against cached values...
            CommonVars vars = new CommonVars();

            int whichPricer = 0; // black

            double K = 0.0295; // one centi-point is fair rate error i.e. < 1 cp
            int j = 2;
            List<CashFlow> leg = vars.makeYoYLeg(vars.evaluationDate,j);
            Instrument cap = vars.makeYoYCapFloor(CapFloorType.Cap,leg, K, 0.01, whichPricer);

            Instrument floor = vars.makeYoYCapFloor(CapFloorType.Floor,leg, K, 0.01, whichPricer);

            // close to atm prices
            double cachedCapNPVblack   = 219.452;
            double cachedFloorNPVblack =  314.641;
            // N.B. notionals are 10e6.
            Assert.IsTrue(Math.Abs(cap.NPV()-cachedCapNPVblack)<0.02,"yoy cap cached NPV wrong "
                                    +cap.NPV()+" should be "+cachedCapNPVblack+" Black pricer"
                                    +" diff was "+(Math.Abs(cap.NPV()-cachedCapNPVblack)));
            Assert.IsTrue(Math.Abs(floor.NPV()-cachedFloorNPVblack)<0.02,"yoy floor cached NPV wrong "
                                    +floor.NPV()+" should be "+cachedFloorNPVblack+" Black pricer"
                                    +" diff was "+(Math.Abs(floor.NPV()-cachedFloorNPVblack)));

            whichPricer = 1; // dd

            cap = vars.makeYoYCapFloor(CapFloorType.Cap,leg, K, 0.01, whichPricer);
            floor = vars.makeYoYCapFloor(CapFloorType.Floor,leg, K, 0.01, whichPricer);

            // close to atm prices
            double cachedCapNPVdd   = 9114.61;
            double cachedFloorNPVdd =  9209.8;
            // N.B. notionals are 10e6.
            Assert.IsTrue(Math.Abs(cap.NPV()-cachedCapNPVdd)<0.22,"yoy cap cached NPV wrong "
                                    +cap.NPV()+" should be "+cachedCapNPVdd+" dd Black pricer"
                                    +" diff was "+(Math.Abs(cap.NPV()-cachedCapNPVdd)));
            Assert.IsTrue(Math.Abs(floor.NPV()-cachedFloorNPVdd)<0.22,"yoy floor cached NPV wrong "
                                    +floor.NPV()+" should be "+cachedFloorNPVdd+" dd Black pricer"
                                    +" diff was "+(Math.Abs(floor.NPV()-cachedFloorNPVdd)));

            whichPricer = 2; // bachelier

            cap = vars.makeYoYCapFloor(CapFloorType.Cap,leg, K, 0.01, whichPricer);
            floor = vars.makeYoYCapFloor(CapFloorType.Floor,leg, K, 0.01, whichPricer);

            // close to atm prices
            double cachedCapNPVbac   = 8852.4;
            double cachedFloorNPVbac =  8947.59;
            // N.B. notionals are 10e6.
            Assert.IsTrue(Math.Abs(cap.NPV()-cachedCapNPVbac)<0.22,"yoy cap cached NPV wrong "
                                    +cap.NPV()+" should be "+cachedCapNPVbac+" bac Black pricer"
                                    +" diff was "+(Math.Abs(cap.NPV()-cachedCapNPVbac)));
            Assert.IsTrue(Math.Abs(floor.NPV()-cachedFloorNPVbac)<0.22,"yoy floor cached NPV wrong "
                                    +floor.NPV()+" should be "+cachedFloorNPVbac+" bac Black pricer"
                                    +" diff was "+(Math.Abs(floor.NPV()-cachedFloorNPVbac)));

            // remove circular refernce
            vars.hy.linkTo(new YoYInflationTermStructure());
        }
예제 #2
0
        public void testInstrumentEquality()
        {
            // Testing inflation capped/floored coupon against inflation capfloor instrument...

            CommonVars vars = new CommonVars();

            int[] lengths = { 1, 2, 3, 5, 7, 10, 15, 20 };
            // vol is low ...
            double[] strikes = { 0.01, 0.025, 0.029, 0.03, 0.031, 0.035, 0.07 };
            // yoy inflation vol is generally very low
            double[] 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 (int whichPricer = 0; whichPricer < 3; whichPricer++)
            {
                for (int i = 0; i < lengths.Length; i++)
                {
                    for (int j = 0; j < strikes.Length; j++)
                    {
                        for (int k = 0; k < vols.Length; k++)
                        {
                            List <CashFlow> leg = vars.makeYoYLeg(vars.evaluationDate, lengths[i]);

                            Instrument cap = vars.makeYoYCapFloor(CapFloorType.Cap,
                                                                  leg, strikes[j], vols[k], whichPricer);

                            Instrument floor = vars.makeYoYCapFloor(CapFloorType.Floor,
                                                                    leg, strikes[j], vols[k], whichPricer);

                            Date     from        = vars.nominalTS.link.referenceDate();
                            Date     to          = from + new Period(lengths[i], TimeUnit.Years);
                            Schedule yoySchedule = new MakeSchedule().from(from).to(to)
                                                   .withTenor(new Period(1, TimeUnit.Years))
                                                   .withCalendar(new UnitedKingdom())
                                                   .withConvention(BusinessDayConvention.Unadjusted)
                                                   .backwards().value();

                            YearOnYearInflationSwap swap = new YearOnYearInflationSwap(YearOnYearInflationSwap.Type.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,
                                                                                       new UnitedKingdom());

                            Handle <YieldTermStructure> hTS = new Handle <YieldTermStructure>(vars.nominalTS);
                            IPricingEngine sppe             = new DiscountingSwapEngine(hTS);
                            swap.setPricingEngine(sppe);

                            List <CashFlow> leg2 = vars.makeYoYCapFlooredLeg(whichPricer, from,
                                                                             lengths[i],
                                                                             new InitializedList <double>(lengths[i], strikes[j]), //cap
                                                                             new List <double>(),                                  //floor
                                                                             vols[k],
                                                                             1.0,                                                  // gearing
                                                                             0.0);                                                 // spread

                            List <CashFlow> leg3 = vars.makeYoYCapFlooredLeg(whichPricer, from,
                                                                             lengths[i],
                                                                             new List <double>(),                                  // cap
                                                                             new InitializedList <double>(lengths[i], strikes[j]), //floor
                                                                             vols[k],
                                                                             1.0,                                                  // gearing
                                                                             0.0);                                                 // spread

                            // N.B. nominals are 10e6
                            double capped = CashFlows.npv(leg2, vars.nominalTS, false);
                            if (Math.Abs(capped - (swap.NPV() - cap.NPV())) > 1.0e-6)
                            {
                                QAssert.Fail(
                                    "capped coupon != swap(0) - cap:\n"
                                    + "    length:      " + lengths[i] + " years\n"
                                    + "    volatility:  " + vols[k] + "\n"
                                    + "    strike:      " + strikes[j] + "\n"
                                    + "    cap value:   " + cap.NPV() + "\n"
                                    + "    swap value:  " + swap.NPV() + "\n"
                                    + "   capped coupon " + capped);
                            }


                            // N.B. nominals are 10e6
                            double floored = CashFlows.npv(leg3, vars.nominalTS, false);
                            if (Math.Abs(floored - (swap.NPV() + floor.NPV())) > 1.0e-6)
                            {
                                QAssert.Fail(
                                    "floored coupon != swap(0) + floor :\n"
                                    + "    length:      " + lengths[i] + " years\n"
                                    + "    volatility:  " + vols[k] + "\n"
                                    + "    strike:      " + strikes[j] + "\n"
                                    + "    floor value: " + floor.NPV() + "\n"
                                    + "    swap value:  " + swap.NPV() + "\n"
                                    + "  floored coupon " + floored);
                            }
                        }
                    }
                }
            }
            // remove circular refernce
            vars.hy.linkTo(null);
        }
예제 #3
0
        public void testParity()
        {
            // Testing yoy inflation cap/floor parity...

             CommonVars vars = new CommonVars();

             int[] lengths = { 1, 2, 3, 5, 7, 10, 15, 20 };
             // vol is low ...
             double[] strikes = { 0.0, 0.025, 0.029, 0.03, 0.031, 0.035, 0.07 };
             // yoy inflation vol is generally very low
             double[] vols = { 0.001, 0.005, 0.010, 0.015, 0.020 };

             // cap-floor-swap parity is model-independent
             for (int whichPricer = 0; whichPricer < 3; whichPricer++) {
                  for (int i=0; i<lengths.Length; i++) {
                        for (int j=0; j<strikes.Length; j++) {
                             for (int k=0; k<vols.Length; k++) {

                                  List<CashFlow> leg = vars.makeYoYLeg(vars.evaluationDate,lengths[i]);

                                  Instrument cap = vars.makeYoYCapFloor(CapFloorType.Cap,
                                                            leg, strikes[j], vols[k], whichPricer);

                                  Instrument floor = vars.makeYoYCapFloor(CapFloorType.Floor,
                                                            leg, strikes[j], vols[k], whichPricer);

                                  Date from = vars.nominalTS.link.referenceDate();
                                  Date to = from+new Period(lengths[i],TimeUnit.Years);
                                  Schedule yoySchedule = new MakeSchedule().from(from).to(to)
                                  .withTenor(new Period(1,TimeUnit.Years))
                                  .withConvention(BusinessDayConvention.Unadjusted)
                                  .withCalendar(new UnitedKingdom()).backwards().value();

                                  YearOnYearInflationSwap swap = new YearOnYearInflationSwap
                                      (YearOnYearInflationSwap.Type.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,
                                        new UnitedKingdom());

                                  Handle<YieldTermStructure> hTS = new Handle<YieldTermStructure>(vars.nominalTS);
                                  IPricingEngine sppe = new DiscountingSwapEngine(hTS);
                                  swap.setPricingEngine(sppe);

                                  // N.B. nominals are 10e6
                                  if (Math.Abs((cap.NPV()-floor.NPV()) - swap.NPV()) > 1.0e-6) {
                                        Assert.Fail(
                                                 "put/call parity violated:\n"
                                                 + "    length:      " + lengths[i] + " years\n"
                                                 + "    volatility:  " + vols[k] + "\n"
                                                 + "    strike:      " + strikes[j] + "\n"
                                                 + "    cap value:   " + cap.NPV() + "\n"
                                                 + "    floor value: " + floor.NPV() + "\n"
                                                 + "    swap value:  " + swap.NPV());
                                  }
                             }
                        }
                  }
             }
             // remove circular refernce
             vars.hy.linkTo(new YoYInflationTermStructure());
        }
예제 #4
0
        public void testConsistency()
        {
            // Testing consistency between yoy inflation cap,floor and collar...
            CommonVars vars = new CommonVars();

            int[] lengths = { 1, 2, 3, 5, 7, 10, 15, 20 };
            double[] cap_rates = { 0.01, 0.025, 0.029, 0.03, 0.031, 0.035, 0.07 };
            double[] floor_rates = { 0.01, 0.025, 0.029, 0.03, 0.031, 0.035, 0.07 };
            double[] vols = { 0.001, 0.005, 0.010, 0.015, 0.020 };

            for (int whichPricer = 0; whichPricer < 3; whichPricer++)
            {
                for (int i=0; i<lengths.Length; i++)
                {
                    for (int j=0; j<cap_rates.Length; j++)
                    {
                        for (int k=0; k<floor_rates.Length; k++)
                        {
                            for (int l=0; l<vols.Length; l++)
                            {

                                List<CashFlow> leg = vars.makeYoYLeg(vars.evaluationDate,lengths[i]);

                                YoYInflationCapFloor cap = vars.makeYoYCapFloor(CapFloorType.Cap,
                                           leg, cap_rates[j], vols[l], whichPricer);

                                YoYInflationCapFloor floor = vars.makeYoYCapFloor(CapFloorType.Floor,
                                           leg, floor_rates[k], vols[l], whichPricer);

                                YoYInflationCollar collar = new YoYInflationCollar(leg,new List<double>(){cap_rates[j]},
                                  new List<double>(){floor_rates[k]});

                                collar.setPricingEngine(vars.makeEngine(vols[l], whichPricer));

                                if (Math.Abs((cap.NPV()-floor.NPV())-collar.NPV()) > 1e-6)
                                {
                                    Assert.Fail(
                                   "inconsistency between cap, floor and collar:\n"
                                   + "    length:       " + lengths[i] + " years\n"
                                   + "    volatility:   " +  "\n"
                                   + "    cap value:    " + cap.NPV()
                                   + " at strike: " + "\n"
                                   + "    floor value:  " + floor.NPV()
                                   + " at strike: " +  "\n"
                                   + "    collar value: " + collar.NPV());

                                }
                                // test re-composition by optionlets, N.B. ONE per year
                                double capletsNPV = 0.0;
                                List<YoYInflationCapFloor> caplets = new List<YoYInflationCapFloor>();
                                for (int m=0; m<lengths[i]*1; m++)
                                {
                                    caplets.Add(cap.optionlet(m));
                                    caplets[m].setPricingEngine(vars.makeEngine(vols[l], whichPricer));
                                    capletsNPV += caplets[m].NPV();
                                }

                                if (Math.Abs(cap.NPV() - capletsNPV) > 1e-6)
                                {
                                    Assert.Fail(
                                    "sum of caplet NPVs does not equal cap NPV:\n"
                                    + "    length:       " + lengths[i] + " years\n"
                                    + "    volatility:   " +  "\n"
                                    + "    cap value:    " + cap.NPV()
                                    + " at strike: " +  "\n"
                                    + "    sum of caplets value:  " + capletsNPV
                                    + " at strike (first): " + caplets[0].capRates()[0] + "\n"
                                    );
                                }

                                double floorletsNPV = 0.0;
                                List<YoYInflationCapFloor>  floorlets = new List<YoYInflationCapFloor>();
                                for (int m=0; m<lengths[i]*1; m++)
                                {
                                    floorlets.Add(floor.optionlet(m));
                                    floorlets[m].setPricingEngine(vars.makeEngine(vols[l], whichPricer));
                                    floorletsNPV += floorlets[m].NPV();
                                }

                                if (Math.Abs(floor.NPV() - floorletsNPV) > 1e-6)
                                {
                                    Assert.Fail(
                                    "sum of floorlet NPVs does not equal floor NPV:\n"
                                    + "    length:       " + lengths[i] + " years\n"
                                    + "    volatility:   " +  "\n"
                                    + "    cap value:    " + floor.NPV()
                                    + " at strike: " + floor_rates[j] + "\n"
                                    + "    sum of floorlets value:  " + floorletsNPV
                                    + " at strike (first): " + floorlets[0].floorRates()[0] + "\n"
                                    );
                                }

                                double collarletsNPV = 0.0;
                                List<YoYInflationCapFloor>  collarlets = new List<YoYInflationCapFloor>();
                                for (int m=0; m<lengths[i]*1; m++)
                                {
                                    collarlets.Add(collar.optionlet(m));
                                    collarlets[m].setPricingEngine(vars.makeEngine(vols[l], whichPricer));
                                    collarletsNPV += collarlets[m].NPV();
                                }

                                if (Math.Abs(collar.NPV() - collarletsNPV) > 1e-6)
                                {
                                    Assert.Fail(
                                    "sum of collarlet NPVs does not equal floor NPV:\n"
                                    + "    length:       " + lengths[i] + " years\n"
                                    + "    volatility:   " + vols[l] + "\n"
                                    + "    cap value:    " + collar.NPV()
                                    + " at strike floor: " + floor_rates[j]
                                    + " at strike cap: " + cap_rates[j] + "\n"
                                    + "    sum of collarlets value:  " + collarletsNPV
                                    + " at strike floor (first): " + collarlets[0].floorRates()[0]
                                    + " at strike cap (first): " + collarlets[0].capRates()[0] + "\n"
                                    );
                            }
                            }
                        }
                    }
                }
            } // pricer loop
            // remove circular refernce
            vars.hy.linkTo(new YoYInflationTermStructure());
        }
예제 #5
0
        public void testCachedValue()
        {
            // Testing Black yoy inflation cap/floor price  against cached values...
            CommonVars vars = new CommonVars();

            int whichPricer = 0;           // black

            double          K   = 0.0295;  // one centi-point is fair rate error i.e. < 1 cp
            int             j   = 2;
            List <CashFlow> leg = vars.makeYoYLeg(vars.evaluationDate, j);
            Instrument      cap = vars.makeYoYCapFloor(CapFloorType.Cap, leg, K, 0.01, whichPricer);

            Instrument floor = vars.makeYoYCapFloor(CapFloorType.Floor, leg, K, 0.01, whichPricer);


            // close to atm prices
            double cachedCapNPVblack   = 219.452;
            double cachedFloorNPVblack = 314.641;

            // N.B. notionals are 10e6.
            Assert.IsTrue(Math.Abs(cap.NPV() - cachedCapNPVblack) < 0.02, "yoy cap cached NPV wrong "
                          + cap.NPV() + " should be " + cachedCapNPVblack + " Black pricer"
                          + " diff was " + (Math.Abs(cap.NPV() - cachedCapNPVblack)));
            Assert.IsTrue(Math.Abs(floor.NPV() - cachedFloorNPVblack) < 0.02, "yoy floor cached NPV wrong "
                          + floor.NPV() + " should be " + cachedFloorNPVblack + " Black pricer"
                          + " diff was " + (Math.Abs(floor.NPV() - cachedFloorNPVblack)));

            whichPricer = 1;             // dd

            cap   = vars.makeYoYCapFloor(CapFloorType.Cap, leg, K, 0.01, whichPricer);
            floor = vars.makeYoYCapFloor(CapFloorType.Floor, leg, K, 0.01, whichPricer);

            // close to atm prices
            double cachedCapNPVdd   = 9114.61;
            double cachedFloorNPVdd = 9209.8;

            // N.B. notionals are 10e6.
            Assert.IsTrue(Math.Abs(cap.NPV() - cachedCapNPVdd) < 0.22, "yoy cap cached NPV wrong "
                          + cap.NPV() + " should be " + cachedCapNPVdd + " dd Black pricer"
                          + " diff was " + (Math.Abs(cap.NPV() - cachedCapNPVdd)));
            Assert.IsTrue(Math.Abs(floor.NPV() - cachedFloorNPVdd) < 0.22, "yoy floor cached NPV wrong "
                          + floor.NPV() + " should be " + cachedFloorNPVdd + " dd Black pricer"
                          + " diff was " + (Math.Abs(floor.NPV() - cachedFloorNPVdd)));

            whichPricer = 2;             // bachelier

            cap   = vars.makeYoYCapFloor(CapFloorType.Cap, leg, K, 0.01, whichPricer);
            floor = vars.makeYoYCapFloor(CapFloorType.Floor, leg, K, 0.01, whichPricer);

            // close to atm prices
            double cachedCapNPVbac   = 8852.4;
            double cachedFloorNPVbac = 8947.59;

            // N.B. notionals are 10e6.
            Assert.IsTrue(Math.Abs(cap.NPV() - cachedCapNPVbac) < 0.22, "yoy cap cached NPV wrong "
                          + cap.NPV() + " should be " + cachedCapNPVbac + " bac Black pricer"
                          + " diff was " + (Math.Abs(cap.NPV() - cachedCapNPVbac)));
            Assert.IsTrue(Math.Abs(floor.NPV() - cachedFloorNPVbac) < 0.22, "yoy floor cached NPV wrong "
                          + floor.NPV() + " should be " + cachedFloorNPVbac + " bac Black pricer"
                          + " diff was " + (Math.Abs(floor.NPV() - cachedFloorNPVbac)));

            // remove circular refernce
            vars.hy.linkTo(new YoYInflationTermStructure());
        }
예제 #6
0
        public void testParity()
        {
            // Testing yoy inflation cap/floor parity...

            CommonVars vars = new CommonVars();

            int[] lengths = { 1, 2, 3, 5, 7, 10, 15, 20 };
            // vol is low ...
            double[] strikes = { 0.0, 0.025, 0.029, 0.03, 0.031, 0.035, 0.07 };
            // yoy inflation vol is generally very low
            double[] vols = { 0.001, 0.005, 0.010, 0.015, 0.020 };

            // cap-floor-swap parity is model-independent
            for (int whichPricer = 0; whichPricer < 3; whichPricer++)
            {
                for (int i = 0; i < lengths.Length; i++)
                {
                    for (int j = 0; j < strikes.Length; j++)
                    {
                        for (int k = 0; k < vols.Length; k++)
                        {
                            List <CashFlow> leg = vars.makeYoYLeg(vars.evaluationDate, lengths[i]);

                            Instrument cap = vars.makeYoYCapFloor(CapFloorType.Cap,
                                                                  leg, strikes[j], vols[k], whichPricer);

                            Instrument floor = vars.makeYoYCapFloor(CapFloorType.Floor,
                                                                    leg, strikes[j], vols[k], whichPricer);

                            Date     from        = vars.nominalTS.link.referenceDate();
                            Date     to          = from + new Period(lengths[i], TimeUnit.Years);
                            Schedule yoySchedule = new MakeSchedule().from(from).to(to)
                                                   .withTenor(new Period(1, TimeUnit.Years))
                                                   .withConvention(BusinessDayConvention.Unadjusted)
                                                   .withCalendar(new UnitedKingdom()).backwards().value();

                            YearOnYearInflationSwap swap = new YearOnYearInflationSwap
                                                               (YearOnYearInflationSwap.Type.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,
                                                               new UnitedKingdom());

                            Handle <YieldTermStructure> hTS = new Handle <YieldTermStructure>(vars.nominalTS);
                            IPricingEngine sppe             = new DiscountingSwapEngine(hTS);
                            swap.setPricingEngine(sppe);

                            // N.B. nominals are 10e6
                            if (Math.Abs((cap.NPV() - floor.NPV()) - swap.NPV()) > 1.0e-6)
                            {
                                Assert.Fail(
                                    "put/call parity violated:\n"
                                    + "    length:      " + lengths[i] + " years\n"
                                    + "    volatility:  " + vols[k] + "\n"
                                    + "    strike:      " + strikes[j] + "\n"
                                    + "    cap value:   " + cap.NPV() + "\n"
                                    + "    floor value: " + floor.NPV() + "\n"
                                    + "    swap value:  " + swap.NPV());
                            }
                        }
                    }
                }
            }
            // remove circular refernce
            vars.hy.linkTo(new YoYInflationTermStructure());
        }
예제 #7
0
        public void testConsistency()
        {
            // Testing consistency between yoy inflation cap,floor and collar...
            CommonVars vars = new CommonVars();

            int[]    lengths     = { 1, 2, 3, 5, 7, 10, 15, 20 };
            double[] cap_rates   = { 0.01, 0.025, 0.029, 0.03, 0.031, 0.035, 0.07 };
            double[] floor_rates = { 0.01, 0.025, 0.029, 0.03, 0.031, 0.035, 0.07 };
            double[] vols        = { 0.001, 0.005, 0.010, 0.015, 0.020 };

            for (int whichPricer = 0; whichPricer < 3; whichPricer++)
            {
                for (int i = 0; i < lengths.Length; i++)
                {
                    for (int j = 0; j < cap_rates.Length; j++)
                    {
                        for (int k = 0; k < floor_rates.Length; k++)
                        {
                            for (int l = 0; l < vols.Length; l++)
                            {
                                List <CashFlow> leg = vars.makeYoYLeg(vars.evaluationDate, lengths[i]);

                                YoYInflationCapFloor cap = vars.makeYoYCapFloor(CapFloorType.Cap,
                                                                                leg, cap_rates[j], vols[l], whichPricer);

                                YoYInflationCapFloor floor = vars.makeYoYCapFloor(CapFloorType.Floor,
                                                                                  leg, floor_rates[k], vols[l], whichPricer);

                                YoYInflationCollar collar = new YoYInflationCollar(leg, new List <double>()
                                {
                                    cap_rates[j]
                                },
                                                                                   new List <double>()
                                {
                                    floor_rates[k]
                                });

                                collar.setPricingEngine(vars.makeEngine(vols[l], whichPricer));

                                if (Math.Abs((cap.NPV() - floor.NPV()) - collar.NPV()) > 1e-6)
                                {
                                    Assert.Fail(
                                        "inconsistency between cap, floor and collar:\n"
                                        + "    length:       " + lengths[i] + " years\n"
                                        + "    volatility:   " + "\n"
                                        + "    cap value:    " + cap.NPV()
                                        + " at strike: " + "\n"
                                        + "    floor value:  " + floor.NPV()
                                        + " at strike: " + "\n"
                                        + "    collar value: " + collar.NPV());
                                }
                                // test re-composition by optionlets, N.B. ONE per year
                                double capletsNPV = 0.0;
                                List <YoYInflationCapFloor> caplets = new List <YoYInflationCapFloor>();
                                for (int m = 0; m < lengths[i] * 1; m++)
                                {
                                    caplets.Add(cap.optionlet(m));
                                    caplets[m].setPricingEngine(vars.makeEngine(vols[l], whichPricer));
                                    capletsNPV += caplets[m].NPV();
                                }

                                if (Math.Abs(cap.NPV() - capletsNPV) > 1e-6)
                                {
                                    Assert.Fail(
                                        "sum of caplet NPVs does not equal cap NPV:\n"
                                        + "    length:       " + lengths[i] + " years\n"
                                        + "    volatility:   " + "\n"
                                        + "    cap value:    " + cap.NPV()
                                        + " at strike: " + "\n"
                                        + "    sum of caplets value:  " + capletsNPV
                                        + " at strike (first): " + caplets[0].capRates()[0] + "\n"
                                        );
                                }

                                double floorletsNPV = 0.0;
                                List <YoYInflationCapFloor> floorlets = new List <YoYInflationCapFloor>();
                                for (int m = 0; m < lengths[i] * 1; m++)
                                {
                                    floorlets.Add(floor.optionlet(m));
                                    floorlets[m].setPricingEngine(vars.makeEngine(vols[l], whichPricer));
                                    floorletsNPV += floorlets[m].NPV();
                                }

                                if (Math.Abs(floor.NPV() - floorletsNPV) > 1e-6)
                                {
                                    Assert.Fail(
                                        "sum of floorlet NPVs does not equal floor NPV:\n"
                                        + "    length:       " + lengths[i] + " years\n"
                                        + "    volatility:   " + "\n"
                                        + "    cap value:    " + floor.NPV()
                                        + " at strike: " + floor_rates[j] + "\n"
                                        + "    sum of floorlets value:  " + floorletsNPV
                                        + " at strike (first): " + floorlets[0].floorRates()[0] + "\n"
                                        );
                                }

                                double collarletsNPV = 0.0;
                                List <YoYInflationCapFloor> collarlets = new List <YoYInflationCapFloor>();
                                for (int m = 0; m < lengths[i] * 1; m++)
                                {
                                    collarlets.Add(collar.optionlet(m));
                                    collarlets[m].setPricingEngine(vars.makeEngine(vols[l], whichPricer));
                                    collarletsNPV += collarlets[m].NPV();
                                }

                                if (Math.Abs(collar.NPV() - collarletsNPV) > 1e-6)
                                {
                                    Assert.Fail(
                                        "sum of collarlet NPVs does not equal floor NPV:\n"
                                        + "    length:       " + lengths[i] + " years\n"
                                        + "    volatility:   " + vols[l] + "\n"
                                        + "    cap value:    " + collar.NPV()
                                        + " at strike floor: " + floor_rates[j]
                                        + " at strike cap: " + cap_rates[j] + "\n"
                                        + "    sum of collarlets value:  " + collarletsNPV
                                        + " at strike floor (first): " + collarlets[0].floorRates()[0]
                                        + " at strike cap (first): " + collarlets[0].capRates()[0] + "\n"
                                        );
                                }
                            }
                        }
                    }
                }
            }             // pricer loop
            // remove circular refernce
            vars.hy.linkTo(new YoYInflationTermStructure());
        }