// Bisection Algorithm for Black Scholes Implied Volatility ================================================================================= public double BisecBSIV(string PutCall, double S, double K, double rf, double q, double T, double a, double b, double MktPrice, double Tol, int MaxIter) { BlackScholesPrice BS = new BlackScholesPrice(); 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); }
static void Main(string[] args) { // 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]); } } // SP500 Historical prices. Oldest prices are first double[] S = new Double[120]; double[] x = new double[120]; using (TextReader reader = File.OpenText("../../SPY Historical Prices.txt")) { for (int k = 0; k <= 119; k++) { string text = reader.ReadLine(); string[] bits = text.Split(' '); S[k] = double.Parse(bits[0]); x[k] = Math.Log(S[k]); } } // Bounds on the parameter estimates // kappa theta sigma v0 rho double e = 1e-2; double[] lb = new double[5] { e, e, e, e, -0.999 }; double[] ub = new double[5] { 30.0, 3.0, 3.0, 2.0, 0.999 }; // Option settings OPSet opsettings; opsettings.S = 137.14; opsettings.r = 0.0010; opsettings.q = 0.0068; opsettings.trap = 1; // Read in SP500 implied volatilities int NT = 4; int NK = 7; double[,] MktIV = new Double[7, 4] { { 0.2780, 0.2638, 0.2532, 0.2518 }, { 0.2477, 0.2402, 0.2364, 0.2369 }, { 0.2186, 0.2158, 0.2203, 0.2239 }, { 0.1878, 0.1930, 0.2047, 0.2098 }, { 0.1572, 0.1712, 0.1894, 0.1970 }, { 0.1334, 0.1517, 0.1748, 0.1849 }, { 0.1323, 0.1373, 0.1618, 0.1736 } }; double[] K = new Double[7] { 120.0, 125.0, 130.0, 135.0, 140.0, 145.0, 150.0 }; double[] T = new Double[4] { 45.0 / 365.0, 98.0 / 365.0, 261.0 / 365.0, 348.0 / 365.0 }; // PutCall identifiers string[,] PutCall = new String[NK, NT]; for (int k = 0; k <= NK - 1; k++) { for (int t = 0; t <= NT - 1; t++) { PutCall[k, t] = "C"; } } // Obtain the market prices BlackScholesPrice BS = new BlackScholesPrice(); double[,] MktPrice = new Double[NK, NT]; for (int k = 0; k <= NK - 1; k++) { for (int t = 0; t <= NT - 1; t++) { MktPrice[k, t] = BS.BlackScholes(opsettings.S, K[k], T[t], opsettings.r, opsettings.q, MktIV[k, t], PutCall[k, t]); } } // Place the market data in the structure MktData data = new MktData(); data.MktIV = MktIV; data.MktPrice = MktPrice; data.K = K; data.T = T; data.PutCall = PutCall; // True parameter values int Lmethod = 2; double[] True = new double[5] { 8.9832, 0.0524, 1.0982, 0.0325, -0.9921 }; double dt = 1.0 / 252.0; // Settings for the objective function OFSet ofsettings; ofsettings.x = x; ofsettings.r = opsettings.r; ofsettings.q = opsettings.q; ofsettings.dt = dt; ofsettings.method = Lmethod; // Settings for the Nelder Mead algorithm NMSet nmsettings; nmsettings.N = 5; // Number of Heston parameters nmsettings.MaxIters = 1000; // Maximum number of iterations nmsettings.Tolerance = 1e-3; // Tolerance on best and worst function values nmsettings.ofsettings = ofsettings; // Starting values (vertices) in vector form. Add random increment about each starting value double kappaS = 9.00; double thetaS = 0.05; double sigmaS = 0.30; double v0S = 0.05; double rhoS = -0.80; int N = nmsettings.N; double[,] xs = new double[N, N + 1]; for (int j = 0; j <= N; j++) { xs[0, j] = kappaS + RandomNum(-0.01, 0.01); xs[1, j] = thetaS + RandomNum(-0.01, 0.01); xs[2, j] = sigmaS + RandomNum(-0.01, 0.01); xs[3, j] = v0S + RandomNum(-0.01, 0.01); xs[4, j] = rhoS + RandomNum(-0.01, 0.01); } // Obtain the parameter estimates NelderMeadAlgo NM = new NelderMeadAlgo(); Likelihood LL = new Likelihood(); double[] B = NM.NelderMead(LL.f, nmsettings, xs); // Output the estimation result Console.WriteLine(" "); Console.WriteLine("Atiya-Wall (2009) MLE parameters --------------------"); Console.WriteLine(" "); Console.WriteLine("Parameter MLE True Value "); Console.WriteLine("----------------------------------------"); Console.WriteLine("kappa {0,10:F5} {1,10:F5}", B[0], True[0]); Console.WriteLine("theta {0,10:F5} {1,10:F5}", B[1], True[1]); Console.WriteLine("sigma {0,10:F5} {1,10:F5}", B[2], True[2]); Console.WriteLine("v0 {0,10:F5} {1,10:F5}", B[3], True[3]); Console.WriteLine("rho {0,10:F5} {1,10:F5}", B[4], True[4]); Console.WriteLine("----------------------------------------"); Console.WriteLine(" "); Console.WriteLine("Value of the objective function is {0:F5}", B[5]); Console.WriteLine(" "); Console.WriteLine("Number of iterations required {0:0}", B[6]); Console.WriteLine(" "); Console.WriteLine("----------------------------------------"); }