Example #1
0
        static void Main(string[] args)
        {
            // Classes
            MiscFunctions     MF = new MiscFunctions();
            DiffEvoAlgo       DE = new DiffEvoAlgo();
            NelderMeadAlgo    NM = new NelderMeadAlgo();
            ObjectiveFunction OF = new ObjectiveFunction();

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

            // Option settings
            OPSet opset;

            opset.S    = 137.14;
            opset.r    = 0.0010;
            opset.q    = 0.0068;
            opset.trap = 1;

            // Bisection algorithm settings
            double a            = 0.01;
            double b            = 3.0;
            double Tol          = 1e-5;
            int    MaxIter      = 1000;
            int    ObjectiveFun = 1;

            // Read in SP500 implied volatilities
            int NT = 4;
            int NK = 7;

            double[,] MktIV = new Double[7, 4]
            {
                { 0.2780, 0.2638, 0.2532, 0.2518 },
                { 0.2477, 0.2402, 0.2364, 0.2369 },
                { 0.2186, 0.2158, 0.2203, 0.2239 },
                { 0.1878, 0.1930, 0.2047, 0.2098 },
                { 0.1572, 0.1712, 0.1894, 0.1970 },
                { 0.1334, 0.1517, 0.1748, 0.1849 },
                { 0.1323, 0.1373, 0.1618, 0.1736 }
            };
            double[] K = new Double[7] {
                120.0, 125.0, 130.0, 135.0, 140.0, 145.0, 150.0
            };
            double[] T = new Double[4] {
                0.123287671232877, 0.268493150684932, 0.715068493150685, 0.953424657534247
            };

            // PutCall identifiers
            string[,] PutCall = new String[NK, NT];
            for (int k = 0; k <= NK - 1; k++)
            {
                for (int t = 0; t <= NT - 1; t++)
                {
                    PutCall[k, t] = "C";
                }
            }

            // Obtain the market prices
            BlackScholesPrice BS = new BlackScholesPrice();

            double[,] MktPrice = new Double[NK, NT];
            for (int k = 0; k <= NK - 1; k++)
            {
                for (int t = 0; t <= NT - 1; t++)
                {
                    MktPrice[k, t] = BS.BlackScholes(opset.S, K[k], T[t], opset.r, opset.q, MktIV[k, t], PutCall[k, t]);
                }
            }

            // Place the market data in the structure
            MktData data = new MktData();

            data.MktIV    = MktIV;
            data.MktPrice = MktPrice;
            data.K        = K;
            data.T        = T;
            data.PutCall  = PutCall;

            // Estimation bounds
            double e = 1e-5;
            double kappaL = e; double kappaU = 10;
            double thetaL = e; double thetaU = 5;
            double sigmaL = e; double sigmaU = 5;
            double v0L = e; double v0U = 1;
            double rhoL = -.9; double rhoU = 0;

            double[] ub = new double[5] {
                kappaU, thetaU, sigmaU, v0U, rhoU
            };
            double[] lb = new double[5] {
                kappaL, thetaL, sigmaL, v0L, rhoL
            };

            // Objective function settings;
            OFSet ofset;

            ofset.opset        = opset;
            ofset.data         = data;
            ofset.X            = X;
            ofset.W            = W;
            ofset.LossFunction = 4;           // Choice of loss function 1=MSE, 2=RMSE, 3=IVMSE, 4=Christoffersen et al.
            ofset.lb           = lb;
            ofset.ub           = ub;
            ofset.CF           = "Heston"; // Choice of c.f. "Heston" or "Attari"

            // Settings for the Differential Evolution algorithm
            DEParam ParamLim = new DEParam();

            ParamLim.ub = ub;
            ParamLim.lb = lb;
            ParamLim.NG = 500;
            ParamLim.NP = 75;
            ParamLim.F  = 0.5;
            ParamLim.CR = 0.8;

            // Run the differential evolution algorithm
            HParam DEparam = DE.HestonDE(ParamLim, opset, data, ObjectiveFun, a, b, Tol, MaxIter, X, W, ofset.CF);

            // Starting values (vertices) in vector form.  Add random increment about each starting value
            double kappaS = 9;
            double thetaS = 0.05;
            double sigmaS = 0.3;
            double v0S    = 0.05;
            double rhoS   = -0.8;
            int    N      = 5;

            double[,] s = new double[N, N + 1];
            for (int j = 0; j <= N; j++)
            {
                s[0, j] = kappaS + MF.RandomNum(-0.10, 0.10);
                s[1, j] = thetaS + MF.RandomNum(-0.01, 0.01);
                s[2, j] = sigmaS + MF.RandomNum(-0.05, 0.05);
                s[3, j] = v0S + MF.RandomNum(-0.01, 0.01);
                s[4, j] = rhoS + MF.RandomNum(-0.05, 0.05);
            }

            // Nelder Mead settings
            NMSet nmset;

            nmset.ofset     = ofset;
            nmset.N         = 5;                // Number of Heston parameters
            nmset.MaxIters  = 1000;             // Maximum number of iterations
            nmset.Tolerance = 1e-4;             // Tolerance on best and worst function values

            // Run the Nelder Mead algorithm
            double[] B       = NM.NelderMead(OF.f, nmset, s);
            HParam   NMparam = new HParam();

            NMparam.kappa = B[0];
            NMparam.theta = B[1];
            NMparam.sigma = B[2];
            NMparam.v0    = B[3];
            NMparam.rho   = B[4];

            // Calculate IVMSE under both parameter estimates
            double[,] NMPrice = new double[NK, NT];
            double[,] DEPrice = new double[NK, NT];
            double[,] NMIV    = new double[NK, NT];
            double[,] DEIV    = new double[NK, NT];
            double NMIVMSE = 0.0;
            double DEIVMSE = 0.0;
            double S       = opset.S;
            double r       = opset.r;
            double q       = opset.q;
            int    trap    = opset.trap;

            HestonPrice HP = new HestonPrice();
            Bisection   BA = new Bisection();

            for (int t = 0; t < NT; t++)
            {
                for (int k = 0; k < NK; k++)
                {
                    NMPrice[k, t] = HP.HestonPriceGaussLaguerre(NMparam, S, K[k], r, q, T[t], trap, PutCall[k, t], X, W);
                    NMIV[k, t]    = BA.BisecBSIV(PutCall[k, t], S, K[k], r, q, T[t], a, b, NMPrice[k, t], Tol, MaxIter);
                    NMIVMSE      += Math.Pow(MktIV[k, t] - NMIV[k, t], 2) / Convert.ToDouble(NT * NK);
                    DEPrice[k, t] = HP.HestonPriceGaussLaguerre(DEparam, S, K[k], r, q, T[t], trap, PutCall[k, t], X, W);
                    DEIV[k, t]    = BA.BisecBSIV(PutCall[k, t], S, K[k], r, q, T[t], a, b, DEPrice[k, t], Tol, MaxIter);
                    DEIVMSE      += Math.Pow(MktIV[k, t] - DEIV[k, t], 2) / Convert.ToDouble(NT * NK);
                }
            }

            // Output the result
            Console.WriteLine("  ");
            Console.WriteLine("Parameter Estimates --------------------");
            Console.WriteLine("                 Differential Evolution     Nelder Mead");
            Console.WriteLine("kappa            {0,10:F4} {1,25:F4}", DEparam.kappa, NMparam.kappa);
            Console.WriteLine("theta            {0,10:F4} {1,25:F4}", DEparam.theta, NMparam.theta);
            Console.WriteLine("sigma            {0,10:F4} {1,25:F4}", DEparam.sigma, NMparam.sigma);
            Console.WriteLine("v0               {0,10:F4} {1,25:F4}", DEparam.v0, NMparam.v0);
            Console.WriteLine("rho              {0,10:F4} {1,25:F4}", DEparam.rho, NMparam.rho);
            Console.WriteLine("  ");
            Console.WriteLine("IV MSE ---------------------------------");
            Console.WriteLine("Differential Evolution IVMSE {0:E8}", DEIVMSE);
            Console.WriteLine("Nelder Mead IVMSE            {0:E8}", NMIVMSE);
            Console.WriteLine("  ");
        }
        // Differential evolution algorithm
        public HParam HestonDE(DEParam DEsettings, OPSet settings, MktData data, int LossFunction, double a, double b, double Tol, int MaxIter, double[] X, double[] W, string CF)
        {
            // NG = Number of generations (iterations)
            // NP = Number of population members
            // CR = Crossover ratio (=0.5)
            // F  = Threshold (=0.8)
            // Hi = Vector of upper bounds for the parameters
            // Lo = Vector of lower bounds for the parameters
            // S  = Spot Price
            // K1 = First strike point for the FRFT
            // rf = Risk free rate
            // q  = Dividend Yield
            // MktPrice = Market quotes for prices
            // K = Vector of Strikes
            // T = Vector of Maturities
            // PutCall = Matrix of 'P'ut or 'C'all
            // MktIV = Market quotes for implied volatilities
            // LossFunction = Type of Objective Function 1 = MSE; 2 = RMSE; 3 = IVMSE; 4 = Christoffersen, Jacobs, Heston (2009)
            // Bisection method settings; a = Lower limit; b = upper limit; Tol = Tolerance; MaxIter = Max number of iterations
            // trap = 1 is "Little Trap" c.f., 0 is Heston c.f.
            // X, W = Gauss Laguerre abscissas and weights
            // CF = "Heston" or "Attari" characteristic function

            ObjectiveFunction OF = new ObjectiveFunction();
            MiscFunctions     MF = new MiscFunctions();

            double[] Hi = DEsettings.ub;
            double[] Lo = DEsettings.lb;
            double   CR = DEsettings.CR;
            double   kappaU = Hi[0]; double kappaL = Lo[0];
            double   thetaU = Hi[1]; double thetaL = Lo[1];
            double   sigmaU = Hi[2]; double sigmaL = Lo[2];
            double   v0U = Hi[3]; double v0L = Lo[3];
            double   rhoU = Hi[4]; double rhoL = Lo[4];

            // Create the structure for the objective function;
            OFSet ofset;

            ofset.opset        = settings;
            ofset.data         = data;
            ofset.X            = X;
            ofset.W            = W;
            ofset.LossFunction = LossFunction;
            ofset.lb           = DEsettings.lb;
            ofset.ub           = DEsettings.ub;
            ofset.CF           = CF;

            // Step1.  Generate the population matrix of random parameters
            int    NP = DEsettings.NP;
            int    NG = DEsettings.NG;
            double F  = DEsettings.F;

            double[,] P = new double[5, NP];
            for (int j = 0; j <= NP - 1; j++)
            {
                P[0, j] = kappaL + (kappaU - kappaL) * MF.RandomNum(0.0, 1.0);
                P[1, j] = thetaL + (thetaU - thetaL) * MF.RandomNum(0.0, 1.0);
                P[2, j] = sigmaL + (sigmaU - sigmaL) * MF.RandomNum(0.0, 1.0);
                P[3, j] = v0L + (v0U - v0L) * MF.RandomNum(0.0, 1.0);
                P[4, j] = rhoL + (rhoU - rhoL) * MF.RandomNum(0.0, 1.0);
            }

            // Generate the random numbers outside the loop
            double[, ,] U = new double[5, NP, NG];
            for (int k = 0; k <= NG - 1; k++)
            {
                for (int j = 0; j <= NP - 1; j++)
                {
                    for (int i = 0; i <= 4; i++)
                    {
                        U[i, j, k] = MF.RandomNum(0.0, 1.0);
                    }
                }
            }

            // Initialize the variables
            double[] Pr1 = new double[5];
            double[] Pr2 = new double[5];
            double[] Pr3 = new double[5];
            double[] P0  = new double[5];

            // Loop through the generations
            for (int k = 0; k <= NG - 1; k++)
            {
                Console.Write("Differential Evolution iteration "); Console.WriteLine(k);

                // Loop through the population
                for (int i = 0; i <= NP - 1; i++)
                {
                    // Select the i-th member of the population
                    for (int s = 0; s <= 4; s++)
                    {
                        P0[s] = P[s, i];
                    }

Step0:
                    // Select random indices for three other distinct members
                    int[] Integers0toNP1 = new int[NP];
                    for (int s = 0; s <= NP - 1; s++)
                    {
                        Integers0toNP1[s] = s;
                    }


                    // Random perumation of the indices (0,1,...,NP-1)
                    int[] I = MF.RandomPerm(Integers0toNP1);

                    // Find the index in I that is not equal to i and keep the first 3 positions
                    int[] L = MF.RemoveIndex(I, i);
                    int[] r = new int[3];
                    for (int s = 0; s <= 2; s++)
                    {
                        r[s] = L[s];
                    }

                    // The three distinct members of the population
                    for (int s = 0; s <= 4; s++)
                    {
                        Pr1[s] = P[s, r[0]];
                        Pr2[s] = P[s, r[1]];
                        Pr3[s] = P[s, r[2]];
                    }
                    int[]    Integers1to5 = { 1, 2, 3, 4, 5 };
                    int[]    R            = MF.RandomPerm(Integers1to5);
                    double[] Pnew         = { 0.0, 0.0, 0.0, 0.0, 0.0 };

                    // Steps 2 and 3.  Mutation and recombination
                    double Ri;
                    double u;
                    for (int j = 0; j <= 4; j++)
                    {
                        Ri = R[0];
                        u  = U[j, i, k];
                        if ((u <= CR) | (j == Ri))
                        {
                            Pnew[j] = Pr1[j] + F * (Pr2[j] - Pr3[j]);
                        }
                        else
                        {
                            Pnew[j] = P0[j];
                        }
                    }
                    // Repeat above to code to ensure new members fall within the
                    // range of acceptable parameter values

                    int[] Flag = new int[5] {
                        0, 0, 0, 0, 0
                    };
                    for (int s = 0; s <= 4; s++)
                    {
                        if (Pnew[s] <= Lo[s] | Pnew[s] >= Hi[s])
                        {
                            Flag[s] = 1;
                        }
                    }
                    int Condition = Flag.Sum();
                    if (Condition > 0)
                    {
                        goto Step0;
                    }

                    // Step 4.  Selection
                    // Calculate the objective function for the ith member and for the candidate
                    double f0   = OF.f(P0, ofset);
                    double fnew = OF.f(Pnew, ofset);

                    // Verify whether the candidate should replace the i-th member
                    // in the population and replace if conditions are satisfied
                    if (fnew < f0)
                    {
                        for (int s = 0; s <= 4; s++)
                        {
                            P[s, i] = Pnew[s];
                        }
                    }
                }
            }

            // Calculate the objective function for each member in the updated population
            double[] fs      = new double[NP];
            double[] Pmember = new double[5];
            for (int s = 0; s <= NP - 1; s++)
            {
                for (int t = 0; t <= 4; t++)
                {
                    Pmember[t] = P[t, s];
                }
                fs[s] = OF.f(Pmember, ofset);
            }

            // Find the member with the lowest objective function
            double Minf   = fs[0];
            int    Minpos = 0;

            for (int s = 0; s <= NP - 1; s++)
            {
                if (fs[s] < Minf)
                {
                    Minf   = fs[s];
                    Minpos = s;
                }
            }

            // Return the selected member/parameter
            HParam Pfinal = new HParam();

            Pfinal.kappa = P[0, Minpos];
            Pfinal.theta = P[1, Minpos];
            Pfinal.sigma = P[2, Minpos];
            Pfinal.v0    = P[3, Minpos];
            Pfinal.rho   = P[4, Minpos];

            return(Pfinal);
        }