コード例 #1
0
        // Objective function ===========================================================================
        public double f(double[] param, OFSet ofset)
        {
            HestonPrice HP = new HestonPrice();
            Bisection   BA = new Bisection();

            double S    = ofset.opset.S;
            double r    = ofset.opset.r;
            double q    = ofset.opset.q;
            int    trap = ofset.opset.trap;

            double[,] MktIV    = ofset.data.MktIV;
            double[,] MktPrice = ofset.data.MktPrice;
            string[,] PutCall  = ofset.data.PutCall;
            double[] K            = ofset.data.K;
            double[] T            = ofset.data.T;
            double[] X            = ofset.X;
            double[] W            = ofset.W;
            string   CF           = ofset.CF;
            int      LossFunction = ofset.LossFunction;

            int NK = PutCall.GetLength(0);
            int NT = PutCall.GetLength(1);
            int NX = X.Length;

            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, NT];
            double[,] ModelIV    = new double[NK, NT];
            double Vega  = 0.0;
            double Error = 0.0;
            double pi    = Math.PI;

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

            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
            {
                Complex   phi       = new Complex(0.0, 0.0);
                double    phi2      = 0.0;
                Complex   i         = new Complex(0.0, 1.0);
                Complex[] f2        = new Complex[NX];
                Complex[] f1        = new Complex[NX];
                Complex[] f         = new Complex[NX];
                double[]  int1      = new double[NX];
                double[]  int2      = new double[NX];
                Complex   I1        = new Complex(0.0, 0.0);
                Complex   I2        = new Complex(0.0, 0.0);
                double    CallPrice = 0.0;

                for (int t = 0; t < NT; t++)
                {
                    for (int j = 0; j < NX; j++)
                    {
                        phi = X[j];
                        if (CF == "Heston")
                        {
                            f2[j] = HP.HestonCF(phi, param2, S, r, q, T[t], trap);
                            f1[j] = HP.HestonCF(phi - i, param2, S, r, q, T[t], trap) / (S * Math.Exp((r - q) * T[t]));
                        }
                        else if (CF == "Attari")
                        {
                            phi2 = X[j];
                        }
                        f[j] = HP.AttariCF(phi2, param2, T[t], S, r, q, trap);
                    }
                    for (int k = 0; k < NK; k++)
                    {
                        double L = Math.Log(Math.Exp(-r * T[t]) * K[k] / S);
                        for (int j = 0; j < NX; j++)
                        {
                            phi = X[j];
                            if (CF == "Heston")
                            {
                                I1      = Complex.Exp(-i * phi * Complex.Log(K[k])) * f1[j] / i / phi;
                                int1[j] = W[j] * I1.Real;
                                I2      = Complex.Exp(-i * phi * Complex.Log(K[k])) * f2[j] / i / phi;
                                int2[j] = W[j] * I2.Real;
                            }
                            else if (CF == "Attari")
                            {
                                phi2 = X[j];
                                double fR = f[j].Real;
                                double fI = f[j].Imaginary;
                                int1[j] = W[j] * ((fR + fI / phi2) * Math.Cos(L * phi2) + (fI - fR / phi2) * Math.Sin(L * phi2)) / (1 + phi2 * phi2);
                            }
                        }
                        if (CF == "Heston")
                        {
                            double P1 = 0.5 + 1.0 / pi * int1.Sum();
                            double P2 = 0.5 + 1.0 / pi * int2.Sum();
                            CallPrice = S * Math.Exp(-q * T[t]) * P1 - K[k] * Math.Exp(-r * T[t]) * P2;
                        }
                        else if (CF == "Attari")
                        {
                            CallPrice = S * Math.Exp(-q * T[t]) - K[k] * Math.Exp(-r * T[t]) * (0.5 + 1.0 / pi * int1.Sum());
                        }

                        if (PutCall[k, t] == "C")
                        {
                            ModelPrice[k, t] = CallPrice;
                        }
                        else
                        {
                            ModelPrice[k, t] = CallPrice - S * Math.Exp(-q * T[t]) + Math.Exp(-r * T[t]) * K[k];
                        }

                        // Select the objective function
                        switch (LossFunction)
                        {
                        case 1:
                            // MSE Loss Function
                            Error += Math.Pow(ModelPrice[k, t] - MktPrice[k, t], 2) / Convert.ToDouble(NT * NK);;
                            break;

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

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

                        case 4:
                            // IVRMSE Christoffersen, Heston, Jacobs proxy
                            double d       = (Math.Log(S / K[k]) + (r - q + MktIV[k, t] * MktIV[k, t] / 2.0) * T[t]) / MktIV[k, t] / Math.Sqrt(T[t]);
                            double NormPDF = Math.Exp(-0.5 * d * d) / Math.Sqrt(2 * pi);
                            Vega   = S * NormPDF * Math.Sqrt(T[t]);
                            Error += Math.Pow(ModelPrice[k, t] - MktPrice[k, t], 2) / Vega / Vega / Convert.ToDouble(NT * NK);
                            break;
                        }
                    }
                }
            }
            return(Error);
        }
