예제 #1
0
        // Bisection Algorithm for Black Scholes Implied Volatility =================================================================================
        public double BisecBSIV(OPSet settings, double K, double T, double a, double b, double MktPrice, double Tol, int MaxIter)
        {
            BlackScholesPrice BS      = new BlackScholesPrice();
            double            S       = settings.S;
            double            rf      = settings.r;
            double            q       = settings.q;
            string            PutCall = settings.PutCall;

            double lowCdif  = MktPrice - BS.BlackScholes(S, K, T, rf, q, a, PutCall);
            double highCdif = MktPrice - BS.BlackScholes(S, K, T, rf, q, b, PutCall);
            double BSIV     = 0.0;
            double midP;

            if (lowCdif * highCdif > 0.0)
            {
                BSIV = -1.0;
            }
            else
            {
                for (int x = 0; x <= MaxIter; x++)
                {
                    midP = (a + b) / 2.0;
                    double midCdif = MktPrice - BS.BlackScholes(S, K, T, rf, q, midP, PutCall);
                    if (Math.Abs(midCdif) < Tol)
                    {
                        break;
                    }
                    else
                    {
                        if (midCdif > 0.0)
                        {
                            a = midP;
                        }
                        else
                        {
                            b = midP;
                        }
                    }
                    BSIV = midP;
                }
            }
            return(BSIV);
        }
