public void BracketTest(Func <double, double> f, double x1, double x2) { double xa, xb; RootUtils.Bracket(f, x1, x2, out xa, out xb); Assert.IsTrue(f(xa) * f(xb) < 0.0); }
public static double ComputeQuantile(double forward, double maturity, Func <double, double> vol, double proba) { double s = vol(forward) * Math.Sqrt(maturity); double kGuess = forward * Math.Exp(s * (NormalDistribution.FastCumulativeInverse(proba) + 0.5 * s)); Func <double, double> cumErr = m => Cumulative(forward, maturity, vol, forward * Math.Exp(m)) - proba; double m1, m2; RootUtils.Bracket(cumErr, Math.Log(kGuess / forward), 0.0, out m1, out m2); var mQuantile = RootUtils.Brenth(cumErr, m1, m2, forward * 1.0e-5, DoubleUtils.MachineEpsilon); return(forward * Math.Exp(mQuantile)); }
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); }