/// <summary> /// Implied volatility from option price. (Inversion of the two step quadrature formula.) /// </summary> /// <param name="maturity">option maturity</param> /// <param name="strike">option strike</param> /// <param name="price">option price </param> /// <param name="q">option type : 1 for call, -1 for put</param> /// <returns></returns> public double ImpliedVol(double maturity, double strike, double price, double q) { //Proxy using Lehman Formula double proxyFwd, proxyDk; affineDivUtils.LehmanProxy(maturity, spot, out proxyFwd, out proxyDk); double lehmanTargetVol = BlackScholesOption.ImpliedVol(price, proxyFwd, strike + proxyDk, maturity, q); var pricer = new BsDivPrice(maturity, strike, spot, affineDivUtils, quadPoints, quadWeights); Func <double, double> volToLehmanVolErr = v => BlackScholesOption.ImpliedVol(pricer.Price(v, q), proxyFwd, strike + proxyDk, maturity, q) - lehmanTargetVol; //Solve double v1 = lehmanTargetVol; double err1 = volToLehmanVolErr(v1); double v2 = lehmanTargetVol - err1; double err2 = volToLehmanVolErr(v2); double v3 = v1 - err1 * (v2 - v1) / (err2 - err1); if (Math.Abs(v3 - v2) < v2 * 1.0e-13) { return(v3); } double err3 = volToLehmanVolErr(v3); double v4 = RootUtils.TrinomRoot(v1, v2, v3, err1, err2, err3); if (Math.Abs(v4 - v3) < v3 * 1.0e-13) { return(v4); } double err4 = volToLehmanVolErr(v4); double v5 = RootUtils.TrinomRoot(v2, v3, v4, err2, err3, err4); return(v5); }
public double[] CalibrateVol(double[] maturities, double[] targetPrices, double[] strikes, double[] optionTypes) { Contract.Requires(EnumerableUtils.IsSorted(maturities)); Contract.Requires(maturities.Length == strikes.Length && strikes.Length == targetPrices.Length && targetPrices.Length == optionTypes.Length); double[] variances = new double[maturities.Length + 1]; double[] varPillars = new[] { 0.0 }.Concat(maturities).ToArray(); double[] calibVols = new double[maturities.Length]; for (int step = 0; step < maturities.Length; step++) { var maturity = maturities[step]; var strike = strikes[step]; var targetPrice = targetPrices[step]; var q = optionTypes[step]; //Proxy using Lehman Formula double proxyFwd, proxyDk; affineDivUtils.LehmanProxy(maturity, spot, out proxyFwd, out proxyDk); double lehmanTargetVol = BlackScholesOption.ImpliedVol(targetPrice, proxyFwd, strike + proxyDk, maturity, q); var pricer = new BsDivPrice(maturities[step], strikes[step], spot, affineDivUtils, quadPoints, quadWeights); Func <double, double> volToLehmanVolErr = v => { variances[1 + step] = v * v * maturity; var varFunc = RrFunctions.LinearInterpolation(varPillars, variances); Func <double, double> volFunc = t => Math.Sqrt(varFunc.Eval(t) / t); var price = pricer.Price(volFunc, q); return(BlackScholesOption.ImpliedVol(price, proxyFwd, strike + proxyDk, maturity, q) - lehmanTargetVol); };//TODO use a cache //Bracket & Solve double v1 = lehmanTargetVol; double v2; if (step == 0) { v2 = lehmanTargetVol - volToLehmanVolErr(lehmanTargetVol); } else { var volIfZeroVolOnStep = Math.Sqrt(calibVols[step - 1] * calibVols[step - 1] * maturities[step - 1] / maturities[step]); var minError = volToLehmanVolErr(volIfZeroVolOnStep); if (minError > 0.0) //saturation case { calibVols[step] = volIfZeroVolOnStep; variances[1 + step] = volIfZeroVolOnStep * volIfZeroVolOnStep * maturity; continue; } v2 = volIfZeroVolOnStep; } if (!RootUtils.Bracket(volToLehmanVolErr, v1, v2, out v1, out v2)) { throw new Exception("Failed to inverse vol"); } var impliedVol = RootUtils.Brenth(volToLehmanVolErr, v1, v2, 1.0e-10, 2.0 * DoubleUtils.MachineEpsilon, 10); calibVols[step] = impliedVol; variances[1 + step] = impliedVol * impliedVol * maturity; } return(calibVols); }