예제 #2
0
        // Parameters for the shortest maturity appear first in the vectors theta, sigma, and rho.
        // Parameters for the longest maturity appear last.
        public double BGMApproxPriceTD(double kappa, double v0, double[] theta, double[] sigma, double[] rho, OPSet settings, double K, double[] T)
        {
            int NT = T.Length;

            double[] a1T = new double[NT];
            double[] a2T = new double[NT];
            double[] b0T = new double[NT];
            double[] b2T = new double[NT];

            // First set of coefficients
            a1T[0] = -rho[0] * sigma[0] * (2.0 * theta[0] * Math.Exp(kappa * T[0]) + v0 * T[0] * kappa + v0 - theta[0] * T[0] * kappa * Math.Exp(kappa * T[0]) - theta[0] * kappa * T[0] - 2.0 * theta[0] - v0 * Math.Exp(kappa * T[0])) / Math.Exp(kappa * T[0]) / Math.Pow(kappa, 2.0);
            a2T[0] = -0.5 * rho[0] * rho[0] * sigma[0] * sigma[0] * (Math.Pow(kappa, 2.0) * v0 * Math.Pow(T[0], 2.0) + 6.0 * theta[0] * Math.Exp(kappa * T[0]) + 2.0 * v0 * T[0] * kappa + 2.0 * v0 - Math.Pow(kappa, 2.0) * theta[0] * Math.Pow(T[0], 2.0) - 4.0 * theta[0] * kappa * T[0] - 2.0 * theta[0] * T[0] * kappa * Math.Exp(kappa * T[0]) - 6.0 * theta[0] - 2.0 * v0 * Math.Exp(kappa * T[0])) / Math.Exp(kappa * T[0]) / Math.Pow(kappa, 3.0);
            b0T[0] = -0.25 * sigma[0] * sigma[0] * (5.0 * theta[0] * Math.Pow(Math.Exp(kappa * T[0]), 2.0) + 4.0 * Math.Exp(kappa * T[0]) * v0 * T[0] * kappa - 4.0 * theta[0] * T[0] * kappa * Math.Exp(kappa * T[0]) - 2.0 * theta[0] * T[0] * kappa * Math.Pow(Math.Exp(kappa * T[0]), 2.0) + 2.0 * v0 - theta[0] - 2.0 * v0 * Math.Pow(Math.Exp(kappa * T[0]), 2.0) - 4.0 * theta[0] * Math.Exp(kappa * T[0])) / Math.Pow(Math.Exp(kappa * T[0]), 2.0) / Math.Pow(kappa, 3.0);
            b2T[0] = a1T[0] * a1T[0] / 2.0;

            double A1 = 0.0, A2 = 0.0, B0 = 0.0;
            int    j;

            // Coefficients under multiple maturities
            if (NT >= 2)
            {
                for (int t = 0; t <= NT - 2; t++)
                {
                    // Coefficients for a1T[t+1]
                    j   = 0;
                    A1 += rho[j] * sigma[j] * (-Math.Exp(-kappa * T[t]) + Math.Exp(-kappa * T[t + 1])) * (theta[j] - v0 * T[j] * kappa + theta[j] * T[j] * kappa - theta[j] * Math.Exp(kappa * T[j])) / Math.Pow(kappa, 2.0);
                    for (j = 1; j <= t; j++)
                    {
                        A1 += -rho[j] * sigma[j] * (-Math.Exp(-kappa * T[t]) + Math.Exp(-kappa * T[t + 1])) * (-v0 * T[j - 1] * kappa + theta[j] * T[j - 1] * kappa - theta[j] * Math.Exp(kappa * T[j - 1]) + v0 * T[j] * kappa - theta[j] * T[j] * kappa + theta[j] * Math.Exp(kappa * T[j])) / Math.Pow(kappa, 2.0);
                    }
                    j          = t + 1;
                    A1        += -rho[j] * sigma[j] * (-v0 * Math.Exp(kappa * T[t + 1]) - v0 * T[t] * kappa * Math.Exp(kappa * T[t]) + theta[j] * Math.Exp(kappa * T[t + 1]) + theta[j] * T[t] * kappa * Math.Exp(kappa * T[t]) + theta[j] * T[t] * kappa * Math.Exp(kappa * (T[t] + T[t + 1])) - theta[j] * Math.Exp(2.0 * kappa * T[t]) + Math.Exp(kappa * T[t]) * v0 - Math.Exp(kappa * (T[t] + T[t + 1])) * theta[j] * T[t + 1] * kappa + theta[j] * Math.Exp(kappa * (T[t] + T[t + 1])) + Math.Exp(kappa * T[t]) * v0 * kappa * T[t + 1] - theta[j] * Math.Exp(kappa * T[t]) - Math.Exp(kappa * T[t]) * theta[j] * kappa * T[t + 1]) / Math.Pow(kappa, 2.0) * Math.Exp(-kappa * (T[t] + T[t + 1]));
                    a1T[t + 1] = a1T[t] + A1;

                    // Coefficients for a2T[t+1]
                    j   = 0;
                    A2 += -Math.Pow(rho[j], 2.0) * Math.Pow(sigma[j], 2.0) * (Math.Exp(-kappa * T[t]) + Math.Exp(-kappa * T[t + 1]) * T[t] * kappa - Math.Exp(-kappa * T[t + 1]) - Math.Exp(-kappa * T[t + 1]) * kappa * T[t + 1]) * (theta[j] - v0 * T[j] * kappa + theta[j] * T[j] * kappa - theta[j] * Math.Exp(kappa * T[j])) / Math.Pow(kappa, 3.0)
                          + 1.0 / 2.0 * Math.Pow(rho[j], 2.0) * Math.Pow(sigma[j], 2.0) * (-Math.Exp(-kappa * T[t]) + Math.Exp(-kappa * T[t + 1])) * (2.0 * kappa * theta[j] * T[t] + 2.0 * theta[j] - 2.0 * v0 * T[t] * T[j] * Math.Pow(kappa, 2.0) + v0 * Math.Pow(T[j], 2.0) * Math.Pow(kappa, 2.0) + 2.0 * theta[j] * T[t] * T[j] * Math.Pow(kappa, 2.0) - theta[j] * Math.Pow(T[j], 2.0) * Math.Pow(kappa, 2.0) - 2.0 * theta[j] * T[t] * Math.Exp(kappa * T[j]) * kappa + 2.0 * theta[j] * Math.Exp(kappa * T[j]) * kappa * T[j] - 2.0 * theta[j] * Math.Exp(kappa * T[j])) / Math.Pow(kappa, 3.0);
                    for (j = 1; j <= t; j++)
                    {
                        A2 += Math.Pow(rho[j], 2.0) * Math.Pow(sigma[j], 2.0) * (Math.Exp(-kappa * T[t]) + Math.Exp(-kappa * T[t + 1]) * T[t] * kappa - Math.Exp(-kappa * T[t + 1]) - Math.Exp(-kappa * T[t + 1]) * kappa * T[t + 1]) * (-v0 * T[j - 1] * kappa + theta[j] * T[j - 1] * kappa - theta[j] * Math.Exp(kappa * T[j - 1]) + v0 * T[j] * kappa - theta[j] * T[j] * kappa + theta[j] * Math.Exp(kappa * T[j])) / Math.Pow(kappa, 3.0)
                              - 1.0 / 2.0 * Math.Pow(rho[j], 2.0) * Math.Pow(sigma[j], 2.0) * (-Math.Exp(-kappa * T[t]) + Math.Exp(-kappa * T[t + 1])) * (-2.0 * v0 * T[t] * T[j - 1] * Math.Pow(kappa, 2.0) + v0 * Math.Pow(T[j - 1], 2.0) * Math.Pow(kappa, 2.0) + 2.0 * theta[j] * T[t] * T[j - 1] * Math.Pow(kappa, 2.0) - theta[j] * Math.Pow(T[j - 1], 2.0) * Math.Pow(kappa, 2.0) - 2.0 * theta[j] * T[t] * Math.Exp(kappa * T[j - 1]) * kappa + 2.0 * theta[j] * Math.Exp(kappa * T[j - 1]) * kappa * T[j - 1] - 2.0 * theta[j] * Math.Exp(kappa * T[j - 1]) + 2.0 * v0 * T[t] * T[j] * Math.Pow(kappa, 2.0) - v0 * Math.Pow(T[j], 2.0) * Math.Pow(kappa, 2.0) - 2.0 * theta[j] * T[t] * T[j] * Math.Pow(kappa, 2.0) + theta[j] * Math.Pow(T[j], 2.0) * Math.Pow(kappa, 2.0) + 2.0 * theta[j] * T[t] * Math.Exp(kappa * T[j]) * kappa - 2.0 * theta[j] * Math.Exp(kappa * T[j]) * kappa * T[j] + 2.0 * theta[j] * Math.Exp(kappa * T[j])) / Math.Pow(kappa, 3.0);
                    }
                    j          = t + 1;
                    A2        += 1.0 / 2.0 * Math.Pow(rho[j], 2.0) * Math.Pow(sigma[j], 2.0) * (2.0 * v0 * Math.Exp(kappa * T[t + 1]) - v0 * Math.Pow(T[t], 2.0) * Math.Pow(kappa, 2.0) * Math.Exp(kappa * T[t]) + 2.0 * v0 * T[t] * kappa * Math.Exp(kappa * T[t]) + 2.0 * v0 * Math.Pow(kappa, 2.0) * T[t + 1] * T[t] * Math.Exp(kappa * T[t]) - 2.0 * theta[j] * Math.Exp(kappa * T[t + 1]) + theta[j] * Math.Pow(T[t], 2.0) * Math.Pow(kappa, 2.0) * Math.Exp(kappa * T[t]) - 2.0 * theta[j] * T[t] * kappa * Math.Exp(kappa * T[t]) - 2.0 * theta[j] * Math.Pow(kappa, 2.0) * T[t + 1] * T[t] * Math.Exp(kappa * T[t]) - 2.0 * theta[j] * T[t] * kappa * Math.Exp(kappa * (T[t] + T[t + 1])) - 2.0 * theta[j] * Math.Exp(2.0 * kappa * T[t]) * kappa * T[t] + 4.0 * theta[j] * Math.Exp(2.0 * kappa * T[t]) + 2.0 * theta[j] * T[t + 1] * Math.Exp(2.0 * kappa * T[t]) * kappa - 2.0 * Math.Exp(kappa * T[t]) * v0 - 2.0 * Math.Exp(kappa * T[t]) * v0 * kappa * T[t + 1] + 2.0 * Math.Exp(kappa * (T[t] + T[t + 1])) * theta[j] * T[t + 1] * kappa - Math.Exp(kappa * T[t]) * Math.Pow(kappa, 2.0) * v0 * Math.Pow(T[t + 1], 2.0) - 4.0 * theta[j] * Math.Exp(kappa * (T[t] + T[t + 1])) + Math.Exp(kappa * T[t]) * Math.Pow(kappa, 2.0) * theta[j] * Math.Pow(T[t + 1], 2.0) + 2.0 * theta[j] * Math.Exp(kappa * T[t]) + 2.0 * Math.Exp(kappa * T[t]) * theta[j] * kappa * T[t + 1]) / Math.Pow(kappa, 3.0) * Math.Exp(-kappa * (T[t] + T[t + 1]));
                    a2T[t + 1] = a2T[t] + A2;

                    // Coefficients for b0T[t+1]
                    j   = 0;
                    B0 += -1.0 / 4.0 * Math.Pow(sigma[j], 2.0) * (-Math.Exp(-2.0 * kappa * T[t]) + 2.0 * Math.Exp(-kappa * (T[t] + T[t + 1])) - Math.Exp(-2.0 * kappa * T[t + 1])) * (-2.0 * v0 + theta[j] + 2.0 * v0 * Math.Exp(kappa * T[j]) - 2.0 * theta[j] * Math.Exp(kappa * T[j]) + theta[j] * Math.Exp(2.0 * kappa * T[j])) / Math.Pow(kappa, 3.0)
                          + 1.0 / 2.0 * Math.Pow(sigma[j], 2.0) * (-theta[j] * Math.Exp(kappa * T[t + 1]) + theta[j] * Math.Exp(kappa * T[t]) + 2.0 * v0 * Math.Exp(kappa * T[t + 1]) - 2.0 * Math.Exp(kappa * T[t]) * v0 - 2.0 * theta[j] * Math.Exp(kappa * (T[t] + T[t + 1])) + 2.0 * theta[j] * Math.Exp(2.0 * kappa * T[t]) + 2.0 * Math.Exp(kappa * (T[t] + T[t + 1])) * v0 * T[j] * kappa - 2.0 * Math.Exp(kappa * (T[t + 1] + T[j])) * v0 - 2.0 * Math.Exp(kappa * (T[t] + T[t + 1])) * theta[j] * T[j] * kappa + 2.0 * Math.Exp(kappa * (T[t + 1] + T[j])) * theta[j] + 2.0 * Math.Exp(kappa * (T[t] + T[t + 1] + T[j])) * theta[j] - Math.Exp(kappa * (T[t + 1] + 2.0 * T[j])) * theta[j] - 2.0 * Math.Exp(2.0 * kappa * T[t]) * v0 * T[j] * kappa + 2.0 * Math.Exp(kappa * (T[t] + T[j])) * v0 + 2.0 * Math.Exp(2.0 * kappa * T[t]) * theta[j] * T[j] * kappa - 2.0 * Math.Exp(kappa * (T[t] + T[j])) * theta[j] - 2.0 * Math.Exp(kappa * (2.0 * T[t] + T[j])) * theta[j] + Math.Exp(kappa * (T[t] + 2.0 * T[j])) * theta[j]) * Math.Exp(-kappa * (2.0 * T[t] + T[t + 1])) / Math.Pow(kappa, 3.0);
                    for (j = 1; j <= t; j++)
                    {
                        B0 += -1.0 / 4.0 * Math.Pow(sigma[j], 2.0) * (-Math.Exp(-2.0 * kappa * T[t]) + 2.0 * Math.Exp(-kappa * (T[t] + T[t + 1])) - Math.Exp(-2.0 * kappa * T[t + 1])) * (-2.0 * v0 * Math.Exp(kappa * T[j - 1]) + 2.0 * theta[j] * Math.Exp(kappa * T[j - 1]) - theta[j] * Math.Exp(2.0 * kappa * T[j - 1]) + 2.0 * v0 * Math.Exp(kappa * T[j]) - 2.0 * theta[j] * Math.Exp(kappa * T[j]) + theta[j] * Math.Exp(2.0 * kappa * T[j])) / Math.Pow(kappa, 3.0)
                              + 1.0 / 2.0 * Math.Pow(sigma[j], 2.0) * (Math.Exp(-kappa * T[t]) - Math.Exp(-kappa * T[t + 1])) * (-2.0 * v0 * T[j - 1] * kappa * Math.Exp(kappa * T[t]) + 2.0 * v0 * Math.Exp(kappa * T[j - 1]) + 2.0 * theta[j] * T[j - 1] * kappa * Math.Exp(kappa * T[t]) - 2.0 * theta[j] * Math.Exp(kappa * T[j - 1]) - 2.0 * theta[j] * Math.Exp(kappa * (T[j - 1] + T[t])) + theta[j] * Math.Exp(2.0 * kappa * T[j - 1]) + 2.0 * v0 * T[j] * kappa * Math.Exp(kappa * T[t]) - 2.0 * v0 * Math.Exp(kappa * T[j]) - 2.0 * theta[j] * T[j] * kappa * Math.Exp(kappa * T[t]) + 2.0 * theta[j] * Math.Exp(kappa * T[j]) + 2.0 * Math.Exp(kappa * (T[t] + T[j])) * theta[j] - theta[j] * Math.Exp(2.0 * kappa * T[j])) * Math.Exp(-kappa * T[t]) / Math.Pow(kappa, 3.0);
                    }
                    j          = t + 1;
                    B0        += -1.0 / 4.0 * Math.Pow(sigma[j], 2.0) * (-2.0 * v0 * Math.Exp(2.0 * kappa * T[t + 1]) - 4.0 * v0 * T[t] * kappa * Math.Exp(kappa * (T[t] + T[t + 1])) + 2.0 * v0 * Math.Exp(2.0 * kappa * T[t]) + 2.0 * theta[j] * Math.Exp(2.0 * kappa * T[t + 1]) + 4.0 * theta[j] * T[t] * kappa * Math.Exp(kappa * (T[t] + T[t + 1])) - 2.0 * theta[j] * Math.Exp(2.0 * kappa * T[t]) + 2.0 * theta[j] * T[t] * kappa * Math.Exp(kappa * (T[t] + 2.0 * T[t + 1])) - 4.0 * theta[j] * Math.Exp(kappa * (2.0 * T[t] + T[t + 1])) + theta[j] * Math.Exp(3 * kappa * T[t]) + 3.0 * theta[j] * Math.Exp(kappa * (T[t] + 2.0 * T[t + 1])) + 4.0 * Math.Exp(kappa * (T[t] + T[t + 1])) * v0 * kappa * T[t + 1] - 4.0 * Math.Exp(kappa * (T[t] + T[t + 1])) * theta[j] * T[t + 1] * kappa - 2.0 * Math.Exp(kappa * (T[t] + 2.0 * T[t + 1])) * theta[j] * T[t + 1] * kappa) / Math.Pow(kappa, 3.0) * Math.Exp(-kappa * (T[t] + 2.0 * T[t + 1]));
                    b0T[t + 1] = b0T[t] + B0;

                    // Coefficients for b2T[t+1]
                    b2T[t + 1] = a1T[t + 1] * a1T[t + 1] / 2.0;
                }
            }

            // Coefficients for the expansion are the last ones in the iterations
            double A1T = a1T[NT - 1];
            double A2T = a2T[NT - 1];
            double B0T = b0T[NT - 1];
            double B2T = b2T[NT - 1];

            double S       = settings.S;
            double rf      = settings.r;
            double q       = settings.q;
            string PutCall = settings.PutCall;

            // Log Spot price
            double x = Math.Log(settings.S);

            // Integrated variance
            double wT = (v0 - theta[NT - 1]) * (1 - Math.Exp(-kappa * T[NT - 1])) / kappa + theta[NT - 1] * T[NT - 1];
            double y  = wT;

            // Black Scholes Put Price
            BlackScholesPrice BS    = new BlackScholesPrice();
            double            g     = Math.Pow(y, -0.5) * (-x + Math.Log(K) - (rf - q) * T[NT - 1]) - 0.5 * Math.Sqrt(y);
            double            f     = Math.Pow(y, -0.5) * (-x + Math.Log(K) - (rf - q) * T[NT - 1]) + 0.5 * Math.Sqrt(y);
            double            BSPut = K * Math.Exp(-rf * T[NT - 1]) * BS.NormCDF(f) - S * Math.Exp(-q * T[NT - 1]) * BS.NormCDF(g);

            // Normal pdf, phi(f) and phi(g)
            double pi   = Math.PI;
            double phif = Math.Exp(-f * f / 2.0) / Math.Sqrt(2.0 * pi);
            double phig = Math.Exp(-g * g / 2.0) / Math.Sqrt(2.0 * pi);

            // Derivatives of f and g
            double fx = -Math.Pow(y, -0.5);
            double fy = -0.5 / y * g;
            double gx = fx;
            double gy = -0.5 / y * f;

            // The cdf PHI(f) and PHI(g)
            double PHIf = BS.NormCDF(f);
            double PHIg = BS.NormCDF(g);

            // Derivatives of the pdf phi(f)
            double phifx = Math.Pow(y, -0.5) * f * phif;
            double phify = 0.5 / y * f * g * phif;

            // Derivatives of the cdf PHI(f)
            double PHIfxy   = 0.5 * Math.Pow(y, -1.5) * phif * (1.0 - f * g);
            double PHIfx2y  = 0.5 * Math.Pow(y, -2.0) * phif * (2.0 * f + g - f * f * g);
            double PHIfy2   = 0.5 * Math.Pow(y, -2.0) * phif * (g + f / 2.0 - f * g * g / 2.0);
            double PHIfx2y2 = 0.5 * ((Math.Pow(y, -2.0) * phify - 2.0 * Math.Pow(y, -3.0) * phif) * (2.0 * f + g - f * f * g) +
                                     Math.Pow(y, -2.0) * phif * (2.0 * fy + gy - 2.0 * f * fy * g - f * f * gy));

            // Derivatives of the pdf phi(g)
            double phigx = Math.Pow(y, -0.5) * g * phig;
            double phigy = 0.5 / y * f * g * phig;

            // Derivatives of cdf PHI(g)
            double PHIgx = -phig *Math.Pow(y, -0.5);

            double PHIgy    = -0.5 * f * phig / y;
            double PHIgxy   = 0.5 * Math.Pow(y, -1.5) * phig * (1.0 - f * g);
            double PHIgx2y  = 0.5 * Math.Pow(y, -2.0) * phig * (2.0 * g + f - g * g * f);
            double PHIgy2   = 0.5 * Math.Pow(y, -2.0) * phig * (f + g / 2.0 - g * f * f / 2.0);
            double PHIgxy2  = 0.5 * Math.Pow(y, -2.0) * (phigx * (f + g / 2.0 - f * f * g / 2.0) + phig * (fx + gx / 2.0 - f * fx * g / 2.0 - f * f * gx / 2.0));
            double PHIgx2y2 = 0.5 * ((Math.Pow(y, -2.0) * phigy - 2.0 * Math.Pow(y, -3.0) * phig) * (2.0 * g + f - g * g * f) +
                                     Math.Pow(y, -2.0) * phig * (2.0 * gy + fy - 2.0 * g * gy * f - g * g * fy));

            // Derivatives of Black-Scholes Put
            double dPdxdy   = K * Math.Exp(-rf * T[NT - 1]) * PHIfxy - Math.Exp(-q * T[NT - 1]) * S * (PHIgy + PHIgxy);
            double dPdx2dy  = K * Math.Exp(-rf * T[NT - 1]) * PHIfx2y - Math.Exp(-q * T[NT - 1]) * S * (PHIgy + 2.0 * PHIgxy + PHIgx2y);
            double dPdy2    = K * Math.Exp(-rf * T[NT - 1]) * PHIfy2 - Math.Exp(-q * T[NT - 1]) * S * PHIgy2;
            double dPdx2dy2 = K * Math.Exp(-rf * T[NT - 1]) * PHIfx2y2 - Math.Exp(-q * T[NT - 1]) * S * (PHIgy + 2.0 * PHIgxy + PHIgx2y + PHIgy2 + 2.0 * PHIgxy2 + PHIgx2y2);

            // Benhamou, Gobet, Miri expansion
            double Put = BSPut + A1T * dPdxdy + A2T * dPdx2dy + B0T * dPdy2 + B2T * dPdx2dy2;

            // Return the put or the call by put-call parity
            if (PutCall == "P")
            {
                return(Put);
            }
            else
            {
                return(Put - K * Math.Exp(-rf * T[NT - 1]) + S * Math.Exp(-q * T[NT - 1]));
            }
        }
