// Objective function =========================================================================== public double f(double[] param, OFSet ofset) { Bisection BA = new Bisection(); HestonPrice HP = new HestonPrice(); double S = ofset.opsettings.S; double r = ofset.opsettings.r; double q = ofset.opsettings.q; int trap = ofset.opsettings.trap; double[,] MktIV = ofset.data.MktIV; double[,] MktPrice = ofset.data.MktPrice; string[,] PutCall = ofset.data.PutCall; double[] K = ofset.data.K; double[] T = ofset.data.T; int NK = PutCall.GetLength(0); int NT = PutCall.GetLength(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, NT]; double[,] ModelIV = new double[NK, NT]; double Vega = 0.0; double Error = 0.0; double pi = Math.PI; double[] lb = ofset.lb; double[] ub = ofset.ub; 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]; int LossFunction = ofset.LossFunction; double[] X = ofset.X; double[] W = ofset.W; // Penalty for inadmissible parameter values 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; k++) { for (int t = 0; t < NT; t++) { ModelPrice[k, t] = HP.HestonPriceGaussLaguerre(param2, S, K[k], r, q, T[t], trap, PutCall[k, t], X, W); switch (LossFunction) { case 1: // MSE Loss Function Error += Math.Pow(ModelPrice[k, t] - MktPrice[k, t], 2) / Convert.ToDouble(NT * NK); break; case 2: // RMSE Loss Function Error += Math.Pow(ModelPrice[k, t] - MktPrice[k, t], 2) / MktPrice[k, t] / Convert.ToDouble(NT * NK); 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) / Convert.ToDouble(NT * NK);; 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); Vega = S * NormPDF * Math.Sqrt(T[t]); Error += Math.Pow(ModelPrice[k, t] - MktPrice[k, t], 2) / Vega / Vega / Convert.ToDouble(NT * NK); break; } } } } return(Error); }
// SVC objective function =========================================================================== public double f(double[] param, OFSet ofset) { HestonPrice HP = new HestonPrice(); Bisection B = new Bisection(); double S = ofset.opset.S; double r = ofset.opset.r; double q = ofset.opset.q; int trap = ofset.opset.trap; double[,] MktIV = ofset.data.MktIV; double[,] MktPrice = ofset.data.MktPrice; string[,] PutCall = ofset.data.PutCall; double[] K = ofset.data.K; double[] T = ofset.data.T; double[] X = ofset.X; double[] W = ofset.W; string CF = ofset.CF; int LossFunction = ofset.LossFunction; int NK = PutCall.GetLength(0); int NT = PutCall.GetLength(1); int NX = X.Length; 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, NT]; double[,] ModelIV = new double[NK, NT]; double Vega = 0.0; double Error = 0.0; double pi = Math.PI; if ((param2.kappa <= 0) || (param2.theta <= 0) || (param2.sigma <= 0) || (param2.v0 <= 0) || (param2.rho <= -1) || (param2.kappa >= 20) || (param2.theta >= 2) || (param2.sigma >= 2) || (param2.v0 >= 3) || (param2.rho >= 1)) { Error = 1e50; } else { Complex phi = new Complex(0.0, 0.0); double phi2 = 0.0; Complex i = new Complex(0.0, 1.0); Complex[] f2 = new Complex[NX]; Complex[] f1 = new Complex[NX]; Complex[] f = new Complex[NX]; double[] int1 = new double[NX]; double[] int2 = new double[NX]; Complex I1 = new Complex(0.0, 0.0); Complex I2 = new Complex(0.0, 0.0); double CallPrice = 0.0; for (int t = 0; t < NT; t++) { for (int j = 0; j < NX; j++) { phi = X[j]; if (CF == "Heston") { f2[j] = HP.HestonCF(phi, param2, S, r, q, T[t], trap); f1[j] = HP.HestonCF(phi - i, param2, S, r, q, T[t], trap) / (S * Math.Exp((r - q) * T[t])); } else if (CF == "Attari") { phi2 = X[j]; } f[j] = HP.AttariCF(phi2, param2, T[t], S, r, q, trap); } for (int k = 0; k < NK; k++) { double L = Math.Log(Math.Exp(-r * T[t]) * K[k] / S); for (int j = 0; j < NX; j++) { phi = X[j]; if (CF == "Heston") { I1 = Complex.Exp(-i * phi * Complex.Log(K[k])) * f1[j] / i / phi; int1[j] = W[j] * I1.Real; I2 = Complex.Exp(-i * phi * Complex.Log(K[k])) * f2[j] / i / phi; int2[j] = W[j] * I2.Real; } else if (CF == "Attari") { phi2 = X[j]; double fR = f[j].Real; double fI = f[j].Imaginary; int1[j] = W[j] * ((fR + fI / phi2) * Math.Cos(L * phi2) + (fI - fR / phi2) * Math.Sin(L * phi2)) / (1 + phi2 * phi2); } } if (CF == "Heston") { double P1 = 0.5 + 1.0 / pi * int1.Sum(); double P2 = 0.5 + 1.0 / pi * int2.Sum(); CallPrice = S * Math.Exp(-q * T[t]) * P1 - K[k] * Math.Exp(-r * T[t]) * P2; } else if (CF == "Attari") { CallPrice = S * Math.Exp(-q * T[t]) - K[k] * Math.Exp(-r * T[t]) * (0.5 + 1.0 / pi * int1.Sum()); } 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]; } // Select the objective function switch (LossFunction) { case 1: // MSE Loss Function Error += Math.Pow(ModelPrice[k, t] - MktPrice[k, t], 2) / Convert.ToDouble(NT * NK);; break; case 2: // RMSE Loss Function Error += Math.Pow(ModelPrice[k, t] - MktPrice[k, t], 2) / MktPrice[k, t] / Convert.ToDouble(NT * NK);; break; case 3: // IVMSE Loss Function ModelIV[k, t] = B.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) / Convert.ToDouble(NT * NK);; 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 * pi); Vega = S * NormPDF * Math.Sqrt(T[t]); Error += Math.Pow(ModelPrice[k, t] - MktPrice[k, t], 2) / Vega / Vega / Convert.ToDouble(NT * NK); break; } } } } return(Error); }
// Nelder Mead Algorithm =============================================================================================== public double[] NelderMead(ObjFun f, NMSet nmsettings, double[,] x) { int NumIters = 0; int i, j; int N = nmsettings.N; int MaxIters = nmsettings.MaxIters; double Tolerance = nmsettings.Tolerance; OFSet ofsettings = nmsettings.ofsettings; // 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, ofsettings); // 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, ofsettings); // 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, ofsettings); // 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, ofsettings); // 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, ofsettings); // Reflection point xr and function fr double[] xr = new Double[N]; xr = VSub(VAdd(xm, xm), xn1); double fr = f(xr, ofsettings); // Expansion point xe and function fe double[] xe = new Double[N]; xe = VSub(VAdd(xr, xr), xm); double fe = f(xe, ofsettings); // 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, ofsettings); // 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, ofsettings); 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); }
// Objective function =========================================================================== public double f(double[] param, OFSet ofsettings) { // Option price settings double S = ofsettings.opsettings.S; double r = ofsettings.opsettings.r; double q = ofsettings.opsettings.q; double T = ofsettings.opsettings.T; int trap = ofsettings.opsettings.trap; // Market data double[] MktIV = ofsettings.data.MktIV; int NK = MktIV.Length; // MS settings double dt = ofsettings.mssettings.dt; double a = ofsettings.mssettings.a; double b = ofsettings.mssettings.b; double tol = ofsettings.mssettings.tol; int MaxIter = ofsettings.mssettings.MaxIter; // Optimizatin settings double[] lb = ofsettings.lb; double[] ub = ofsettings.ub; double[] X = ofsettings.X; double[] W = ofsettings.W; HParam param2 = new HParam(); param2.kappa = param[0]; param2.theta = param[1]; param2.sigma = param[2]; param2.v0 = param[3]; param2.rho = param[4]; param2.lambda = 0.0; // Settings for the Bisection algorithm a = 0.01; b = 3.0; double B = 3.0; // 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[] Error = new double[NK]; double SumError = 0.0; // Parameter bounds 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]; // Classes MSPrices MS = new MSPrices(); BisectionImpliedVol BA = new BisectionImpliedVol(); // Penalty for inadmissible parameter values 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)) { SumError = 1.0e50; } // Penalty for inadmissible implied vol else { Console.WriteLine("----------------------------------------"); Console.WriteLine("ModelPrice ImpliedVol MktVol Error"); Console.WriteLine("----------------------------------------"); for (int k = 0; k <= NK - 1; k++) { double Strike = ofsettings.data.K[k]; ofsettings.opsettings.K = Strike; double[] output = new double[6]; output = MS.MSPriceHeston(param2, ofsettings.opsettings, ofsettings.mssettings); ModelPrice[k] = Math.Max(0.01, output[2]); ModelIV[k] = BA.BisectionMSIV(S, Strike, r, q, T, a, b, ModelPrice[k], tol, MaxIter, B, dt); if (ModelIV[k] == -1.0) { Error[k] = 1.0e50; } else { Error[k] = Math.Pow(ModelIV[k] - MktIV[k], 2.0); } Console.WriteLine("{0,7:F4} {1,10:F4} {2,10:F4} {3,10:F6}", ModelPrice[k], ModelIV[k], MktIV[k], Error[k]); SumError += Error[k]; } } return(SumError); }
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]); } }
public double f(double[] param, OFSet ofset) { // Name the Heston parameters double kappa = param[0]; double theta = param[1]; double sigma = param[2]; double v0 = param[3]; double rho = param[4]; // Likelihood function settings double[] x = ofset.x; double r = ofset.r; double q = ofset.q; double dt = ofset.dt; int Lmethod = ofset.method; // Atiya and Wall parameterization double alpha = kappa * theta; double beta = kappa; // Number of log-stock prices int T = x.Length; // Drift term double mu = r - q; // Equation (17) double betap = 1.0 - beta * dt; // Equation (18) - denominator of d(t) double D = 2.0 * Math.PI * sigma * Math.Sqrt(1.0 - rho * rho) * dt; // Equation (14) double a = (betap * betap + rho * sigma * betap * dt + sigma * sigma * dt * dt / 4.0) / (2.0 * sigma * sigma * (1.0 - rho * rho) * dt); // Variance and likelihood at time t = 0 double[] v = new Double[T]; double[] L = new Double[T]; v[0] = v0; if (Lmethod == 1) { L[0] = Math.Exp(-v[0]); // Construct the Likelihood } else if (Lmethod == 2) { L[0] = -v[0]; // Construct the log-likelihood } // Construction the likelihood for time t = 1 through t = T double dx, B, C, bt, x1, x2, E; for (int t = 0; t <= T - 2; t++) { // Stock price increment dx = x[t + 1] - x[t]; // Equations (31) and (32) B = -alpha * dt - rho * sigma * (dx - mu * dt); C = alpha * alpha * dt * dt + 2.0 * rho * sigma * alpha * dt * (dx - mu * dt) + sigma * sigma * Math.Pow(dx - mu * dt, 2.0) - 2.0 * v[t] * v[t] * a * sigma * sigma * (1.0 - rho * rho) * dt; // Equation (30) to update the variance if (B * B - C > 0.0) { v[t + 1] = Math.Sqrt(B * B - C) - B; } else { // If v[t+1] is negative, use the approximation Equation (33) bt = (Math.Pow(v[t] - alpha * dt, 2.0) - 2.0 * rho * sigma * (v[t] - alpha * dt) * (dx - mu * dt) + sigma * sigma * Math.Pow(dx - mu * dt, 2.0)) / (2.0 * sigma * sigma * (1.0 - rho * rho) * dt); if (bt / a > 0.0) { v[t + 1] = Math.Sqrt(bt / a); } else { // If v[t+1] is still negative, take the previous value v[t + 1] = v[t]; } } // Equation (15) and (16) bt = (Math.Pow(v[t + 1] - alpha * dt, 2.0) - 2.0 * rho * sigma * (v[t + 1] - alpha * dt) * (dx - mu * dt) + sigma * sigma * Math.Pow(dx - mu * dt, 2.0)) / (2.0 * sigma * sigma * (1.0 - rho * rho) * dt); x1 = ((2.0 * betap + rho * sigma * dt) * (v[t + 1] - alpha * dt) - (2.0 * rho * sigma * betap + sigma * sigma * dt) * (dx - mu * dt)) / (2.0 * sigma * sigma * (1.0 - rho * rho) * dt); x2 = -2.0 * Math.Sqrt(a * bt); // Compbined exponent for Equation (34) E = Math.Exp(x1 + x2) / D; if (Lmethod == 1) { // Equation (34) for the likelihood L[t+1] L[t + 1] = Math.Pow(a * bt, -0.25) * E * L[t]; } else if (Lmethod == 2) { // Alternatively, use the log-likelihood, log of Equation (34) L[t + 1] = -0.25 * Math.Log(a * bt) + x1 + x2 - Math.Log(D) + L[t]; } } // Negative likelihood is the last term. // Since we maximize the likelihood, we minimize the negative likelihood. return(-L[T - 1]); }
// Objective function =========================================================================== public double f(double[] param, OFSet ofset) { BGMPrice BGM = new BGMPrice(); BisectionAlgo BA = new BisectionAlgo(); double S = ofset.opset.S; double r = ofset.opset.r; double q = ofset.opset.q; int trap = ofset.opset.trap; int ObjFunction = ofset.ObjFunction; OPSet opset = ofset.opset; double[,] MktIV = ofset.data.MktIV; double[,] MktPrice = ofset.data.MktPrice; string[,] PutCall = ofset.data.PutCall; double[] K = ofset.data.K; double[] T = ofset.data.T; double[] X = ofset.X; double[] W = ofset.W; int NK = PutCall.GetLength(0); int NT = PutCall.GetLength(1); // Separate out the parameters from the "param" vector; int N = param.Length; double kappa = param[0]; double v0 = param[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] = param[3 * k + 2]; SIGMA[k] = param[3 * k + 3]; RHO[k] = param[3 * k + 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, NT]; double[,] ModelIV = new double[NK, NT]; double Vega = 0.0; double Error = 0.0; double pi = Math.PI; double[] lb = ofset.lb; double[] ub = ofset.ub; 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]; 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; if ((kappa <= kappaLB) || (kappa >= kappaUB) || (v0 <= v0LB) || (v0 >= v0UB)) { Error = 1e50; } for (int k = 0; k <= NT - 1; k++) { if ((THETA[k] <= thetaLB) || (THETA[k] >= thetaUB) || (SIGMA[k] <= sigmaLB) || (SIGMA[k] >= sigmaUB) || (RHO[k] <= rhoLB) || (RHO[k] >= rhoUB)) { Error = 1e50; } } { for (int t = 0; t < NT; t++) { MatList.Add(T[t]); thetaList.Add(THETA[t]); sigmaList.Add(SIGMA[t]); rhoList.Add(RHO[t]); Mat = MatList.ToArray(); theta = thetaList.ToArray(); sigma = sigmaList.ToArray(); rho = rhoList.ToArray(); Mat.Reverse(); Array.Reverse(theta); Array.Reverse(sigma); Array.Reverse(rho); for (int k = 0; k < NK; k++) { ModelPrice[k, t] = BGM.BGMApproxPriceTD(kappa, v0, theta, sigma, rho, opset, K[k], Mat); switch (ObjFunction) { 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(opset, K[k], T[t], a, b, ModelPrice[k, t], Tol, MaxIter); 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); Vega = S * NormPDF * Math.Sqrt(T[t]); Error += Math.Pow(ModelPrice[k, t] - MktPrice[k, t], 2) / (Vega * Vega); break; } } } } return(Error); }