コード例 #2
0
        static void Main(string[] args)
        {
            // Classes
            HestonPrice       HP = new HestonPrice();
            BlackScholesPrice BS = new BlackScholesPrice();
            Bisection         BA = new Bisection();
            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;

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

            // settings for the market data
            MktData data = new MktData();

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

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

            double[] lb = new double[5] {
                e, e, e, e, -0.99
            };
            double[] ub = new double[5] {
                20.0, 2.0, 2.0, 2.0, 0.99
            };

            // C.F. choice and Loss function choice
            string CF           = "Heston";         // Choice of c.f. "Heston" or "Attari"
            int    LossFunction = 1;                // Choice of loss function
                                                    // 1=MSE, 2=RMSE, 3=IVMSE, 4=Christoffersen et al.

            // Settings for the objective function
            OFSet ofset;

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

            // Settings for the Nelder Mead algorithm
            NMSet nmsettings;

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

            // 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      = nmsettings.N;

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

            // Find the Nelder-Mead parameter estimates
            double[] B = NM.NelderMead(OF.f, nmsettings, s);

            // Output the estimation result
            Console.WriteLine("  ");
            Console.WriteLine("Parameter Estimates --------------------");
            Console.WriteLine("  ");
            Console.WriteLine("kappa   =  {0:F5}", B[0]);
            Console.WriteLine("theta   =  {0:F5}", B[1]);
            Console.WriteLine("sigma   =  {0:F5}", B[2]);
            Console.WriteLine("v0      =  {0:F5}", B[3]);
            Console.WriteLine("rho     =  {0:F5}", B[4]);
            Console.WriteLine("  ");
            Console.WriteLine("Value of the objective function is  {0:F5}", B[5]);
            Console.WriteLine("  ");
            Console.WriteLine("Number of iterations required       {0}", B[6]);
            Console.WriteLine("  ");
            Console.WriteLine("----------------------------------------");

            // Obtain the model prices and model implied volatilities
            HParam paramNM = new HParam();

            paramNM.kappa = B[0];
            paramNM.theta = B[1];
            paramNM.sigma = B[2];
            paramNM.v0    = B[3];
            paramNM.rho   = B[4];
            double a       = 0.01;
            double b       = 3.0;
            double Tol     = 1e-5;
            int    MaxIter = 1000;

            double[,] ModelPrice = new double[NK, NT];
            double[,] ModelIV    = new double[NK, NT];
            double IVMSE = 0.0;

            double S    = ofset.opset.S;
            double r    = ofset.opset.r;
            double q    = ofset.opset.q;
            int    trap = ofset.opset.trap;

            for (int k = 0; k < NK; k++)
            {
                for (int t = 0; t < NT; t++)
                {
                    ModelPrice[k, t] = HP.HestonPriceGaussLaguerre(paramNM, S, K[k], r, q, T[t], trap, PutCall[k, t], X, W);
                    ModelIV[k, t]    = BA.BisecBSIV(PutCall[k, t], S, K[k], r, q, T[t], a, b, ModelPrice[k, t], Tol, MaxIter);
                    IVMSE           += Math.Pow(MktIV[k, t] - ModelIV[k, t], 2) / Convert.ToDouble(NT * NK);
                }
            }

            // Output the results
            Console.Write("MSE between model and market implied vols  = {0}", IVMSE);
            Console.WriteLine("  ");
            Console.WriteLine("----------------------------------------");
            Console.WriteLine("Market implied volatilities");
            Console.WriteLine("  ");
            for (int k = 0; k < NK; k++)
            {
                for (int t = 0; t < NT; t++)
                {
                    if (t < NT - 1)
                    {
                        Console.Write("{0:F4}   ", MktIV[k, t]);
                    }
                    else
                    {
                        Console.WriteLine("{0:F4}   ", MktIV[k, t]);
                    }
                }
            }

            Console.WriteLine("  ");
            Console.WriteLine("----------------------------------------");
            Console.WriteLine("Model implied volatilities");
            Console.WriteLine("  ");
            for (int k = 0; k < NK; k++)
            {
                for (int t = 0; t < NT; t++)
                {
                    if (t < NT - 1)
                    {
                        Console.Write("{0:F4}   ", ModelIV[k, t]);
                    }
                    else
                    {
                        Console.WriteLine("{0:F4}   ", ModelIV[k, t]);
                    }
                }
            }

            Console.WriteLine("----------------------------------------");
        }