예제 #3
0
        static void Main(string[] args)
        {
            // Classes
            BGMPrice      BGM = new BGMPrice();
            BisectionAlgo BA  = new BisectionAlgo();

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

            // DJIA ETF (ticker DIA) put data
            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[] { 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[] { 37.0 / 365.0, 72.0 / 365.0, 135.0 / 365.0, 226.0 / 365.0, };
            double[,] MktIV = PutIV;
            double S  = 129.14;
            double r  = 0.0010;
            double q  = 0.0068;
            int    NT = 4;
            int    NK = 13;

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

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

            // Bounds on the parameter estimates
            double e = 1e-3;
            double kappaU = 20.0;       double kappaL = e;
            double thetaU = 3.0;       double thetaL = e;
            double sigmaU = 10.0;       double sigmaL = e;
            double v0U = 3.0;       double v0L = e;
            double rhoU = 0.99;       double rhoL = -0.99;
            int    N = 2 + 3 * NT;

            double[] ub = new double[N];
            double[] lb = new double[N];
            for (int j = 0; j <= N; j++)
            {
                ub[0] = kappaU; lb[0] = kappaL;
                ub[1] = v0U; lb[1] = v0L;
                for (int k = 1; k <= NT; k++)
                {
                    ub[3 * k - 1] = thetaU; lb[3 * k - 1] = thetaL;
                    ub[3 * k]     = sigmaU; lb[3 * k - 1] = sigmaL;
                    ub[3 * k + 1] = rhoU; lb[3 * k + 1] = rhoL;
                }
            }

            // Starting values for the parameters
            // Parameter order is [kappa v0 | theta[0] sigma[0] rho[0] | theta[1] sigma[1] rho[1] | etc...];
            double kappaS = 2.0;
            double thetaS = 0.1;
            double sigmaS = 1.2;
            double v0S    = 0.05;
            double rhoS   = -0.5;

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

            // Structure for the option settings
            OPSet opset;

            opset.S       = S;
            opset.r       = r;
            opset.q       = q;
            opset.trap    = 1;
            opset.PutCall = "P";

            // Structure for the market data
            MktData data;

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

            // Structure for the objective function settings
            OFSet ofset;

            ofset.opset       = opset;
            ofset.data        = data;
            ofset.X           = X;
            ofset.W           = W;
            ofset.ObjFunction = 4;
            ofset.lb          = lb;
            ofset.ub          = ub;

            // Structure for the Nelder Mead algorithm settings
            NMSet nmset;

            nmset.ofset     = ofset;
            nmset.MaxIters  = 5000;
            nmset.Tolerance = 1e-8;
            nmset.N         = N;

            // Run the Nelder Mead Algorithm
            NelderMeadAlgo    NM = new NelderMeadAlgo();
            ObjectiveFunction OF = new ObjectiveFunction();

            double[] B  = NM.NelderMead(OF.f, nmset, s);
            int      NB = B.Length;

            // Separate the parameter vector into different vectors
            double kappa = B[0];
            double v0    = B[1];

            double[] theta = new double[NT];
            double[] sigma = new double[NT];
            double[] rho   = new double[NT];
            for (int k = 0; k <= NT - 1; k++)
            {
                theta[k] = B[3 * k + 2];
                sigma[k] = B[3 * k + 3];
                rho[k]   = B[3 * k + 4];
            }

            // Bisection algorithm settings
            double a       = 0.01;
            double b       = 4.0;
            double Tol     = 1e-5;
            int    MaxIter = 2500;

            // Find the implied volatilities
            double[,] ModelPrice = new double[NK, NT];
            double[,] ModelIV    = new double[NK, NT];
            double Error = 0.00;

            List <double> MatList   = new List <double>();
            List <double> thetaList = new List <double>();
            List <double> sigmaList = new List <double>();
            List <double> rhoList   = new List <double>();

            double[] Mat, Theta, Sigma, Rho;
            for (int t = 0; t < NT; t++)
            {
                // Stack the parameters
                MatList.Add(T[t]);
                thetaList.Add(theta[t]);
                sigmaList.Add(sigma[t]);
                rhoList.Add(rho[t]);
                // Convert to arrays
                Mat   = MatList.ToArray();
                Theta = thetaList.ToArray();
                Sigma = sigmaList.ToArray();
                Rho   = rhoList.ToArray();
                for (int k = 0; k < NK; k++)
                {
                    ModelPrice[k, t] = BGM.BGMApproxPriceTD(kappa, v0, Theta, Sigma, Rho, opset, K[k], Mat);
                    ModelIV[k, t]    = BA.BisecBSIV(opset, K[k], T[t], a, b, ModelPrice[k, t], Tol, MaxIter);
                    Error           += Math.Pow(ModelIV[k, t] - MktIV[k, t], 2.0) / Convert.ToDouble(NT * NK);
                }
            }

            // Output the results
            Console.WriteLine("----------------------------------------------------------");
            Console.WriteLine("    kappa      v0         theta      sigma        rho");
            Console.WriteLine("----------------------------------------------------------");
            for (int k = 0; k <= NT - 1; k++)
            {
                Console.WriteLine("{0,10:F5} {1,10:F5} {2,10:F5} {3,10:F5} {4,10:F5}", kappa, v0, theta[k], sigma[k], rho[k]);
            }

            Console.WriteLine("----------------------------------------------------------");
            Console.WriteLine("Value of objective function   {0:F5} ", B[NB - 2]);
            Console.WriteLine("Number of iterations required {0:0}  ", B[NB - 1]);
            Console.WriteLine("----------------------------------------------------------");
            Console.WriteLine(" ");
            Console.WriteLine("Model/Market implied volatilities");
            Console.WriteLine("----------------------------------------------------------");
            Console.WriteLine("Strike    Mat1       Mat2        Mat3       Mat4");
            Console.WriteLine("----------------------------------------------------------");
            for (int k = 0; k <= NK - 1; k++)
            {
                Console.WriteLine("{0,5:0} {1,10:F5} {2,10:F5} {3,10:F5} {4,10:F5}", K[k], ModelIV[k, 0], ModelIV[k, 1], ModelIV[k, 2], ModelIV[k, 3]);
                Console.WriteLine("{0,5:0} {1,10:F5} {2,10:F5} {3,10:F5} {4,10:F5}", K[k], MktIV[k, 0], MktIV[k, 1], MktIV[k, 2], MktIV[k, 3]);
                Console.WriteLine("----------------------------------------------------------");
            }
            Console.WriteLine(" ");
            Console.WriteLine("IV Estimation error {0,5:E5}", Error);
            Console.WriteLine(" ");
        }