private void FrmCadastroFeriado_Load(object sender, EventArgs e) { dtAno = DateTime.Now; ListaFeriados(dtAno); TxtAno.Text = dtAno.ToString("yyyy"); MktData.Select(); }
private void Reset() { TxtAno.Text = dtAno.ToString("yyyy"); TxtDescricao.Clear(); BtnAlterar.Enabled = false; BtnExcluir.Enabled = false; BtnGravar.Enabled = true; MktData.Clear(); MktData.Focus(); }
private void _Core_OnManagedTickPrice(object sender, TickPriceArg e) { string field = TickType.getField(e.field); var mkt = _MktData.Where(x => x.ReqId == e.tickerId).FirstOrDefault(); if (mkt == null) { mkt = new MktData(e.tickerId, field, e.price); _MktData.Add(mkt); } else { mkt.AddUpdate(field, e.price); } }
private void _Core_OntickOptionComputation(object sender, TickOptionComputationArg e) { var mkt = _MktData.Where(x => x.ReqId == e.tickerId).FirstOrDefault(); if (mkt == null) { mkt = new MktData(e.tickerId, "delta", e.delta); _MktData.Add(mkt); } else { mkt.AddUpdate("delta", e.delta); } mkt.AddUpdate("gamma", e.gamma); mkt.AddUpdate("theta", e.theta); mkt.AddUpdate("vega", e.vega); mkt.AddUpdate("iv", e.impliedVolatility); mkt.AddUpdate("optPrice", e.optPrice); mkt.AddUpdate("undPrice", e.undPrice); mkt.AddUpdate("pvDividend", e.pvDividend); }
static void Main(string[] args) { // Classes MiscFunctions MF = new MiscFunctions(); DiffEvoAlgo DE = new DiffEvoAlgo(); NelderMeadAlgo NM = new NelderMeadAlgo(); ObjectiveFunction OF = new ObjectiveFunction(); // 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]); } // Option settings OPSet opset; opset.S = 137.14; opset.r = 0.0010; opset.q = 0.0068; opset.trap = 1; // Bisection algorithm settings double a = 0.01; double b = 3.0; double Tol = 1e-5; int MaxIter = 1000; int ObjectiveFun = 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] { 0.123287671232877, 0.268493150684932, 0.715068493150685, 0.953424657534247 }; // 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(opset.S, K[k], T[t], opset.r, opset.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; // Estimation bounds double e = 1e-5; double kappaL = e; double kappaU = 10; double thetaL = e; double thetaU = 5; double sigmaL = e; double sigmaU = 5; double v0L = e; double v0U = 1; double rhoL = -.9; double rhoU = 0; double[] ub = new double[5] { kappaU, thetaU, sigmaU, v0U, rhoU }; double[] lb = new double[5] { kappaL, thetaL, sigmaL, v0L, rhoL }; // Objective function settings; OFSet ofset; ofset.opset = opset; ofset.data = data; ofset.X = X; ofset.W = W; ofset.LossFunction = 4; // Choice of loss function 1=MSE, 2=RMSE, 3=IVMSE, 4=Christoffersen et al. ofset.lb = lb; ofset.ub = ub; ofset.CF = "Heston"; // Choice of c.f. "Heston" or "Attari" // Settings for the Differential Evolution algorithm DEParam ParamLim = new DEParam(); ParamLim.ub = ub; ParamLim.lb = lb; ParamLim.NG = 500; ParamLim.NP = 75; ParamLim.F = 0.5; ParamLim.CR = 0.8; // Run the differential evolution algorithm HParam DEparam = DE.HestonDE(ParamLim, opset, data, ObjectiveFun, a, b, Tol, MaxIter, X, W, ofset.CF); // Starting values (vertices) in vector form. Add random increment about each starting value double kappaS = 9; double thetaS = 0.05; double sigmaS = 0.3; double v0S = 0.05; double rhoS = -0.8; int N = 5; double[,] s = new double[N, N + 1]; for (int j = 0; j <= N; j++) { s[0, j] = kappaS + MF.RandomNum(-0.10, 0.10); s[1, j] = thetaS + MF.RandomNum(-0.01, 0.01); s[2, j] = sigmaS + MF.RandomNum(-0.05, 0.05); s[3, j] = v0S + MF.RandomNum(-0.01, 0.01); s[4, j] = rhoS + MF.RandomNum(-0.05, 0.05); } // Nelder Mead settings NMSet nmset; nmset.ofset = ofset; nmset.N = 5; // Number of Heston parameters nmset.MaxIters = 1000; // Maximum number of iterations nmset.Tolerance = 1e-4; // Tolerance on best and worst function values // Run the Nelder Mead algorithm double[] B = NM.NelderMead(OF.f, nmset, s); HParam NMparam = new HParam(); NMparam.kappa = B[0]; NMparam.theta = B[1]; NMparam.sigma = B[2]; NMparam.v0 = B[3]; NMparam.rho = B[4]; // Calculate IVMSE under both parameter estimates double[,] NMPrice = new double[NK, NT]; double[,] DEPrice = new double[NK, NT]; double[,] NMIV = new double[NK, NT]; double[,] DEIV = new double[NK, NT]; double NMIVMSE = 0.0; double DEIVMSE = 0.0; double S = opset.S; double r = opset.r; double q = opset.q; int trap = opset.trap; HestonPrice HP = new HestonPrice(); Bisection BA = new Bisection(); for (int t = 0; t < NT; t++) { for (int k = 0; k < NK; k++) { NMPrice[k, t] = HP.HestonPriceGaussLaguerre(NMparam, S, K[k], r, q, T[t], trap, PutCall[k, t], X, W); NMIV[k, t] = BA.BisecBSIV(PutCall[k, t], S, K[k], r, q, T[t], a, b, NMPrice[k, t], Tol, MaxIter); NMIVMSE += Math.Pow(MktIV[k, t] - NMIV[k, t], 2) / Convert.ToDouble(NT * NK); DEPrice[k, t] = HP.HestonPriceGaussLaguerre(DEparam, S, K[k], r, q, T[t], trap, PutCall[k, t], X, W); DEIV[k, t] = BA.BisecBSIV(PutCall[k, t], S, K[k], r, q, T[t], a, b, DEPrice[k, t], Tol, MaxIter); DEIVMSE += Math.Pow(MktIV[k, t] - DEIV[k, t], 2) / Convert.ToDouble(NT * NK); } } // Output the result Console.WriteLine(" "); Console.WriteLine("Parameter Estimates --------------------"); Console.WriteLine(" Differential Evolution Nelder Mead"); Console.WriteLine("kappa {0,10:F4} {1,25:F4}", DEparam.kappa, NMparam.kappa); Console.WriteLine("theta {0,10:F4} {1,25:F4}", DEparam.theta, NMparam.theta); Console.WriteLine("sigma {0,10:F4} {1,25:F4}", DEparam.sigma, NMparam.sigma); Console.WriteLine("v0 {0,10:F4} {1,25:F4}", DEparam.v0, NMparam.v0); Console.WriteLine("rho {0,10:F4} {1,25:F4}", DEparam.rho, NMparam.rho); Console.WriteLine(" "); Console.WriteLine("IV MSE ---------------------------------"); Console.WriteLine("Differential Evolution IVMSE {0:E8}", DEIVMSE); Console.WriteLine("Nelder Mead IVMSE {0:E8}", NMIVMSE); Console.WriteLine(" "); }
// Objective function =========================================================================== public double DHObjFunSVC(double[] param, OpSet settings, MktData data, double[] X, double[] W, int objectivefun, double[] lb, double[] ub) { HestonPriceDH HPDH = new HestonPriceDH(); BisectionAlgo BA = new BisectionAlgo(); double S = settings.S; double r = settings.r; double q = settings.q; int trap = settings.trap; double[,] MktIV = data.MktIV; double[,] MktPrice = data.MktPrice; string[,] PutCall = data.PutCall; double[] K = data.K; double[] T = data.T; int NK = PutCall.GetLength(0); int NT = PutCall.GetLength(1); int NX = X.Length; DHParam param2 = new DHParam(); param2.kappa1 = param[0]; param2.theta1 = param[1]; param2.sigma1 = param[2]; param2.v01 = param[3]; param2.rho1 = param[4]; param2.kappa2 = param[5]; param2.theta2 = param[6]; param2.sigma2 = param[7]; param2.v02 = param[8]; param2.rho2 = param[9]; // Settings for the Bisection algorithm double a = 0.001; double b = 3.0; double Tol = 1e5; int BSIVMaxIter = 10000; // Initialize the model price and model implied vol vectors, and the objective function value double[,] ModelPrice = new double[NK, NT]; double[,] ModelIV = new double[NK, NT]; double BSVega = 0.0; double Error = 0.0; double pi = Math.PI; double kappaLB = lb[0]; double kappaUB = ub[0]; double thetaLB = lb[1]; double thetaUB = ub[1]; double sigmaLB = lb[2]; double sigmaUB = ub[2]; double v0LB = lb[3]; double v0UB = ub[3]; double rhoLB = lb[4]; double rhoUB = ub[4]; double phi, P1, P2, CallPrice; Complex Integrand1, Integrand2; Complex i = Complex.ImaginaryOne; Complex[] f2 = new Complex[NX]; Complex[] f1 = new Complex[NX]; double[] int1 = new double[NX]; double[] int2 = new double[NX]; if ((param2.kappa1 <= kappaLB) || (param2.theta1 <= thetaLB) || (param2.sigma1 <= sigmaLB) || (param2.v01 <= v0LB) || (param2.rho1 <= rhoLB) || (param2.kappa1 >= kappaUB) || (param2.theta1 >= thetaUB) || (param2.sigma1 >= sigmaUB) || (param2.v01 >= v0UB) || (param2.rho1 >= rhoUB) || (param2.kappa2 <= kappaLB) || (param2.theta2 <= thetaLB) || (param2.sigma2 <= sigmaLB) || (param2.v02 <= v0LB) || (param2.rho2 <= rhoLB) || (param2.kappa2 >= kappaUB) || (param2.theta2 >= thetaUB) || (param2.sigma2 >= sigmaUB) || (param2.v02 >= v0UB) || (param2.rho2 >= rhoUB)) { Error = 1e50; } else { for (int t = 0; t < NT; t++) { for (int j = 0; j < NX; j++) { settings.T = T[t]; phi = X[j]; f2[j] = HPDH.HestonDoubleCF(phi, param2, settings); f1[j] = HPDH.HestonDoubleCF(phi - i, param2, settings); } for (int k = 0; k < NK; k++) { for (int j = 0; j < NX; j++) { phi = X[j]; Integrand2 = Complex.Exp(-i * phi * Complex.Log(K[k])) * f2[j] / i / phi; int2[j] = W[j] * Integrand2.Real; Integrand1 = Complex.Exp(-i * phi * Complex.Log(K[k])) * f1[j] / i / phi / S / Complex.Exp((r - q) * T[t]); int1[j] = W[j] * Integrand1.Real; } P1 = 0.5 + 1.0 / pi * int1.Sum(); P2 = 0.5 + 1.0 / pi * int2.Sum(); // The call price CallPrice = S * Math.Exp(-q * T[t]) * P1 - K[k] * Math.Exp(-r * T[t]) * P2; if (PutCall[k, t] == "C") { ModelPrice[k, t] = CallPrice; } else { ModelPrice[k, t] = CallPrice - S * Math.Exp(-q * T[t]) + Math.Exp(-r * T[t]) * K[k]; } switch (objectivefun) { case 1: // MSE Loss Function Error += Math.Pow(ModelPrice[k, t] - MktPrice[k, t], 2.0); break; case 2: // RMSE Loss Function Error += Math.Pow(ModelPrice[k, t] - MktPrice[k, t], 2.0) / MktPrice[k, t]; break; case 3: // IVMSE Loss Function ModelIV[k, t] = BA.BisecBSIV(PutCall[k, t], S, K[k], r, q, T[t], a, b, ModelPrice[k, t], Tol, BSIVMaxIter); Error += Math.Pow(ModelIV[k, t] - MktIV[k, t], 2); break; case 4: // IVRMSE Christoffersen, Heston, Jacobs proxy double d = (Math.Log(S / K[k]) + (r - q + MktIV[k, t] * MktIV[k, t] / 2.0) * T[t]) / MktIV[k, t] / Math.Sqrt(T[t]); double NormPDF = Math.Exp(-0.5 * d * d) / Math.Sqrt(2.0 * pi); BSVega = S * NormPDF * Math.Sqrt(T[t]); Error += Math.Pow(ModelPrice[k, t] - MktPrice[k, t], 2.0) / (BSVega * BSVega); break; } } } } return(Error); }
static void Main(string[] args) { // 32 point Gauss Laguerre 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]); } } // Bounds on the parameter estimates // kappa theta sigma v0 rho double e = 1e-5; double[] lb = new double[5] { e, e, e, e, -0.99 }; double[] ub = new double[5] { 20.0, 2.0, 5.0, 2.0, 0.99 }; //// IBM put prices May 7, 2010 double S = 122.10; //double[,] MktPrice = new double[8,5]{ // { 0.130, 0.620, 1.275, 2.950, 4.750}, // { 0.260, 0.955, 1.830, 3.925, 6.075}, // { 0.485, 1.500, 2.610, 5.200, 7.625}, // { 0.995, 2.445, 3.775, 6.800, 9.375}, // { 2.155, 3.900, 5.475, 8.800, 11.550}, // { 4.525, 6.225, 7.775, 11.225, 13.975}, // { 8.375, 9.525, 10.850, 14.125, 16.775}, // {13.075, 13.600, 14.575, 17.425, 19.900}}; // Use the first set of prices, for the first maturity double[] MktPrice = new double[8] { 0.130, 0.260, 0.485, 0.995, 2.155, 4.525, 8.375, 13.075 }; double[] K = new double[8] { 100.0, 105.0, 110.0, 115.0, 120.0, 125.0, 130.0, 135.0 }; double r = 0.015; double q = 0.01; //double[] T = new double[5] {0.0384,0.1151, 0.1918, 0.4411, 0.7096}; double T = 0.0384; int NK = 8; // Bisection algorithm double a = 0.01; double b = 2.00; double Tol = 1.0e-5; int MaxIter = 1000; double B = 2.0; double dt = 1.0e-10; // Implied volatilities BisectionImpliedVol BA = new BisectionImpliedVol(); double[] MktIV = new double[NK]; for (int k = 0; k <= NK - 1; k++) { MktIV[k] = BA.BisectionMSIV(S, K[k], r, q, T, a, b, MktPrice[k], Tol, MaxIter, B, dt); } MktData MktData = new MktData(); MktData.MktIV = MktIV; MktData.K = K; MktData.PutCall = "P"; OpSet opsettings = new OpSet(); opsettings.S = S; opsettings.T = T; opsettings.r = r; opsettings.q = q; opsettings.PutCall = "P"; opsettings.trap = 1; MSSet mssettings = new MSSet(); mssettings.method = 3; mssettings.A = 0.0001; mssettings.B = 100.0; mssettings.N = 3000; mssettings.dt = 1.0e-10; mssettings.tol = 1.0e-5; mssettings.MaxIter = 1000; mssettings.NumTerms = 3; mssettings.yinf = 1.0e4; mssettings.a = -10.0; mssettings.b = +10.0; OFSet ofsettings = new OFSet(); ofsettings.opsettings = opsettings; ofsettings.mssettings = mssettings; ofsettings.data = MktData; ofsettings.X = X; ofsettings.W = W; ofsettings.lb = lb; ofsettings.ub = ub; // Settings for the Nelder Mead algorithm NMSet nmsettings; nmsettings.N = 5; // Number of Heston parameters nmsettings.MaxIters = 20; // Maximum number of iterations nmsettings.Tolerance = 1e-6; // Tolerance on best and worst function values nmsettings.ofsettings = ofsettings; // Starting values (vertices) in vector form. Add random increment about each starting value NelderMeadAlgo NM = new NelderMeadAlgo(); double kappaS = 20.00; double thetaS = 0.036; double sigmaS = 3.9; double v0S = 0.15; double rhoS = -0.46; int N = nmsettings.N; double[,] x = new double[N, N + 1]; for (int j = 0; j <= N; j++) { x[0, j] = kappaS + NM.RandomNum(-0.01, 0.01) * kappaS; x[1, j] = thetaS + NM.RandomNum(-0.01, 0.01) * thetaS; x[2, j] = sigmaS + NM.RandomNum(-0.01, 0.01) * sigmaS; x[3, j] = v0S + NM.RandomNum(-0.01, 0.01) * v0S; x[4, j] = rhoS + NM.RandomNum(-0.01, 0.01) * rhoS; } // Obtain the parameter estimates ObjectiveFunction OF = new ObjectiveFunction(); double[] BB = NM.NelderMead(OF.f, nmsettings, x); HParam paramEst = new HParam(); paramEst.kappa = BB[0]; paramEst.theta = BB[1]; paramEst.sigma = BB[2]; paramEst.v0 = BB[3]; paramEst.rho = BB[4]; paramEst.lambda = 0.0; // Obtain the fitted implied vols MSPrices MS = new MSPrices(); double[] ModelIV = new double[NK]; double[] ModelPrice = new double[NK]; double[] output = new double[6]; double IVMSE = 0.0; for (int k = 0; k <= NK - 1; k++) { opsettings.K = K[k]; output = MS.MSPriceHeston(paramEst, opsettings, mssettings); ModelPrice[k] = output[2]; ModelIV[k] = BA.BisectionMSIV(S, K[k], r, q, T, a, b, ModelPrice[k], Tol, MaxIter, B, dt); IVMSE += Math.Pow(ModelIV[k] - MktIV[k], 2.0); } // Output the estimation result Console.WriteLine(" "); Console.WriteLine("Parameter Estimates --------------------"); Console.WriteLine(" "); Console.WriteLine("kappa = {0:F5}", BB[0]); Console.WriteLine("theta = {0:F5}", BB[1]); Console.WriteLine("sigma = {0:F5}", BB[2]); Console.WriteLine("v0 = {0:F5}", BB[3]); Console.WriteLine("rho = {0:F5}", BB[4]); Console.WriteLine(" "); Console.WriteLine("Value of the objective function is {0:E5}", BB[5]); Console.WriteLine("Number of iterations required {0:0}", BB[6]); Console.WriteLine(" "); Console.WriteLine("----------------------------------------"); Console.WriteLine(" "); Console.WriteLine("Strike MktIV ModelIV"); Console.WriteLine("-------------------------"); for (int k = 0; k <= NK - 1; k++) { Console.WriteLine("{0,3:F0} {1,12:F5} {2,12:F5}", K[k], MktIV[k], ModelIV[k]); } }
// Nelder Mead Algorithm =============================================================================================== public double[] NelderMead(ObjFun f, int N, double NumIters, int MaxIters, double Tolerance, double[,] x, OpSet settings, MktData data, double[] X, double[] W, int LossFunction, double[] lb, double[] ub) { int i, j; // Value of the function at the vertices double[][] F = new Double[N + 1][]; for (i = 0; i <= N; i++) { F[i] = new double[2] { 0.0, 0.0 } } ; // Step 0. Ordering and Best and Worst points // Order according to the functional values, compute the best and worst points step0: NumIters = NumIters + 1; Console.Write("Nelder Mead iteration "); Console.WriteLine(NumIters); for (j = 0; j <= N; j++) { double[] z = new double[N]; for (i = 0; i <= N - 1; i++) { z[i] = x[i, j]; F[j][0] = f(z, settings, data, X, W, LossFunction, lb, ub); // Function values F[j][1] = j; // Original index positions } } // Sort the F array w.r.t column 0 int column = 0; Array.Sort(F, delegate(double[] w1, double[] w2) { return((w1[column] as IComparable).CompareTo(w2[column])); }); // New vertices order first N best initial vectors and // last (N+1)st vertice is the worst vector // y is the matrix of vertices, ordered so that the worst vertice is last double[,] y = new double[N, N + 1]; for (j = 0; j <= N; j++) { for (i = 0; i <= N - 1; i++) { y[i, j] = x[i, Convert.ToInt32(F[j][1])]; } } // First best vector y(1) and function value f1 double[] x1 = new double[N]; for (i = 0; i <= N - 1; i++) { x1[i] = y[i, 0]; } double f1 = f(x1, settings, data, X, W, LossFunction, lb, ub); // Last best vector y(N) and function value fn double[] xn = new Double[N]; for (i = 0; i <= N - 1; i++) { xn[i] = y[i, N - 1]; } double fn = f(xn, settings, data, X, W, LossFunction, lb, ub); // Worst vector y(N+1) and function value fn1 double[] xn1 = new Double[N]; for (i = 0; i <= N - 1; i++) { xn1[i] = y[i, N]; } double fn1 = f(xn1, settings, data, X, W, LossFunction, lb, ub); // z is the first N vectors from y, excludes the worst y(N+1) double[,] zz = new Double[N, N]; for (j = 0; j <= N - 1; j++) { for (i = 0; i <= N - 1; i++) { zz[i, j] = y[i, j]; } } // Mean of best N values and function value fm double[] xm = new Double[N]; xm = VMean(zz, N); double fm = f(xm, settings, data, X, W, LossFunction, lb, ub); // Reflection point xr and function fr double[] xr = new Double[N]; xr = VSub(VAdd(xm, xm), xn1); double fr = f(xr, settings, data, X, W, LossFunction, lb, ub); // Expansion point xe and function fe double[] xe = new Double[N]; xe = VSub(VAdd(xr, xr), xm); double fe = f(xe, settings, data, X, W, LossFunction, lb, ub); // Outside contraction point and function foc double[] xoc = new Double[N]; xoc = VAdd(VMult(xr, 0.5), VMult(xm, 0.5)); double foc = f(xoc, settings, data, X, W, LossFunction, lb, ub); // Inside contraction point and function foc double[] xic = new Double[N]; xic = VAdd(VMult(xm, 0.5), VMult(xn1, 0.5)); double fic = f(xic, settings, data, X, W, LossFunction, lb, ub); while ((NumIters <= MaxIters) && (Math.Abs(f1 - fn1) >= Tolerance)) { // Step 1. Reflection Rule if ((f1 <= fr) && (fr < fn)) { for (j = 0; j <= N - 1; j++) { for (i = 0; i <= N - 1; i++) { x[i, j] = y[i, j]; } } for (i = 0; i <= N - 1; i++) { x[i, N] = xr[i]; } goto step0; } // Step 2. Expansion Rule if (fr < f1) { for (j = 0; j <= N - 1; j++) { for (i = 0; i <= N - 1; i++) { x[i, j] = y[i, j]; } } if (fe < fr) { for (i = 0; i <= N - 1; i++) { x[i, N] = xe[i]; } } else { for (i = 0; i <= N - 1; i++) { x[i, N] = xr[i]; } } goto step0; } // Step 3. Outside contraction Rule if ((fn <= fr) && (fr < fn1) && (foc <= fr)) { for (j = 0; j <= N - 1; j++) { for (i = 0; i <= N - 1; i++) { x[i, j] = y[i, j]; } } for (i = 0; i <= N - 1; i++) { x[i, N] = xoc[i]; } goto step0; } // Step 4. Inside contraction Rule if ((fr >= fn1) && (fic < fn1)) { for (j = 0; j <= N - 1; j++) { for (i = 0; i <= N - 1; i++) { x[i, j] = y[i, j]; } } for (i = 0; i <= N - 1; i++) { x[i, N] = xic[i]; } goto step0; } // Step 5. Shrink Step for (i = 0; i <= N - 1; i++) { x[i, 0] = y[i, 0]; } for (i = 0; i <= N - 1; i++) { for (j = 1; j <= N; j++) { x[i, j] = 0.5 * (y[i, j] + x[i, 0]); } } goto step0; } // Output component double[] outvec = new Double[N + 2]; for (i = 0; i <= N - 1; i++) { outvec[i] = x1[i]; } outvec[N] = f1; outvec[N + 1] = NumIters; return(outvec); }
static void Main(string[] args) { // Classes HestonPrice HP = new HestonPrice(); BlackScholesPrice BS = new BlackScholesPrice(); Bisection BA = new Bisection(); NelderMeadAlgo NM = new NelderMeadAlgo(); ObjectiveFunction OF = new ObjectiveFunction(); // 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]); } } // Option settings OPSet opset; opset.S = 137.14; opset.r = 0.0010; opset.q = 0.0068; opset.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] { 0.123287671232877, 0.268493150684932, 0.715068493150685, 0.953424657534247 }; // 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 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(opset.S, K[k], T[t], opset.r, opset.q, MktIV[k, t], PutCall[k, t]); } } // settings for the market data MktData data = new MktData(); data.MktIV = MktIV; data.MktPrice = MktPrice; data.K = K; data.T = T; data.PutCall = PutCall; // Bounds on the parameter estimates // kappa theta sigma v0 rho double e = 1e-5; double[] lb = new double[5] { e, e, e, e, -0.99 }; double[] ub = new double[5] { 20.0, 2.0, 2.0, 2.0, 0.99 }; // C.F. choice and Loss function choice string CF = "Heston"; // Choice of c.f. "Heston" or "Attari" int LossFunction = 1; // Choice of loss function // 1=MSE, 2=RMSE, 3=IVMSE, 4=Christoffersen et al. // Settings for the objective function OFSet ofset; ofset.opset = opset; ofset.data = data; ofset.X = X; ofset.W = W; ofset.LossFunction = LossFunction; ofset.lb = lb; ofset.ub = ub; ofset.CF = CF; // Settings for the Nelder Mead algorithm NMSet nmsettings; nmsettings.ofset = ofset; nmsettings.N = 5; // Number of Heston parameters nmsettings.MaxIters = 1000; // Maximum number of iterations nmsettings.Tolerance = 1e-4; // Tolerance on best and worst function values // Starting values (vertices) in vector form. Add random increment about each starting value double kappaS = 9; double thetaS = 0.05; double sigmaS = 0.3; double v0S = 0.05; double rhoS = -0.8; int N = nmsettings.N; double[,] s = new double[N, N + 1]; for (int j = 0; j <= N; j++) { s[0, j] = kappaS + NM.RandomNum(-0.10, 0.10); s[1, j] = thetaS + NM.RandomNum(-0.01, 0.01); s[2, j] = sigmaS + NM.RandomNum(-0.05, 0.05); s[3, j] = v0S + NM.RandomNum(-0.01, 0.01); s[4, j] = rhoS + NM.RandomNum(-0.05, 0.05); } // Find the Nelder-Mead parameter estimates double[] B = NM.NelderMead(OF.f, nmsettings, s); // Output the estimation result Console.WriteLine(" "); Console.WriteLine("Parameter Estimates --------------------"); Console.WriteLine(" "); Console.WriteLine("kappa = {0:F5}", B[0]); Console.WriteLine("theta = {0:F5}", B[1]); Console.WriteLine("sigma = {0:F5}", B[2]); Console.WriteLine("v0 = {0:F5}", B[3]); Console.WriteLine("rho = {0:F5}", B[4]); Console.WriteLine(" "); Console.WriteLine("Value of the objective function is {0:F5}", B[5]); Console.WriteLine(" "); Console.WriteLine("Number of iterations required {0}", B[6]); Console.WriteLine(" "); Console.WriteLine("----------------------------------------"); // Obtain the model prices and model implied volatilities HParam paramNM = new HParam(); paramNM.kappa = B[0]; paramNM.theta = B[1]; paramNM.sigma = B[2]; paramNM.v0 = B[3]; paramNM.rho = B[4]; double a = 0.01; double b = 3.0; double Tol = 1e-5; int MaxIter = 1000; double[,] ModelPrice = new double[NK, NT]; double[,] ModelIV = new double[NK, NT]; double IVMSE = 0.0; double S = ofset.opset.S; double r = ofset.opset.r; double q = ofset.opset.q; int trap = ofset.opset.trap; for (int k = 0; k < NK; k++) { for (int t = 0; t < NT; t++) { ModelPrice[k, t] = HP.HestonPriceGaussLaguerre(paramNM, S, K[k], r, q, T[t], trap, PutCall[k, t], X, W); ModelIV[k, t] = BA.BisecBSIV(PutCall[k, t], S, K[k], r, q, T[t], a, b, ModelPrice[k, t], Tol, MaxIter); IVMSE += Math.Pow(MktIV[k, t] - ModelIV[k, t], 2) / Convert.ToDouble(NT * NK); } } // Output the results Console.Write("MSE between model and market implied vols = {0}", IVMSE); Console.WriteLine(" "); Console.WriteLine("----------------------------------------"); Console.WriteLine("Market implied volatilities"); Console.WriteLine(" "); for (int k = 0; k < NK; k++) { for (int t = 0; t < NT; t++) { if (t < NT - 1) { Console.Write("{0:F4} ", MktIV[k, t]); } else { Console.WriteLine("{0:F4} ", MktIV[k, t]); } } } Console.WriteLine(" "); Console.WriteLine("----------------------------------------"); Console.WriteLine("Model implied volatilities"); Console.WriteLine(" "); for (int k = 0; k < NK; k++) { for (int t = 0; t < NT; t++) { if (t < NT - 1) { Console.Write("{0:F4} ", ModelIV[k, t]); } else { Console.WriteLine("{0:F4} ", ModelIV[k, t]); } } } Console.WriteLine("----------------------------------------"); }
// Differential evolution algorithm public HParam HestonDE(DEParam DEsettings, OPSet settings, MktData data, int LossFunction, double a, double b, double Tol, int MaxIter, double[] X, double[] W, string CF) { // NG = Number of generations (iterations) // NP = Number of population members // CR = Crossover ratio (=0.5) // F = Threshold (=0.8) // Hi = Vector of upper bounds for the parameters // Lo = Vector of lower bounds for the parameters // S = Spot Price // K1 = First strike point for the FRFT // rf = Risk free rate // q = Dividend Yield // MktPrice = Market quotes for prices // K = Vector of Strikes // T = Vector of Maturities // PutCall = Matrix of 'P'ut or 'C'all // MktIV = Market quotes for implied volatilities // LossFunction = Type of Objective Function 1 = MSE; 2 = RMSE; 3 = IVMSE; 4 = Christoffersen, Jacobs, Heston (2009) // Bisection method settings; a = Lower limit; b = upper limit; Tol = Tolerance; MaxIter = Max number of iterations // trap = 1 is "Little Trap" c.f., 0 is Heston c.f. // X, W = Gauss Laguerre abscissas and weights // CF = "Heston" or "Attari" characteristic function ObjectiveFunction OF = new ObjectiveFunction(); MiscFunctions MF = new MiscFunctions(); double[] Hi = DEsettings.ub; double[] Lo = DEsettings.lb; double CR = DEsettings.CR; double kappaU = Hi[0]; double kappaL = Lo[0]; double thetaU = Hi[1]; double thetaL = Lo[1]; double sigmaU = Hi[2]; double sigmaL = Lo[2]; double v0U = Hi[3]; double v0L = Lo[3]; double rhoU = Hi[4]; double rhoL = Lo[4]; // Create the structure for the objective function; OFSet ofset; ofset.opset = settings; ofset.data = data; ofset.X = X; ofset.W = W; ofset.LossFunction = LossFunction; ofset.lb = DEsettings.lb; ofset.ub = DEsettings.ub; ofset.CF = CF; // Step1. Generate the population matrix of random parameters int NP = DEsettings.NP; int NG = DEsettings.NG; double F = DEsettings.F; double[,] P = new double[5, NP]; for (int j = 0; j <= NP - 1; j++) { P[0, j] = kappaL + (kappaU - kappaL) * MF.RandomNum(0.0, 1.0); P[1, j] = thetaL + (thetaU - thetaL) * MF.RandomNum(0.0, 1.0); P[2, j] = sigmaL + (sigmaU - sigmaL) * MF.RandomNum(0.0, 1.0); P[3, j] = v0L + (v0U - v0L) * MF.RandomNum(0.0, 1.0); P[4, j] = rhoL + (rhoU - rhoL) * MF.RandomNum(0.0, 1.0); } // Generate the random numbers outside the loop double[, ,] U = new double[5, NP, NG]; for (int k = 0; k <= NG - 1; k++) { for (int j = 0; j <= NP - 1; j++) { for (int i = 0; i <= 4; i++) { U[i, j, k] = MF.RandomNum(0.0, 1.0); } } } // Initialize the variables double[] Pr1 = new double[5]; double[] Pr2 = new double[5]; double[] Pr3 = new double[5]; double[] P0 = new double[5]; // Loop through the generations for (int k = 0; k <= NG - 1; k++) { Console.Write("Differential Evolution iteration "); Console.WriteLine(k); // Loop through the population for (int i = 0; i <= NP - 1; i++) { // Select the i-th member of the population for (int s = 0; s <= 4; s++) { P0[s] = P[s, i]; } Step0: // Select random indices for three other distinct members int[] Integers0toNP1 = new int[NP]; for (int s = 0; s <= NP - 1; s++) { Integers0toNP1[s] = s; } // Random perumation of the indices (0,1,...,NP-1) int[] I = MF.RandomPerm(Integers0toNP1); // Find the index in I that is not equal to i and keep the first 3 positions int[] L = MF.RemoveIndex(I, i); int[] r = new int[3]; for (int s = 0; s <= 2; s++) { r[s] = L[s]; } // The three distinct members of the population for (int s = 0; s <= 4; s++) { Pr1[s] = P[s, r[0]]; Pr2[s] = P[s, r[1]]; Pr3[s] = P[s, r[2]]; } int[] Integers1to5 = { 1, 2, 3, 4, 5 }; int[] R = MF.RandomPerm(Integers1to5); double[] Pnew = { 0.0, 0.0, 0.0, 0.0, 0.0 }; // Steps 2 and 3. Mutation and recombination double Ri; double u; for (int j = 0; j <= 4; j++) { Ri = R[0]; u = U[j, i, k]; if ((u <= CR) | (j == Ri)) { Pnew[j] = Pr1[j] + F * (Pr2[j] - Pr3[j]); } else { Pnew[j] = P0[j]; } } // Repeat above to code to ensure new members fall within the // range of acceptable parameter values int[] Flag = new int[5] { 0, 0, 0, 0, 0 }; for (int s = 0; s <= 4; s++) { if (Pnew[s] <= Lo[s] | Pnew[s] >= Hi[s]) { Flag[s] = 1; } } int Condition = Flag.Sum(); if (Condition > 0) { goto Step0; } // Step 4. Selection // Calculate the objective function for the ith member and for the candidate double f0 = OF.f(P0, ofset); double fnew = OF.f(Pnew, ofset); // Verify whether the candidate should replace the i-th member // in the population and replace if conditions are satisfied if (fnew < f0) { for (int s = 0; s <= 4; s++) { P[s, i] = Pnew[s]; } } } } // Calculate the objective function for each member in the updated population double[] fs = new double[NP]; double[] Pmember = new double[5]; for (int s = 0; s <= NP - 1; s++) { for (int t = 0; t <= 4; t++) { Pmember[t] = P[t, s]; } fs[s] = OF.f(Pmember, ofset); } // Find the member with the lowest objective function double Minf = fs[0]; int Minpos = 0; for (int s = 0; s <= NP - 1; s++) { if (fs[s] < Minf) { Minf = fs[s]; Minpos = s; } } // Return the selected member/parameter HParam Pfinal = new HParam(); Pfinal.kappa = P[0, Minpos]; Pfinal.theta = P[1, Minpos]; Pfinal.sigma = P[2, Minpos]; Pfinal.v0 = P[3, Minpos]; Pfinal.rho = P[4, Minpos]; return(Pfinal); }
// Objective function =========================================================================== public double DHObjFun(double[] param, OpSet settings, MktData data, double[] X, double[] W, int objectivefun, double[] lb, double[] ub) { HestonPriceDH HPDH = new HestonPriceDH(); BisectionAlgo BA = new BisectionAlgo(); double S = settings.S; double r = settings.r; double q = settings.q; int trap = settings.trap; double[,] MktIV = data.MktIV; double[,] MktPrice = data.MktPrice; string[,] PutCall = data.PutCall; double[] K = data.K; double[] T = data.T; int NK = PutCall.GetLength(0); int NT = PutCall.GetLength(1); DHParam param2 = new DHParam(); param2.kappa1 = param[0]; param2.theta1 = param[1]; param2.sigma1 = param[2]; param2.v01 = param[3]; param2.rho1 = param[4]; param2.kappa2 = param[5]; param2.theta2 = param[6]; param2.sigma2 = param[7]; param2.v02 = param[8]; param2.rho2 = param[9]; // Settings for the Bisection algorithm double a = 0.001; double b = 3.0; double Tol = 1e5; int MaxIter = 10000; // Initialize the model price and model implied vol vectors, and the objective function value double[,] ModelPrice = new double[NK, NT]; double[,] ModelIV = new double[NK, NT]; double BSVega = 0.0; double Error = 0.0; double pi = Math.PI; double kappaLB = lb[0]; double kappaUB = ub[0]; double thetaLB = lb[1]; double thetaUB = ub[1]; double sigmaLB = lb[2]; double sigmaUB = ub[2]; double v0LB = lb[3]; double v0UB = ub[3]; double rhoLB = lb[4]; double rhoUB = ub[4]; if ((param2.kappa1 <= kappaLB) || (param2.theta1 <= thetaLB) || (param2.sigma1 <= sigmaLB) || (param2.v01 <= v0LB) || (param2.rho1 <= rhoLB) || (param2.kappa1 >= kappaUB) || (param2.theta1 >= thetaUB) || (param2.sigma1 >= sigmaUB) || (param2.v01 >= v0UB) || (param2.rho1 >= rhoUB) || (param2.kappa2 <= kappaLB) || (param2.theta2 <= thetaLB) || (param2.sigma2 <= sigmaLB) || (param2.v02 <= v0LB) || (param2.rho2 <= rhoLB) || (param2.kappa2 >= kappaUB) || (param2.theta2 >= thetaUB) || (param2.sigma2 >= sigmaUB) || (param2.v02 >= v0UB) || (param2.rho2 >= rhoUB)) { Error = 1e50; } else { for (int k = 0; k < NK; k++) { for (int t = 0; t < NT; t++) { settings.K = K[k]; settings.T = T[t]; ModelPrice[k, t] = HPDH.DoubleHestonPriceGaussLaguerre(param2, settings, X, W); switch (objectivefun) { case 1: // MSE Loss Function Error += Math.Pow(ModelPrice[k, t] - MktPrice[k, t], 2.0); break; case 2: // RMSE Loss Function Error += Math.Pow(ModelPrice[k, t] - MktPrice[k, t], 2.0) / MktPrice[k, t]; break; case 3: // IVMSE Loss Function ModelIV[k, t] = BA.BisecBSIV(PutCall[k, t], S, K[k], r, q, T[t], a, b, ModelPrice[k, t], Tol, MaxIter); Error += Math.Pow(ModelIV[k, t] - MktIV[k, t], 2.0); break; case 4: // IVRMSE Christoffersen, Heston, Jacobs proxy double d = (Math.Log(S / K[k]) + (r - q + MktIV[k, t] * MktIV[k, t] / 2.0) * T[t]) / MktIV[k, t] / Math.Sqrt(T[t]); double NormPDF = Math.Exp(-0.5 * d * d) / Math.Sqrt(2.0 * pi); BSVega = S * NormPDF * Math.Sqrt(T[t]); Error += Math.Pow(ModelPrice[k, t] - MktPrice[k, t], 2.0) / (BSVega * BSVega); break; } } } } return(Error); }
static void Main(string[] args) { // Classes BisectionAlgo BA = new BisectionAlgo(); BlackScholesPrice BS = new BlackScholesPrice(); HestonPrice HP = new HestonPrice(); ElicesAlgo EA = new ElicesAlgo(); NelderMeadAlgo NM = new NelderMeadAlgo(); ObjectiveFunction OF = new ObjectiveFunction(); // 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]); } } // Bounds on the parameter estimates // kappa theta sigma v0 rho double e = 1.0e-2; double[] lb = new double[5] { e, e, e, -0.99, e }; double[] ub = new double[5] { 10.0, 3.0, 10.0, 0.99, 3.0 }; // Read in SP500 implied volatilities const int NT = 4; const int NK = 13; double[][] MktIV = new double[NT][]; MktIV[0] = new double[NK] { 0.1962, 0.1910, 0.1860, 0.1810, 0.1761, 0.1718, 0.1671, 0.1644, 0.1645, 0.1661, 0.1701, 0.1755, 0.1796 }; MktIV[1] = new double[NK] { 0.1947, 0.1905, 0.1861, 0.1812, 0.1774, 0.1743, 0.1706, 0.1671, 0.1641, 0.1625, 0.1602, 0.1610, 0.1657 }; MktIV[2] = new double[NK] { 0.2019, 0.1980, 0.1943, 0.1907, 0.1871, 0.1842, 0.1813, 0.1783, 0.1760, 0.1743, 0.1726, 0.1716, 0.1724 }; MktIV[3] = new double[NK] { 0.2115, 0.2082, 0.2057, 0.2021, 0.2000, 0.1974, 0.1950, 0.1927, 0.1899, 0.1884, 0.1862, 0.1846, 0.1842 }; double[] K = new double[NK] { 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[NT] { 37.0 / 365.0, 72.0 / 365.0, 135.0 / 365.0, 226.0 / 365.0 }; // PutCall identifier string PutCall = "P"; // Maturity increments double[] tau = new double[NT]; tau[0] = T[0]; for (int t = 1; t <= NT - 1; t++) { tau[t] = T[t] - T[t - 1]; } // Option settings OPSet opsettings; opsettings.S = 129.14; opsettings.r = 0.0010; opsettings.q = 0.0068; opsettings.trap = 1; // Obtain the market prices for Elices, jagged array, and Heston (two dimensional array) double[][] MktPrice = new double[4][]; double[,] MktPriceH = new double[NT, NK]; double[,] MktIVH = new double[NT, NK]; for (int t = 0; t <= NT - 1; t++) { MktPrice[t] = new double[13]; for (int k = 0; k <= NK - 1; k++) { MktPrice[t][k] = BS.BlackScholes(opsettings.S, K[k], T[t], opsettings.r, opsettings.q, MktIV[t][k], PutCall); MktPriceH[t, k] = MktPrice[t][k]; MktIVH[t, k] = MktIV[t][k]; } } // Settings for the Elices objective function OFSetE ofsetE = new OFSetE(); ofsetE.opsettings = opsettings; ofsetE.X = X; ofsetE.W = W; ofsetE.LossFunction = 1; ofsetE.lb = lb; ofsetE.ub = ub; // Elices Settings for the Market data MktData MktData = new MktData(); MktData.K = K; MktData.PutCall = PutCall; ofsetE.data = MktData; // Settings for the Heston objective function OFSetH ofsetH = new OFSetH(); ofsetH.opsettings = opsettings; ofsetH.X = X; ofsetH.W = W; ofsetH.LossFunction = ofsetE.LossFunction; ofsetH.lb = ofsetE.lb; ofsetH.ub = ofsetE.ub; ofsetH.MktPrice = MktPriceH; ofsetH.MktIV = MktIVH; ofsetH.K = K; ofsetH.T = T; ofsetH.PutCall = PutCall; // Parameter initial values double kappaS = 2.00; double thetaS = 0.10; double sigmaS = 1.20; double v0S = 0.03; double rhoS = -0.80; // Heston and Elices starting values (vertices) in vector form. Add random increment about each starting value double[,] startH = new double[5, 6]; double[,] startE = new double[4, 5]; for (int j = 0; j <= 5; j++) { startH[0, j] = kappaS + BA.RandomNum(-0.05, 0.05) * kappaS; startH[1, j] = thetaS + BA.RandomNum(-0.05, 0.05) * thetaS; startH[2, j] = sigmaS + BA.RandomNum(-0.05, 0.05) * sigmaS; startH[3, j] = rhoS + BA.RandomNum(-0.05, 0.05) * rhoS; startH[4, j] = v0S + BA.RandomNum(-0.05, 0.05) * v0S; } for (int j = 0; j <= 4; j++) { startE[0, j] = startH[0, j]; startE[1, j] = startH[1, j]; startE[2, j] = startH[2, j]; startE[3, j] = startH[3, j]; } // Settings for the Nelder Mead algorithm NMSet nmsettings = new NMSet(); nmsettings.MaxIters = 50; // Maximum number of iterations nmsettings.Tolerance = 1e-20; // Tolerance on best and worst function values nmsettings.ofsettingsE = ofsetE; // Settings for the Elices objective function nmsettings.ofsettingsH = ofsetH; // Settings for the Heston objective function // Heston Parameter Estimation all maturities =========================================================================================== nmsettings.choice = "Heston"; int N = 5; nmsettings.N = N; double[] ParamH = new double[7]; ParamH = NM.NelderMead(OF.f, nmsettings, startH); // Elices Parameter Estimation maturity by maturity ==================================================================================== nmsettings.choice = "Elices"; N = 4; nmsettings.N = N; double v0 = ParamH[4]; ofsetE.v0 = v0; double[][] ParamTD = new double[4][]; double[] B = new double[6]; double[] LossFunction = new double[NT]; // Initialize maturities List <double> MatsList = new List <double>(); double[] Mats; MatsList.Add(tau[0]); MatsList.Add(tau[1]); Mats = MatsList.ToArray(); // Loop through the maturities, estimating parameters for (int t = 0; t <= NT - 1; t++) { ofsetE.t = t; ofsetE.data.MktIV = MktIV[t]; // Vector of market Implied vol ofsetE.data.MktPrice = MktPrice[t]; // Vector of market prices ofsetE.paramfixed = ParamTD; // Fixed parameters if (t >= 1) { for (int j = 0; j <= N; j++) // Starting values are last period's estimates { startE[0, j] = B[0] + BA.RandomNum(-0.01, 0.01) * B[0]; startE[1, j] = B[1] + BA.RandomNum(-0.01, 0.01) * B[1]; startE[2, j] = B[2] + BA.RandomNum(-0.01, 0.01) * B[2]; startE[3, j] = B[3] + BA.RandomNum(-0.01, 0.01) * B[3]; } } if (t >= 2) { MatsList.Add(tau[t]); Mats = MatsList.ToArray(); } ofsetE.T = Mats; nmsettings.ofsettingsE = ofsetE; B = NM.NelderMead(OF.f, nmsettings, startE); ParamTD[t] = new double[4] { B[0], B[1], B[2], B[3] }; LossFunction[t] = B[4]; Console.WriteLine("------ Finished maturity {0:F0} ------", ofsetE.t); } // Write the parameters Console.WriteLine("Heston statict parameters"); Console.WriteLine("------------------------------------------------------------------"); Console.WriteLine(" kappa theta sigma rho v0 LossFunction"); Console.WriteLine("------------------------------------------------------------------"); Console.WriteLine("{0,10:F5} {1,10:F5} {2,10:F5} {3,10:F5} {4,10:F5} {5,10:F5}", ParamH[0], ParamH[1], ParamH[2], ParamH[3], ParamH[4], ParamH[5]); Console.WriteLine("------------------------------------------------------------------"); Console.WriteLine(" "); Console.WriteLine("Elices Time dependent parameters"); Console.WriteLine("-------------------------------------------------------------"); Console.WriteLine(" kappa theta sigma rho LossFunction"); Console.WriteLine("-------------------------------------------------------------"); for (int t = 0; t <= NT - 1; t++) { Console.WriteLine("{0,10:F5} {1,10:F5} {2,10:F5} {3,10:F5} {4,10:F5}", ParamTD[t][0], ParamTD[t][1], ParamTD[t][2], ParamTD[t][3], LossFunction[t]); } Console.WriteLine("-------------------------------------------------------------"); // Fitting Elices prices and implied vols ========================================================================================================== // Matrices for prices, parameters, and fixed parameters double[,] PriceE = new double[NK, NT]; double[] param = new double[4]; double[][] paramfixed = new double[NT - 1][]; paramfixed[0] = ParamTD[0]; // Define the dynamic array for the maturities and assign the first two maturities List <double> MatList = new List <double>(); double[] Mat; MatList.Add(tau[0]); MatList.Add(tau[1]); Mat = MatList.ToArray(); // Elices Implied volatilities double[,] IVE = new double[NK, NT]; double a = 0.01; double b = 3.0; double tol = 1.0e-5; int MaxIter = 1000; double ElicesIVRMSE = 0.0; // Find the prices and implied vols for (int t = 0; t <= NT - 1; t++) { for (int j = 0; j <= 3; j++) { param[j] = ParamTD[t][j]; } if (t == 0) { for (int k = 0; k <= NK - 1; k++) { PriceE[k, 0] = EA.ElicesPrice(PutCall, opsettings.S, K[k], Mat, opsettings.r, opsettings.q, param, v0, opsettings.trap, X, W); } } if (t == 1) { for (int k = 0; k <= NK - 1; k++) { PriceE[k, 1] = EA.ElicesPrice(PutCall, opsettings.S, K[k], Mat, opsettings.r, opsettings.q, param, v0, opsettings.trap, X, W, paramfixed); } } if (t >= 2) { MatList.Add(tau[t]); Mat = MatList.ToArray(); paramfixed[t - 1] = ParamTD[t - 1]; for (int k = 0; k <= NK - 1; k++) { PriceE[k, t] = EA.ElicesPrice(PutCall, opsettings.S, K[k], Mat, opsettings.r, opsettings.q, param, v0, opsettings.trap, X, W, paramfixed); } } for (int k = 0; k <= NK - 1; k++) { IVE[k, t] = BA.BisecBSIV(PutCall, opsettings.S, K[k], opsettings.r, opsettings.q, T[t], a, b, PriceE[k, t], tol, MaxIter); ElicesIVRMSE += Math.Pow(IVE[k, t] - MktIV[t][k], 2.0); } } // Fitting Heston prices and implied vols ========================================================================================================== double[,] PriceH = new double[NK, NT]; double[,] IVH = new double[NK, NT]; HParam ParamTI = new HParam(); ParamTI.kappa = ParamH[0]; ParamTI.sigma = ParamH[1]; ParamTI.theta = ParamH[2]; ParamTI.rho = ParamH[3]; ParamTI.v0 = ParamH[4]; double HestonIVRMSE = 0.0; for (int k = 0; k <= NK - 1; k++) { for (int t = 0; t <= NT - 1; t++) { PriceH[k, t] = HP.HestonPriceGaussLaguerre(ParamTI, ofsetH.opsettings, K[k], T[t], PutCall, X, W); IVH[k, t] = BA.BisecBSIV(PutCall, opsettings.S, K[k], opsettings.r, opsettings.q, T[t], a, b, PriceH[k, t], tol, MaxIter); HestonIVRMSE += Math.Pow(IVH[k, t] - MktIVH[t, k], 2.0); } } Console.WriteLine("Heston IVRMSE {0,2:E5}", HestonIVRMSE); Console.WriteLine("Elices IVRMSE {0,2:E5}", ElicesIVRMSE); }
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("----------------------------------------"); }
static void Main(string[] args) { BlackScholesPrice BA = new BlackScholesPrice(); NelderMeadAlgo NM = new NelderMeadAlgo(); ObjectiveFunction OF = new ObjectiveFunction(); ObjectiveFunctionSVC OFSVC = new ObjectiveFunctionSVC(); // Settings for the Nelder Mead algorithm int N = 10; // Number of Heston parameters int NumIters = 1; // First Iteration int MaxIters = 5000; // Maximum number of iterations double Tolerance = 1e-3; // Tolerance on best and worst function values int LossFunction = 3; // Choice of loss function // 1=MSE, 2=RMSE, 3=IVMSE, 4=Christoffersen et al. // 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]); } int NK = 13; int NT = 4; double[,] MktIV = 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.1764, 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.1661, 0.1641, 0.1760, 0.1899 }, { 0.1661, 0.1625, 0.1743, 0.1884 }, { 0.1701, 0.1602, 0.1726, 0.1871 }, { 0.1755, 0.1610, 0.1716, 0.1846 }, { 0.1786, 0.1657, 0.1724, 0.1842 } }; double[] K = new double[13] { 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[4] { 37.0 / 365.0, 72.0 / 365.0, 135.0 / 365.0, 226.0 / 365.0 }; 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"; } } // Option settings OpSet settings = new OpSet(); settings.S = 129.14; settings.r = 0.0010; settings.q = 0.0068; settings.trap = 1; settings.r = 0.0; settings.q = 0.0; // Obtain the market prices 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] = BA.BlackScholes(settings.S, K[k], T[t], settings.r, settings.q, MktIV[k, t], PutCall[k, t]); } } // Market data MktData data = new MktData(); data.MktIV = MktIV; data.MktPrice = MktPrice; data.K = K; data.T = T; data.PutCall = PutCall; // starting values for the Nelder Mean algorithm double kappaS = 3.0; double thetaS = 0.06; double sigmaS = 0.5; double v0S = 0.04; double rhoS = -0.8; // Create the vertices for the starting values double[,] s = new double[N, N + 1]; for (int j = 0; j <= N; j++) { s[0, j] = kappaS + BA.RandomNum(-0.10, 0.10); s[1, j] = thetaS + BA.RandomNum(-0.01, 0.01); s[2, j] = sigmaS + BA.RandomNum(-0.01, 0.01); s[3, j] = v0S + BA.RandomNum(-0.01, 0.01); s[4, j] = rhoS + BA.RandomNum(-0.05, 0.05); s[5, j] = kappaS + BA.RandomNum(-0.10, 0.10); s[6, j] = thetaS + BA.RandomNum(-0.01, 0.01); s[7, j] = sigmaS + BA.RandomNum(-0.01, 0.01); s[8, j] = v0S + BA.RandomNum(-0.01, 0.01); s[9, j] = rhoS + BA.RandomNum(-0.05, 0.05); } // Upper and lower parameter bounds // kappa theta sigma v0 rho double e = 1e-5; double[] lb = new double[5] { e, e, e, e, -0.999 }; double[] ub = new double[5] { 20.0, 2.0, 2.0, 3.0, 0.999 }; // Settings for the clock; Stopwatch sw = new Stopwatch(); // Estimation of parameters by Nelder Mean -- Ordinary method sw.Reset(); sw.Start(); double[] B = NM.NelderMead(OF.DHObjFun, N, NumIters, MaxIters, Tolerance, s, settings, data, X, W, LossFunction, lb, ub); sw.Stop(); TimeSpan tsOrd = sw.Elapsed; // Estimation of parameters by Nelder Mean -- Strike Vector Computation method sw.Reset(); sw.Start(); double[] C = NM.NelderMead(OFSVC.DHObjFunSVC, N, NumIters, MaxIters, Tolerance, s, settings, data, X, W, LossFunction, lb, ub); sw.Stop(); TimeSpan tsSVC = sw.Elapsed; // Output the results Console.WriteLine("---------------------------------------------------"); Console.WriteLine("Double Heston parameter estimation"); Console.WriteLine("Parameter Ordinary method SVC Method "); Console.WriteLine("---------------------------------------------------"); Console.WriteLine("kappa1 {0,20:F5} {1,10:F5} ", B[0], C[0]); Console.WriteLine("theta1 {0,20:F5} {1,10:F5} ", B[1], C[1]); Console.WriteLine("sigma1 {0,20:F5} {1,10:F5} ", B[2], C[2]); Console.WriteLine("v01 {0,20:F5} {1,10:F5} ", B[3], C[3]); Console.WriteLine("rho1 {0,20:F5} {1,10:F5} ", B[4], C[4]); Console.WriteLine(" "); Console.WriteLine("kappa2 {0,20:F5} {1,10:F5} ", B[5], C[5]); Console.WriteLine("theta2 {0,20:F5} {1,10:F5} ", B[6], C[6]); Console.WriteLine("sigma2 {0,20:F5} {1,10:F5} ", B[7], C[7]); Console.WriteLine("v02 {0,20:F5} {1,10:F5} ", B[8], C[8]); Console.WriteLine("rho2 {0,20:F5} {1,10:F5} ", B[9], C[9]); Console.WriteLine("---------------------------------------------------"); Console.WriteLine("Value of obj function {0,7:F2} {1,10:F2} ", B[10], C[10]); Console.WriteLine("Number of iterations {0,6:0} {1,6:0} ", B[11], C[11]); Console.WriteLine("---------------------------------------------------"); Console.WriteLine(" "); Console.WriteLine("Estimation time Min Sec mSec"); Console.WriteLine("-------------------------------------------"); Console.WriteLine("Ordinary ObjFun {0,10:F0} {1,6:F0} {2,6:F0}", tsOrd.Minutes, tsOrd.Seconds, tsOrd.Milliseconds); Console.WriteLine("SVC ObjFun {0,10:F0} {1,6:F0} {2,6:F0}", tsSVC.Minutes, tsSVC.Seconds, tsSVC.Milliseconds); Console.WriteLine("-------------------------------------------"); Console.WriteLine(" "); }
// Objective function =========================================================================== public double f(double[] param, double tau, double[] tau0, double[,] param0, MktData data, double S, double[] K, double r, double q, int trap, double[] X, double[] W, int LossFunction, double[] lb, double[] ub) { HestonPriceTD HPTD = new HestonPriceTD(); BisectionAlgo BA = new BisectionAlgo(); double[] MktIV = data.MktIV; double[] MktPrice = data.MktPrice; string[] PutCall = data.PutCall; int NK = PutCall.Length; int NT = 1; HParam param2 = new HParam(); param2.kappa = param[0]; param2.theta = param[1]; param2.sigma = param[2]; param2.v0 = param[3]; param2.rho = param[4]; // Settings for the Bisection algorithm double a = 0.001; double b = 3.0; double Tol = 1e5; int MaxIter = 10000; // Initialize the model price and model implied vol vectors, and the objective function value double[] ModelPrice = new double[NK]; double[] ModelIV = new double[NK]; double Vega = 0.0; double Error = 0.0; double pi = Math.PI; double kappaLB = lb[0]; double kappaUB = ub[0]; double thetaLB = lb[1]; double thetaUB = ub[1]; double sigmaLB = lb[2]; double sigmaUB = ub[2]; double v0LB = lb[3]; double v0UB = ub[3]; double rhoLB = lb[4]; double rhoUB = ub[4]; if ((param2.kappa <= kappaLB) || (param2.theta <= thetaLB) || (param2.sigma <= sigmaLB) || (param2.v0 <= v0LB) || (param2.rho <= rhoLB) || (param2.kappa >= kappaUB) || (param2.theta >= thetaUB) || (param2.sigma >= sigmaUB) || (param2.v0 >= v0UB) || (param2.rho >= rhoUB)) { Error = 1e50; } else { for (int k = 0; k <= NK - 1; k++) { ModelPrice[k] = HPTD.MNPriceGaussLaguerre(param2, param0, tau, tau0, S, K[k], r, q, PutCall[k], trap, X, W); switch (LossFunction) { case 1: // MSE Loss Function Error += Math.Pow(ModelPrice[k] - MktPrice[k], 2); break; case 2: // RMSE Loss Function Error += Math.Pow(ModelPrice[k] - MktPrice[k], 2) / MktPrice[k]; break; case 3: // IVMSE Loss Function ModelIV[k] = BA.BisecBSIV(PutCall[k], S, K[k], r, q, tau, a, b, ModelPrice[k], Tol, MaxIter); Error += Math.Pow(ModelIV[k] - MktIV[k], 2); break; case 4: // IVRMSE Christoffersen, Heston, Jacobs proxy double d = (Math.Log(S / K[k]) + (r - q + MktIV[k] * MktIV[k] / 2.0) * tau) / MktIV[k] / Math.Sqrt(tau); double NormPDF = Math.Exp(-0.5 * d * d) / Math.Sqrt(2.0 * pi); Vega = S * NormPDF * Math.Sqrt(tau); Error += Math.Pow(ModelPrice[k] - MktPrice[k], 2) / (Vega * Vega); break; } } } return(Error); // / Convert.ToDouble(NT*NK); }
static void Main(string[] args) { // Classes BlackScholesPrice BS = new BlackScholesPrice(); BisectionAlgo BA = new BisectionAlgo(); NelderMeadAlgo NM = new NelderMeadAlgo(); ObjectiveFunction OF = new ObjectiveFunction(); HestonPriceTD HPTD = new HestonPriceTD(); // 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]); } int NT = 4; int NK = 13; 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[13] { 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[4] { 0.1014, 0.1973, 0.3699, 0.6192 }; string[,] PutCall = new string[13, 4]; double[,] MktPrice = new double[13, 4]; double Spot = 129.14; double r = 0.0010; double q = 0.0068; int trap = 1; int LossFunction = 2; for (int t = 0; t <= NT - 1; t++) { for (int k = 0; k <= NK - 1; k++) { PutCall[k, t] = "P"; MktPrice[k, t] = BS.BlackScholes(Spot, K[k], T[t], r, q, PutIV[k, t], PutCall[k, t]); } } // Create the maturity increments double[] tau = new double[4]; tau[0] = T[0]; for (int t = 1; t <= NT - 1; t++) { tau[t] = T[t] - T[t - 1]; } // Starting values and upper bounds double e = 1e-5; double[] lb = new double[5] { e, e, e, e, -0.999 }; // Lower bound on the estimates double[] ub = new double[5] { 20.0, 2.0, 2.0, 3.0, 0.999 }; // Upper bound on the estimates // Starting values (vertices) in vector form. Add random increment about each starting value int N = 5; double kappaS = 4.0; double thetaS = 0.1; double sigmaS = 1.5; double v0S = 0.04; double rhoS = -0.30; double[,] s = new double[N, N + 1]; for (int j = 0; j <= N; j++) { s[0, j] = kappaS + BA.RandomNum(-0.01, 0.01); s[1, j] = thetaS + BA.RandomNum(-0.01, 0.01); s[2, j] = sigmaS + BA.RandomNum(-0.01, 0.01); s[3, j] = v0S + BA.RandomNum(-0.01, 0.01); s[4, j] = rhoS + BA.RandomNum(-0.01, 0.01); } // Arrays for old maturities and parameters, and current parameters (old parameters, but stacked) ArrayList OldTau = new ArrayList(); double[,] param0 = new double[NT - 1, 5]; double[,] paramTD = new double[NT, 5]; // Nelder Mead settings int MaxIters = 1000; double Tolerance = 1e-5; // Market data MktData data = new MktData(); double[] MKIV = new double[NK]; double[] MKPR = new double[NK]; string[] PC = new string[NK]; // Step-by-step parameter estimates double[] B = new double[6]; double[] ObjectiveFun = new double[NT]; double[] NumIteration = new double[NT]; // First maturity parameter estimates =================================================================== double[] tau0 = { 0.0 }; for (int k = 0; k <= NK - 1; k++) { MKIV[k] = PutIV[k, 0]; MKPR[k] = MktPrice[k, 0]; PC[k] = PutCall[k, 0]; } data.MktIV = MKIV; data.PutCall = PC; data.MktPrice = MKPR; B = NM.NelderMead(OF.f, N, MaxIters, Tolerance, s, tau[0], tau0, param0, data, Spot, K, r, q, trap, X, W, LossFunction, lb, ub); Console.WriteLine("Finished parameter estimate set 1"); for (int k = 0; k <= 4; k++) { paramTD[0, k] = B[k]; } ObjectiveFun[0] = B[5]; NumIteration[0] = B[6]; // Remaining Maturity estimates and updated starting values ================================================== for (int mat = 1; mat <= NT - 1; mat++) { OldTau.Add(tau[mat - 1]); for (int k = 0; k <= 4; k++) { param0[mat - 1, k] = B[k]; } for (int k = 0; k <= NK - 1; k++) { MKIV[k] = PutIV[k, mat]; MKPR[k] = MktPrice[k, mat]; PC[k] = PutCall[k, mat]; } data.MktIV = MKIV; data.PutCall = PC; data.MktPrice = MKPR; for (int j = 0; j <= N; j++) { s[0, j] = B[0] + BA.RandomNum(-0.01, 0.01); s[1, j] = B[1] + BA.RandomNum(-0.01, 0.01); s[2, j] = B[2] + BA.RandomNum(-0.01, 0.01); s[3, j] = B[3] + BA.RandomNum(-0.01, 0.01); s[4, j] = B[4] + BA.RandomNum(-0.01, 0.01); } B = NM.NelderMead(OF.f, N, MaxIters, Tolerance, s, tau[mat], tau0, param0, data, Spot, K, r, q, trap, X, W, LossFunction, lb, ub); Console.WriteLine("Finished parameter estimate set {0}", mat + 1); for (int k = 0; k <= 4; k++) { paramTD[mat, k] = B[k]; } ObjectiveFun[mat] = B[5]; NumIteration[mat] = B[6]; } // Fit the prices and implied volatilities ==================================================================== // Bisection algorithm settings double a = 0.001; double b = 5.0; double Tol = 1e-5; int MaxIter = 5000; ArrayList OldTau00 = new ArrayList(); double[] tau00 = { 0.0 }; double[,] ModelPrice = new double[NK, NT]; double[,] ModelIV = new double[NK, NT]; Array.Clear(param0, 0, 15); // First maturity HParam param = new HParam(); param.kappa = paramTD[0, 0]; param.theta = paramTD[0, 1]; param.sigma = paramTD[0, 2]; param.v0 = paramTD[0, 3]; param.rho = paramTD[0, 4]; for (int k = 0; k <= NK - 1; k++) { ModelPrice[k, 0] = HPTD.MNPriceGaussLaguerre(param, param0, tau[0], tau00, Spot, K[k], r, q, PutCall[k, 0], trap, X, W); ModelIV[k, 0] = BA.BisecBSIV(PutCall[k, 0], Spot, K[k], r, q, T[0], a, b, ModelPrice[k, 0], Tol, MaxIter); } // Remaining maturity for (int t = 1; t <= NT - 1; t++) { OldTau00.Add(tau[t - 1]); param.kappa = paramTD[t, 0]; param.theta = paramTD[t, 1]; param.sigma = paramTD[t, 2]; param.v0 = paramTD[t, 3]; param.rho = paramTD[t, 4]; for (int j = 0; j <= 4; j++) { param0[t - 1, j] = paramTD[t - 1, j]; } for (int k = 0; k <= NK - 1; k++) { ModelPrice[k, t] = HPTD.MNPriceGaussLaguerre(param, param0, tau[t], tau00, Spot, K[k], r, q, PutCall[k, t], trap, X, W); ModelIV[k, t] = BA.BisecBSIV(PutCall[k, t], Spot, K[k], r, q, T[t], a, b, ModelPrice[k, t], Tol, MaxIter); } } // Mean Square Implied Volatility Error double Error = 0.0; for (int t = 0; t <= NT - 1; t++) { for (int k = 0; k <= NK - 1; k++) { Error += Math.Pow(ModelIV[k, t] - PutIV[k, t], 2); } } Error /= (Convert.ToDouble(NT) * Convert.ToDouble(NK)); // Output the estimation result Console.WriteLine("-----------------------------------------------------------------------------"); Console.WriteLine("Market/Model implied volatilities"); Console.WriteLine(" Mat1 Mat2 Mat3 Mat4"); for (int k = 0; k <= NK - 1; k++) { Console.WriteLine("--------------------------------------------- Strike {0} Market/Model", K[k]); Console.WriteLine("{0,10:F4} {1,10:F4} {2,10:F4} {3,10:F4}", PutIV[k, 0], PutIV[k, 1], PutIV[k, 2], PutIV[k, 3]); Console.WriteLine("{0,10:F4} {1,10:F4} {2,10:F4} {3,10:F4}", ModelIV[k, 0], ModelIV[k, 1], ModelIV[k, 2], ModelIV[k, 3]); } Console.WriteLine("-----------------------------------------------------------------------------"); Console.WriteLine("Model Price "); for (int k = 0; k <= NK - 1; k++) { Console.WriteLine("{0,10:0} {1,10:F4} {2,10:F4} {3,10:F4} {4,10:F4}", K[k], ModelPrice[k, 0], ModelPrice[k, 1], ModelPrice[k, 2], ModelPrice[k, 3]); } Console.WriteLine("-----------------------------------------------------------------------------"); Console.WriteLine("Results of time dependent parameter estimation"); Console.WriteLine(" "); Console.WriteLine("Maturity kappa theta sigma v0 rho ObjecFun NumIterations"); Console.WriteLine("-----------------------------------------------------------------------------"); for (int t = 0; t <= NT - 1; t++) { Console.WriteLine("{0} {1,10:F4} {2,7:F4} {3,7:F4} {4,7:F4} {5,8:F4} {6,8:F4} {7,7:0}", T[t], paramTD[t, 0], paramTD[t, 1], paramTD[t, 2], paramTD[t, 3], paramTD[t, 4], ObjectiveFun[t], NumIteration[t]); } Console.WriteLine("-----------------------------------------------------------------------------"); Console.WriteLine("IVMSE from time dependent estimates {0,10:E5}", Error); Console.WriteLine("-----------------------------------------------------------------------------"); }