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);
        }
示例#2
0
        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("---------------------------------------");
        }