Ejemplo n.º 1
0
        static void Main(string[] args)
        {
            // Classes
            BisectionAlgo     BA = new BisectionAlgo();
            BlackScholesPrice BS = new BlackScholesPrice();
            HestonPrice       HP = new HestonPrice();
            ElicesAlgo        EA = new ElicesAlgo();
            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]);
                }
            }

            // Bounds on the parameter estimates
            // kappa theta sigma v0 rho
            double e = 1.0e-2;

            double[] lb = new double[5] {
                e, e, e, -0.99, e
            };
            double[] ub = new double[5] {
                10.0, 3.0, 10.0, 0.99, 3.0
            };

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

            double[][] MktIV = new double[NT][];
            MktIV[0] = new double[NK] {
                0.1962, 0.1910, 0.1860, 0.1810, 0.1761, 0.1718, 0.1671, 0.1644, 0.1645, 0.1661, 0.1701, 0.1755, 0.1796
            };
            MktIV[1] = new double[NK] {
                0.1947, 0.1905, 0.1861, 0.1812, 0.1774, 0.1743, 0.1706, 0.1671, 0.1641, 0.1625, 0.1602, 0.1610, 0.1657
            };
            MktIV[2] = new double[NK] {
                0.2019, 0.1980, 0.1943, 0.1907, 0.1871, 0.1842, 0.1813, 0.1783, 0.1760, 0.1743, 0.1726, 0.1716, 0.1724
            };
            MktIV[3] = new double[NK] {
                0.2115, 0.2082, 0.2057, 0.2021, 0.2000, 0.1974, 0.1950, 0.1927, 0.1899, 0.1884, 0.1862, 0.1846, 0.1842
            };
            double[] K = new double[NK] {
                124.0, 125.0, 126.0, 127.0, 128.0, 129.0, 130.0, 131.0, 132.0, 133.0, 134.0, 135.0, 136.0,
            };
            double[] T = new double[NT] {
                37.0 / 365.0, 72.0 / 365.0, 135.0 / 365.0, 226.0 / 365.0
            };

            // PutCall identifier
            string PutCall = "P";

            // Maturity increments
            double[] tau = new double[NT];
            tau[0] = T[0];
            for (int t = 1; t <= NT - 1; t++)
            {
                tau[t] = T[t] - T[t - 1];
            }

            // Option settings
            OPSet opsettings;

            opsettings.S    = 129.14;
            opsettings.r    = 0.0010;
            opsettings.q    = 0.0068;
            opsettings.trap = 1;

            // Obtain the market prices for Elices, jagged array, and Heston (two dimensional array)
            double[][] MktPrice = new double[4][];
            double[,] MktPriceH = new double[NT, NK];
            double[,] MktIVH    = new double[NT, NK];
            for (int t = 0; t <= NT - 1; t++)
            {
                MktPrice[t] = new double[13];
                for (int k = 0; k <= NK - 1; k++)
                {
                    MktPrice[t][k]  = BS.BlackScholes(opsettings.S, K[k], T[t], opsettings.r, opsettings.q, MktIV[t][k], PutCall);
                    MktPriceH[t, k] = MktPrice[t][k];
                    MktIVH[t, k]    = MktIV[t][k];
                }
            }

            // Settings for the Elices objective function
            OFSetE ofsetE = new OFSetE();

            ofsetE.opsettings   = opsettings;
            ofsetE.X            = X;
            ofsetE.W            = W;
            ofsetE.LossFunction = 1;
            ofsetE.lb           = lb;
            ofsetE.ub           = ub;

            // Elices Settings for the Market data
            MktData MktData = new MktData();

            MktData.K       = K;
            MktData.PutCall = PutCall;
            ofsetE.data     = MktData;

            // Settings for the Heston objective function
            OFSetH ofsetH = new OFSetH();

            ofsetH.opsettings   = opsettings;
            ofsetH.X            = X;
            ofsetH.W            = W;
            ofsetH.LossFunction = ofsetE.LossFunction;
            ofsetH.lb           = ofsetE.lb;
            ofsetH.ub           = ofsetE.ub;
            ofsetH.MktPrice     = MktPriceH;
            ofsetH.MktIV        = MktIVH;
            ofsetH.K            = K;
            ofsetH.T            = T;
            ofsetH.PutCall      = PutCall;

            // Parameter initial values
            double kappaS = 2.00;
            double thetaS = 0.10;
            double sigmaS = 1.20;
            double v0S    = 0.03;
            double rhoS   = -0.80;

            // Heston and Elices starting values (vertices) in vector form.  Add random increment about each starting value
            double[,] startH = new double[5, 6];
            double[,] startE = new double[4, 5];
            for (int j = 0; j <= 5; j++)
            {
                startH[0, j] = kappaS + BA.RandomNum(-0.05, 0.05) * kappaS;
                startH[1, j] = thetaS + BA.RandomNum(-0.05, 0.05) * thetaS;
                startH[2, j] = sigmaS + BA.RandomNum(-0.05, 0.05) * sigmaS;
                startH[3, j] = rhoS + BA.RandomNum(-0.05, 0.05) * rhoS;
                startH[4, j] = v0S + BA.RandomNum(-0.05, 0.05) * v0S;
            }
            for (int j = 0; j <= 4; j++)
            {
                startE[0, j] = startH[0, j];
                startE[1, j] = startH[1, j];
                startE[2, j] = startH[2, j];
                startE[3, j] = startH[3, j];
            }

            // Settings for the Nelder Mead algorithm
            NMSet nmsettings = new NMSet();

            nmsettings.MaxIters    = 50;        // Maximum number of iterations
            nmsettings.Tolerance   = 1e-20;     // Tolerance on best and worst function values
            nmsettings.ofsettingsE = ofsetE;    // Settings for the Elices objective function
            nmsettings.ofsettingsH = ofsetH;    // Settings for the Heston objective function

            // Heston Parameter Estimation all maturities ===========================================================================================
            nmsettings.choice = "Heston";
            int N = 5;

            nmsettings.N = N;
            double[] ParamH = new double[7];
            ParamH = NM.NelderMead(OF.f, nmsettings, startH);

            // Elices Parameter Estimation maturity by maturity ====================================================================================
            nmsettings.choice = "Elices";
            N            = 4;
            nmsettings.N = N;
            double v0 = ParamH[4];

            ofsetE.v0 = v0;
            double[][] ParamTD      = new double[4][];
            double[]   B            = new double[6];
            double[]   LossFunction = new double[NT];

            // Initialize maturities
            List <double> MatsList = new List <double>();

            double[] Mats;
            MatsList.Add(tau[0]);
            MatsList.Add(tau[1]);
            Mats = MatsList.ToArray();

            // Loop through the maturities, estimating parameters
            for (int t = 0; t <= NT - 1; t++)
            {
                ofsetE.t             = t;
                ofsetE.data.MktIV    = MktIV[t];            // Vector of market Implied vol
                ofsetE.data.MktPrice = MktPrice[t];         // Vector of market prices
                ofsetE.paramfixed    = ParamTD;             // Fixed parameters
                if (t >= 1)
                {
                    for (int j = 0; j <= N; j++)                       // Starting values are last period's estimates
                    {
                        startE[0, j] = B[0] + BA.RandomNum(-0.01, 0.01) * B[0];
                        startE[1, j] = B[1] + BA.RandomNum(-0.01, 0.01) * B[1];
                        startE[2, j] = B[2] + BA.RandomNum(-0.01, 0.01) * B[2];
                        startE[3, j] = B[3] + BA.RandomNum(-0.01, 0.01) * B[3];
                    }
                }
                if (t >= 2)
                {
                    MatsList.Add(tau[t]);
                    Mats = MatsList.ToArray();
                }
                ofsetE.T = Mats;
                nmsettings.ofsettingsE = ofsetE;
                B          = NM.NelderMead(OF.f, nmsettings, startE);
                ParamTD[t] = new double[4] {
                    B[0], B[1], B[2], B[3]
                };
                LossFunction[t] = B[4];
                Console.WriteLine("------ Finished maturity {0:F0} ------", ofsetE.t);
            }
            // Write the parameters
            Console.WriteLine("Heston statict parameters");
            Console.WriteLine("------------------------------------------------------------------");
            Console.WriteLine("    kappa      theta      sigma      rho         v0    LossFunction");
            Console.WriteLine("------------------------------------------------------------------");
            Console.WriteLine("{0,10:F5} {1,10:F5} {2,10:F5} {3,10:F5} {4,10:F5} {5,10:F5}",
                              ParamH[0], ParamH[1], ParamH[2], ParamH[3], ParamH[4], ParamH[5]);
            Console.WriteLine("------------------------------------------------------------------");
            Console.WriteLine(" ");
            Console.WriteLine("Elices Time dependent parameters");
            Console.WriteLine("-------------------------------------------------------------");
            Console.WriteLine("    kappa      theta      sigma      rho     LossFunction");
            Console.WriteLine("-------------------------------------------------------------");
            for (int t = 0; t <= NT - 1; t++)
            {
                Console.WriteLine("{0,10:F5} {1,10:F5} {2,10:F5} {3,10:F5} {4,10:F5}",
                                  ParamTD[t][0], ParamTD[t][1], ParamTD[t][2], ParamTD[t][3], LossFunction[t]);
            }
            Console.WriteLine("-------------------------------------------------------------");


            // Fitting Elices prices and implied vols ==========================================================================================================
            // Matrices for prices, parameters, and fixed parameters
            double[,] PriceE = new double[NK, NT];
            double[]   param      = new double[4];
            double[][] paramfixed = new double[NT - 1][];
            paramfixed[0] = ParamTD[0];

            // Define the dynamic array for the maturities and assign the first two maturities
            List <double> MatList = new List <double>();

            double[] Mat;
            MatList.Add(tau[0]);
            MatList.Add(tau[1]);
            Mat = MatList.ToArray();

            // Elices Implied volatilities
            double[,] IVE = new double[NK, NT];
            double a            = 0.01;
            double b            = 3.0;
            double tol          = 1.0e-5;
            int    MaxIter      = 1000;
            double ElicesIVRMSE = 0.0;

            // Find the prices and implied vols
            for (int t = 0; t <= NT - 1; t++)
            {
                for (int j = 0; j <= 3; j++)
                {
                    param[j] = ParamTD[t][j];
                }
                if (t == 0)
                {
                    for (int k = 0; k <= NK - 1; k++)
                    {
                        PriceE[k, 0] = EA.ElicesPrice(PutCall, opsettings.S, K[k], Mat, opsettings.r, opsettings.q, param, v0, opsettings.trap, X, W);
                    }
                }
                if (t == 1)
                {
                    for (int k = 0; k <= NK - 1; k++)
                    {
                        PriceE[k, 1] = EA.ElicesPrice(PutCall, opsettings.S, K[k], Mat, opsettings.r, opsettings.q, param, v0, opsettings.trap, X, W, paramfixed);
                    }
                }
                if (t >= 2)
                {
                    MatList.Add(tau[t]);
                    Mat = MatList.ToArray();
                    paramfixed[t - 1] = ParamTD[t - 1];
                    for (int k = 0; k <= NK - 1; k++)
                    {
                        PriceE[k, t] = EA.ElicesPrice(PutCall, opsettings.S, K[k], Mat, opsettings.r, opsettings.q, param, v0, opsettings.trap, X, W, paramfixed);
                    }
                }
                for (int k = 0; k <= NK - 1; k++)
                {
                    IVE[k, t]     = BA.BisecBSIV(PutCall, opsettings.S, K[k], opsettings.r, opsettings.q, T[t], a, b, PriceE[k, t], tol, MaxIter);
                    ElicesIVRMSE += Math.Pow(IVE[k, t] - MktIV[t][k], 2.0);
                }
            }
            // Fitting Heston prices and implied vols ==========================================================================================================
            double[,] PriceH = new double[NK, NT];
            double[,] IVH    = new double[NK, NT];
            HParam ParamTI = new HParam();

            ParamTI.kappa = ParamH[0];
            ParamTI.sigma = ParamH[1];
            ParamTI.theta = ParamH[2];
            ParamTI.rho   = ParamH[3];
            ParamTI.v0    = ParamH[4];
            double HestonIVRMSE = 0.0;

            for (int k = 0; k <= NK - 1; k++)
            {
                for (int t = 0; t <= NT - 1; t++)
                {
                    PriceH[k, t]  = HP.HestonPriceGaussLaguerre(ParamTI, ofsetH.opsettings, K[k], T[t], PutCall, X, W);
                    IVH[k, t]     = BA.BisecBSIV(PutCall, opsettings.S, K[k], opsettings.r, opsettings.q, T[t], a, b, PriceH[k, t], tol, MaxIter);
                    HestonIVRMSE += Math.Pow(IVH[k, t] - MktIVH[t, k], 2.0);
                }
            }
            Console.WriteLine("Heston IVRMSE {0,2:E5}", HestonIVRMSE);
            Console.WriteLine("Elices IVRMSE {0,2:E5}", ElicesIVRMSE);
        }
