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()); }
public void testImpliedVolatility() { CommonVars vars = new CommonVars(); int maxEvaluations = 100; double tolerance = 1.0e-6; CapFloorType[] types = { CapFloorType.Cap, CapFloorType.Floor }; double[] strikes = { 0.02, 0.03, 0.04 }; int[] lengths = { 1, 5, 10 }; // test data double[] rRates = { 0.02, 0.03, 0.04 }; double[] vols = { 0.01, 0.20, 0.30, 0.70, 0.90 }; for (int k = 0; k < lengths.Length; k++) { List <CashFlow> leg = vars.makeLeg(vars.settlement, lengths[k]); for (int i = 0; i < types.Length; i++) { for (int j = 0; j < strikes.Length; j++) { CapFloor capfloor = vars.makeCapFloor(types[i], leg, strikes[j], 0.0); for (int n = 0; n < rRates.Length; n++) { for (int m = 0; m < vols.Length; m++) { double r = rRates[n]; double v = vols[m]; vars.termStructure.linkTo(Utilities.flatRate(vars.settlement, r, new Actual360())); capfloor.setPricingEngine(vars.makeEngine(v)); double value = capfloor.NPV(); double implVol = 0.0; try { implVol = capfloor.impliedVolatility(value, vars.termStructure, 0.10, tolerance, maxEvaluations); } catch (Exception e) { // couldn't bracket? capfloor.setPricingEngine(vars.makeEngine(0.0)); double value2 = capfloor.NPV(); if (Math.Abs(value - value2) < tolerance) { // ok, just skip: continue; } // otherwise, report error QAssert.Fail("implied vol failure: " + typeToString(types[i]) + " strike: " + strikes[j] + " risk-free: " + r + " length: " + lengths[k] + "Y" + " volatility: " + v + e.Message); } if (Math.Abs(implVol - v) > tolerance) { // the difference might not matter capfloor.setPricingEngine(vars.makeEngine(implVol)); double value2 = capfloor.NPV(); if (Math.Abs(value - value2) > tolerance) { QAssert.Fail( typeToString(types[i]) + ":" + " strike: " + strikes[j] + "\n" + " risk-free rate: " + r + "\n" + " length: " + lengths[k] + " years\n\n" + " original volatility: " + v + "\n" + " price: " + value + "\n" + " implied volatility: " + implVol + "\n" + " corresponding price: " + value2); } } } } } } } }
public void testImpliedVolatility() { //"Testing implied volatility for swaptions..."; CommonVars vars=new CommonVars(); int maxEvaluations = 100; double tolerance = 1.0e-08; Settlement.Type[] types = { Settlement.Type.Physical, Settlement.Type.Cash }; // test data double[] strikes = { 0.02, 0.03, 0.04, 0.05, 0.06, 0.07 }; double[] vols = { 0.01, 0.05, 0.10, 0.20, 0.30, 0.70, 0.90 }; for (int i = 0; i < exercises.Length; i++) { for (int j = 0; j < lengths.Length; j++) { Date exerciseDate = vars.calendar.advance(vars.today, exercises[i]); Date startDate = vars.calendar.advance(exerciseDate, vars.settlementDays, TimeUnit.Days); Date maturity = vars.calendar.advance(startDate, lengths[j], vars.floatingConvention); for (int t = 0; t < strikes.Length; t++) { for (int k = 0; k < type.Length; k++) { VanillaSwap swap = new MakeVanillaSwap(lengths[j], vars.index, strikes[t]) .withEffectiveDate(startDate) .withFloatingLegSpread(0.0) .withType(type[k]); for (int h = 0; h < types.Length; h++) { for (int u = 0; u < vols.Length; u++) { Swaption swaption = vars.makeSwaption(swap, exerciseDate, vols[u], types[h]); // Black price double value = swaption.NPV(); double implVol = 0.0; try { implVol = swaption.impliedVolatility(value, vars.termStructure, 0.10, tolerance, maxEvaluations); } catch (System.Exception e) { // couldn't bracket? swaption.setPricingEngine(vars.makeEngine(0.0)); double value2 = swaption.NPV(); if (Math.Abs(value - value2) < tolerance) { // ok, just skip: continue; } // otherwise, report error Assert.Fail("implied vol failure: " + exercises[i] + "x" + lengths[j] + " " + type[k] + "\nsettlement: " + types[h] + "\nstrike " + strikes[t] + "\natm level: " + swap.fairRate() + "\nvol: " + vols[u] + "\nprice: " + value + "\n" + e.Message.ToString()); } if (Math.Abs(implVol - vols[u]) > tolerance) { // the difference might not matter swaption.setPricingEngine(vars.makeEngine(implVol)); double value2 = swaption.NPV(); if (Math.Abs(value - value2) > tolerance) { Assert.Fail("implied vol failure: " + exercises[i] + "x" + lengths[j] + " " + type[k] + "\nsettlement: " + types[h] + "\nstrike " + strikes[t] + "\natm level: " + swap.fairRate() + "\nvol: " + vols[u] + "\nprice: " + value + "\nimplied vol: " + implVol + "\nimplied price: " + value2); } } } } } } } } }
public void testDecomposition() { // Testing collared coupon against its decomposition... CommonVars vars= new CommonVars(); double tolerance = 1e-10; double npvVanilla,npvCappedLeg,npvFlooredLeg,npvCollaredLeg,npvCap,npvFloor,npvCollar; double error; double floorstrike = 0.05; double capstrike = 0.10; InitializedList<double> caps = new InitializedList<double>(vars.length,capstrike); List<double> caps0 = new List<double>(); InitializedList<double> floors = new InitializedList<double>(vars.length,floorstrike); List<double> floors0 = new List<double>(); double gearing_p = 0.5; double spread_p = 0.002; double gearing_n = -1.5; double spread_n = 0.12; // fixed leg with zero rate List<CashFlow> fixedLeg = vars.makeFixedLeg(vars.startDate,vars.length); // floating leg with gearing=1 and spread=0 List<CashFlow> floatLeg = vars.makeYoYLeg(vars.startDate,vars.length); // floating leg with positive gearing (gearing_p) and spread<>0 List<CashFlow> floatLeg_p = vars.makeYoYLeg(vars.startDate,vars.length,gearing_p,spread_p); // floating leg with negative gearing (gearing_n) and spread<>0 List<CashFlow> 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 = new Swap(fixedLeg,floatLeg); // Swap with null fixed leg and floating leg with positive gearing and spread<>0 Swap vanillaLeg_p = new Swap(fixedLeg,floatLeg_p); // Swap with null fixed leg and floating leg with negative gearing and spread<>0 Swap vanillaLeg_n = new Swap(fixedLeg,floatLeg_n); IPricingEngine 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 // int whichPricer = 0; // Case gearing = 1 and spread = 0 List<CashFlow> cappedLeg = vars.makeYoYCapFlooredLeg(whichPricer,vars.startDate,vars.length, caps,floors0,vars.volatility); Swap capLeg = new Swap(fixedLeg,cappedLeg); capLeg.setPricingEngine(engine); YoYInflationCap cap = new YoYInflationCap(floatLeg, new List<double>(){capstrike}); cap.setPricingEngine(vars.makeEngine(vars.volatility,whichPricer)); npvVanilla = vanillaLeg.NPV(); npvCappedLeg = capLeg.NPV(); npvCap = cap.NPV(); error = Math.Abs(npvCappedLeg - (npvVanilla-npvCap)); if (error>tolerance) { Assert.Fail("\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 // List<CashFlow> flooredLeg = vars.makeYoYCapFlooredLeg(whichPricer,vars.startDate,vars.length, caps0,floors,vars.volatility); Swap floorLeg = new Swap(fixedLeg,flooredLeg); floorLeg.setPricingEngine(engine); YoYInflationFloor floor= new YoYInflationFloor(floatLeg, new List<double>(){floorstrike}); floor.setPricingEngine(vars.makeEngine(vars.volatility,whichPricer)); npvFlooredLeg = floorLeg.NPV(); npvFloor = floor.NPV(); error = Math.Abs(npvFlooredLeg-(npvVanilla + npvFloor)); if (error>tolerance) { Assert.Fail("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 // List<CashFlow> collaredLeg = vars.makeYoYCapFlooredLeg(whichPricer,vars.startDate,vars.length, caps,floors,vars.volatility); Swap collarLeg = new Swap(fixedLeg,collaredLeg); collarLeg.setPricingEngine(engine); YoYInflationCollar collar = new YoYInflationCollar(floatLeg, new List<double>(){capstrike}, new List<double>(){floorstrike}); collar.setPricingEngine(vars.makeEngine(vars.volatility,whichPricer)); npvCollaredLeg = collarLeg.NPV(); npvCollar = collar.NPV(); error = Math.Abs(npvCollaredLeg -(npvVanilla - npvCollar)); if (error>tolerance) { Assert.Fail("\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 List<CashFlow> cappedLeg_p = vars.makeYoYCapFlooredLeg(whichPricer,vars.startDate,vars.length,caps,floors0, vars.volatility,gearing_p,spread_p); Swap capLeg_p = new Swap(fixedLeg,cappedLeg_p); capLeg_p.setPricingEngine(engine); YoYInflationCap cap_p = new YoYInflationCap(floatLeg_p,new List<double>(){capstrike}); cap_p.setPricingEngine(vars.makeEngine(vars.volatility,whichPricer)); npvVanilla = vanillaLeg_p.NPV(); npvCappedLeg = capLeg_p.NPV(); npvCap = cap_p.NPV(); error = Math.Abs(npvCappedLeg - (npvVanilla-npvCap)); if (error>tolerance) { Assert.Fail("\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 List<CashFlow> cappedLeg_n = vars.makeYoYCapFlooredLeg(whichPricer,vars.startDate,vars.length,caps,floors0, vars.volatility,gearing_n,spread_n); Swap capLeg_n = new Swap(fixedLeg,cappedLeg_n); capLeg_n.setPricingEngine(engine); YoYInflationFloor floor_n = new YoYInflationFloor(floatLeg,new List<double>(){(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 = Math.Abs(npvCappedLeg - (npvVanilla+ gearing_n*npvFloor)); if (error>tolerance) { Assert.Fail("\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 List<CashFlow> flooredLeg_p1 = vars.makeYoYCapFlooredLeg(whichPricer,vars.startDate,vars.length,caps0,floors, vars.volatility,gearing_p,spread_p); Swap floorLeg_p1 = new Swap(fixedLeg,flooredLeg_p1); floorLeg_p1.setPricingEngine(engine); YoYInflationFloor floor_p1 = new YoYInflationFloor(floatLeg_p,new List<double>(){floorstrike}); floor_p1.setPricingEngine(vars.makeEngine(vars.volatility,whichPricer)); npvVanilla = vanillaLeg_p.NPV(); npvFlooredLeg = floorLeg_p1.NPV(); npvFloor = floor_p1.NPV(); error = Math.Abs(npvFlooredLeg - (npvVanilla+npvFloor)); if (error>tolerance) { Assert.Fail("\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 List<CashFlow> flooredLeg_n = vars.makeYoYCapFlooredLeg(whichPricer,vars.startDate,vars.length,caps0,floors, vars.volatility,gearing_n,spread_n); Swap floorLeg_n = new Swap(fixedLeg,flooredLeg_n); floorLeg_n.setPricingEngine(engine); YoYInflationCap cap_n = new YoYInflationCap(floatLeg,new List<double>(){(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 = Math.Abs(npvFlooredLeg - (npvVanilla - gearing_n*npvCap)); if (error>tolerance) { Assert.Fail("\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 List<CashFlow> collaredLeg_p = vars.makeYoYCapFlooredLeg(whichPricer,vars.startDate,vars.length,caps,floors, vars.volatility,gearing_p,spread_p); Swap collarLeg_p1 = new Swap(fixedLeg,collaredLeg_p); collarLeg_p1.setPricingEngine(engine); YoYInflationCollar collar_p = new YoYInflationCollar(floatLeg_p, new List<double>(){capstrike}, new List<double>(){floorstrike}); collar_p.setPricingEngine(vars.makeEngine(vars.volatility,whichPricer)); npvVanilla = vanillaLeg_p.NPV(); npvCollaredLeg = collarLeg_p1.NPV(); npvCollar = collar_p.NPV(); error = Math.Abs(npvCollaredLeg - (npvVanilla - npvCollar)); if (error>tolerance) { Assert.Fail("\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 List<CashFlow> collaredLeg_n = vars.makeYoYCapFlooredLeg(whichPricer,vars.startDate,vars.length,caps,floors, vars.volatility,gearing_n,spread_n); Swap collarLeg_n1 = new Swap(fixedLeg,collaredLeg_n); collarLeg_n1.setPricingEngine(engine); YoYInflationCollar collar_n = new YoYInflationCollar(floatLeg, new List<double>(){(floorstrike-spread_n)/gearing_n}, new List<double>(){(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 = Math.Abs(npvCollaredLeg - (npvVanilla - gearing_n*npvCollar)); if (error>tolerance) { Assert.Fail("\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 ); } // remove circular refernce vars.hy.linkTo(new YoYInflationTermStructure()); }
public void testDecomposition() { // Testing collared coupon against its decomposition... CommonVars vars = new CommonVars(); double tolerance = 1e-10; double npvVanilla, npvCappedLeg, npvFlooredLeg, npvCollaredLeg, npvCap, npvFloor, npvCollar; double error; double floorstrike = 0.05; double capstrike = 0.10; InitializedList <double> caps = new InitializedList <double>(vars.length, capstrike); List <double> caps0 = new List <double>(); InitializedList <double> floors = new InitializedList <double>(vars.length, floorstrike); List <double> floors0 = new List <double>(); double gearing_p = 0.5; double spread_p = 0.002; double gearing_n = -1.5; double spread_n = 0.12; // fixed leg with zero rate List <CashFlow> fixedLeg = vars.makeFixedLeg(vars.startDate, vars.length); // floating leg with gearing=1 and spread=0 List <CashFlow> floatLeg = vars.makeYoYLeg(vars.startDate, vars.length); // floating leg with positive gearing (gearing_p) and spread<>0 List <CashFlow> floatLeg_p = vars.makeYoYLeg(vars.startDate, vars.length, gearing_p, spread_p); // floating leg with negative gearing (gearing_n) and spread<>0 List <CashFlow> 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 = new Swap(fixedLeg, floatLeg); // Swap with null fixed leg and floating leg with positive gearing and spread<>0 Swap vanillaLeg_p = new Swap(fixedLeg, floatLeg_p); // Swap with null fixed leg and floating leg with negative gearing and spread<>0 Swap vanillaLeg_n = new Swap(fixedLeg, floatLeg_n); IPricingEngine 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 // int whichPricer = 0; // Case gearing = 1 and spread = 0 List <CashFlow> cappedLeg = vars.makeYoYCapFlooredLeg(whichPricer, vars.startDate, vars.length, caps, floors0, vars.volatility); Swap capLeg = new Swap(fixedLeg, cappedLeg); capLeg.setPricingEngine(engine); YoYInflationCap cap = new YoYInflationCap(floatLeg, new List <double>() { capstrike }); cap.setPricingEngine(vars.makeEngine(vars.volatility, whichPricer)); npvVanilla = vanillaLeg.NPV(); npvCappedLeg = capLeg.NPV(); npvCap = cap.NPV(); error = Math.Abs(npvCappedLeg - (npvVanilla - npvCap)); if (error > tolerance) { QAssert.Fail("\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 // List <CashFlow> flooredLeg = vars.makeYoYCapFlooredLeg(whichPricer, vars.startDate, vars.length, caps0, floors, vars.volatility); Swap floorLeg = new Swap(fixedLeg, flooredLeg); floorLeg.setPricingEngine(engine); YoYInflationFloor floor = new YoYInflationFloor(floatLeg, new List <double>() { floorstrike }); floor.setPricingEngine(vars.makeEngine(vars.volatility, whichPricer)); npvFlooredLeg = floorLeg.NPV(); npvFloor = floor.NPV(); error = Math.Abs(npvFlooredLeg - (npvVanilla + npvFloor)); if (error > tolerance) { QAssert.Fail("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 // List <CashFlow> collaredLeg = vars.makeYoYCapFlooredLeg(whichPricer, vars.startDate, vars.length, caps, floors, vars.volatility); Swap collarLeg = new Swap(fixedLeg, collaredLeg); collarLeg.setPricingEngine(engine); YoYInflationCollar collar = new YoYInflationCollar(floatLeg, new List <double>() { capstrike }, new List <double>() { floorstrike }); collar.setPricingEngine(vars.makeEngine(vars.volatility, whichPricer)); npvCollaredLeg = collarLeg.NPV(); npvCollar = collar.NPV(); error = Math.Abs(npvCollaredLeg - (npvVanilla - npvCollar)); if (error > tolerance) { QAssert.Fail("\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 List <CashFlow> cappedLeg_p = vars.makeYoYCapFlooredLeg(whichPricer, vars.startDate, vars.length, caps, floors0, vars.volatility, gearing_p, spread_p); Swap capLeg_p = new Swap(fixedLeg, cappedLeg_p); capLeg_p.setPricingEngine(engine); YoYInflationCap cap_p = new YoYInflationCap(floatLeg_p, new List <double>() { capstrike }); cap_p.setPricingEngine(vars.makeEngine(vars.volatility, whichPricer)); npvVanilla = vanillaLeg_p.NPV(); npvCappedLeg = capLeg_p.NPV(); npvCap = cap_p.NPV(); error = Math.Abs(npvCappedLeg - (npvVanilla - npvCap)); if (error > tolerance) { QAssert.Fail("\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 List <CashFlow> cappedLeg_n = vars.makeYoYCapFlooredLeg(whichPricer, vars.startDate, vars.length, caps, floors0, vars.volatility, gearing_n, spread_n); Swap capLeg_n = new Swap(fixedLeg, cappedLeg_n); capLeg_n.setPricingEngine(engine); YoYInflationFloor floor_n = new YoYInflationFloor(floatLeg, new List <double>() { (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 = Math.Abs(npvCappedLeg - (npvVanilla + gearing_n * npvFloor)); if (error > tolerance) { QAssert.Fail("\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 List <CashFlow> flooredLeg_p1 = vars.makeYoYCapFlooredLeg(whichPricer, vars.startDate, vars.length, caps0, floors, vars.volatility, gearing_p, spread_p); Swap floorLeg_p1 = new Swap(fixedLeg, flooredLeg_p1); floorLeg_p1.setPricingEngine(engine); YoYInflationFloor floor_p1 = new YoYInflationFloor(floatLeg_p, new List <double>() { floorstrike }); floor_p1.setPricingEngine(vars.makeEngine(vars.volatility, whichPricer)); npvVanilla = vanillaLeg_p.NPV(); npvFlooredLeg = floorLeg_p1.NPV(); npvFloor = floor_p1.NPV(); error = Math.Abs(npvFlooredLeg - (npvVanilla + npvFloor)); if (error > tolerance) { QAssert.Fail("\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 List <CashFlow> flooredLeg_n = vars.makeYoYCapFlooredLeg(whichPricer, vars.startDate, vars.length, caps0, floors, vars.volatility, gearing_n, spread_n); Swap floorLeg_n = new Swap(fixedLeg, flooredLeg_n); floorLeg_n.setPricingEngine(engine); YoYInflationCap cap_n = new YoYInflationCap(floatLeg, new List <double>() { (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 = Math.Abs(npvFlooredLeg - (npvVanilla - gearing_n * npvCap)); if (error > tolerance) { QAssert.Fail("\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 List <CashFlow> collaredLeg_p = vars.makeYoYCapFlooredLeg(whichPricer, vars.startDate, vars.length, caps, floors, vars.volatility, gearing_p, spread_p); Swap collarLeg_p1 = new Swap(fixedLeg, collaredLeg_p); collarLeg_p1.setPricingEngine(engine); YoYInflationCollar collar_p = new YoYInflationCollar(floatLeg_p, new List <double>() { capstrike }, new List <double>() { floorstrike }); collar_p.setPricingEngine(vars.makeEngine(vars.volatility, whichPricer)); npvVanilla = vanillaLeg_p.NPV(); npvCollaredLeg = collarLeg_p1.NPV(); npvCollar = collar_p.NPV(); error = Math.Abs(npvCollaredLeg - (npvVanilla - npvCollar)); if (error > tolerance) { QAssert.Fail("\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 List <CashFlow> collaredLeg_n = vars.makeYoYCapFlooredLeg(whichPricer, vars.startDate, vars.length, caps, floors, vars.volatility, gearing_n, spread_n); Swap collarLeg_n1 = new Swap(fixedLeg, collaredLeg_n); collarLeg_n1.setPricingEngine(engine); YoYInflationCollar collar_n = new YoYInflationCollar(floatLeg, new List <double>() { (floorstrike - spread_n) / gearing_n }, new List <double>() { (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 = Math.Abs(npvCollaredLeg - (npvVanilla - gearing_n * npvCollar)); if (error > tolerance) { QAssert.Fail("\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); } // remove circular refernce vars.hy.linkTo(null); }
public void testImpliedVolatility() { // Testing implied volatility for swaptions CommonVars vars = new CommonVars(); int maxEvaluations = 100; double tolerance = 1.0e-08; Settlement.Type[] types = { Settlement.Type.Physical, Settlement.Type.Cash }; // test data double[] strikes = { 0.02, 0.03, 0.04, 0.05, 0.06, 0.07 }; double[] vols = { 0.01, 0.05, 0.10, 0.20, 0.30, 0.70, 0.90 }; for (int i = 0; i < exercises.Length; i++) { for (int j = 0; j < lengths.Length; j++) { Date exerciseDate = vars.calendar.advance(vars.today, exercises[i]); Date startDate = vars.calendar.advance(exerciseDate, vars.settlementDays, TimeUnit.Days); Date maturity = vars.calendar.advance(startDate, lengths[j], vars.floatingConvention); for (int t = 0; t < strikes.Length; t++) { for (int k = 0; k < type.Length; k++) { VanillaSwap swap = new MakeVanillaSwap(lengths[j], vars.index, strikes[t]) .withEffectiveDate(startDate) .withFloatingLegSpread(0.0) .withType(type[k]); for (int h = 0; h < types.Length; h++) { for (int u = 0; u < vols.Length; u++) { Swaption swaption = vars.makeSwaption(swap, exerciseDate, vols[u], types[h]); // Black price double value = swaption.NPV(); double implVol = 0.0; try { implVol = swaption.impliedVolatility(value, vars.termStructure, 0.10, tolerance, maxEvaluations); } catch (System.Exception e) { // couldn't bracket? swaption.setPricingEngine(vars.makeEngine(0.0)); double value2 = swaption.NPV(); if (Math.Abs(value - value2) < tolerance) { // ok, just skip: continue; } // otherwise, report error QAssert.Fail("implied vol failure: " + exercises[i] + "x" + lengths[j] + " " + type[k] + "\nsettlement: " + types[h] + "\nstrike " + strikes[t] + "\natm level: " + swap.fairRate() + "\nvol: " + vols[u] + "\nprice: " + value + "\n" + e.Message.ToString()); } if (Math.Abs(implVol - vols[u]) > tolerance) { // the difference might not matter swaption.setPricingEngine(vars.makeEngine(implVol)); double value2 = swaption.NPV(); if (Math.Abs(value - value2) > tolerance) { QAssert.Fail("implied vol failure: " + exercises[i] + "x" + lengths[j] + " " + type[k] + "\nsettlement: " + types[h] + "\nstrike " + strikes[t] + "\natm level: " + swap.fairRate() + "\nvol: " + vols[u] + "\nprice: " + value + "\nimplied vol: " + implVol + "\nimplied price: " + value2); } } } } } } } } }
public void testImpliedVolatility() { CommonVars vars = new CommonVars(); int maxEvaluations = 100; double tolerance = 1.0e-6; CapFloorType[] types = { CapFloorType.Cap, CapFloorType.Floor }; double[] strikes = { 0.02, 0.03, 0.04 }; int[] lengths = { 1, 5, 10 }; // test data double[] rRates = { 0.02, 0.03, 0.04 }; double[] vols = { 0.01, 0.20, 0.30, 0.70, 0.90 }; for (int k = 0; k < lengths.Length; k++) { List<CashFlow> leg = vars.makeLeg(vars.settlement, lengths[k]); for (int i = 0; i < types.Length; i++) { for (int j = 0; j < strikes.Length; j++) { CapFloor capfloor = vars.makeCapFloor(types[i], leg, strikes[j], 0.0); for (int n = 0; n < rRates.Length; n++) { for (int m = 0; m < vols.Length; m++) { double r = rRates[n]; double v = vols[m]; vars.termStructure.linkTo(Utilities.flatRate(vars.settlement, r, new Actual360())); capfloor.setPricingEngine(vars.makeEngine(v)); double value = capfloor.NPV(); double implVol = 0.0; try { implVol = capfloor.impliedVolatility(value, vars.termStructure, 0.10, tolerance, maxEvaluations); } catch (Exception e) { // couldn't bracket? capfloor.setPricingEngine(vars.makeEngine(0.0)); double value2 = capfloor.NPV(); if (Math.Abs(value - value2) < tolerance) { // ok, just skip: continue; } // otherwise, report error Assert.Fail("implied vol failure: " + typeToString(types[i]) + " strike: " + strikes[j] + " risk-free: " + r + " length: " + lengths[k] + "Y" + " volatility: " + v + e.Message); } if (Math.Abs(implVol - v) > tolerance) { // the difference might not matter capfloor.setPricingEngine(vars.makeEngine(implVol)); double value2 = capfloor.NPV(); if (Math.Abs(value - value2) > tolerance) { Assert.Fail( typeToString(types[i]) + ":" + " strike: " + strikes[j] + "\n" + " risk-free rate: " + r + "\n" + " length: " + lengths[k] + " years\n\n" + " original volatility: " + v + "\n" + " price: " + value + "\n" + " implied volatility: " + implVol + "\n" + " corresponding price: " + value2); } } } } } } } }
public void testConsistency() { CommonVars vars = new CommonVars(); int[] lengths = { 1, 2, 3, 5, 7, 10, 15, 20 }; double[] cap_rates = { 0.03, 0.04, 0.05, 0.06, 0.07 }; double[] floor_rates = { 0.03, 0.04, 0.05, 0.06, 0.07 }; double[] vols = { 0.01, 0.05, 0.10, 0.15, 0.20 }; Date startDate = vars.termStructure.link.referenceDate(); 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.makeLeg(startDate, lengths[i]); Instrument cap = vars.makeCapFloor(CapFloorType.Cap, leg, cap_rates[j], vols[l]); Instrument floor = vars.makeCapFloor(CapFloorType.Floor, leg, floor_rates[k], vols[l]); Collar collar = new Collar(leg, new InitializedList<double>(1, cap_rates[j]), new InitializedList<double>(1, floor_rates[k])); collar.setPricingEngine(vars.makeEngine(vols[l])); if (Math.Abs((cap.NPV() - floor.NPV()) - collar.NPV()) > 1e-10) { Assert.Fail( "inconsistency between cap, floor and collar:\n" + " length: " + lengths[i] + " years\n" + " volatility: " + vols[l] + "\n" + " cap value: " + cap.NPV() + " at strike: " + cap_rates[j] + "\n" + " floor value: " + floor.NPV() + " at strike: " + floor_rates[k] + "\n" + " collar value: " + collar.NPV()); } } } } } }
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) { QAssert.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) { QAssert.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) { QAssert.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) { QAssert.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(null); }
public void testDecomposition() { // Testing collared coupon against its decomposition CommonVars vars = new CommonVars(); double tolerance = 1e-12; double npvVanilla, npvCappedLeg, npvFlooredLeg, npvCollaredLeg, npvCap, npvFloor, npvCollar; double error; double floorstrike = 0.05; double capstrike = 0.10; List <double> caps = new InitializedList <double>(vars.length, capstrike); List <double> caps0 = new List <double>(); List <double> floors = new InitializedList <double>(vars.length, floorstrike); List <double> floors0 = new List <double>(); double gearing_p = 0.5; double spread_p = 0.002; double gearing_n = -1.5; double spread_n = 0.12; // fixed leg with zero rate List <CashFlow> fixedLeg = vars.makeFixedLeg(vars.startDate, vars.length); // floating leg with gearing=1 and spread=0 List <CashFlow> floatLeg = vars.makeFloatingLeg(vars.startDate, vars.length); // floating leg with positive gearing (gearing_p) and spread<>0 List <CashFlow> floatLeg_p = vars.makeFloatingLeg(vars.startDate, vars.length, gearing_p, spread_p); // floating leg with negative gearing (gearing_n) and spread<>0 List <CashFlow> floatLeg_n = vars.makeFloatingLeg(vars.startDate, vars.length, gearing_n, spread_n); // Swap with null fixed leg and floating leg with gearing=1 and spread=0 Swap vanillaLeg = new Swap(fixedLeg, floatLeg); // Swap with null fixed leg and floating leg with positive gearing and spread<>0 Swap vanillaLeg_p = new Swap(fixedLeg, floatLeg_p); // Swap with null fixed leg and floating leg with negative gearing and spread<>0 Swap vanillaLeg_n = new Swap(fixedLeg, floatLeg_n); IPricingEngine engine = new DiscountingSwapEngine(vars.termStructure); vanillaLeg.setPricingEngine(engine); 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 */ // Case gearing = 1 and spread = 0 List <CashFlow> cappedLeg = vars.makeCapFlooredLeg(vars.startDate, vars.length, caps, floors0, vars.volatility); Swap capLeg = new Swap(fixedLeg, cappedLeg); capLeg.setPricingEngine(engine); Cap cap = new Cap(floatLeg, new InitializedList <double>(1, capstrike)); cap.setPricingEngine(vars.makeEngine(vars.volatility)); npvVanilla = vanillaLeg.NPV(); npvCappedLeg = capLeg.NPV(); npvCap = cap.NPV(); error = Math.Abs(npvCappedLeg - (npvVanilla - npvCap)); if (error > tolerance) { QAssert.Fail("\nCapped 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 */ List <CashFlow> flooredLeg = vars.makeCapFlooredLeg(vars.startDate, vars.length, caps0, floors, vars.volatility); Swap floorLeg = new Swap(fixedLeg, flooredLeg); floorLeg.setPricingEngine(engine); Floor floor = new Floor(floatLeg, new InitializedList <double>(1, floorstrike)); floor.setPricingEngine(vars.makeEngine(vars.volatility)); npvFlooredLeg = floorLeg.NPV(); npvFloor = floor.NPV(); error = Math.Abs(npvFlooredLeg - (npvVanilla + npvFloor)); if (error > tolerance) { QAssert.Fail("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 */ List <CashFlow> collaredLeg = vars.makeCapFlooredLeg(vars.startDate, vars.length, caps, floors, vars.volatility); Swap collarLeg = new Swap(fixedLeg, collaredLeg); collarLeg.setPricingEngine(engine); Collar collar = new Collar(floatLeg, new InitializedList <double>(1, capstrike), new InitializedList <double>(1, floorstrike)); collar.setPricingEngine(vars.makeEngine(vars.volatility)); npvCollaredLeg = collarLeg.NPV(); npvCollar = collar.NPV(); error = Math.Abs(npvCollaredLeg - (npvVanilla - npvCollar)); if (error > tolerance) { QAssert.Fail("\nCollared 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 List <CashFlow> cappedLeg_p = vars.makeCapFlooredLeg(vars.startDate, vars.length, caps, floors0, vars.volatility, gearing_p, spread_p); Swap capLeg_p = new Swap(fixedLeg, cappedLeg_p); capLeg_p.setPricingEngine(engine); Cap cap_p = new Cap(floatLeg_p, new InitializedList <double>(1, capstrike)); cap_p.setPricingEngine(vars.makeEngine(vars.volatility)); npvVanilla = vanillaLeg_p.NPV(); npvCappedLeg = capLeg_p.NPV(); npvCap = cap_p.NPV(); error = Math.Abs(npvCappedLeg - (npvVanilla - npvCap)); if (error > tolerance) { QAssert.Fail("\nCapped 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 List <CashFlow> cappedLeg_n = vars.makeCapFlooredLeg(vars.startDate, vars.length, caps, floors0, vars.volatility, gearing_n, spread_n); Swap capLeg_n = new Swap(fixedLeg, cappedLeg_n); capLeg_n.setPricingEngine(engine); Floor floor_n = new Floor(floatLeg, new InitializedList <double>(1, (capstrike - spread_n) / gearing_n)); floor_n.setPricingEngine(vars.makeEngine(vars.volatility)); npvVanilla = vanillaLeg_n.NPV(); npvCappedLeg = capLeg_n.NPV(); npvFloor = floor_n.NPV(); error = Math.Abs(npvCappedLeg - (npvVanilla + gearing_n * npvFloor)); if (error > tolerance) { QAssert.Fail("\nCapped 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 List <CashFlow> flooredLeg_p1 = vars.makeCapFlooredLeg(vars.startDate, vars.length, caps0, floors, vars.volatility, gearing_p, spread_p); Swap floorLeg_p1 = new Swap(fixedLeg, flooredLeg_p1); floorLeg_p1.setPricingEngine(engine); Floor floor_p1 = new Floor(floatLeg_p, new InitializedList <double>(1, floorstrike)); floor_p1.setPricingEngine(vars.makeEngine(vars.volatility)); npvVanilla = vanillaLeg_p.NPV(); npvFlooredLeg = floorLeg_p1.NPV(); npvFloor = floor_p1.NPV(); error = Math.Abs(npvFlooredLeg - (npvVanilla + npvFloor)); if (error > tolerance) { QAssert.Fail("\nFloored 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 List <CashFlow> flooredLeg_n = vars.makeCapFlooredLeg(vars.startDate, vars.length, caps0, floors, vars.volatility, gearing_n, spread_n); Swap floorLeg_n = new Swap(fixedLeg, flooredLeg_n); floorLeg_n.setPricingEngine(engine); Cap cap_n = new Cap(floatLeg, new InitializedList <double>(1, (floorstrike - spread_n) / gearing_n)); cap_n.setPricingEngine(vars.makeEngine(vars.volatility)); npvVanilla = vanillaLeg_n.NPV(); npvFlooredLeg = floorLeg_n.NPV(); npvCap = cap_n.NPV(); error = Math.Abs(npvFlooredLeg - (npvVanilla - gearing_n * npvCap)); if (error > tolerance) { QAssert.Fail("\nCapped 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 List <CashFlow> collaredLeg_p = vars.makeCapFlooredLeg(vars.startDate, vars.length, caps, floors, vars.volatility, gearing_p, spread_p); Swap collarLeg_p1 = new Swap(fixedLeg, collaredLeg_p); collarLeg_p1.setPricingEngine(engine); Collar collar_p = new Collar(floatLeg_p, new InitializedList <double>(1, capstrike), new InitializedList <double>(1, floorstrike)); collar_p.setPricingEngine(vars.makeEngine(vars.volatility)); npvVanilla = vanillaLeg_p.NPV(); npvCollaredLeg = collarLeg_p1.NPV(); npvCollar = collar_p.NPV(); error = Math.Abs(npvCollaredLeg - (npvVanilla - npvCollar)); if (error > tolerance) { QAssert.Fail("\nCollared 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 List <CashFlow> collaredLeg_n = vars.makeCapFlooredLeg(vars.startDate, vars.length, caps, floors, vars.volatility, gearing_n, spread_n); Swap collarLeg_n1 = new Swap(fixedLeg, collaredLeg_n); collarLeg_n1.setPricingEngine(engine); Collar collar_n = new Collar(floatLeg, new InitializedList <double>(1, (floorstrike - spread_n) / gearing_n), new InitializedList <double>(1, (capstrike - spread_n) / gearing_n)); collar_n.setPricingEngine(vars.makeEngine(vars.volatility)); npvVanilla = vanillaLeg_n.NPV(); npvCollaredLeg = collarLeg_n1.NPV(); npvCollar = collar_n.NPV(); error = Math.Abs(npvCollaredLeg - (npvVanilla - gearing_n * npvCollar)); if (error > tolerance) { QAssert.Fail("\nCollared 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); } }