// Objective function ===========================================================================
        public double f(double[] param, double tau, double[] tau0, double[,] param0, MktData data, double S, double[] K, double r, double q, int trap,
                        double[] X, double[] W, int LossFunction, double[] lb, double[] ub)
        {
            HestonPriceTD HPTD = new HestonPriceTD();
            BisectionAlgo BA   = new BisectionAlgo();

            double[] MktIV    = data.MktIV;
            double[] MktPrice = data.MktPrice;
            string[] PutCall  = data.PutCall;

            int NK = PutCall.Length;
            int NT = 1;

            HParam param2 = new HParam();

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

            // Settings for the Bisection algorithm
            double a       = 0.001;
            double b       = 3.0;
            double Tol     = 1e5;
            int    MaxIter = 10000;

            // 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   Error      = 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 v0LB = lb[3]; double v0UB = ub[3];
            double rhoLB = lb[4]; double rhoUB = ub[4];

            if ((param2.kappa <= kappaLB) || (param2.theta <= thetaLB) || (param2.sigma <= sigmaLB) || (param2.v0 <= v0LB) || (param2.rho <= rhoLB) ||
                (param2.kappa >= kappaUB) || (param2.theta >= thetaUB) || (param2.sigma >= sigmaUB) || (param2.v0 >= v0UB) || (param2.rho >= rhoUB))
            {
                Error = 1e50;
            }
            else
            {
                for (int k = 0; k <= NK - 1; k++)
                {
                    ModelPrice[k] = HPTD.MNPriceGaussLaguerre(param2, param0, tau, tau0, S, K[k], r, q, PutCall[k], trap, X, W);
                    switch (LossFunction)
                    {
                    case 1:
                        // MSE Loss Function
                        Error += Math.Pow(ModelPrice[k] - MktPrice[k], 2);
                        break;

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

                    case 3:
                        // IVMSE Loss Function
                        ModelIV[k] = BA.BisecBSIV(PutCall[k], S, K[k], r, q, tau, a, b, ModelPrice[k], Tol, MaxIter);
                        Error     += Math.Pow(ModelIV[k] - MktIV[k], 2);
                        break;

                    case 4:
                        // IVRMSE Christoffersen, Heston, Jacobs proxy
                        double d       = (Math.Log(S / K[k]) + (r - q + MktIV[k] * MktIV[k] / 2.0) * tau) / MktIV[k] / Math.Sqrt(tau);
                        double NormPDF = Math.Exp(-0.5 * d * d) / Math.Sqrt(2.0 * pi);
                        Vega   = S * NormPDF * Math.Sqrt(tau);
                        Error += Math.Pow(ModelPrice[k] - MktPrice[k], 2) / (Vega * Vega);
                        break;
                    }
                }
            }
            return(Error);  //       / Convert.ToDouble(NT*NK);
        }