Ejemplo n.º 2
0
        // Nelder Mead Algorithm ===============================================================================================
        public double[] NelderMead(ObjFun f, NMSet nmsettings, double[,] x)
        {
            ObjectiveFunction OF = new ObjectiveFunction();

            OFSetE ofsetE = nmsettings.ofsettingsE;
            OFSetH ofsetH = nmsettings.ofsettingsH;
            int    N = nmsettings.N;
            int    MaxIters = nmsettings.MaxIters;
            string choice = nmsettings.choice;
            double Tolerance = nmsettings.Tolerance;
            int    NumIters = 0;
            int    i, j;

            // Value of the function at the vertices
            double[][] F = new Double[N + 1][];
            for (i = 0; i <= N; i++)
            {
                F[i] = new double[2] {
                    0.0, 0.0
                }
            }
            ;

            // Step 0.  Ordering and Best and Worst points
            // Order according to the functional values, compute the best and worst points
step0:
            NumIters = NumIters + 1;
            Console.Write("Nelder Mead iteration ");
            Console.WriteLine(NumIters);
            for (j = 0; j <= N; j++)
            {
                double[] z = new double[N];
                for (i = 0; i <= N - 1; i++)
                {
                    z[i]    = x[i, j];
                    F[j][0] = OF.f(z, ofsetE, ofsetH, choice);                      // Function values
                    F[j][1] = j;                                                    // Original index positions
                }
            }
            // Sort the F array w.r.t column 0
            int column = 0;

            Array.Sort(F, delegate(double[] w1, double[] w2)
            {
                return((w1[column] as IComparable).CompareTo(w2[column]));
            });

            // New vertices order first N best initial vectors and
            // last (N+1)st vertice is the worst vector
            // y is the matrix of vertices, ordered so that the worst vertice is last
            double[,] y = new double[N, N + 1];
            for (j = 0; j <= N; j++)
            {
                for (i = 0; i <= N - 1; i++)
                {
                    y[i, j] = x[i, Convert.ToInt32(F[j][1])];
                }
            }

            //  First best vector y(1) and function value f1
            double[] x1 = new double[N]; for (i = 0; i <= N - 1; i++)
            {
                x1[i] = y[i, 0];
            }
            double f1 = OF.f(x1, ofsetE, ofsetH, choice);

            // Last best vector y(N) and function value fn
            double[] xn = new Double[N]; for (i = 0; i <= N - 1; i++)
            {
                xn[i] = y[i, N - 1];
            }
            double fn = OF.f(xn, ofsetE, ofsetH, choice);

            // Worst vector y(N+1) and function value fn1
            double[] xn1 = new Double[N]; for (i = 0; i <= N - 1; i++)
            {
                xn1[i] = y[i, N];
            }
            double fn1 = OF.f(xn1, ofsetE, ofsetH, choice);

            // z is the first N vectors from y, excludes the worst y(N+1)
            double[,] zz = new Double[N, N];
            for (j = 0; j <= N - 1; j++)
            {
                for (i = 0; i <= N - 1; i++)
                {
                    zz[i, j] = y[i, j];
                }
            }

            // Mean of best N values and function value fm
            double[] xm = new Double[N]; xm = VMean(zz, N);
            double   fm = OF.f(xm, ofsetE, ofsetH, choice);

            // Reflection point xr and function fr
            double[] xr = new Double[N]; xr = VSub(VAdd(xm, xm), xn1);
            double   fr = OF.f(xr, ofsetE, ofsetH, choice);

            // Expansion point xe and function fe
            double[] xe = new Double[N]; xe = VSub(VAdd(xr, xr), xm);
            double   fe = OF.f(xe, ofsetE, ofsetH, choice);

            // Outside contraction point and function foc
            double[] xoc = new Double[N]; xoc = VAdd(VMult(xr, 0.5), VMult(xm, 0.5));
            double   foc = OF.f(xoc, ofsetE, ofsetH, choice);

            // Inside contraction point and function foc
            double[] xic = new Double[N]; xic = VAdd(VMult(xm, 0.5), VMult(xn1, 0.5));
            double   fic = OF.f(xic, ofsetE, ofsetH, choice);

            while ((NumIters <= MaxIters) && (Math.Abs(f1 - fn1) >= Tolerance))
            {
                // Step 1. Reflection Rule
                if ((f1 <= fr) && (fr < fn))
                {
                    for (j = 0; j <= N - 1; j++)
                    {
                        for (i = 0; i <= N - 1; i++)
                        {
                            x[i, j] = y[i, j];
                        }
                    }
                    for (i = 0; i <= N - 1; i++)
                    {
                        x[i, N] = xr[i];
                    }
                    goto step0;
                }
                // Step 2.  Expansion Rule
                if (fr < f1)
                {
                    for (j = 0; j <= N - 1; j++)
                    {
                        for (i = 0; i <= N - 1; i++)
                        {
                            x[i, j] = y[i, j];
                        }
                    }
                    if (fe < fr)
                    {
                        for (i = 0; i <= N - 1; i++)
                        {
                            x[i, N] = xe[i];
                        }
                    }
                    else
                    {
                        for (i = 0; i <= N - 1; i++)
                        {
                            x[i, N] = xr[i];
                        }
                    }
                    goto step0;
                }
                // Step 3.  Outside contraction Rule
                if ((fn <= fr) && (fr < fn1) && (foc <= fr))
                {
                    for (j = 0; j <= N - 1; j++)
                    {
                        for (i = 0; i <= N - 1; i++)
                        {
                            x[i, j] = y[i, j];
                        }
                    }
                    for (i = 0; i <= N - 1; i++)
                    {
                        x[i, N] = xoc[i];
                    }
                    goto step0;
                }
                // Step 4.  Inside contraction Rule
                if ((fr >= fn1) && (fic < fn1))
                {
                    for (j = 0; j <= N - 1; j++)
                    {
                        for (i = 0; i <= N - 1; i++)
                        {
                            x[i, j] = y[i, j];
                        }
                    }
                    for (i = 0; i <= N - 1; i++)
                    {
                        x[i, N] = xic[i];
                    }
                    goto step0;
                }
                // Step 5. Shrink Step
                for (i = 0; i <= N - 1; i++)
                {
                    x[i, 0] = y[i, 0];
                }
                for (i = 0; i <= N - 1; i++)
                {
                    for (j = 1; j <= N; j++)
                    {
                        x[i, j] = 0.5 * (y[i, j] + x[i, 0]);
                    }
                }
                goto step0;
            }

            // Output component
            double[] outvec = new Double[N + 2];
            for (i = 0; i <= N - 1; i++)
            {
                outvec[i] = x1[i];
            }
            outvec[N]     = f1;
            outvec[N + 1] = NumIters;
            return(outvec);
        }
