// Bisection Algorithm for Black Scholes Implied Volatility ================================================================================= public double BisecBSIV(OpSet settings, double K, double T, double a, double b, double MktPrice, double Tol, int MaxIter) { BlackScholesPrice BS = new BlackScholesPrice(); double S = settings.S; double rf = settings.r; double q = settings.q; string PutCall = settings.PutCall; double lowCdif = MktPrice - BS.BlackScholes(S, K, T, rf, q, a, PutCall); double highCdif = MktPrice - BS.BlackScholes(S, K, T, rf, q, b, PutCall); double BSIV = 0.0; double midP; if (lowCdif * highCdif > 0.0) { BSIV = -1.0; } else { for (int x = 0; x <= MaxIter; x++) { midP = (a + b) / 2.0; double midCdif = MktPrice - BS.BlackScholes(S, K, T, rf, q, midP, PutCall); if (Math.Abs(midCdif) < Tol) { break; } else { if (midCdif > 0.0) { a = midP; } else { b = midP; } } BSIV = midP; } } return(BSIV); }
// Parameters for the shortest maturity appear first in the vectors theta, sigma, and rho. // Parameters for the longest maturity appear last. public double BGMApproxPriceTD(double kappa, double v0, double[] theta, double[] sigma, double[] rho, OpSet settings, double K, double[] T) { int NT = T.Length; double[] a1T = new double[NT]; double[] a2T = new double[NT]; double[] b0T = new double[NT]; double[] b2T = new double[NT]; // First set of coefficients a1T[0] = -rho[0] * sigma[0] * (2.0 * theta[0] * Math.Exp(kappa * T[0]) + v0 * T[0] * kappa + v0 - theta[0] * T[0] * kappa * Math.Exp(kappa * T[0]) - theta[0] * kappa * T[0] - 2.0 * theta[0] - v0 * Math.Exp(kappa * T[0])) / Math.Exp(kappa * T[0]) / Math.Pow(kappa, 2.0); a2T[0] = -0.5 * rho[0] * rho[0] * sigma[0] * sigma[0] * (Math.Pow(kappa, 2.0) * v0 * Math.Pow(T[0], 2.0) + 6.0 * theta[0] * Math.Exp(kappa * T[0]) + 2.0 * v0 * T[0] * kappa + 2.0 * v0 - Math.Pow(kappa, 2.0) * theta[0] * Math.Pow(T[0], 2.0) - 4.0 * theta[0] * kappa * T[0] - 2.0 * theta[0] * T[0] * kappa * Math.Exp(kappa * T[0]) - 6.0 * theta[0] - 2.0 * v0 * Math.Exp(kappa * T[0])) / Math.Exp(kappa * T[0]) / Math.Pow(kappa, 3.0); b0T[0] = -0.25 * sigma[0] * sigma[0] * (5.0 * theta[0] * Math.Pow(Math.Exp(kappa * T[0]), 2.0) + 4.0 * Math.Exp(kappa * T[0]) * v0 * T[0] * kappa - 4.0 * theta[0] * T[0] * kappa * Math.Exp(kappa * T[0]) - 2.0 * theta[0] * T[0] * kappa * Math.Pow(Math.Exp(kappa * T[0]), 2.0) + 2.0 * v0 - theta[0] - 2.0 * v0 * Math.Pow(Math.Exp(kappa * T[0]), 2.0) - 4.0 * theta[0] * Math.Exp(kappa * T[0])) / Math.Pow(Math.Exp(kappa * T[0]), 2.0) / Math.Pow(kappa, 3.0); b2T[0] = a1T[0] * a1T[0] / 2.0; double A1 = 0.0, A2 = 0.0, B0 = 0.0; int j; // Coefficients under multiple maturities if (NT >= 2) { for (int t = 0; t <= NT - 2; t++) { // Coefficients for a1T[t+1] j = 0; A1 += rho[j] * sigma[j] * (-Math.Exp(-kappa * T[t]) + Math.Exp(-kappa * T[t + 1])) * (theta[j] - v0 * T[j] * kappa + theta[j] * T[j] * kappa - theta[j] * Math.Exp(kappa * T[j])) / Math.Pow(kappa, 2.0); for (j = 1; j <= t; j++) { A1 += -rho[j] * sigma[j] * (-Math.Exp(-kappa * T[t]) + Math.Exp(-kappa * T[t + 1])) * (-v0 * T[j - 1] * kappa + theta[j] * T[j - 1] * kappa - theta[j] * Math.Exp(kappa * T[j - 1]) + v0 * T[j] * kappa - theta[j] * T[j] * kappa + theta[j] * Math.Exp(kappa * T[j])) / Math.Pow(kappa, 2.0); } j = t + 1; A1 += -rho[j] * sigma[j] * (-v0 * Math.Exp(kappa * T[t + 1]) - v0 * T[t] * kappa * Math.Exp(kappa * T[t]) + theta[j] * Math.Exp(kappa * T[t + 1]) + theta[j] * T[t] * kappa * Math.Exp(kappa * T[t]) + theta[j] * T[t] * kappa * Math.Exp(kappa * (T[t] + T[t + 1])) - theta[j] * Math.Exp(2.0 * kappa * T[t]) + Math.Exp(kappa * T[t]) * v0 - Math.Exp(kappa * (T[t] + T[t + 1])) * theta[j] * T[t + 1] * kappa + theta[j] * Math.Exp(kappa * (T[t] + T[t + 1])) + Math.Exp(kappa * T[t]) * v0 * kappa * T[t + 1] - theta[j] * Math.Exp(kappa * T[t]) - Math.Exp(kappa * T[t]) * theta[j] * kappa * T[t + 1]) / Math.Pow(kappa, 2.0) * Math.Exp(-kappa * (T[t] + T[t + 1])); a1T[t + 1] = a1T[t] + A1; // Coefficients for a2T[t+1] j = 0; A2 += -Math.Pow(rho[j], 2.0) * Math.Pow(sigma[j], 2.0) * (Math.Exp(-kappa * T[t]) + Math.Exp(-kappa * T[t + 1]) * T[t] * kappa - Math.Exp(-kappa * T[t + 1]) - Math.Exp(-kappa * T[t + 1]) * kappa * T[t + 1]) * (theta[j] - v0 * T[j] * kappa + theta[j] * T[j] * kappa - theta[j] * Math.Exp(kappa * T[j])) / Math.Pow(kappa, 3.0) + 1.0 / 2.0 * Math.Pow(rho[j], 2.0) * Math.Pow(sigma[j], 2.0) * (-Math.Exp(-kappa * T[t]) + Math.Exp(-kappa * T[t + 1])) * (2.0 * kappa * theta[j] * T[t] + 2.0 * theta[j] - 2.0 * v0 * T[t] * T[j] * Math.Pow(kappa, 2.0) + v0 * Math.Pow(T[j], 2.0) * Math.Pow(kappa, 2.0) + 2.0 * theta[j] * T[t] * T[j] * Math.Pow(kappa, 2.0) - theta[j] * Math.Pow(T[j], 2.0) * Math.Pow(kappa, 2.0) - 2.0 * theta[j] * T[t] * Math.Exp(kappa * T[j]) * kappa + 2.0 * theta[j] * Math.Exp(kappa * T[j]) * kappa * T[j] - 2.0 * theta[j] * Math.Exp(kappa * T[j])) / Math.Pow(kappa, 3.0); for (j = 1; j <= t; j++) { A2 += Math.Pow(rho[j], 2.0) * Math.Pow(sigma[j], 2.0) * (Math.Exp(-kappa * T[t]) + Math.Exp(-kappa * T[t + 1]) * T[t] * kappa - Math.Exp(-kappa * T[t + 1]) - Math.Exp(-kappa * T[t + 1]) * kappa * T[t + 1]) * (-v0 * T[j - 1] * kappa + theta[j] * T[j - 1] * kappa - theta[j] * Math.Exp(kappa * T[j - 1]) + v0 * T[j] * kappa - theta[j] * T[j] * kappa + theta[j] * Math.Exp(kappa * T[j])) / Math.Pow(kappa, 3.0) - 1.0 / 2.0 * Math.Pow(rho[j], 2.0) * Math.Pow(sigma[j], 2.0) * (-Math.Exp(-kappa * T[t]) + Math.Exp(-kappa * T[t + 1])) * (-2.0 * v0 * T[t] * T[j - 1] * Math.Pow(kappa, 2.0) + v0 * Math.Pow(T[j - 1], 2.0) * Math.Pow(kappa, 2.0) + 2.0 * theta[j] * T[t] * T[j - 1] * Math.Pow(kappa, 2.0) - theta[j] * Math.Pow(T[j - 1], 2.0) * Math.Pow(kappa, 2.0) - 2.0 * theta[j] * T[t] * Math.Exp(kappa * T[j - 1]) * kappa + 2.0 * theta[j] * Math.Exp(kappa * T[j - 1]) * kappa * T[j - 1] - 2.0 * theta[j] * Math.Exp(kappa * T[j - 1]) + 2.0 * v0 * T[t] * T[j] * Math.Pow(kappa, 2.0) - v0 * Math.Pow(T[j], 2.0) * Math.Pow(kappa, 2.0) - 2.0 * theta[j] * T[t] * T[j] * Math.Pow(kappa, 2.0) + theta[j] * Math.Pow(T[j], 2.0) * Math.Pow(kappa, 2.0) + 2.0 * theta[j] * T[t] * Math.Exp(kappa * T[j]) * kappa - 2.0 * theta[j] * Math.Exp(kappa * T[j]) * kappa * T[j] + 2.0 * theta[j] * Math.Exp(kappa * T[j])) / Math.Pow(kappa, 3.0); } j = t + 1; A2 += 1.0 / 2.0 * Math.Pow(rho[j], 2.0) * Math.Pow(sigma[j], 2.0) * (2.0 * v0 * Math.Exp(kappa * T[t + 1]) - v0 * Math.Pow(T[t], 2.0) * Math.Pow(kappa, 2.0) * Math.Exp(kappa * T[t]) + 2.0 * v0 * T[t] * kappa * Math.Exp(kappa * T[t]) + 2.0 * v0 * Math.Pow(kappa, 2.0) * T[t + 1] * T[t] * Math.Exp(kappa * T[t]) - 2.0 * theta[j] * Math.Exp(kappa * T[t + 1]) + theta[j] * Math.Pow(T[t], 2.0) * Math.Pow(kappa, 2.0) * Math.Exp(kappa * T[t]) - 2.0 * theta[j] * T[t] * kappa * Math.Exp(kappa * T[t]) - 2.0 * theta[j] * Math.Pow(kappa, 2.0) * T[t + 1] * T[t] * Math.Exp(kappa * T[t]) - 2.0 * theta[j] * T[t] * kappa * Math.Exp(kappa * (T[t] + T[t + 1])) - 2.0 * theta[j] * Math.Exp(2.0 * kappa * T[t]) * kappa * T[t] + 4.0 * theta[j] * Math.Exp(2.0 * kappa * T[t]) + 2.0 * theta[j] * T[t + 1] * Math.Exp(2.0 * kappa * T[t]) * kappa - 2.0 * Math.Exp(kappa * T[t]) * v0 - 2.0 * Math.Exp(kappa * T[t]) * v0 * kappa * T[t + 1] + 2.0 * Math.Exp(kappa * (T[t] + T[t + 1])) * theta[j] * T[t + 1] * kappa - Math.Exp(kappa * T[t]) * Math.Pow(kappa, 2.0) * v0 * Math.Pow(T[t + 1], 2.0) - 4.0 * theta[j] * Math.Exp(kappa * (T[t] + T[t + 1])) + Math.Exp(kappa * T[t]) * Math.Pow(kappa, 2.0) * theta[j] * Math.Pow(T[t + 1], 2.0) + 2.0 * theta[j] * Math.Exp(kappa * T[t]) + 2.0 * Math.Exp(kappa * T[t]) * theta[j] * kappa * T[t + 1]) / Math.Pow(kappa, 3.0) * Math.Exp(-kappa * (T[t] + T[t + 1])); a2T[t + 1] = a2T[t] + A2; // Coefficients for b0T[t+1] j = 0; B0 += -1.0 / 4.0 * Math.Pow(sigma[j], 2.0) * (-Math.Exp(-2.0 * kappa * T[t]) + 2.0 * Math.Exp(-kappa * (T[t] + T[t + 1])) - Math.Exp(-2.0 * kappa * T[t + 1])) * (-2.0 * v0 + theta[j] + 2.0 * v0 * Math.Exp(kappa * T[j]) - 2.0 * theta[j] * Math.Exp(kappa * T[j]) + theta[j] * Math.Exp(2.0 * kappa * T[j])) / Math.Pow(kappa, 3.0) + 1.0 / 2.0 * Math.Pow(sigma[j], 2.0) * (-theta[j] * Math.Exp(kappa * T[t + 1]) + theta[j] * Math.Exp(kappa * T[t]) + 2.0 * v0 * Math.Exp(kappa * T[t + 1]) - 2.0 * Math.Exp(kappa * T[t]) * v0 - 2.0 * theta[j] * Math.Exp(kappa * (T[t] + T[t + 1])) + 2.0 * theta[j] * Math.Exp(2.0 * kappa * T[t]) + 2.0 * Math.Exp(kappa * (T[t] + T[t + 1])) * v0 * T[j] * kappa - 2.0 * Math.Exp(kappa * (T[t + 1] + T[j])) * v0 - 2.0 * Math.Exp(kappa * (T[t] + T[t + 1])) * theta[j] * T[j] * kappa + 2.0 * Math.Exp(kappa * (T[t + 1] + T[j])) * theta[j] + 2.0 * Math.Exp(kappa * (T[t] + T[t + 1] + T[j])) * theta[j] - Math.Exp(kappa * (T[t + 1] + 2.0 * T[j])) * theta[j] - 2.0 * Math.Exp(2.0 * kappa * T[t]) * v0 * T[j] * kappa + 2.0 * Math.Exp(kappa * (T[t] + T[j])) * v0 + 2.0 * Math.Exp(2.0 * kappa * T[t]) * theta[j] * T[j] * kappa - 2.0 * Math.Exp(kappa * (T[t] + T[j])) * theta[j] - 2.0 * Math.Exp(kappa * (2.0 * T[t] + T[j])) * theta[j] + Math.Exp(kappa * (T[t] + 2.0 * T[j])) * theta[j]) * Math.Exp(-kappa * (2.0 * T[t] + T[t + 1])) / Math.Pow(kappa, 3.0); for (j = 1; j <= t; j++) { B0 += -1.0 / 4.0 * Math.Pow(sigma[j], 2.0) * (-Math.Exp(-2.0 * kappa * T[t]) + 2.0 * Math.Exp(-kappa * (T[t] + T[t + 1])) - Math.Exp(-2.0 * kappa * T[t + 1])) * (-2.0 * v0 * Math.Exp(kappa * T[j - 1]) + 2.0 * theta[j] * Math.Exp(kappa * T[j - 1]) - theta[j] * Math.Exp(2.0 * kappa * T[j - 1]) + 2.0 * v0 * Math.Exp(kappa * T[j]) - 2.0 * theta[j] * Math.Exp(kappa * T[j]) + theta[j] * Math.Exp(2.0 * kappa * T[j])) / Math.Pow(kappa, 3.0) + 1.0 / 2.0 * Math.Pow(sigma[j], 2.0) * (Math.Exp(-kappa * T[t]) - Math.Exp(-kappa * T[t + 1])) * (-2.0 * v0 * T[j - 1] * kappa * Math.Exp(kappa * T[t]) + 2.0 * v0 * Math.Exp(kappa * T[j - 1]) + 2.0 * theta[j] * T[j - 1] * kappa * Math.Exp(kappa * T[t]) - 2.0 * theta[j] * Math.Exp(kappa * T[j - 1]) - 2.0 * theta[j] * Math.Exp(kappa * (T[j - 1] + T[t])) + theta[j] * Math.Exp(2.0 * kappa * T[j - 1]) + 2.0 * v0 * T[j] * kappa * Math.Exp(kappa * T[t]) - 2.0 * v0 * Math.Exp(kappa * T[j]) - 2.0 * theta[j] * T[j] * kappa * Math.Exp(kappa * T[t]) + 2.0 * theta[j] * Math.Exp(kappa * T[j]) + 2.0 * Math.Exp(kappa * (T[t] + T[j])) * theta[j] - theta[j] * Math.Exp(2.0 * kappa * T[j])) * Math.Exp(-kappa * T[t]) / Math.Pow(kappa, 3.0); } j = t + 1; B0 += -1.0 / 4.0 * Math.Pow(sigma[j], 2.0) * (-2.0 * v0 * Math.Exp(2.0 * kappa * T[t + 1]) - 4.0 * v0 * T[t] * kappa * Math.Exp(kappa * (T[t] + T[t + 1])) + 2.0 * v0 * Math.Exp(2.0 * kappa * T[t]) + 2.0 * theta[j] * Math.Exp(2.0 * kappa * T[t + 1]) + 4.0 * theta[j] * T[t] * kappa * Math.Exp(kappa * (T[t] + T[t + 1])) - 2.0 * theta[j] * Math.Exp(2.0 * kappa * T[t]) + 2.0 * theta[j] * T[t] * kappa * Math.Exp(kappa * (T[t] + 2.0 * T[t + 1])) - 4.0 * theta[j] * Math.Exp(kappa * (2.0 * T[t] + T[t + 1])) + theta[j] * Math.Exp(3 * kappa * T[t]) + 3.0 * theta[j] * Math.Exp(kappa * (T[t] + 2.0 * T[t + 1])) + 4.0 * Math.Exp(kappa * (T[t] + T[t + 1])) * v0 * kappa * T[t + 1] - 4.0 * Math.Exp(kappa * (T[t] + T[t + 1])) * theta[j] * T[t + 1] * kappa - 2.0 * Math.Exp(kappa * (T[t] + 2.0 * T[t + 1])) * theta[j] * T[t + 1] * kappa) / Math.Pow(kappa, 3.0) * Math.Exp(-kappa * (T[t] + 2.0 * T[t + 1])); b0T[t + 1] = b0T[t] + B0; // Coefficients for b2T[t+1] b2T[t + 1] = a1T[t + 1] * a1T[t + 1] / 2.0; } } // Coefficients for the expansion are the last ones in the iterations double A1T = a1T[NT - 1]; double A2T = a2T[NT - 1]; double B0T = b0T[NT - 1]; double B2T = b2T[NT - 1]; double S = settings.S; double rf = settings.r; double q = settings.q; string PutCall = settings.PutCall; // Log Spot price double x = Math.Log(settings.S); // Integrated variance double wT = (v0 - theta[NT - 1]) * (1 - Math.Exp(-kappa * T[NT - 1])) / kappa + theta[NT - 1] * T[NT - 1]; double y = wT; // Black Scholes Put Price BlackScholesPrice BS = new BlackScholesPrice(); double g = Math.Pow(y, -0.5) * (-x + Math.Log(K) - (rf - q) * T[NT - 1]) - 0.5 * Math.Sqrt(y); double f = Math.Pow(y, -0.5) * (-x + Math.Log(K) - (rf - q) * T[NT - 1]) + 0.5 * Math.Sqrt(y); double BSPut = K * Math.Exp(-rf * T[NT - 1]) * BS.NormCDF(f) - S * Math.Exp(-q * T[NT - 1]) * BS.NormCDF(g); // Normal pdf, phi(f) and phi(g) double pi = Math.PI; double phif = Math.Exp(-f * f / 2.0) / Math.Sqrt(2.0 * pi); double phig = Math.Exp(-g * g / 2.0) / Math.Sqrt(2.0 * pi); // Derivatives of f and g double fx = -Math.Pow(y, -0.5); double fy = -0.5 / y * g; double gx = fx; double gy = -0.5 / y * f; // The cdf PHI(f) and PHI(g) double PHIf = BS.NormCDF(f); double PHIg = BS.NormCDF(g); // Derivatives of the pdf phi(f) double phifx = Math.Pow(y, -0.5) * f * phif; double phify = 0.5 / y * f * g * phif; // Derivatives of the cdf PHI(f) double PHIfxy = 0.5 * Math.Pow(y, -1.5) * phif * (1.0 - f * g); double PHIfx2y = 0.5 * Math.Pow(y, -2.0) * phif * (2.0 * f + g - f * f * g); double PHIfy2 = 0.5 * Math.Pow(y, -2.0) * phif * (g + f / 2.0 - f * g * g / 2.0); double PHIfx2y2 = 0.5 * ((Math.Pow(y, -2.0) * phify - 2.0 * Math.Pow(y, -3.0) * phif) * (2.0 * f + g - f * f * g) + Math.Pow(y, -2.0) * phif * (2.0 * fy + gy - 2.0 * f * fy * g - f * f * gy)); // Derivatives of the pdf phi(g) double phigx = Math.Pow(y, -0.5) * g * phig; double phigy = 0.5 / y * f * g * phig; // Derivatives of cdf PHI(g) double PHIgx = -phig *Math.Pow(y, -0.5); double PHIgy = -0.5 * f * phig / y; double PHIgxy = 0.5 * Math.Pow(y, -1.5) * phig * (1.0 - f * g); double PHIgx2y = 0.5 * Math.Pow(y, -2.0) * phig * (2.0 * g + f - g * g * f); double PHIgy2 = 0.5 * Math.Pow(y, -2.0) * phig * (f + g / 2.0 - g * f * f / 2.0); double PHIgxy2 = 0.5 * Math.Pow(y, -2.0) * (phigx * (f + g / 2.0 - f * f * g / 2.0) + phig * (fx + gx / 2.0 - f * fx * g / 2.0 - f * f * gx / 2.0)); double PHIgx2y2 = 0.5 * ((Math.Pow(y, -2.0) * phigy - 2.0 * Math.Pow(y, -3.0) * phig) * (2.0 * g + f - g * g * f) + Math.Pow(y, -2.0) * phig * (2.0 * gy + fy - 2.0 * g * gy * f - g * g * fy)); // Derivatives of Black-Scholes Put double dPdxdy = K * Math.Exp(-rf * T[NT - 1]) * PHIfxy - Math.Exp(-q * T[NT - 1]) * S * (PHIgy + PHIgxy); double dPdx2dy = K * Math.Exp(-rf * T[NT - 1]) * PHIfx2y - Math.Exp(-q * T[NT - 1]) * S * (PHIgy + 2.0 * PHIgxy + PHIgx2y); double dPdy2 = K * Math.Exp(-rf * T[NT - 1]) * PHIfy2 - Math.Exp(-q * T[NT - 1]) * S * PHIgy2; double dPdx2dy2 = K * Math.Exp(-rf * T[NT - 1]) * PHIfx2y2 - Math.Exp(-q * T[NT - 1]) * S * (PHIgy + 2.0 * PHIgxy + PHIgx2y + PHIgy2 + 2.0 * PHIgxy2 + PHIgx2y2); // Benhamou, Gobet, Miri expansion double Put = BSPut + A1T * dPdxdy + A2T * dPdx2dy + B0T * dPdy2 + B2T * dPdx2dy2; // Return the put or the call by put-call parity if (PutCall == "P") { return(Put); } else { return(Put - K * Math.Exp(-rf * T[NT - 1]) + S * Math.Exp(-q * T[NT - 1])); } }