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)); } }