Ejemplo n.º 3
0
        // Objective function for Elices prices ===========================================================================
        public double f(double[] param, OFSetE ofsettingsE, OFSetH ofsettingsH, string choice)
        {
            ElicesAlgo  EA = new ElicesAlgo();
            HestonPrice HP = new HestonPrice();

            double Error = 0.0;

            if (choice == "Elices")
            {
                double S    = ofsettingsE.opsettings.S;
                double r    = ofsettingsE.opsettings.r;
                double q    = ofsettingsE.opsettings.q;
                int    trap = ofsettingsE.opsettings.trap;

                double[] MktIV    = ofsettingsE.data.MktIV;
                double[] MktPrice = ofsettingsE.data.MktPrice;
                string   PutCall  = ofsettingsE.data.PutCall;
                double[] K        = ofsettingsE.data.K;
                int      NK       = K.Length;
                int      t        = ofsettingsE.t;

                double[][] paramfixed = ofsettingsE.paramfixed;
                double[]   T          = ofsettingsE.T;
                double     v0         = ofsettingsE.v0;
                int        NT         = T.Length;

                double[] lb = ofsettingsE.lb;
                double[] ub = ofsettingsE.ub;

                int      LossFunction = ofsettingsE.LossFunction;
                double[] X            = ofsettingsE.X;
                double[] W            = ofsettingsE.W;

                // Initialize the model price and model implied vol vectors, and the objective function value
                double[] ModelPrice = new double[NK];
                double[] ModelIV    = new double[NK];
                double   Vega       = 0.0;
                double   pi         = Math.PI;

                double kappaLB = lb[0]; double kappaUB = ub[0];
                double thetaLB = lb[1]; double thetaUB = ub[1];
                double sigmaLB = lb[2]; double sigmaUB = ub[2];
                double rhoLB = lb[3]; double rhoUB = ub[3];

                // Penalty for inadmissible parameter values
                if ((param[0] <= kappaLB) || (param[1] <= thetaLB) || (param[2] <= sigmaLB) || (param[3] <= rhoLB) ||
                    (param[0] >= kappaUB) || (param[1] >= thetaUB) || (param[2] >= sigmaUB) || (param[3] >= rhoUB))
                {
                    Error = 1.0e50;
                }
                else
                {
                    for (int k = 0; k < NK - 1; k++)
                    {
                        if (t == 0)
                        {
                            ModelPrice[k] = EA.ElicesPrice(PutCall, S, K[k], T, r, q, param, v0, trap, X, W);
                        }
                        else
                        {
                            ModelPrice[k] = EA.ElicesPrice(PutCall, S, K[k], T, r, q, param, v0, trap, X, W, paramfixed);
                        }
                        switch (LossFunction)
                        {
                        case 1:
                            // MSE Loss Function
                            Error += Math.Pow(ModelPrice[k] - MktPrice[k], 2.0);
                            break;

                        case 2:
                            // RMSE Loss Function
                            Error += Math.Pow(ModelPrice[k] - MktPrice[k], 2.0) / MktPrice[k];
                            break;

                        case 3:
                            // IVRMSE Christoffersen, Heston, Jacobs proxy
                            double mat     = T[NT - 1];
                            double d       = (Math.Log(S / K[k]) + (r + MktIV[k] * MktIV[k] / 2.0) * mat) / MktIV[k] / Math.Sqrt(mat);
                            double NormPDF = Math.Exp(-0.5 * d * d) / Math.Sqrt(2.0 * pi);
                            Vega   = S * NormPDF * Math.Sqrt(mat);
                            Error += Math.Pow(ModelPrice[k] - MktPrice[k], 2.0) / (Vega * Vega);
                            break;
                        }
                    }
                    Error /= Convert.ToDouble(NK);
                }
            }
            else if (choice == "Heston")
            // Objective function for Heston prices ===========================================================================
            {
                double[,] MktPrice = ofsettingsH.MktPrice;
                double[,] MktIV    = ofsettingsH.MktIV;
                string   PutCall = ofsettingsH.PutCall;
                double[] K       = ofsettingsH.K;
                int      NK      = K.Length;

                double[] T  = ofsettingsH.T;
                int      NT = T.Length;

                double[] lb = ofsettingsH.lb;
                double[] ub = ofsettingsH.ub;

                int      LossFunction = ofsettingsH.LossFunction;
                double[] X            = ofsettingsH.X;
                double[] W            = ofsettingsH.W;

                // Initialize the model price and model implied vol vectors, and the objective function value
                double[,] ModelPrice = new double[NK, NT];
                double[,] ModelIV    = new double[NK, NT];
                double Vega = 0.0;
                double pi   = Math.PI;

                double kappaLB = lb[0]; double kappaUB = ub[0];
                double thetaLB = lb[1]; double thetaUB = ub[1];
                double sigmaLB = lb[2]; double sigmaUB = ub[2];
                double rhoLB = lb[3]; double rhoUB = ub[3];
                double v0LB = lb[4]; double v0UB = ub[4];

                HParam param2 = new HParam();
                param2.kappa = param[0];
                param2.theta = param[1];
                param2.sigma = param[2];
                param2.rho   = param[3];
                param2.v0    = param[4];

                // Penalty for inadmissible parameter values
                if ((param[0] <= kappaLB) || (param[1] <= thetaLB) || (param[2] <= sigmaLB) || (param[3] <= rhoLB) || (param[4] <= v0LB) ||
                    (param[0] >= kappaUB) || (param[1] >= thetaUB) || (param[2] >= sigmaUB) || (param[3] >= rhoUB) || (param[4] >= v0UB))
                {
                    Error = 1.0e50;
                }
                else
                {
                    for (int t = 0; t <= NT - 1; t++)
                    {
                        for (int k = 0; k <= NK - 1; k++)
                        {
                            ModelPrice[k, t] = HP.HestonPriceGaussLaguerre(param2, ofsettingsH.opsettings, K[k], T[t], PutCall, X, W);
                            switch (LossFunction)
                            {
                            case 1:
                                // MSE Loss Function
                                Error += Math.Pow(ModelPrice[k, t] - MktPrice[t, k], 2.0);
                                break;

                            case 2:
                                // RMSE Loss Function
                                Error += Math.Pow(ModelPrice[k, t] - MktPrice[t, k], 2.0) / MktPrice[k, t];
                                break;

                            case 3:
                                // IVRMSE Christoffersen, Heston, Jacobs proxy
                                double S       = ofsettingsH.opsettings.S;
                                double r       = ofsettingsH.opsettings.r;
                                double q       = ofsettingsH.opsettings.q;
                                double mat     = T[NT - 1];
                                double d       = (Math.Log(S / K[k]) + (r + MktIV[t, k] * MktIV[t, k] / 2.0) * mat) / MktIV[k, t] / Math.Sqrt(mat);
                                double NormPDF = Math.Exp(-0.5 * d * d) / Math.Sqrt(2.0 * pi);
                                Vega   = S * NormPDF * Math.Sqrt(mat);
                                Error += Math.Pow(ModelPrice[k, t] - MktPrice[t, k], 2.0) / (Vega * Vega);
                                break;
                            }
                        }
                    }
                    Error /= Convert.ToDouble(NK * NT);
                }
            }
            return(Error);
        }