// 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])); } }
static void Main(string[] args) { // Classes BGMPrice BGM = new BGMPrice(); BisectionAlgo BA = new BisectionAlgo(); // 32-point Gauss-Laguerre Abscissas and weights double[] X = new Double[32]; double[] W = new Double[32]; using (TextReader reader = File.OpenText("../../GaussLaguerre32.txt")) for (int k = 0; k <= 31; k++) { string text = reader.ReadLine(); string[] bits = text.Split(' '); X[k] = double.Parse(bits[0]); W[k] = double.Parse(bits[1]); } // DJIA ETF (ticker DIA) put data double[,] PutIV = new double[13, 4] { { 0.1962, 0.1947, 0.2019, 0.2115 }, { 0.1910, 0.1905, 0.1980, 0.2082 }, { 0.1860, 0.1861, 0.1943, 0.2057 }, { 0.1810, 0.1812, 0.1907, 0.2021 }, { 0.1761, 0.1774, 0.1871, 0.2000 }, { 0.1718, 0.1743, 0.1842, 0.1974 }, { 0.1671, 0.1706, 0.1813, 0.1950 }, { 0.1644, 0.1671, 0.1783, 0.1927 }, { 0.1645, 0.1641, 0.1760, 0.1899 }, { 0.1661, 0.1625, 0.1743, 0.1884 }, { 0.1701, 0.1602, 0.1726, 0.1862 }, { 0.1755, 0.1610, 0.1716, 0.1846 }, { 0.1796, 0.1657, 0.1724, 0.1842 } }; double[] K = new double[] { 124.0, 125.0, 126.0, 127.0, 128.0, 129.0, 130.0, 131.0, 132.0, 133.0, 134.0, 135.0, 136.0 }; double[] T = new double[] { 37.0 / 365.0, 72.0 / 365.0, 135.0 / 365.0, 226.0 / 365.0, }; double[,] MktIV = PutIV; double S = 129.14; double r = 0.0010; double q = 0.0068; int NT = 4; int NK = 13; // Create the market prices BlackScholesPrice BS = new BlackScholesPrice(); string[,] PutCall = new string[NK, NT]; double[,] MktPrice = new double[NK, NT]; for (int t = 0; t < NT; t++) { for (int k = 0; k < NK; k++) { PutCall[k, t] = "P"; MktPrice[k, t] = BS.BlackScholes(S, K[k], T[t], r, q, MktIV[k, t], PutCall[k, t]); } } // Bounds on the parameter estimates double e = 1e-3; double kappaU = 20.0; double kappaL = e; double thetaU = 3.0; double thetaL = e; double sigmaU = 10.0; double sigmaL = e; double v0U = 3.0; double v0L = e; double rhoU = 0.99; double rhoL = -0.99; int N = 2 + 3 * NT; double[] ub = new double[N]; double[] lb = new double[N]; for (int j = 0; j <= N; j++) { ub[0] = kappaU; lb[0] = kappaL; ub[1] = v0U; lb[1] = v0L; for (int k = 1; k <= NT; k++) { ub[3 * k - 1] = thetaU; lb[3 * k - 1] = thetaL; ub[3 * k] = sigmaU; lb[3 * k - 1] = sigmaL; ub[3 * k + 1] = rhoU; lb[3 * k + 1] = rhoL; } } // Starting values for the parameters // Parameter order is [kappa v0 | theta[0] sigma[0] rho[0] | theta[1] sigma[1] rho[1] | etc...]; double kappaS = 2.0; double thetaS = 0.1; double sigmaS = 1.2; double v0S = 0.05; double rhoS = -0.5; double[,] s = new double[N, N + 1]; for (int j = 0; j <= N; j++) { s[0, j] = kappaS + BS.RandomNum(-0.01, 0.01) * kappaS; s[1, j] = v0S + BS.RandomNum(-0.01, 0.01) * v0S; for (int k = 1; k <= NT; k++) { s[3 * k - 1, j] = thetaS + BS.RandomNum(-0.01, 0.01) * thetaS; s[3 * k, j] = sigmaS + BS.RandomNum(-0.01, 0.01) * sigmaS; s[3 * k + 1, j] = rhoS + BS.RandomNum(-0.01, 0.01) * rhoS; } } // Structure for the option settings OPSet opset; opset.S = S; opset.r = r; opset.q = q; opset.trap = 1; opset.PutCall = "P"; // Structure for the market data MktData data; data.MktIV = MktIV; data.MktPrice = MktPrice; data.K = K; data.T = T; data.PutCall = PutCall; // Structure for the objective function settings OFSet ofset; ofset.opset = opset; ofset.data = data; ofset.X = X; ofset.W = W; ofset.ObjFunction = 4; ofset.lb = lb; ofset.ub = ub; // Structure for the Nelder Mead algorithm settings NMSet nmset; nmset.ofset = ofset; nmset.MaxIters = 5000; nmset.Tolerance = 1e-8; nmset.N = N; // Run the Nelder Mead Algorithm NelderMeadAlgo NM = new NelderMeadAlgo(); ObjectiveFunction OF = new ObjectiveFunction(); double[] B = NM.NelderMead(OF.f, nmset, s); int NB = B.Length; // Separate the parameter vector into different vectors double kappa = B[0]; double v0 = B[1]; double[] theta = new double[NT]; double[] sigma = new double[NT]; double[] rho = new double[NT]; for (int k = 0; k <= NT - 1; k++) { theta[k] = B[3 * k + 2]; sigma[k] = B[3 * k + 3]; rho[k] = B[3 * k + 4]; } // Bisection algorithm settings double a = 0.01; double b = 4.0; double Tol = 1e-5; int MaxIter = 2500; // Find the implied volatilities double[,] ModelPrice = new double[NK, NT]; double[,] ModelIV = new double[NK, NT]; double Error = 0.00; List <double> MatList = new List <double>(); List <double> thetaList = new List <double>(); List <double> sigmaList = new List <double>(); List <double> rhoList = new List <double>(); double[] Mat, Theta, Sigma, Rho; for (int t = 0; t < NT; t++) { // Stack the parameters MatList.Add(T[t]); thetaList.Add(theta[t]); sigmaList.Add(sigma[t]); rhoList.Add(rho[t]); // Convert to arrays Mat = MatList.ToArray(); Theta = thetaList.ToArray(); Sigma = sigmaList.ToArray(); Rho = rhoList.ToArray(); for (int k = 0; k < NK; k++) { ModelPrice[k, t] = BGM.BGMApproxPriceTD(kappa, v0, Theta, Sigma, Rho, opset, K[k], Mat); ModelIV[k, t] = BA.BisecBSIV(opset, K[k], T[t], a, b, ModelPrice[k, t], Tol, MaxIter); Error += Math.Pow(ModelIV[k, t] - MktIV[k, t], 2.0) / Convert.ToDouble(NT * NK); } } // Output the results Console.WriteLine("----------------------------------------------------------"); Console.WriteLine(" kappa v0 theta sigma rho"); Console.WriteLine("----------------------------------------------------------"); for (int k = 0; k <= NT - 1; k++) { Console.WriteLine("{0,10:F5} {1,10:F5} {2,10:F5} {3,10:F5} {4,10:F5}", kappa, v0, theta[k], sigma[k], rho[k]); } Console.WriteLine("----------------------------------------------------------"); Console.WriteLine("Value of objective function {0:F5} ", B[NB - 2]); Console.WriteLine("Number of iterations required {0:0} ", B[NB - 1]); Console.WriteLine("----------------------------------------------------------"); Console.WriteLine(" "); Console.WriteLine("Model/Market implied volatilities"); Console.WriteLine("----------------------------------------------------------"); Console.WriteLine("Strike Mat1 Mat2 Mat3 Mat4"); Console.WriteLine("----------------------------------------------------------"); for (int k = 0; k <= NK - 1; k++) { Console.WriteLine("{0,5:0} {1,10:F5} {2,10:F5} {3,10:F5} {4,10:F5}", K[k], ModelIV[k, 0], ModelIV[k, 1], ModelIV[k, 2], ModelIV[k, 3]); Console.WriteLine("{0,5:0} {1,10:F5} {2,10:F5} {3,10:F5} {4,10:F5}", K[k], MktIV[k, 0], MktIV[k, 1], MktIV[k, 2], MktIV[k, 3]); Console.WriteLine("----------------------------------------------------------"); } Console.WriteLine(" "); Console.WriteLine("IV Estimation error {0,5:E5}", Error); Console.WriteLine(" "); }