public DHSim DHQuadExpSim(DHParam param, double S0, double Strike, double Mat, double r, double q, int T, int N, string PutCall)
        {
            RandomNumber RN = new RandomNumber();
            NormInverse  NI = new NormInverse();
            Regression   RE = new Regression();

            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 variance and stock processes
            double[,] V1 = new double[T, N];
            double[,] V2 = 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
                V1[0, k] = v01;     // Heston v0 initial variance
                V2[0, k] = v02;     // Heston v0 initial variance
            }

            // Generate the stock and volatility paths
            double m1, s1, phi1, p1, U1, b1, a1, Zv1;
            double m2, s2, phi2, p2, U2, b2, a2, Zv2;
            double beta1, beta2, B1, B2, logS;

            for (int k = 0; k <= N - 1; k++)
            {
                for (int t = 1; t <= T - 1; t++)
                {
                    m1 = theta1 + (V1[t - 1, k] - theta1) * Math.Exp(-kappa1 * dt);
                    s1 = V1[t - 1, k] * sigma1 *sigma1 *Math.Exp(-kappa1 *dt) / kappa1 * (1.0 - Math.Exp(-kappa1 * dt))
                         + theta1 * sigma1 * sigma1 / 2.0 / kappa1 * Math.Pow(1.0 - Math.Exp(-kappa1 * dt), 2.0);

                    phi1 = s1 / (m1 * m1);
                    p1   = (phi1 - 1.0) / (phi1 + 1.0);
                    U1   = RN.RandomNum(0.0, 1.0);
                    if (phi1 < 0.5)
                    {
                        b1       = Math.Sqrt(2.0 / phi1 - 1.0 + Math.Sqrt(2.0 / phi1 * (2.0 / phi1 - 1.0)));
                        a1       = m1 / (1.0 + b1 * b1);
                        Zv1      = NI.normICDF(U1);
                        V1[t, k] = a1 * (b1 + Zv1) * (b1 + Zv1);
                    }
                    else if (phi1 >= 0.5)
                    {
                        if (U1 <= p1)
                        {
                            V1[t, k] = 0.0;
                        }
                        else if (U1 > p1)
                        {
                            beta1    = (1.0 - p1) / m1;
                            V1[t, k] = Math.Log((1.0 - p1) / (1 - U1)) / beta1;
                        }
                    }
                    m2 = theta2 + (V2[t - 1, k] - theta2) * Math.Exp(-kappa2 * dt);
                    s2 = V2[t - 1, k] * sigma2 *sigma2 *Math.Exp(-kappa2 *dt) / kappa2 * (1.0 - Math.Exp(-kappa2 * dt))
                         + theta2 * sigma2 * sigma2 / 2.0 / kappa2 * Math.Pow(1.0 - Math.Exp(-kappa2 * dt), 2.0);

                    phi2 = s2 / (m2 * m2);
                    p2   = (phi2 - 1.0) / (phi2 + 1.0);
                    U2   = RN.RandomNum(0.0, 1.0);
                    if (phi2 < 0.5)
                    {
                        b2       = Math.Sqrt(2.0 / phi2 - 1.0 + Math.Sqrt(2.0 / phi2 * (2.0 / phi2 - 1.0)));
                        a2       = m2 / (1.0 + b2 * b2);
                        Zv2      = NI.normICDF(U2);
                        V2[t, k] = a2 * (b2 + Zv2) * (b2 + Zv2);
                    }
                    else if (phi2 >= 0.5)
                    {
                        if (U2 <= p2)
                        {
                            V2[t, k] = 0.0;
                        }
                        else if (U2 > p2)
                        {
                            beta2    = (1.0 - p2) / m2;
                            V2[t, k] = Math.Log((1.0 - p2) / (1.0 - U2)) / beta2;
                        }
                    }
                    // 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 * V1[t - 1, k] + K21 * V1[t, k] + Math.Sqrt(K31 * (V1[t, k] + V1[t - 1, k])) * B1
                           + K02 + K12 * V2[t - 1, k] + K22 * V2[t, k] + Math.Sqrt(K32 * (V2[t, k] + V2[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
            double SimPrice = Math.Exp(-r * Mat) * RE.VMean(Payoff);

            // Output the results
            DHSim output = new DHSim();

            output.S         = S;
            output.EuroPrice = SimPrice;
            return(output);
        }
Beispiel #2
0
        // 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 DHEulerAlfonsiSim(string scheme, DHParam param, double S0, double Strike, double Mat, double r, double q, int T, int N, string PutCall)
        {
            RandomNumber RN = new RandomNumber();
            Regression   RE = new Regression();

            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;

            HParam param1;

            param1.kappa = kappa1;
            param1.theta = theta1;
            param1.sigma = sigma1;
            param1.v0    = v01;
            param1.rho   = rho1;

            HParam param2;

            param2.kappa = kappa2;
            param2.theta = theta2;
            param2.sigma = sigma2;
            param2.v0    = v02;
            param2.rho   = 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 variance and stock processes
            double[,] V1 = new double[T, N];
            double[,] V2 = 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
                V1[0, k] = v01;     // Heston v0 initial variance
                V2[0, k] = v02;     // Heston v0 initial variance
            }
            double G1, G2, B1, B2, logS;

            // Generate the stock and volatility paths
            for (int k = 0; k <= N - 1; k++)
            {
                for (int t = 1; t <= T - 1; t++)
                {
                    if (scheme == "Euler")
                    {
                        // Generate two in dependent N(0,1) variables
                        G1 = RN.RandomNorm();
                        G2 = RN.RandomNorm();
                        // Euler discretization with full truncation for the variances
                        V1[t, k] = V1[t - 1, k] + kappa1 * (theta1 - V1[t - 1, k]) * dt + sigma1 * Math.Sqrt(V1[t - 1, k] * dt) * G1;
                        V2[t, k] = V2[t - 1, k] + kappa2 * (theta2 - V2[t - 1, k]) * dt + sigma2 * Math.Sqrt(V2[t - 1, k] * dt) * G2;
                        V1[t, k] = Math.Max(0, V1[t, k]);
                        V2[t, k] = Math.Max(0, V2[t, k]);
                    }
                    else if (scheme == "Alfonsi")
                    {
                        // Alfonsi discretization
                        V1[t, k] = AlfonsiV(param1, V1[t - 1, k], dt);
                        V2[t, k] = AlfonsiV(param2, V2[t - 1, k], dt);
                    }
                    // 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 * V1[t - 1, k] + K21 * V1[t, k] + Math.Sqrt(K31 * (V1[t, k] + V1[t - 1, k])) * B1
                           + K02 + K12 * V2[t - 1, k] + K22 * V2[t, k] + Math.Sqrt(K32 * (V2[t, k] + V2[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
            double SimPrice = Math.Exp(-r * Mat) * RE.VMean(Payoff);

            // Output the results
            DHSim output = new DHSim();

            output.S         = S;
            output.EuroPrice = SimPrice;

            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);
        }