// Longstaff-Schwartz for American puts and calls // Also returns Euro price public double[] HestonLSM(double[,] S, double K, double r, double q, double T, int NT, int NS, string PutCall) { Regression RE = new Regression(); // Time increment double dt = T / Convert.ToDouble(NT); // Initialize the Cash Flows. double[,] CF = new double[NS, NT]; // Set the last cash flows to the intrinsic value. for (int s = 0; s <= NS - 1; s++) { if (PutCall == "P") { CF[s, NT - 1] = Math.Max(K - S[s, NT - 1], 0.0); } else if (PutCall == "C") { CF[s, NT - 1] = Math.Max(S[s, NT - 1] - K, 0.0); } } // European price double EuroPrice = 0.0; for (int s = 0; s <= NS - 1; s++) { EuroPrice += Math.Exp(-r * T) * CF[s, NT - 1] / Convert.ToDouble(NS); } // Work backwards through the stock prices until time t=2. // We could work through to time t=1 but the regression will not be // of full rank at time 1, so this is cleaner. for (int t = NT - 2; t >= 1; t--) { // Indices for stock paths in-the-money at time t int[] I = new int[NS]; for (int s = 0; s <= NS - 1; s++) { I[s] = 0; if (((PutCall == "P") & (S[s, t] < K)) | ((PutCall == "C") & (S[s, t] > K))) { I[s] = 1; } } // Stock paths in-the-money at time t int NI = 0; List <double> X = new List <double>(); List <int> Xi = new List <int>(); for (int s = 0; s <= NS - 1; s++) { if (I[s] == 1) { X.Add(S[s, t]); Xi.Add(s); NI += 1; } } // Cash flows at time t+1, discounted one period double[] Y = new double[NI]; for (int s = 0; s <= NI - 1; s++) { Y[s] = CF[Xi[s], t + 1] * Math.Exp(-r * dt); } // Design matrix for regression to predict cash flows double[,] Z = new double[NI, 3]; for (int s = 0; s <= NI - 1; s++) { Z[s, 0] = 1.0; Z[s, 1] = (1.0 - X[s]); Z[s, 2] = (2.0 - 4.0 * X[s] - X[s] * X[s]) / 2.0; } // Regression parameters and predicted cash flows double[] beta = RE.Beta(Z, Y); double[] PredCF = RE.MVMult(Z, beta); // Indices for stock paths where immediate exercise is optimal // J[s] contains the path number // E[s] contains the List <int> E = new List <int>(); List <int> J = new List <int>(); int NE = 0; for (int s = 0; s <= NI - 1; s++) { if ((PutCall == "P" & (K - X[s] > 0 & K - X[s] > PredCF[s])) | (PutCall == "C" & (X[s] - K > 0 & X[s] - K > PredCF[s]))) { J.Add(s); E.Add(Xi[s]); NE += 1; } } // All other paths --> Continuation is optimal int[] All = new int[NS]; for (int k = 0; k <= NS - 1; k++) { All[k] = k; } // C contains indices for continuation paths IEnumerable <int> C = All.Except(E); int[] Co = C.ToArray(); int NC = Co.Length; // Replace cash flows with exercise value where exercise is optimal for (int s = 0; s <= NE - 1; s++) { if (PutCall == "P") { CF[E[s], t] = Math.Max(K - X[J[s]], 0.0); } else if (PutCall == "C") { CF[E[s], t] = Math.Max(X[J[s]] - K, 0.0); } } for (int s = 0; s <= NC - 1; s++) { CF[Co[s], t] = Math.Exp(-r * dt) * CF[Co[s], t + 1]; } } // Calculate the cash flow at time 2 double[] Op = new double[NS]; for (int s = 0; s <= NS - 1; s++) { Op[s] = CF[s, 1]; } // Calculate the American price double AmerPrice = Math.Exp(-r * dt) * RE.VMean(Op); // Return the European and American prices double[] output = new double[2] { EuroPrice, AmerPrice }; return(output); }
public DHSim DHTransVolSim(string scheme, DHParam param, double S0, double Strike, double Mat, double r, double q, int T, int N, string PutCall) { // Heston parameters double kappa1 = param.kappa1; double theta1 = param.theta1; double sigma1 = param.sigma1; double v01 = param.v01; double rho1 = param.rho1; double kappa2 = param.kappa2; double theta2 = param.theta2; double sigma2 = param.sigma2; double v02 = param.v02; double rho2 = param.rho2; // Time increment double dt = Mat / T; // Required quantities double K01 = -rho1 * kappa1 * theta1 * dt / sigma1; double K11 = dt / 2.0 * (kappa1 * rho1 / sigma1 - 0.5) - rho1 / sigma1; double K21 = dt / 2.0 * (kappa1 * rho1 / sigma1 - 0.5) + rho1 / sigma1; double K31 = dt / 2.0 * (1.0 - rho1 * rho1); double K02 = -rho2 * kappa2 * theta2 * dt / sigma2; double K12 = dt / 2.0 * (kappa2 * rho2 / sigma2 - 0.5) - rho2 / sigma2; double K22 = dt / 2.0 * (kappa2 * rho2 / sigma2 - 0.5) + rho2 / sigma2; double K32 = dt / 2.0 * (1.0 - rho2 * rho2); // Initialize the volatility and stock processes double[,] w1 = new double[T, N]; double[,] w2 = new double[T, N]; double[,] S = new double[T, N]; // Starting values for the variance and stock processes for (int k = 0; k <= N - 1; k++) { S[0, k] = S0; // Spot price w1[0, k] = Math.Sqrt(v01); // Heston initial volatility w2[0, k] = Math.Sqrt(v02); } // Generate the stock and volatility paths RandomNumber RN = new RandomNumber(); double Zv1, m11, m12, beta1, thetav1; double Zv2, m21, m22, beta2, thetav2; double B1, B2, logS; for (int k = 0; k <= N - 1; k++) { for (int t = 1; t <= T - 1; t++) { Zv1 = RN.RandomNorm(); Zv2 = RN.RandomNorm(); if (scheme == "ZhuEuler") { // Transformed variance scheme w1[t, k] = w1[t - 1, k] + 0.5 * kappa1 * ((theta1 - sigma1 * sigma1 / 4.0 / kappa1) / w1[t - 1, k] - w1[t - 1, k]) * dt + 0.5 * sigma1 * Math.Sqrt(dt) * Zv1; w2[t, k] = w2[t - 1, k] + 0.5 * kappa2 * ((theta2 - sigma2 * sigma2 / 4.0 / kappa2) / w2[t - 1, k] - w2[t - 1, k]) * dt + 0.5 * sigma2 * Math.Sqrt(dt) * Zv2; } else if (scheme == "ZhuTV") { // Zhu (2010) process for the transformed volatility m11 = theta1 + (w1[t - 1, k] * w1[t - 1, k] - theta1) * Math.Exp(-kappa1 * dt); m21 = theta2 + (w2[t - 1, k] * w2[t - 1, k] - theta2) * Math.Exp(-kappa2 * dt); m12 = sigma1 * sigma1 / 4.0 / kappa1 * (1.0 - Math.Exp(-kappa1 * dt)); m22 = sigma2 * sigma2 / 4.0 / kappa2 * (1.0 - Math.Exp(-kappa2 * dt)); beta1 = Math.Sqrt(Math.Max(0, m11 - m12)); beta2 = Math.Sqrt(Math.Max(0, m21 - m22)); thetav1 = (beta1 - w1[t - 1, k] * Math.Exp(-kappa1 * dt / 2.0)) / (1.0 - Math.Exp(-kappa1 * dt / 2.0)); thetav2 = (beta2 - w2[t - 1, k] * Math.Exp(-kappa2 * dt / 2.0)) / (1.0 - Math.Exp(-kappa2 * dt / 2.0)); w1[t, k] = w1[t - 1, k] + 0.5 * kappa1 * (thetav1 - w1[t - 1, k]) * dt + 0.5 * sigma1 * Math.Sqrt(dt) * Zv1; w2[t, k] = w2[t - 1, k] + 0.5 * kappa2 * (thetav2 - w2[t - 1, k]) * dt + 0.5 * sigma2 * Math.Sqrt(dt) * Zv2; } // Predictor-Corrector for the stock price B1 = RN.RandomNorm(); B2 = RN.RandomNorm(); logS = Math.Log(Math.Exp(-r * Convert.ToDouble(t) * dt) * S[t - 1, k]) + K01 + K11 * w1[t - 1, k] * w1[t - 1, k] + K21 * w1[t, k] * w1[t, k] + Math.Sqrt(K31 * (w1[t, k] * w1[t, k] + w1[t - 1, k] * w1[t - 1, k])) * B1 + K02 + K12 * w2[t - 1, k] * w2[t - 1, k] + K22 * w2[t, k] * w2[t, k] + Math.Sqrt(K32 * (w2[t, k] * w2[t, k] + w2[t - 1, k] * w2[t - 1, k])) * B2; S[t, k] = Math.Exp(logS) * Math.Exp(r * Convert.ToDouble(t + 1) * dt); } } // Terminal stock prices double[] ST = new double[N]; for (int k = 0; k <= N - 1; k++) { ST[k] = S[T - 1, k]; } // Payoff vectors double[] Payoff = new double[N]; for (int k = 0; k <= N - 1; k++) { if (PutCall == "C") { Payoff[k] = Math.Max(ST[k] - Strike, 0.0); } else if (PutCall == "P") { Payoff[k] = Math.Max(Strike - ST[k], 0.0); } } // Simulated price Regression RE = new Regression(); double SimPrice = Math.Exp(-r * Mat) * RE.VMean(Payoff); // Output the results DHSim output = new DHSim(); output.S = S; output.EuroPrice = SimPrice; return(output); }