public Uu HestonExplicitPDENonUniformGrid(HParam param, double K, double r, double q, double[] S, double[] V, double[] T) { // Finite differences for the Heston PDE for a European Call // Uses uneven grid sizes // In 'T Hout and Foulon "ADI Finite Difference Schemes for Option Pricing // in the Heston Modelo with Correlation" Int J of Num Analysis and Modeling, 2010. // INPUTS // params = 6x1 vector of Heston parameters // K = Strike price // r = risk free rate // q = Dividend // S = vector for stock price grid // V = vector for volatility grid // T = vector for maturity grid // OUTPUTS // U = U(S,v) 2-D array of size (nS+1)x(nV+1)x(nT+1) for the call price // Heston parameters double kappa = param.kappa; double theta = param.theta; double sigma = param.sigma; double v0 = param.v0; double rho = param.rho; double lambda = param.lambda; // Grid measurements int NS = S.Length; int NV = V.Length; int NT = T.Length; double Smin = S[0]; double Smax = S[NS - 1]; double Vmin = V[0]; double Vmax = V[NV - 1]; double Tmin = T[0]; double Tmax = T[NT - 1]; double dt = (Tmax - Tmin) / Convert.ToDouble(NT - 1); // Initialize the 2-D grid with zeros double[,] U = new double[NS, NV]; // Temporary grid for previous time steps double[,] u = new double[NS, NV]; //// Solve the PDE // Round each value of U(S,v) at each step // Boundary condition for t=maturity for (int s = 0; s <= NS - 1; s++) { for (int v = 0; v <= NV - 1; v++) { U[s, v] = Math.Max(S[s] - K, 0.0); } } double LHS, derV, derS, derSS, derVV, derSV, L; for (int t = 0; t <= NT - 2; t++) { // Boundary condition for Smin and Smax for (int v = 0; v <= NV - 2; v++) { U[0, v] = 0.0; U[NS - 1, v] = Math.Max(0.0, Smax - K); } // Boundary condition for Vmax for (int s = 0; s <= NS - 1; s++) { U[s, NV - 1] = Math.Max(0.0, S[s] - K); } // Update the temporary grid u(s,t) with the boundary conditions for (int s = 0; s <= NS - 1; s++) { for (int v = 0; v <= NV - 1; v++) { u[s, v] = U[s, v]; } } // Boundary condition for Vmin. // Previous time step values are in the temporary grid u(s,t) for (int s = 1; s <= NS - 2; s++) { derV = (u[s, 1] - u[s, 0]) / (V[1] - V[0]); // Forward difference derS = (u[s + 1, 0] - u[s - 1, 0]) / (S[s + 1] - S[s - 1]); // Central difference LHS = -r * u[s, 0] + (r - q) * S[s] * derS + kappa * theta * derV; U[s, 0] = LHS * dt + u[s, 0]; } // Update the temporary grid u(s,t) with the boundary conditions for (int s = 0; s <= NS - 1; s++) { for (int v = 0; v <= NV - 1; v++) { u[s, v] = U[s, v]; } } // Interior points of the grid (non boundary). // Previous time step values are in the temporary grid u(s,t) for (int s = 1; s <= NS - 2; s++) { for (int v = 1; v <= NV - 2; v++) { derS = (u[s + 1, v] - u[s - 1, v]) / (S[s + 1] - S[s - 1]); // Central difference for dU/dS derV = (u[s, v + 1] - u[s, v - 1]) / (V[v + 1] - V[v - 1]); // Central difference for dU/dV derSS = ((u[s + 1, v] - u[s, v]) / (S[s + 1] - S[s]) - (u[s, v] - u[s - 1, v]) / (S[s] - S[s - 1])) / (S[s + 1] - S[s]); // d2U/dS2 derVV = ((u[s, v + 1] - u[s, v]) / (V[v + 1] - V[v]) - (u[s, v] - u[s, v - 1]) / (V[v] - V[v - 1])) / (V[v + 1] - V[v]); // d2U/dV2 derSV = (u[s + 1, v + 1] - u[s - 1, v + 1] - U[s + 1, v - 1] + U[s - 1, v - 1]) / (S[s + 1] - S[s - 1]) / (V[v + 1] - V[v - 1]); // d2U/dSdV L = 0.5 * V[v] * S[s] * S[s] * derSS + rho * sigma * V[v] * S[s] * derSV + 0.5 * sigma * sigma * V[v] * derVV - r * u[s, v] + (r - q) * S[s] * derS + kappa * (theta - V[v]) * derV; U[s, v] = L * dt + u[s, v]; } } } Uu output = new Uu(); output.bigU = U; output.smallU = u; return(output); }
static void Main(string[] args) { // Classes Interpolation IP = new Interpolation(); ExplicitPDE EP = new ExplicitPDE(); // Number of grid points for the stock, volatility, and maturity int nS = 79; // Stock price int nV = 39; // Volatility int nT = 3000; // Maturity // Option flavor string PutCall = "P"; string EuroAmer = "A"; // Strike price, risk free rate, dividend yield, and maturity // True prices from Clarke and Parrott (1999) double K = 10.0; double r = 0.1; double q = 0.00; double Mat = 0.25; double[] S0 = new double[5] { 8.0, 9.0, 10.0, 11.0, 12.0 }; double[] TruePrice = new double[5] { 2.0000, 1.107641, 0.520030, 0.213668, 0.082036 }; // Heston parameters HParam param; param.kappa = 5; param.theta = 0.16; param.sigma = 0.9; param.rho = 0.1; param.v0 = 0.0625; param.lambda = 0; // Settings for the European option price calculation // 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]); } // Minimum and maximum values for the Stock Price, Volatility, and Maturity double Smin = 0.0; double Smax = 2.0 * K; double Vmin = 0.0; double Vmax = 0.5; double Tmin = 0.0; double Tmax = Mat; // The maturity time increment and grid double dt = (Tmax - Tmin) / Convert.ToDouble(nT); double[] T = new double[nT + 1]; for (int i = 0; i <= nT; i++) { T[i] = Convert.ToDouble(i) * dt; } // Obtain the prices by 2-D interpolation and the errors compared to Clarke and Parrott // Also obtain the Greeks double V0 = param.v0; int N = S0.Length; double[] PDEPrice = new double[N]; double D1, D2, V1, V2, dCdv0, C1, C2, C3, C4, dC2, T1, T2; double[] DeltaPDE = new double[N]; double[] GammaPDE = new double[N]; double[] Vega1PDE = new double[N]; double[] VannaPDE = new double[N]; double[] VolgaPDE = new double[N]; double[] ThetaPDE = new double[N]; double dS, dv, c, dz, d, dn; double[] z = new double[nS + 1]; double[] S = new double[nS + 1]; double[] n = new double[nV + 1]; double[] V = new double[nV + 1]; for (int k = 0; k <= N - 1; k++) { //// Pricing Using a Non-Uniform Grid // The stock price grid c = S0[k] / 5.0; dz = 1.0 / nS * (IP.aSinh((Smax - S0[k]) / c) - IP.aSinh(-S0[k] / c)); for (int i = 0; i <= nS; i++) { z[i] = IP.aSinh(-S0[k] / c) + Convert.ToDouble(i) * dz; S[i] = S0[k] + c * Math.Sinh(z[i]); } S[0] = 0; // The volatility grid d = Vmax / 500.0; dn = IP.aSinh(Vmax / d) / nV; for (int j = 0; j <= nV; j++) { n[j] = Convert.ToDouble(j) * dn; V[j] = d * Math.Sinh(n[j]); } // Solve the PDE and return U(S,v,T) and U(S,v,T-dt); Uu output = EP.HestonExplicitPDENonUniformGrid(param, K, r, q, S, V, T, PutCall, EuroAmer); double[,] U = output.bigU; double[,] u = output.smallU; // Price, Delta, Gamma dS = 0.01 * S0[k]; dv = 0.01 * V0; PDEPrice[k] = IP.interp2(V, S, U, V0, S0[k]); D1 = IP.interp2(V, S, U, V0, S0[k] + dS); D2 = IP.interp2(V, S, U, V0, S0[k] - dS); DeltaPDE[k] = (D1 - D2) / 2.0 / dS; GammaPDE[k] = (D1 - 2.0 * PDEPrice[k] + D2) / dS / dS; // Vega #1 V1 = IP.interp2(V, S, U, V0 + dv, S0[k]); V2 = IP.interp2(V, S, U, V0 - dv, S0[k]); dCdv0 = (V1 - V2) / 2.0 / dv; Vega1PDE[k] = dCdv0 * 2.0 * Math.Sqrt(V0); // Vanna and volga C1 = IP.interp2(V, S, U, V0 + dv, S0[k] + dS); C2 = IP.interp2(V, S, U, V0 - dv, S0[k] + dS); C3 = IP.interp2(V, S, U, V0 + dv, S0[k] - dS); C4 = IP.interp2(V, S, U, V0 - dv, S0[k] - dS); VannaPDE[k] = (C1 - C2 - C3 + C4) / 4.0 / dv / dS * 2.0 * Math.Sqrt(V0); dC2 = (V1 - 2 * PDEPrice[k] + V2) / dv / dv; VolgaPDE[k] = 4.0 * Math.Sqrt(V0) * (dC2 * Math.Sqrt(V0) + Vega1PDE[k] / 4.0 / V0); // Theta T1 = IP.interp2(V, S, U, V0, S0[k]); // U(S,v,T) T2 = IP.interp2(V, S, u, V0, S0[k]); // U(s,v,T-dt) ThetaPDE[k] = -(T1 - T2) / dt; } // Output the results Console.WriteLine("Stock price grid size {0}", nS + 1); Console.WriteLine("Volatility grid size {0}", nV + 1); Console.WriteLine("Number of time steps {0}", nT); Console.WriteLine("--------------------------------------------------------------------------"); Console.WriteLine("Spot Price Delta Gamma Vega1 Vanna Volga Theta"); Console.WriteLine("--------------------------------------------------------------------------"); for (int k = 0; k <= N - 1; k++) { Console.WriteLine(" {0,2:F0} {1,10:F4} {2,10:F4} {3,8:F4} {4,8:F4} {5,8:F4} {6,8:F4} {7,8:F4}", S0[k], PDEPrice[k], DeltaPDE[k], GammaPDE[k], Vega1PDE[k], VannaPDE[k], VolgaPDE[k], ThetaPDE[k]); } Console.WriteLine("--------------------------------------------------------------------------"); }
static void Main(string[] args) { // Classes Interpolation IP = new Interpolation(); ExplicitPDE EP = new ExplicitPDE(); HestonPrice HP = new HestonPrice(); // Illustration of pricing using uniform and non-uniform grids // Strike price, risk free rate, dividend yield, and maturity double K = 100.0; double r = 0.02; double q = 0.0; double Mat = 0.15; // Heston parameters HParam param; param.kappa = 1.5; param.theta = 0.04; param.sigma = 0.3; param.rho = -0.9; param.v0 = 0.05; param.lambda = 0.0; // Minimum and maximum values for the Stock Price, Volatility, and Maturity double Smin = 0.0; double Smax = 2.0 * K; double Vmin = 0.0; double Vmax = 0.5; double Tmin = 0.0; double Tmax = Mat; // Number of grid points for the stock, volatility, and maturity int nS = 79; // Stock price int nV = 39; // Volatility int nT = 3000; // Maturity // The maturity time increment and grid double dt = (Tmax - Tmin) / Convert.ToDouble(nT); double[] T = new double[nT + 1]; for (int i = 0; i <= nT; i++) { T[i] = Convert.ToDouble(i) * dt; } //// Pricing Using a Non-Uniform Grid // The stock price grid double c = K / 5.0; double dz = 1.0 / nS * (IP.aSinh((Smax - K) / c) - IP.aSinh(-K / c)); double[] z = new double[nS + 1]; double[] S = new double[nS + 1]; for (int i = 0; i <= nS; i++) { z[i] = IP.aSinh(-K / c) + Convert.ToDouble(i) * dz; S[i] = K + c * Math.Sinh(z[i]); } S[0] = 0; // The volatility grid double d = Vmax / 500.0; double dn = IP.aSinh(Vmax / d) / nV; double[] n = new double[nV + 1]; double[] V = new double[nV + 1]; for (int j = 0; j <= nV; j++) { n[j] = Convert.ToDouble(j) * dn; V[j] = d * Math.Sinh(n[j]); } // Solve the PDE and return U(S,v,T) and U(S,v,T-dt) Uu output = EP.HestonExplicitPDENonUniformGrid(param, K, r, q, S, V, T); double[,] U = output.bigU; double[,] u = output.smallU; // Settings for the option price calculation // 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]); } // Values double S0 = 101.52; double V0 = 0.05412; int trap = 1; string PutCall = "C"; // The closed form price and finite difference Greeks param.v0 = V0; double PriceClosed = HP.HestonPriceGaussLaguerre(param, S0, K, r, q, Mat, trap, PutCall, X, W); double dS = 1.0; double dV = 1e-2; // Delta double D1 = HP.HestonPriceGaussLaguerre(param, S0 + dS, K, r, q, Mat, trap, PutCall, X, W); double D2 = HP.HestonPriceGaussLaguerre(param, S0 - dS, K, r, q, Mat, trap, PutCall, X, W); double DeltaFD = (D1 - D2) / 2.0 / dS; // Gamma double GammaFD = (D1 - 2.0 * PriceClosed + D2) / dS / dS; // Vega #1 param.v0 = V0 + dV; double V1 = HP.HestonPriceGaussLaguerre(param, S0, K, r, q, Mat, trap, PutCall, X, W); param.v0 = V0 - dV; double V2 = HP.HestonPriceGaussLaguerre(param, S0, K, r, q, Mat, trap, PutCall, X, W); double Vega1FD = (V1 - V2) / 2.0 / dV * 2.0 * Math.Sqrt(V0); // Vanna param.v0 = V0 + dV; double C1 = HP.HestonPriceGaussLaguerre(param, S0 + dS, K, r, q, Mat, trap, PutCall, X, W); double C3 = HP.HestonPriceGaussLaguerre(param, S0 - dS, K, r, q, Mat, trap, PutCall, X, W); param.v0 = V0 - dV; double C2 = HP.HestonPriceGaussLaguerre(param, S0 + dS, K, r, q, Mat, trap, PutCall, X, W); double C4 = HP.HestonPriceGaussLaguerre(param, S0 - dS, K, r, q, Mat, trap, PutCall, X, W); double VannaFD = (C1 - C2 - C3 + C4) / 4.0 / dV / dS * 2.0 * Math.Sqrt(V0); param.v0 = V0; // Volga double dC2 = (V1 - 2.0 * PriceClosed + V2) / (dV * dV); double VolgaFD = 4.0 * Math.Sqrt(V0) * (dC2 * Math.Sqrt(V0) + Vega1FD / 4.0 / V0); // Theta double T1 = HP.HestonPriceGaussLaguerre(param, S0, K, r, q, Mat, trap, PutCall, X, W); double T2 = HP.HestonPriceGaussLaguerre(param, S0, K, r, q, Mat - dt, trap, PutCall, X, W); double ThetaFD = -(T1 - T2) / dt; // The PDE price and Greeks double PricePDE = IP.interp2(V, S, U, V0, S0); // Delta D1 = IP.interp2(V, S, U, V0, S0 + dS); D2 = IP.interp2(V, S, U, V0, S0 - dS); double DeltaPDE = (D1 - D2) / 2.0 / dS; // Gamma double GammaPDE = (D1 - 2.0 * PricePDE + D2) / dS / dS; // Vega #1 V1 = IP.interp2(V, S, U, V0 + dV, S0); V2 = IP.interp2(V, S, U, V0 - dV, S0); double Vega1PDE = (V1 - V2) / 2.0 / dV * 2.0 * Math.Sqrt(V0); // Vanna C1 = IP.interp2(V, S, U, V0 + dV, S0 + dS); C2 = IP.interp2(V, S, U, V0 - dV, S0 + dS); C3 = IP.interp2(V, S, U, V0 + dV, S0 - dS); C4 = IP.interp2(V, S, U, V0 - dV, S0 - dS); double VannaPDE = (C1 - C2 - C3 + C4) / 4.0 / dV / dS * 2.0 * Math.Sqrt(V0); // Volga dC2 = (V1 - 2.0 * PricePDE + V2) / (dV * dV); double VolgaPDE = 4 * Math.Sqrt(V0) * (dC2 * Math.Sqrt(V0) + Vega1PDE / 4.0 / V0); // Theta T1 = IP.interp2(V, S, U, V0, S0); T2 = IP.interp2(V, S, u, V0, S0); double ThetaPDE = -(T1 - T2) / dt; // Output the results Console.WriteLine("Stock price grid size {0}", nS + 1); Console.WriteLine("Volatility grid size {0}", nV + 1); Console.WriteLine("Number of time steps {0}", nT); Console.WriteLine("---------------------------------------"); Console.WriteLine("Greek PDE FiniteDiff"); Console.WriteLine("---------------------------------------"); Console.WriteLine("Price {0,10:F5} {1,12:F5} ", PricePDE, PriceClosed); Console.WriteLine("Delta {0,10:F5} {1,12:F5} ", DeltaPDE, DeltaFD); Console.WriteLine("Gamma {0,10:F5} {1,12:F5} ", GammaPDE, GammaFD); Console.WriteLine("Vega #1 {0,10:F5} {1,12:F5} ", Vega1PDE, Vega1FD); Console.WriteLine("Vanna {0,10:F5} {1,12:F5} ", VannaPDE, VannaFD); Console.WriteLine("Volga {0,10:F5} {1,12:F5} ", VolgaPDE, VolgaFD); Console.WriteLine("Theta {0,10:F5} {1,12:F5} ", ThetaPDE, ThetaFD); Console.WriteLine("---------------------------------------"); }