public double volatility(double strike, VolatilityType volatilityType, double shift = 0.0) { if (volatilityType == volatilityType_ && Utils.close(shift, this.shift())) { return(volatility(strike)); } double?atm = atmLevel(); Utils.QL_REQUIRE(atm != null, () => "smile section must provide atm level to compute converted volatilties"); Option.Type type = strike >= atm ? Option.Type.Call : Option.Type.Put; double premium = optionPrice(strike, type); double premiumAtm = optionPrice(atm.Value, type); if (volatilityType == VolatilityType.ShiftedLognormal) { try { return(Utils.blackFormulaImpliedStdDev(type, strike, atm.Value, premium, 1.0, shift) / Math.Sqrt(exerciseTime())); } catch (Exception) { return(Utils.blackFormulaImpliedStdDevChambers(type, strike, atm.Value, premium, premiumAtm, 1.0, shift) / Math.Sqrt(exerciseTime())); } } else { return(Utils.bachelierBlackFormulaImpliedVol(type, strike, atm.Value, exerciseTime(), premium)); } }
//! \name 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) { //volQuotes_[i][j] = new SimpleQuote(); 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]); } } }