Exemple #2
0
        static void Main(string[] args)
        {
            // Classes
            BlackScholesPrice BS   = new BlackScholesPrice();
            BisectionAlgo     BA   = new BisectionAlgo();
            NelderMeadAlgo    NM   = new NelderMeadAlgo();
            ObjectiveFunction OF   = new ObjectiveFunction();
            HestonPriceTD     HPTD = new HestonPriceTD();

            // 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]);
                }
            int NT = 4;
            int NK = 13;

            double[,] PutIV = new double[13, 4] {
                { 0.1962, 0.1947, 0.2019, 0.2115 }, { 0.1910, 0.1905, 0.1980, 0.2082 },
                { 0.1860, 0.1861, 0.1943, 0.2057 }, { 0.1810, 0.1812, 0.1907, 0.2021 },
                { 0.1761, 0.1774, 0.1871, 0.2000 }, { 0.1718, 0.1743, 0.1842, 0.1974 },
                { 0.1671, 0.1706, 0.1813, 0.1950 }, { 0.1644, 0.1671, 0.1783, 0.1927 },
                { 0.1645, 0.1641, 0.1760, 0.1899 }, { 0.1661, 0.1625, 0.1743, 0.1884 },
                { 0.1701, 0.1602, 0.1726, 0.1862 }, { 0.1755, 0.1610, 0.1716, 0.1846 },
                { 0.1796, 0.1657, 0.1724, 0.1842 }
            };
            double[] K = new double[13] {
                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[4] {
                0.1014, 0.1973, 0.3699, 0.6192
            };
            string[,] PutCall  = new string[13, 4];
            double[,] MktPrice = new double[13, 4];
            double Spot         = 129.14;
            double r            = 0.0010;
            double q            = 0.0068;
            int    trap         = 1;
            int    LossFunction = 2;

            for (int t = 0; t <= NT - 1; t++)
            {
                for (int k = 0; k <= NK - 1; k++)
                {
                    PutCall[k, t]  = "P";
                    MktPrice[k, t] = BS.BlackScholes(Spot, K[k], T[t], r, q, PutIV[k, t], PutCall[k, t]);
                }
            }

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

            // Starting values and upper bounds
            double e = 1e-5;

            double[] lb = new double[5] {
                e, e, e, e, -0.999
            };                                                          // Lower bound on the estimates
            double[] ub = new double[5] {
                20.0, 2.0, 2.0, 3.0, 0.999
            };                                                          // Upper bound on the estimates

            // Starting values (vertices) in vector form.  Add random increment about each starting value
            int    N      = 5;
            double kappaS = 4.0;
            double thetaS = 0.1;
            double sigmaS = 1.5;
            double v0S    = 0.04;
            double rhoS   = -0.30;

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

            // Arrays for old maturities and parameters, and current parameters (old parameters, but stacked)
            ArrayList OldTau = new ArrayList();

            double[,] param0  = new double[NT - 1, 5];
            double[,] paramTD = new double[NT, 5];

            // Nelder Mead settings
            int    MaxIters  = 1000;
            double Tolerance = 1e-5;

            // Market data
            MktData data = new MktData();

            double[] MKIV = new double[NK];
            double[] MKPR = new double[NK];
            string[] PC   = new string[NK];

            // Step-by-step parameter estimates
            double[] B            = new double[6];
            double[] ObjectiveFun = new double[NT];
            double[] NumIteration = new double[NT];

            // First maturity parameter estimates ===================================================================
            double[] tau0 = { 0.0 };
            for (int k = 0; k <= NK - 1; k++)
            {
                MKIV[k] = PutIV[k, 0];
                MKPR[k] = MktPrice[k, 0];
                PC[k]   = PutCall[k, 0];
            }
            data.MktIV    = MKIV;
            data.PutCall  = PC;
            data.MktPrice = MKPR;
            B             = NM.NelderMead(OF.f, N, MaxIters, Tolerance, s, tau[0], tau0, param0, data, Spot, K, r, q, trap, X, W, LossFunction, lb, ub);
            Console.WriteLine("Finished parameter estimate set 1");
            for (int k = 0; k <= 4; k++)
            {
                paramTD[0, k] = B[k];
            }
            ObjectiveFun[0] = B[5];
            NumIteration[0] = B[6];

            // Remaining Maturity estimates and updated starting values ==================================================
            for (int mat = 1; mat <= NT - 1; mat++)
            {
                OldTau.Add(tau[mat - 1]);
                for (int k = 0; k <= 4; k++)
                {
                    param0[mat - 1, k] = B[k];
                }
                for (int k = 0; k <= NK - 1; k++)
                {
                    MKIV[k] = PutIV[k, mat];
                    MKPR[k] = MktPrice[k, mat];
                    PC[k]   = PutCall[k, mat];
                }
                data.MktIV    = MKIV;
                data.PutCall  = PC;
                data.MktPrice = MKPR;
                for (int j = 0; j <= N; j++)
                {
                    s[0, j] = B[0] + BA.RandomNum(-0.01, 0.01);
                    s[1, j] = B[1] + BA.RandomNum(-0.01, 0.01);
                    s[2, j] = B[2] + BA.RandomNum(-0.01, 0.01);
                    s[3, j] = B[3] + BA.RandomNum(-0.01, 0.01);
                    s[4, j] = B[4] + BA.RandomNum(-0.01, 0.01);
                }
                B = NM.NelderMead(OF.f, N, MaxIters, Tolerance, s, tau[mat], tau0, param0, data, Spot, K, r, q, trap, X, W, LossFunction, lb, ub);
                Console.WriteLine("Finished parameter estimate set {0}", mat + 1);
                for (int k = 0; k <= 4; k++)
                {
                    paramTD[mat, k] = B[k];
                }
                ObjectiveFun[mat] = B[5];
                NumIteration[mat] = B[6];
            }

            // Fit the prices and implied volatilities ====================================================================
            // Bisection algorithm settings
            double a       = 0.001;
            double b       = 5.0;
            double Tol     = 1e-5;
            int    MaxIter = 5000;

            ArrayList OldTau00 = new ArrayList();

            double[] tau00 = { 0.0 };
            double[,] ModelPrice = new double[NK, NT];
            double[,] ModelIV    = new double[NK, NT];
            Array.Clear(param0, 0, 15);

            // First maturity
            HParam param = new HParam();

            param.kappa = paramTD[0, 0];
            param.theta = paramTD[0, 1];
            param.sigma = paramTD[0, 2];
            param.v0    = paramTD[0, 3];
            param.rho   = paramTD[0, 4];
            for (int k = 0; k <= NK - 1; k++)
            {
                ModelPrice[k, 0] = HPTD.MNPriceGaussLaguerre(param, param0, tau[0], tau00, Spot, K[k], r, q, PutCall[k, 0], trap, X, W);
                ModelIV[k, 0]    = BA.BisecBSIV(PutCall[k, 0], Spot, K[k], r, q, T[0], a, b, ModelPrice[k, 0], Tol, MaxIter);
            }

            // Remaining maturity
            for (int t = 1; t <= NT - 1; t++)
            {
                OldTau00.Add(tau[t - 1]);
                param.kappa = paramTD[t, 0];
                param.theta = paramTD[t, 1];
                param.sigma = paramTD[t, 2];
                param.v0    = paramTD[t, 3];
                param.rho   = paramTD[t, 4];
                for (int j = 0; j <= 4; j++)
                {
                    param0[t - 1, j] = paramTD[t - 1, j];
                }
                for (int k = 0; k <= NK - 1; k++)
                {
                    ModelPrice[k, t] = HPTD.MNPriceGaussLaguerre(param, param0, tau[t], tau00, Spot, K[k], r, q, PutCall[k, t], trap, X, W);
                    ModelIV[k, t]    = BA.BisecBSIV(PutCall[k, t], Spot, K[k], r, q, T[t], a, b, ModelPrice[k, t], Tol, MaxIter);
                }
            }

            // Mean Square Implied Volatility Error
            double Error = 0.0;

            for (int t = 0; t <= NT - 1; t++)
            {
                for (int k = 0; k <= NK - 1; k++)
                {
                    Error += Math.Pow(ModelIV[k, t] - PutIV[k, t], 2);
                }
            }

            Error /= (Convert.ToDouble(NT) * Convert.ToDouble(NK));

            // Output the estimation result
            Console.WriteLine("-----------------------------------------------------------------------------");
            Console.WriteLine("Market/Model implied volatilities");
            Console.WriteLine("    Mat1       Mat2       Mat3       Mat4");
            for (int k = 0; k <= NK - 1; k++)
            {
                Console.WriteLine("--------------------------------------------- Strike {0} Market/Model", K[k]);
                Console.WriteLine("{0,10:F4} {1,10:F4} {2,10:F4} {3,10:F4}", PutIV[k, 0], PutIV[k, 1], PutIV[k, 2], PutIV[k, 3]);
                Console.WriteLine("{0,10:F4} {1,10:F4} {2,10:F4} {3,10:F4}", ModelIV[k, 0], ModelIV[k, 1], ModelIV[k, 2], ModelIV[k, 3]);
            }
            Console.WriteLine("-----------------------------------------------------------------------------");
            Console.WriteLine("Model Price ");
            for (int k = 0; k <= NK - 1; k++)
            {
                Console.WriteLine("{0,10:0} {1,10:F4} {2,10:F4} {3,10:F4} {4,10:F4}", K[k], ModelPrice[k, 0], ModelPrice[k, 1], ModelPrice[k, 2], ModelPrice[k, 3]);
            }
            Console.WriteLine("-----------------------------------------------------------------------------");
            Console.WriteLine("Results of time dependent parameter estimation");
            Console.WriteLine("  ");
            Console.WriteLine("Maturity   kappa   theta   sigma   v0       rho     ObjecFun  NumIterations");
            Console.WriteLine("-----------------------------------------------------------------------------");
            for (int t = 0; t <= NT - 1; t++)
            {
                Console.WriteLine("{0} {1,10:F4} {2,7:F4} {3,7:F4} {4,7:F4} {5,8:F4} {6,8:F4} {7,7:0}",
                                  T[t], paramTD[t, 0], paramTD[t, 1], paramTD[t, 2], paramTD[t, 3], paramTD[t, 4], ObjectiveFun[t], NumIteration[t]);
            }
            Console.WriteLine("-----------------------------------------------------------------------------");
            Console.WriteLine("IVMSE from time dependent estimates {0,10:E5}", Error);
            Console.WriteLine("-----------------------------------------------------------------------------");
        }