// Heston Price by Gauss-Laguerre Integration ================================================================= static float HestonPriceGaussLaguerre(HParam param, float S, float K, float r, float q, float T, int trap, string PutCall, float[] x, float[] w) { float[] int1 = new float[32]; float[] int2 = new float[32]; // Numerical integration for (int j = 0; j <= 31; j++) { int1[j] = w[j] * HestonProb(x[j], param, S, K, r, q, T, 1, trap); int2[j] = w[j] * HestonProb(x[j], param, S, K, r, q, T, 2, trap); } // Define P1 and P2 float pi = Convert.ToSingle(Math.PI); float P1 = 0.5f + 1.0f / pi * int1.Sum(); float P2 = 0.5f + 1.0f / pi * int2.Sum(); // The call price float HestonC = Convert.ToSingle(S * Math.Exp(-q * T) * P1 - K * Math.Exp(-r * T) * P2); // The put price by put-call parity float HestonP = Convert.ToSingle(HestonC - S * Math.Exp(-q * T) + K * Math.Exp(-r * T)); // Output the option price if (PutCall == "C") { return(HestonC); } else { return(HestonP); } }
public double CZEuroCall(double S0, double tau, HParam param, double K, double rf, double q, double[] Xs, double[] Ws) { Complex i = new Complex(0.0, 1.0); double xi = 0; Complex psi = 0; int Nx = Xs.Length; double[] Int1 = new double[Nx]; double[] Int2 = new double[Nx]; Complex I1, I2; // Compute the integrands CharFun CF = new CharFun(); for (int k = 0; k <= Nx - 1; k++) { Complex phi = Xs[k]; I1 = Complex.Exp(-i * phi * Math.Log(K)) * CF.CZCharFun(S0, tau, xi, param, K, rf, q, phi, psi, 1) / (i * phi); Int1[k] = Ws[k] * I1.Real; I2 = Complex.Exp(-i * phi * Math.Log(K)) * CF.CZCharFun(S0, tau, xi, param, K, rf, q, phi, psi, 2) / (i * phi); Int2[k] = Ws[k] * I2.Real; } // Define the probabilities double pi = Math.PI; double P1 = 0.5 + (1.0 / pi) * Int1.Sum(); double P2 = 0.5 + (1.0 / pi) * Int2.Sum(); // Return the call price return(S0 * Math.Exp(-q * tau) * P1 - K * Math.Exp(-rf * tau) * P2); }
// Heston Price by Gauss-Laguerre Integration public double HestonPriceConsol(HParam param, OpSet settings, double[] x, double[] w) { double[] int1 = new Double[32]; // Numerical integration for (int j = 0; j <= 31; j++) { int1[j] = w[j] * HestonProbConsol(x[j], param, settings); } // Define P1 and P2 double pi = Math.PI; double I = int1.Sum(); // The call price double S = settings.S; double K = settings.K; double r = settings.r; double q = settings.q; double T = settings.T; string PutCall = settings.PutCall; double HestonC = 0.5 * S * Math.Exp(-q * T) - 0.5 * K * Math.Exp(-r * T) + I / pi; // The put price by put-call parity double HestonP = HestonC - S * Math.Exp(-q * T) + K * Math.Exp(-r * T); // Output the option price if (PutCall == "C") { return(HestonC); } else { return(HestonP); } }
// Mikhailov and Nogel (2003) Time dependent C function public Complex Ct(double phi, HParam param, double r, double q, double T, OpSet settings, int Pnum, Complex C0, Complex D0) { Complex i = new Complex(0.0, 1.0); // Imaginary unit double kappa = param.kappa; double theta = param.theta; double sigma = param.sigma; double v0 = param.v0; double rho = param.rho; double a = kappa * theta; int Trap = settings.trap; Complex b, u, d, g, G, C = new Complex(); // Parameters "u" and "b" are different for P1 and P2 if (Pnum == 1) { u = 0.5; b = kappa - rho * sigma; } else { u = -0.5; b = kappa; } d = Complex.Sqrt(Complex.Pow(rho * sigma * i * phi - b, 2) - sigma * sigma * (2.0 * u * i * phi - phi * phi)); g = (b - rho * sigma * i * phi + d - D0 * sigma * sigma) / (b - rho * sigma * i * phi - d - D0 * sigma * sigma); G = (1.0 - g * Complex.Exp(d * T)) / (1.0 - g); C = (r - q) * i * phi * T + a / sigma / sigma * ((b - rho * sigma * i * phi + d) * T - 2.0 * Complex.Log(G)) + C0; return(C); }
// Time dependent D function public Complex Dt(double phi, HParam param, double S, double K, double r, double q, double T, int Pnum, int Trap, Complex C0, Complex D0) { Complex i = new Complex(0.0, 1.0); // Imaginary unit double kappa = param.kappa; double theta = param.theta; double sigma = param.sigma; double v0 = param.v0; double rho = param.rho; Complex b, u, d, g, G, D = new Complex(); // Parameters "u" and "b" are different for P1 and P2 if (Pnum == 1) { u = 0.5; b = kappa - rho * sigma; } else { u = -0.5; b = kappa; } d = Complex.Sqrt(Complex.Pow(rho * sigma * i * phi - b, 2) - sigma * sigma * (2.0 * u * i * phi - phi * phi)); g = (b - rho * sigma * i * phi + d - D0 * sigma * sigma) / (b - rho * sigma * i * phi - d - D0 * sigma * sigma); G = (1.0 - g * Complex.Exp(d * T)) / (1.0 - g); D = ((b - rho * sigma * i * phi) * (1 - g * Complex.Exp(d * T)) + d * (1 + g * Complex.Exp(d * T))) / (sigma * sigma * (1 - g * Complex.Exp(d * T))); return(D); }
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]); } HParam param = new HParam(); param.kappa = 6.2; param.theta = 0.06; param.sigma = 0.5; param.v0 = 0.03; param.rho = -0.7; param.lambda = 0.0; OpSet settings = new OpSet(); settings.S = 100.0; settings.K = 100.0; settings.T = 0.25; settings.r = 0.03; settings.q = 0.02; settings.PutCall = "C"; settings.trap = 1; // Simulation settings double gamma1 = 0.5; double gamma2 = 0.5; double phic = 1.5; int MC = 1; int NT = 100; int NS = 1500; // Calculate the IJK price QESimulation QE = new QESimulation(); double QuadExpPrice = QE.QEPrice(param, settings, gamma1, gamma2, NT, NS, MC, phic, settings.PutCall); // Calculate the closed-form European option price HestonPrice HP = new HestonPrice(); double ClosedPrice = HP.HestonPriceGaussLaguerre(param, settings, x, w); // Calculate the errors; double QError = Math.Abs((QuadExpPrice - ClosedPrice) / ClosedPrice * 100); // Output the results Console.WriteLine("European Heston price by simulation --------------------"); Console.WriteLine("Uses {0:0} stock price paths and {1:0} time increments", NS, NT); Console.WriteLine("--------------------------------------------------------"); Console.WriteLine("Closed form price {0:F5}", ClosedPrice); Console.WriteLine("Quadratic exp price {0:F5} Error {1,5:F5}", QuadExpPrice, QError); Console.WriteLine("--------------------------------------------------------"); Console.WriteLine(" "); }
// Heston Price by Gauss-Laguerre Integration ================================================================= public double HestonPriceGaussLaguerre(HParam param, double S, double K, double r, double q, double T, int trap, string PutCall, double[] x, double[] w) { double[] int1 = new Double[32]; double[] int2 = new Double[32]; // Numerical integration for (int j = 0; j <= 31; j++) { int1[j] = w[j] * HestonProb(x[j], param, S, K, r, q, T, 1, trap); int2[j] = w[j] * HestonProb(x[j], param, S, K, r, q, T, 2, trap); } // Define P1 and P2 double pi = Math.PI; double P1 = 0.5 + 1.0 / pi * int1.Sum(); double P2 = 0.5 + 1.0 / pi * int2.Sum(); // The call price double HestonC = S * Math.Exp(-q * T) * P1 - K * Math.Exp(-r * T) * P2; // The put price by put-call parity double HestonP = HestonC - S * Math.Exp(-q * T) + K * Math.Exp(-r * T); // Output the option price if (PutCall == "C") { return(HestonC); } else { return(HestonP); } }
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]); } HParam param = new HParam(); param.kappa = 6.2; param.theta = 0.06; param.sigma = 0.5; param.v0 = 0.03; param.rho = -0.7; param.lambda = 0.0; OpSet settings = new OpSet(); settings.S = 100.0; settings.K = 90.0; settings.T = 3.0 / 12.0; settings.r = 0.03; settings.q = 0.02; settings.PutCall = "C"; settings.trap = 1; // Simulation settings int NT = 100; int NS = 2000; // Calculate the Euler price and the transformed volatility price TVSimulation TV = new TVSimulation(); double TVolPrice = TV.TransVolPrice("TV", param, settings, NT, NS); double EulerPrice = TV.TransVolPrice("Euler", param, settings, NT, NS); // Calculate the closed-form European option price double ClosedPrice = HestonPriceGaussLaguerre(param, settings, x, w); // Calculate the errors; double TVolError = Math.Abs((TVolPrice - ClosedPrice) / ClosedPrice * 100); double EulerError = Math.Abs((EulerPrice - ClosedPrice) / ClosedPrice * 100); // Output the results Console.WriteLine("Uses {0:0} stock price paths and {1:0} time increments", NS, NT); Console.WriteLine("--------------------------------------------------------"); Console.WriteLine("Method Price PercentError"); Console.WriteLine("--------------------------------------------------------"); Console.WriteLine("Exact with Gauss Laguerre {0,10:F5}", ClosedPrice); Console.WriteLine("Euler discretization {0,10:F5} {1,10:F5}", EulerPrice, EulerError); Console.WriteLine("Transformed volatility {0,10:F5} {1,10:F5}", TVolPrice, TVolError); Console.WriteLine("--------------------------------------------------------"); Console.WriteLine(" "); }
// Double integral using trapezoidal rule public double DoubleTrapezoidal(HParam param, double S0, double K, double tau, double rf, double q, double b0, double b1, double[] X, double[] T, int funNum) { Complex i = new Complex(0.0, 1.0); double a, b, c, d, qr = 0.0; Complex fun1, fun2, fun3, fun4; double g1, g2, g3, g4, term1, term2, term3; int Nt = T.Length; int Nx = X.Length; // Select rate or dividend if (funNum == 1) { qr = q; } else if (funNum == 2) { qr = rf; } double[,] Int = new double[Nt, Nx]; double sumInt = 0.0; // Double trapezoidal rule CharFun CF = new CharFun(); for (int t = 1; t <= Nt - 1; t++) { a = T[t - 1]; b = T[t]; for (int x = 1; x <= Nx - 1; x++) { c = X[x - 1]; d = X[x]; fun1 = Complex.Exp(-b0 * i * c) * CF.CZCharFun(S0, tau, a, param, K, rf, q, c, -b1 * c, funNum) / (i * c); fun2 = Complex.Exp(-b0 * i * d) * CF.CZCharFun(S0, tau, a, param, K, rf, q, d, -b1 * d, funNum) / (i * d); fun3 = Complex.Exp(-b0 * i * c) * CF.CZCharFun(S0, tau, b, param, K, rf, q, c, -b1 * c, funNum) / (i * c); fun4 = Complex.Exp(-b0 * i * d) * CF.CZCharFun(S0, tau, b, param, K, rf, q, d, -b1 * d, funNum) / (i * d); g1 = Math.Exp(qr * a) * fun1.Real; g2 = Math.Exp(qr * a) * fun2.Real; g3 = Math.Exp(qr * b) * fun3.Real; g4 = Math.Exp(qr * b) * fun4.Real; term1 = g1 + g2 + g3 + g4; fun1 = Complex.Exp(-b0 * i * c) * CF.CZCharFun(S0, tau, (a + b) / 2.0, param, K, rf, q, c, -b1 * c, funNum) / (i * c); fun2 = Complex.Exp(-b0 * i * d) * CF.CZCharFun(S0, tau, (a + b) / 2.0, param, K, rf, q, d, -b1 * d, funNum) / (i * d); fun3 = Complex.Exp(-b0 * i * (c + d) / 2.0) * CF.CZCharFun(S0, tau, a, param, K, rf, q, (c + d) / 2.0, -b1 * (c + d) / 2.0, funNum) / (i * (c + d) / 2.0); fun4 = Complex.Exp(-b0 * i * (c + d) / 2.0) * CF.CZCharFun(S0, tau, b, param, K, rf, q, (c + d) / 2.0, -b1 * (c + d) / 2.0, funNum) / (i * (c + d) / 2.0); g1 = Math.Exp(qr * (a + b) / 2.0) * fun1.Real; g2 = Math.Exp(qr * (a + b) / 2.0) * fun2.Real; g3 = Math.Exp(qr * a) * fun3.Real; g4 = Math.Exp(qr * b) * fun4.Real; term2 = g1 + g2 + g3 + g4; fun1 = Complex.Exp(-b0 * i * (c + d) / 2.0) * CF.CZCharFun(S0, tau, (a + b) / 2.0, param, K, rf, q, (c + d) / 2.0, -b1 * (c + d) / 2.0, funNum) / (i * (c + d) / 2.0); term3 = Math.Exp(qr * (a + b) / 2.0) * fun1.Real; Int[t, x] = (b - a) * (d - c) / 16.0 * (term1 + 2 * term2 + 4 * term3); sumInt += Int[t, x]; } } return(sumInt); }
// Heston Integrand public double HestonProb(double phi, HParam param, OpSet settings, int Pnum) { Complex i = new Complex(0.0, 1.0); // Imaginary unit double S = settings.S; double K = settings.K; double T = settings.T; double r = settings.r; double q = settings.q; double kappa = param.kappa; double theta = param.theta; double sigma = param.sigma; double v0 = param.v0; double rho = param.rho; double lambda = param.lambda; double x = Math.Log(S); double a = kappa * theta; int Trap = settings.trap; Complex b, u, d, g, c, D, G, C, f, integrand = new Complex(); // Parameters "u" and "b" are different for P1 and P2 if (Pnum == 1) { u = 0.5; b = kappa + lambda - rho * sigma; } else { u = -0.5; b = kappa + lambda; } d = Complex.Sqrt(Complex.Pow(rho * sigma * i * phi - b, 2) - sigma * sigma * (2.0 * u * i * phi - phi * phi)); g = (b - rho * sigma * i * phi + d) / (b - rho * sigma * i * phi - d); if (Trap == 1) { // "Little Heston Trap" formulation c = 1.0 / g; D = (b - rho * sigma * i * phi - d) / sigma / sigma * ((1.0 - Complex.Exp(-d * T)) / (1.0 - c * Complex.Exp(-d * T))); G = (1.0 - c * Complex.Exp(-d * T)) / (1 - c); C = (r - q) * i * phi * T + a / sigma / sigma * ((b - rho * sigma * i * phi - d) * T - 2.0 * Complex.Log(G)); } else { // Original Heston formulation. G = (1.0 - g * Complex.Exp(d * T)) / (1.0 - g); C = (r - q) * i * phi * T + a / sigma / sigma * ((b - rho * sigma * i * phi + d) * T - 2.0 * Complex.Log(G)); D = (b - rho * sigma * i * phi + d) / sigma / sigma * ((1.0 - Complex.Exp(d * T)) / (1.0 - g * Complex.Exp(d * T))); } // The characteristic function. f = Complex.Exp(C + D * v0 + i * phi * x); // The integrand. integrand = Complex.Exp(-i * phi * Complex.Log(K)) * f / i / phi; // Return the real part of the integrand. return(integrand.Real); }
public Complex CZCharFun(double S0, double tau, double t, HParam param, double K, double rf, double q, Complex phi, Complex psi, int FunNum) { Complex i = new Complex(0.0, 1.0); double kappa = param.kappa; double theta = param.theta; double sigma = param.sigma; double v0 = param.v0; double rho = param.rho; double lambda = param.lambda; double x = Math.Log(S0); // Parameters "a" and "b" double a = kappa * theta; double b = kappa + lambda; // "d" and "g" functions Complex d = Complex.Sqrt(Complex.Pow(rho * sigma * i * phi - b, 2.0) + sigma * sigma * phi * (phi + i)); Complex g = (b - rho * sigma * i * phi - sigma * sigma * i * psi + d) / (b - rho * sigma * i * phi - sigma * sigma * i * psi - d); // The components of the affine characteristic function. Complex G = (1.0 - g * Complex.Exp(d * (tau - t))) / (1.0 - g); Complex C = (rf - q) * i * phi * (tau - t) + a / sigma / sigma * ((b - rho * sigma * i * phi + d) * (tau - t) - 2.0 * Complex.Log(G)); Complex F = (1.0 - Complex.Exp(d * (tau - t))) / (1.0 - g * Complex.Exp(d * (tau - t))); Complex D = i * psi + (b - rho * sigma * i * phi - sigma * sigma * i * psi + d) / sigma / sigma * F; // The characteristic function. Complex f2 = Complex.Exp(C + D * v0 + i * phi * x); if (FunNum == 2) { return(f2); } else if (FunNum == 1) { d = Complex.Sqrt(Complex.Pow(rho * sigma * i * (phi - i) - b, 2.0) + sigma * sigma * (phi - i) * phi); g = (b - rho * sigma * i * (phi - i) - sigma * sigma * i * psi + d) / (b - rho * sigma * i * (phi - i) - sigma * sigma * i * psi - d); // The components of the affine characteristic function. G = (1.0 - g * Complex.Exp(d * (tau - t))) / (1.0 - g); C = (rf - q) * i * (phi - i) * (tau - t) + a / sigma / sigma * ((b - rho * sigma * i * (phi - i) + d) * (tau - t) - 2.0 * Complex.Log(G)); F = (1.0 - Complex.Exp(d * (tau - t))) / (1.0 - g * Complex.Exp(d * (tau - t))); D = i * psi + (b - rho * sigma * i * (phi - i) - sigma * sigma * i * psi + d) / sigma / sigma * F; Complex F2 = Complex.Exp(C + D * v0 + i * (phi - i) * x); return(1.0 / S0 * Complex.Exp(-(rf - q) * (tau - t)) * F2); } else { return(0.0); } }
public double[] CZAmerCall(double S0, double tau, HParam param, double K, double rf, double q, double[] xs, double[] ws, double[] xt, double[] wt, int Nt, double b0, double b1, double a, double b, double c, double d, string DoubleType) { EarlyExercise EE = new EarlyExercise(); double Euro = CZEuroCall(S0, tau, param, K, rf, q, xs, ws); double Premium = EE.CZEarlyExercise(S0, tau, param, K, rf, q, xt, wt, xt, wt, Nt, b0, b1, a, b, c, d, DoubleType); double Amer = Euro + Premium; double[] output = new double[2]; output[0] = Amer; output[1] = Euro; return(output); }
// Heston Integrand static float HestonProb(float phi, HParam param, float S, float K, float r, float q, float T, int Pnum, int Trap) { Complex i = new Complex(0.0, 1.0); // Imaginary unit float kappa = param.kappa; float theta = param.theta; float sigma = param.sigma; float v0 = param.v0; float rho = param.rho; float lambda = 0.0f; float x = Convert.ToSingle(Math.Log(S)); float a = kappa * theta; Complex b, u, d, g, c, D, G, C, f, integrand = new Complex(); // Parameters "u" and "b" are different for P1 and P2 if (Pnum == 1) { u = 0.5; b = kappa + lambda - rho * sigma; } else { u = -0.5; b = kappa + lambda; } d = Complex.Sqrt(Complex.Pow(rho * sigma * i * phi - b, 2) - sigma * sigma * (2.0 * u * i * phi - phi * phi)); g = (b - rho * sigma * i * phi + d) / (b - rho * sigma * i * phi - d); if (Trap == 1) { // "Little Heston Trap" formulation c = 1.0 / g; D = (b - rho * sigma * i * phi - d) / sigma / sigma * ((1.0 - Complex.Exp(-d * T)) / (1.0 - c * Complex.Exp(-d * T))); G = (1.0 - c * Complex.Exp(-d * T)) / (1 - c); C = (r - q) * i * phi * T + a / sigma / sigma * ((b - rho * sigma * i * phi - d) * T - 2.0 * Complex.Log(G)); } else { // Original Heston formulation. G = (1.0 - g * Complex.Exp(d * T)) / (1.0 - g); C = (r - q) * i * phi * T + a / sigma / sigma * ((b - rho * sigma * i * phi + d) * T - 2.0 * Complex.Log(G)); D = (b - rho * sigma * i * phi + d) / sigma / sigma * ((1.0 - Complex.Exp(d * T)) / (1.0 - g * Complex.Exp(d * T))); } // The characteristic function. f = Complex.Exp(C + D * v0 + i * phi * x); // The integrand. integrand = Complex.Exp(-i * phi * Complex.Log(K)) * f / i / phi; // Return the real part of the integrand. return(Convert.ToSingle(integrand.Real)); }
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]); } } // Heston parameters HParam param = new HParam(); param.kappa = 1.5; param.theta = 0.04; param.sigma = 0.3; param.v0 = 0.05412; param.rho = -0.9; param.lambda = 0.0; // Option settings OpSet settings = new OpSet(); settings.S = 101.52; settings.K = 100.0; settings.T = 0.15; settings.r = 0.02; settings.q = 0.0; settings.PutCall = "C"; settings.trap = 1; // The Heston price HestonPrice HP = new HestonPrice(); double Price = HP.HestonPriceGaussLaguerre(param, settings, x, w); Console.WriteLine("Heston price using 32-point Gauss Laguerre"); Console.WriteLine("------------------------------------------ "); Console.WriteLine("Option Flavor = {0,0:F5}", settings.PutCall); Console.WriteLine("Strike Price = {0,0:0}", settings.K); Console.WriteLine("Maturity = {0,0:F2}", settings.T); Console.WriteLine("Price = {0,0:F4}", Price); Console.WriteLine("------------------------------------------ "); Console.WriteLine(" "); }
public Complex HestonBivariateCF(Complex phi1, Complex phi2, HParam param, OpSet settings) { Complex i = new Complex(0.0, 1.0); // Imaginary unit Complex S = new Complex(settings.S, 0.0); // Spot Price Complex K = new Complex(settings.K, 0.0); // Strike Price Complex T = new Complex(settings.T, 0.0); // Maturity in years Complex r = new Complex(settings.r, 0.0); // Interest rate Complex q = new Complex(settings.q, 0.0); // Dividend yield Complex rho = new Complex(param.rho, 0.0); // Heston parameter: correlation Complex kappa = new Complex(param.kappa, 0.0); // Heston parameter: mean reversion speed Complex theta = new Complex(param.theta, 0.0); // Heston parameter: mean reversion speed Complex lambda = new Complex(param.lambda, 0.0); // Heston parameter: price of volatility risk Complex sigma = new Complex(param.sigma, 0.0); // Heston parameter: volatility of variance Complex v0 = new Complex(param.v0, 0.0); // Heston parameter: initial variance Complex x = Complex.Log(S); Complex a = kappa * theta; int trap = settings.trap; Complex b, u, d, g, c, A = 0.0, C = 0.0, B, G = new Complex(); // Log stock price Complex x0 = Complex.Log(S); u = -0.5; b = kappa + lambda; d = Complex.Sqrt(Complex.Pow(rho * sigma * i * phi1 - b, 2) - sigma * sigma * (2.0 * u * i * phi1 - phi1 * phi1)); g = (b - rho * sigma * i * phi1 + d - sigma * sigma * i * phi2) / (b - rho * sigma * i * phi1 - d - sigma * sigma * i * phi2); c = 1 / g; B = i * phi1; if (trap == 1) { // Little Trap formulation in Kahl (2008) G = (c * Complex.Exp(-d * T) - 1.0) / (c - 1.0); A = (r - q) * i * phi1 * T + a / sigma / sigma * ((b - rho * sigma * i * phi1 - d) * T - 2.0 * Complex.Log(G)); C = ((b - rho * sigma * i * phi1 - d) - (b - rho * sigma * i * phi1 + d) * c * Complex.Exp(-d * T)) / (sigma * sigma) / (1 - c * Complex.Exp(-d * T)); } else if (trap == 0) { // Original Heston formulation. G = (1.0 - g * Complex.Exp(d * T)) / (1.0 - g); A = (r - q) * i * phi1 * T + a / sigma / sigma * ((b - rho * sigma * i * phi1 + d) * T - 2.0 * Complex.Log(G)); C = ((b - rho * sigma * i * phi1 + d) - (b - rho * sigma * i * phi1 - d) * g * Complex.Exp(d * T)) / (sigma * sigma) / (1.0 - g * Complex.Exp(d * T)); } // The characteristic function. return(Complex.Exp(A + B * x0 + C * v0)); }
// Heston Price by Gauss-Laguerre Integration public double HestonPriceGaussLaguerre(HParam param, OpSet settings, double[] x, double[] w) { Complex i = new Complex(0.0, 1.0); Complex[] f1 = new Complex[32]; Complex[] f2 = new Complex[32]; double[] int1 = new double[32]; double[] int2 = new double[32]; double S = settings.S; double K = settings.K; double T = settings.T; double r = settings.r; double q = settings.q; // Numerical integration for (int j = 0; j <= 31; j++) { double phi = x[j]; f1[j] = HestonCF(phi - i, param, settings) / (S * Math.Exp((r - q) * T)); f2[j] = HestonCF(phi, param, settings); Complex FF1 = Complex.Exp(-i * phi * Complex.Log(K)) * f1[j] / i / phi; Complex FF2 = Complex.Exp(-i * phi * Complex.Log(K)) * f2[j] / i / phi; int1[j] = w[j] * FF1.Real; int2[j] = w[j] * FF2.Real; } // Define P1 and P2 double pi = Math.PI; double P1 = 0.5 + 1.0 / pi * int1.Sum(); double P2 = 0.5 + 1.0 / pi * int2.Sum(); // The call price string PutCall = settings.PutCall; double HestonC = S * Math.Exp(-q * T) * P1 - K * Math.Exp(-r * T) * P2; // The put price by put-call parity double HestonP = HestonC - S * Math.Exp(-q * T) + K * Math.Exp(-r * T); // Output the option price if (PutCall == "C") { return(HestonC); } else { return(HestonP); } }
// Simulation of stock price paths and variance paths using Euler or Milstein schemes public double[,] MMSim(HParam param, OpSet settings, int NT, int NS, double[,] Zv, double[,] Zs) { // 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; // option settings double r = settings.r; double q = settings.q; // Time increment double dt = settings.T / Convert.ToDouble(NT); // Initialize the variance and stock processes double[,] V = new double[NT, NS]; double[,] S = new double[NT, NS]; // Flags for negative variances int F = 0; // Starting values for the variance and stock processes for (int s = 0; s <= NS - 1; s++) { S[0, s] = settings.S; // Spot price V[0, s] = v0; // Heston v0 initial variance } // Generate the stock and volatility paths for (int s = 0; s <= NS - 1; s++) { for (int t = 1; t <= NT - 1; t++) { // Matched moment lognormal approximation double dW = Math.Sqrt(dt) * Zv[t, s]; double num = 0.5 * sigma * sigma * V[t - 1, s] * (1.0 - Math.Exp(-2.0 * kappa * dt)) / kappa; double den = Math.Pow(Math.Exp(-kappa * dt) * V[t - 1, s] + (1.0 - Math.Exp(-kappa * dt)) * theta, 2); double Gam = Math.Log(1 + num / den); V[t, s] = (Math.Exp(-kappa * dt) * V[t - 1, s] + (1.0 - Math.Exp(-kappa * dt)) * theta) * Math.Exp(-0.5 * Gam * Gam + Gam * Zv[t, s]); // Euler/Milstein discretization scheme for the log stock prices S[t, s] = S[t - 1, s] * Math.Exp((r - q - V[t - 1, s] / 2) * dt + Math.Sqrt(V[t - 1, s] * dt) * Zs[t, s]); } } return(S); }
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]); } } HParam param = new HParam(); param.rho = -0.9; // Heston Parameter: Correlation param.kappa = 2; // Heston Parameter: Mean reversion speed param.theta = 0.05; // Heston Parameter: Mean reversion level param.lambda = 0.0; // Heston Parameter: Risk parameter param.sigma = 0.3; // Heston Parameter: Volatility of Variance param.v0 = 0.035; // Heston Parameter: Current Variance OpSet settings = new OpSet(); settings.S = 100.0; // Spot Price settings.K = 100.0; // Strike Price settings.T = 0.25; // Maturity in Years settings.r = 0.05; // Interest Rate settings.q = 0.02; // Dividend yield settings.PutCall = "P"; // "P"ut or "C"all settings.trap = 1; // 1="Little Trap" characteristic function // The Heston price HestonPriceConsolidated HPC = new HestonPriceConsolidated(); double Price = HPC.HestonPriceConsol(param, settings, x, w); Console.WriteLine("Heston price using consolidated integral"); Console.WriteLine("------------------------------------------------ "); Console.WriteLine("Option Flavor = {0,0:F5}", settings.PutCall); Console.WriteLine("Strike Price = {0,0:0}", settings.S); Console.WriteLine("Maturity = {0,0:F2}", settings.T); Console.WriteLine("Price = {0,0:F4}", Price); Console.WriteLine("------------------------------------------------ "); Console.WriteLine(" "); }
// Price by simulation public double EulerMilsteinPrice(string scheme, string negvar, HParam param, OpSet settings, double alpha, int NT, int NS, string PutCall) { RandomNumbers RA = new RandomNumbers(); double[] STe = EulerMilsteinSim(scheme, negvar, param, settings, alpha, NT, NS); double[] Price = new double[NS]; for (int s = 0; s <= NS - 1; s++) { if (settings.PutCall == "C") { Price[s] = Math.Max(STe[s] - settings.K, 0.0); } else if (settings.PutCall == "P") { Price[s] = Math.Max(settings.K - STe[s], 0.0); } } return(Math.Exp(-settings.r * settings.T) * RA.Mean(Price)); }
// Price by simulation public double QEPrice(HParam param, OpSet settings, double gamma1, double gamma2, int NT, int NS, int MC, double phic, string PutCall) { RandomNumbers RN = new RandomNumbers(); double[] STe = QESim(param, settings, gamma1, gamma2, NT, NS, MC, phic); double[] Price = new double[NS]; for (int s = 0; s <= NS - 1; s++) { if (settings.PutCall == "C") { Price[s] = Math.Max(STe[s] - settings.K, 0.0); } else if (settings.PutCall == "P") { Price[s] = Math.Max(settings.K - STe[s], 0.0); } } return(Math.Exp(-settings.r * settings.T) * RN.Mean(Price)); }
// Price by simulation public double MMPrice(HParam param, OpSet settings, int NT, int NS) { RandomNumbers RN = new RandomNumbers(); double[] STe = MMSim(param, settings, NT, NS); double[] Price = new double[NS]; for (int s = 0; s <= NS - 1; s++) { if (settings.PutCall == "C") { Price[s] = Math.Max(STe[s] - settings.K, 0.0); } else if (settings.PutCall == "P") { Price[s] = Math.Max(settings.K - STe[s], 0.0); } } return(Math.Exp(-settings.r * settings.T) * RN.Mean(Price)); }
// Heston Price by Gauss-Laguerre Integration ============================================================================================================= public double HestonPriceGaussLaguerre(HParam param, double S, double K, double r, double q, double T, int trap, string PutCall, double[] X, double[] W) { Complex i = new Complex(0.0, 1.0); Complex[] f1 = new Complex[32]; Complex[] f2 = new Complex[32]; double[] int1 = new double[32]; double[] int2 = new double[32]; // Numerical integration for (int j = 0; j <= 31; j++) { double phi = X[j]; f1[j] = HestonCF(phi - i, param, S, r, q, T, trap) / (S * Math.Exp((r - q) * T)); f2[j] = HestonCF(phi, param, S, r, q, T, trap); Complex FF1 = Complex.Exp(-i * phi * Complex.Log(K)) * f1[j] / i / phi; Complex FF2 = Complex.Exp(-i * phi * Complex.Log(K)) * f2[j] / i / phi; int1[j] = W[j] * FF1.Real; int2[j] = W[j] * FF2.Real; } // Define P1 and P2 double pi = Math.PI; double P1 = 0.5 + 1.0 / pi * int1.Sum(); double P2 = 0.5 + 1.0 / pi * int2.Sum(); // The call price double HestonC = S * Math.Exp(-q * T) * P1 - K * Math.Exp(-r * T) * P2; // The put price by put-call parity double HestonP = HestonC - S * Math.Exp(-q * T) + K * Math.Exp(-r * T); // Output the option price if (PutCall == "C") { return(HestonC); } else { return(HestonP); } }
// Heston characteristic function (f2) public Complex HestonCF(Complex phi, HParam param, OpSet settings) { Complex i = new Complex(0.0, 1.0); // Imaginary unit double S = settings.S; double K = settings.K; double T = settings.T; double r = settings.r; double q = settings.q; double kappa = param.kappa; double theta = param.theta; double sigma = param.sigma; double v0 = param.v0; double rho = param.rho; double lambda = param.lambda; double x = Math.Log(S); double a = kappa * theta; int Trap = settings.trap; Complex b, u, d, g, c, D, G, C = new Complex(); u = -0.5; b = kappa + lambda; d = Complex.Sqrt(Complex.Pow(rho * sigma * i * phi - b, 2) - sigma * sigma * (2.0 * u * i * phi - phi * phi)); g = (b - rho * sigma * i * phi + d) / (b - rho * sigma * i * phi - d); if (Trap == 1) { // "Little Heston Trap" formulation c = 1.0 / g; D = (b - rho * sigma * i * phi - d) / sigma / sigma * ((1.0 - Complex.Exp(-d * T)) / (1.0 - c * Complex.Exp(-d * T))); G = (1.0 - c * Complex.Exp(-d * T)) / (1 - c); C = (r - q) * i * phi * T + a / sigma / sigma * ((b - rho * sigma * i * phi - d) * T - 2.0 * Complex.Log(G)); } else { // Original Heston formulation. G = (1.0 - g * Complex.Exp(d * T)) / (1.0 - g); C = (r - q) * i * phi * T + a / sigma / sigma * ((b - rho * sigma * i * phi + d) * T - 2.0 * Complex.Log(G)); D = (b - rho * sigma * i * phi + d) / sigma / sigma * ((1.0 - Complex.Exp(d * T)) / (1.0 - g * Complex.Exp(d * T))); } // The characteristic function. return(Complex.Exp(C + D * v0 + i * phi * x)); }
// Heston Univariate characteristic function (f2) public Complex HestonCF(Complex phi, HParam param, OpSet settings) { Complex i = new Complex(0.0, 1.0); // Imaginary unit Complex S = new Complex(settings.S, 0.0); // Spot Price Complex K = new Complex(settings.K, 0.0); // Strike Price Complex T = new Complex(settings.T, 0.0); // Maturity in years Complex r = new Complex(settings.r, 0.0); // Interest rate Complex q = new Complex(settings.q, 0.0); // Dividend yield Complex rho = new Complex(param.rho, 0.0); // Heston parameter: correlation Complex kappa = new Complex(param.kappa, 0.0); // Heston parameter: mean reversion speed Complex theta = new Complex(param.theta, 0.0); // Heston parameter: mean reversion speed Complex lambda = new Complex(param.lambda, 0.0); // Heston parameter: price of volatility risk Complex sigma = new Complex(param.sigma, 0.0); // Heston parameter: volatility of variance Complex v0 = new Complex(param.v0, 0.0); // Heston parameter: initial variance Complex x = Complex.Log(S); Complex a = kappa * theta; int Trap = settings.trap; Complex b, u, d, g, c, D, G, C = new Complex(); u = -0.5; b = kappa + lambda; d = Complex.Sqrt(Complex.Pow(rho * sigma * i * phi - b, 2) - sigma * sigma * (2.0 * u * i * phi - phi * phi)); g = (b - rho * sigma * i * phi + d) / (b - rho * sigma * i * phi - d); if (Trap == 1) { // "Little Heston Trap" formulation c = 1.0 / g; D = (b - rho * sigma * i * phi - d) / sigma / sigma * ((1.0 - Complex.Exp(-d * T)) / (1.0 - c * Complex.Exp(-d * T))); G = (1.0 - c * Complex.Exp(-d * T)) / (1 - c); C = (r - q) * i * phi * T + a / sigma / sigma * ((b - rho * sigma * i * phi - d) * T - 2.0 * Complex.Log(G)); } else { // Original Heston formulation. G = (1.0 - g * Complex.Exp(d * T)) / (1.0 - g); C = (r - q) * i * phi * T + a / sigma / sigma * ((b - rho * sigma * i * phi + d) * T - 2.0 * Complex.Log(G)); D = (b - rho * sigma * i * phi + d) / sigma / sigma * ((1.0 - Complex.Exp(d * T)) / (1.0 - g * Complex.Exp(d * T))); } // The characteristic function. return(Complex.Exp(C + D * v0 + i * phi * x)); }
// Double integral using Gauss-Legendre public double DoubleGaussLegendre(double S0, double tau, HParam param, double K, double rf, double q, double b0, double b1, double[] xt, double[] wt, double[] xs, double[] ws, double a, double b, double c, double d, int funNum) { Complex i = new Complex(0.0, 1.0); int Nt = xt.Length; int Ns = xs.Length; double h1 = (b - a) / 2.0; double h2 = (b + a) / 2.0; double k1 = (d - c) / 2.0; double k2 = (d + c) / 2.0; double time, phi, realfun, qr = 0.0; Complex fun; // Select rate or dividend if (funNum == 1) { qr = q; } else if (funNum == 2) { qr = rf; } double Int = 0.0; // Double integral CharFun CF = new CharFun(); for (int t = 0; t <= Nt - 1; t++) { time = h1 * xt[t] + h2; for (int x = 0; x <= Ns - 1; x++) { phi = k1 * xs[x] + k2; fun = Complex.Exp(-b0 * i * phi) * CF.CZCharFun(S0, tau, time, param, K, rf, q, phi, -b1 * phi, funNum) / (i * phi); realfun = Math.Exp(qr * time) * fun.Real; Int += h1 * k1 * wt[t] * ws[x] * realfun; } } return(Int); }
// Heston Price by Gauss-Legendre Integration public double HestonPriceGaussLegendre(HParam param, OpSet settings, double[] x, double[] w, double a, double b) { double[] int1 = new Double[32]; double[] int2 = new Double[32]; // Numerical integration for (int j = 0; j <= 31; j++) { double X = (a + b) / 2.0 + (b - a) / 2.0 * x[j]; int1[j] = w[j] * HestonProb(X, param, settings, 1); int2[j] = w[j] * HestonProb(X, param, settings, 2); } // Define P1 and P2 double pi = Math.PI; double P1 = 0.5 + 1.0 / pi * int1.Sum() * (b - a) / 2.0; double P2 = 0.5 + 1.0 / pi * int2.Sum() * (b - a) / 2.0; // The call price double S = settings.S; double K = settings.K; double T = settings.T; double r = settings.r; double q = settings.q; string PutCall = settings.PutCall; double HestonC = S * Math.Exp(-q * T) * P1 - K * Math.Exp(-r * T) * P2; // The put price by put-call parity double HestonP = HestonC - S * Math.Exp(-q * T) + K * Math.Exp(-r * T); // Output the option price if (PutCall == "C") { return(HestonC); } else { return(HestonP); } }
// Heston Price by Gauss-Laguerre Integration public double HestonPriceGaussLaguerre(HParam param, OPSet settings, string PutCall, double T, int trap, double[] x, double[] w) { int Nx = x.Length; double[] int1 = new Double[Nx]; double[] int2 = new Double[Nx]; // Numerical integration for (int j = 0; j <= Nx - 1; j++) { int1[j] = w[j] * HestonProb(x[j], param, settings, T, trap, 1); int2[j] = w[j] * HestonProb(x[j], param, settings, T, trap, 2); } // Define P1 and P2 double pi = Math.PI; double P1 = 0.5 + 1.0 / pi * int1.Sum(); double P2 = 0.5 + 1.0 / pi * int2.Sum(); // The call price double S = settings.S; double K = settings.K; double r = settings.r; double q = settings.q; double HestonC = S * Math.Exp(-q * T) * P1 - K * Math.Exp(-r * T) * P2; // The put price by put-call parity double HestonP = HestonC - S * Math.Exp(-q * T) + K * Math.Exp(-r * T); // Output the option price if (PutCall == "C") { return(HestonC); } else { return(HestonP); } }
// The early exercise price using the composite trapezoidal rule public double CZEarlyExercise(double S0, double tau, HParam param, double K, double rf, double q, double[] xs, double[] ws, double[] xt, double[] wt, int Nt, double b0, double b1, double a, double b, double c, double d, string DoubleType) { DoubleIntegral DI = new DoubleIntegral(); double Int1 = 0.0, Int2 = 0.0; if (DoubleType == "GLe") { Int1 = DI.DoubleGaussLegendre(S0, tau, param, K, rf, q, b0, b1, xt, wt, xt, wt, a, b, c, d, 1); Int2 = DI.DoubleGaussLegendre(S0, tau, param, K, rf, q, b0, b1, xt, wt, xt, wt, a, b, c, d, 2); } else if (DoubleType == "Trapz") { double ht = (b - a) / Nt; double hs = (d - c) / Nt; double[] T = new double[Nt + 1]; double[] X = new double[Nt + 1]; for (int j = 0; j <= Nt; j++) { T[j] = a + j * ht; X[j] = c + j * hs; } Int1 = DI.DoubleTrapezoidal(param, S0, K, tau, rf, q, b0, b1, X, T, 1); Int2 = DI.DoubleTrapezoidal(param, S0, K, tau, rf, q, b0, b1, X, T, 2); } else { return(0.0); } // The early exercise premium double pi = Math.PI; double V1 = S0 * (1 - Math.Exp(-q * tau)) / 2 + (1 / pi) * S0 * q * Math.Exp(-q * tau) * Int1; double V2 = K * (1 - Math.Exp(-rf * tau)) / 2 + (1 / pi) * K * rf * Math.Exp(-rf * tau) * Int2; return(V1 - V2); }
// Mikhailov-Nogel Price by Gauss-Laguerre Integration public double MNPriceGaussLaguerre(HParam param, double[,] param0, double tau, double[] tau0, OpSet settings, double[] x, double[] w) { double[] int1 = new Double[32]; double[] int2 = new Double[32]; // Numerical integration for (int j = 0; j <= 31; j++) { int1[j] = w[j] * HestonProbTD(x[j], param, param0, tau, tau0, settings, 1); int2[j] = w[j] * HestonProbTD(x[j], param, param0, tau, tau0, settings, 2); } // Define P1 and P2 double pi = Math.PI; double P1 = 0.5 + 1.0 / pi * int1.Sum(); double P2 = 0.5 + 1.0 / pi * int2.Sum(); // The call price double S = settings.S; double K = settings.K; double r = settings.r; double q = settings.q; string PutCall = settings.PutCall; double HestonC = S * Math.Exp(-q * tau) * P1 - K * Math.Exp(-r * tau) * P2; // The put price by put-call parity double HestonP = HestonC - S * Math.Exp(-q * tau) + K * Math.Exp(-r * tau); // Output the option price if (PutCall == "C") { return(HestonC); } else { return(HestonP); } }
// CIR moments public double[] CIRmoments(HParam param, double Vs, double dt) { double kappa = param.kappa; double theta = param.theta; double sigma = param.sigma; double v0 = param.v0; double rho = param.rho; // E[vt | vs]; double e = theta + (Vs - theta) * Math.Exp(-kappa * dt); // Var[vt | vs] double v = Vs * sigma * sigma * Math.Exp(-kappa * dt) / kappa * (1.0 - Math.Exp(-kappa * dt)) + theta * sigma * sigma / 2.0 / kappa * Math.Pow((1.0 - Math.Exp(-kappa * dt)), 2.0); // E[vt^2 | vs] double e2 = v + e * e; double[] output = new double[2]; output[0] = e; output[1] = e2; return(output); }