// LazyObject interface protected override void performCalculations() { //// optionletStripper data optionletDates_ = new List <Date>(stripper1_.optionletFixingDates()); optionletPaymentDates_ = new List <Date>(stripper1_.optionletPaymentDates()); optionletAccrualPeriods_ = new List <double>(stripper1_.optionletAccrualPeriods()); optionletTimes_ = new List <double>(stripper1_.optionletFixingTimes()); atmOptionletRate_ = new List <double>(stripper1_.atmOptionletRates()); for (int i = 0; i < optionletTimes_.Count; ++i) { optionletStrikes_[i] = new List <double>(stripper1_.optionletStrikes(i)); optionletVolatilities_[i] = new List <double>(stripper1_.optionletVolatilities(i)); } // atmCapFloorTermVolCurve data List <Period> optionExpiriesTenors = new List <Period>(atmCapFloorTermVolCurve_.link.optionTenors()); List <double> optionExpiriesTimes = new List <double>(atmCapFloorTermVolCurve_.link.optionTimes()); for (int j = 0; j < nOptionExpiries_; ++j) { double atmOptionVol = atmCapFloorTermVolCurve_.link.volatility(optionExpiriesTimes[j], 33.3333); // dummy strike BlackCapFloorEngine engine = new BlackCapFloorEngine(iborIndex_.forwardingTermStructure(), atmOptionVol, dc_); CapFloor test = new MakeCapFloor(CapFloorType.Cap, optionExpiriesTenors[j], iborIndex_, null, new Period(0, TimeUnit.Days)).withPricingEngine(engine).value(); caps_.Add(test); atmCapFloorStrikes_[j] = caps_[j].atmRate(iborIndex_.forwardingTermStructure()); atmCapFloorPrices_[j] = caps_[j].NPV(); } spreadsVolImplied_ = spreadsVolImplied(); StrippedOptionletAdapter adapter = new StrippedOptionletAdapter(stripper1_); double unadjustedVol, adjustedVol; for (int j = 0; j < nOptionExpiries_; ++j) { for (int i = 0; i < optionletVolatilities_.Count; ++i) { if (i <= caps_[j].floatingLeg().Count) { unadjustedVol = adapter.volatility(optionletTimes_[i], atmCapFloorStrikes_[j]); adjustedVol = unadjustedVol + spreadsVolImplied_[j]; var previous = optionletStrikes_[i].FindIndex(x => x >= atmCapFloorStrikes_[j]); int insertIndex = previous; optionletStrikes_[i].Insert(insertIndex, atmCapFloorStrikes_[j]); optionletVolatilities_[i].Insert(insertIndex, adjustedVol); } } } }
// LazyObject interface protected override void performCalculations() { // update dates Date referenceDate = termVolSurface_.referenceDate(); DayCounter dc = termVolSurface_.dayCounter(); BlackCapFloorEngine dummy = new BlackCapFloorEngine( // discounting does not matter here iborIndex_.forwardingTermStructure(), 0.20, dc); for (int i = 0; i < nOptionletTenors_; ++i) { CapFloor temp = new MakeCapFloor(CapFloorType.Cap, capFloorLengths_[i], iborIndex_, 0.04, // dummy strike new Period(0, TimeUnit.Days)) .withPricingEngine(dummy); FloatingRateCoupon lFRC = temp.lastFloatingRateCoupon(); optionletDates_[i] = lFRC.fixingDate(); optionletPaymentDates_[i] = lFRC.date(); optionletAccrualPeriods_[i] = lFRC.accrualPeriod(); optionletTimes_[i] = dc.yearFraction(referenceDate, optionletDates_[i]); atmOptionletRate_[i] = lFRC.indexFixing(); } if (floatingSwitchStrike_ && capFlooMatrixNotInitialized_) { double averageAtmOptionletRate = 0.0; for (int i = 0; i < nOptionletTenors_; ++i) { averageAtmOptionletRate += atmOptionletRate_[i]; } switchStrike_ = averageAtmOptionletRate / nOptionletTenors_; } Handle <YieldTermStructure> discountCurve = discount_.empty() ? iborIndex_.forwardingTermStructure() : discount_; List <double> strikes = new List <double>(termVolSurface_.strikes()); // initialize CapFloorMatrix if (capFlooMatrixNotInitialized_) { for (int i = 0; i < nOptionletTenors_; ++i) { capFloors_[i] = new List <CapFloor>(nStrikes_); } // construction might go here for (int j = 0; j < nStrikes_; ++j) { // using out-of-the-money options CapFloorType capFloorType = strikes[j] < switchStrike_ ? CapFloorType.Floor : CapFloorType.Cap; for (int i = 0; i < nOptionletTenors_; ++i) { if (volatilityType_ == VolatilityType.ShiftedLognormal) { BlackCapFloorEngine engine = new BlackCapFloorEngine(discountCurve, new Handle <Quote>(volQuotes_[i][j]), dc, displacement_); capFloors_[i].Add(new MakeCapFloor(capFloorType, capFloorLengths_[i], iborIndex_, strikes[j], new Period(0, TimeUnit.Days)).withPricingEngine(engine)); } else if (volatilityType_ == VolatilityType.Normal) { BachelierCapFloorEngine engine = new BachelierCapFloorEngine(discountCurve, new Handle <Quote>(volQuotes_[i][j]), dc); capFloors_[i].Add(new MakeCapFloor(capFloorType, capFloorLengths_[i], iborIndex_, strikes[j], new Period(0, TimeUnit.Days)).withPricingEngine(engine)); } else { Utils.QL_FAIL("unknown volatility type: " + volatilityType_); } } } capFlooMatrixNotInitialized_ = false; } for (int j = 0; j < nStrikes_; ++j) { Option.Type optionletType = strikes[j] < switchStrike_ ? Option.Type.Put : Option.Type.Call; double previousCapFloorPrice = 0.0; for (int i = 0; i < nOptionletTenors_; ++i) { capFloorVols_[i, j] = termVolSurface_.volatility(capFloorLengths_[i], strikes[j], true); volQuotes_[i][j].setValue(capFloorVols_[i, j]); capFloorPrices_[i, j] = capFloors_[i][j].NPV(); optionletPrices_[i, j] = capFloorPrices_[i, j] - previousCapFloorPrice; previousCapFloorPrice = capFloorPrices_[i, j]; double d = discountCurve.link.discount(optionletPaymentDates_[i]); double optionletAnnuity = optionletAccrualPeriods_[i] * d; try { if (volatilityType_ == VolatilityType.ShiftedLognormal) { optionletStDevs_[i, j] = Utils.blackFormulaImpliedStdDev(optionletType, strikes[j], atmOptionletRate_[i], optionletPrices_[i, j], optionletAnnuity, displacement_, optionletStDevs_[i, j], accuracy_, maxIter_); } else if (volatilityType_ == VolatilityType.Normal) { optionletStDevs_[i, j] = Math.Sqrt(optionletTimes_[i]) * Utils.bachelierBlackFormulaImpliedVol( optionletType, strikes[j], atmOptionletRate_[i], optionletTimes_[i], optionletPrices_[i, j], optionletAnnuity); } else { Utils.QL_FAIL("Unknown volatility type: " + volatilityType_); } } catch (Exception e) { if (dontThrow_) { optionletStDevs_[i, j] = 0.0; } else { Utils.QL_FAIL("could not bootstrap optionlet:" + "\n type: " + optionletType + "\n strike: " + (strikes[j]) + "\n atm: " + (atmOptionletRate_[i]) + "\n price: " + optionletPrices_[i, j] + "\n annuity: " + optionletAnnuity + "\n expiry: " + optionletDates_[i] + "\n error: " + e.Message); } } optionletVolatilities_[i][j] = optionletStDevs_[i, j] / Math.Sqrt(optionletTimes_[i]); } } }