/// <summary>Gets the implied Black volatility of a specific european call option.
            /// </summary>
            /// <param name="strike">The strike.</param>
            /// <param name="forward">The forward.</param>
            /// <param name="timeToExpiry">The time span between valuation date and expiry date in its <see cref="System.Double"/> representation.</param>
            /// <param name="c0">The dimensionless option price, i.e. option price divided by forward and discount factor.</param>
            /// <param name="value">The implied Black volatility (output).</param>
            /// <returns>A value indicating whether <paramref name="value"/> contains valid data.</returns>
            private ImpliedCalculationResultState TryGetImpliedCallVolatility(double strike, double forward, double timeToExpiry, double c0, out double value)
            {
                var x           = Math.Log(forward / strike);
                var expOfMinusx = strike / forward;

                if (x > 0) // "in-out" duality
                {
                    x           = -x;
                    expOfMinusx = forward / strike;

                    c0 = expOfMinusx * c0 + 1 - expOfMinusx;  // expOfMinusx = exp( [original] x )
                }
                var v0 = GetInitialCallOptionTotalVolatility(x, c0);
                var oneOverOnePlusW = 1.0 / (1 + m_RelaxationParameter);

                var v = v0;

                for (int j = 0; j < m_MaxNumberOfIterations; j++)
                {
                    var    nPlus  = StandardNormalDistribution.GetCdfValue(x / v + 0.5 * v);
                    var    nMinus = expOfMinusx * StandardNormalDistribution.GetCdfValue(x / v - 0.5 * v);
                    double c      = nPlus - nMinus; // undiscounted normalized option value

                    var temp = StandardNormalDistribution.GetInverseCdfValue((c0 + nMinus + m_RelaxationParameter * nPlus) * oneOverOnePlusW);
                    v = temp + Math.Sqrt(temp * temp + 2.0 * Math.Abs(x));

                    if (Math.Abs(c - c0) < m_Tolerance)
                    {
                        value = v / Math.Sqrt(timeToExpiry);  // here, we assume that the 'new' total volatility estimation is better than the one used for the calculation of 'c'
                        return(ImpliedCalculationResultState.ProperResult);
                    }
                }
                value = v / Math.Sqrt(timeToExpiry);
                return(ImpliedCalculationResultState.NoProperResult);
            }
Exemple #2
0
 /// <summary>Computes the inverse cumulative normal distribution function values of vector elements.
 /// </summary>
 /// <param name="n">The number of elements to be calculated.</param>
 /// <param name="a">The input vector a.</param>
 /// <param name="y">The output vector N^{-1}(a[j]), j=0,...,<paramref name="n"/>-1, where N(x) =\int_{-\infty}^x  1/\sqrt(2*PI) * exp(-1/2 *t^2) dt.</param>
 public void CdfNormInv(int n, double[] a, double[] y)
 {
     for (int j = 0; j < n; j++)
     {
         y[j] = StandardNormalDistribution.GetInverseCdfValue(a[j]);
     }
 }
Exemple #3
0
 /// <summary>Computes the inverse cumulative normal distribution function values of vector elements.
 /// </summary>
 /// <param name="n">The number of elements to be calculated.</param>
 /// <param name="a">The input vector a.</param>
 /// <param name="y">The output vector y with y[<paramref name="startIndexY"/> + j] := N^{-1}(a[<paramref name="startIndexA"/> + j]) for j = 0,...,<paramref name="n"/>-1, where N(x) =\int_{-\infty}^x  1/\sqrt(2*PI) * exp(-1/2 *t^2) dt.</param>
 /// <param name="startIndexA">The null-based start index of <paramref name="a"/>.</param>
 /// <param name="startIndexY">The null-based start index of <paramref name="y"/>.</param>
 public void CdfNormInv(int n, double[] a, double[] y, int startIndexA, int startIndexY)
 {
     for (int j = 0; j < n; j++)
     {
         y[j + startIndexY] = StandardNormalDistribution.GetInverseCdfValue(a[j + startIndexA]);
     }
 }
 /// <summary>Gets the implied strike for a specific option price.
 /// </summary>
 /// <param name="optionValue">The value of the option.</param>
 /// <param name="volatility">The volatility.</param>
 /// <param name="impliedStrike">The implied strike (output).</param>
 /// <returns>A value indicating whether <paramref name="impliedStrike"/> contains valid data.</returns>
 public ImpliedCalculationResultState TryGetImpliedStrike(double optionValue, double volatility, out double impliedStrike)
 {
     if (m_TimeToExpiration < MachineConsts.Epsilon)
     {
         impliedStrike = m_Forward;
         return(ImpliedCalculationResultState.ProperResult);
     }
     impliedStrike = m_Forward * Math.Exp(StandardNormalDistribution.GetInverseCdfValue(optionValue / m_DiscountFactor) * volatility * Math.Sqrt(m_TimeToExpiration) - 0.5 * volatility * volatility * m_TimeToExpiration);
     return((Double.IsNaN(impliedStrike) == false) ? ImpliedCalculationResultState.ProperResult : ImpliedCalculationResultState.NoProperResult);
 }
Exemple #5
0
 /// <summary>Gets the value of the copula at some point.
 /// </summary>
 /// <param name="x">The argument x.</param>
 /// <param name="y">The argument y.</param>
 /// <returns>
 /// The value of the copula at (<paramref name="x"/>,<paramref name="y"/>).
 /// </returns>
 public double GetValue(double x, double y)
 {
     if ((x < MachineConsts.SuperTinyEpsilon) || (y < MachineConsts.SuperTinyEpsilon))
     {
         return(0.0);
     }
     if (1.0 - x < MachineConsts.SuperTinyEpsilon)
     {
         if (1.0 - y < MachineConsts.SuperTinyEpsilon)
         {
             return(x);
         }
         return(y);
     }
     if (1.0 - y < MachineConsts.SuperTinyEpsilon)
     {
         return(x);
     }
     return(BivariateNormalDistribution.StandardCDFValue(StandardNormalDistribution.GetInverseCdfValue(x), StandardNormalDistribution.GetInverseCdfValue(y), m_Correlation));
 }
        /// <summary>Gets the implied volatility for a specific non-discounted option price.
        /// </summary>
        /// <param name="noneDiscountedValue">The value of the option at the time of expiry, thus the price but <b>not</b> discounted to time 0.</param>
        /// <param name="impliedVolatility">The implied volatility (output).</param>
        /// <returns>A value indicating whether <paramref name="impliedVolatility" /> contains valid data.</returns>
        /// <remarks>This method is the inverse function of <see cref="IConstantVolatilityStandardEuropeanOption.GetNoneDiscountedValue(double)" />.</remarks>
        public ImpliedCalculationResultState TryGetImpliedVolatilityOfNonDiscountedValue(double noneDiscountedValue, out double impliedVolatility)
        {
            double x        = Math.Log(m_Forward / m_Strike);
            double NInverse = StandardNormalDistribution.GetInverseCdfValue(noneDiscountedValue);

            if (NInverse * NInverse + 2 * x >= 0)
            {
                double root = Math.Sqrt(NInverse * NInverse + 2 * x);
                impliedVolatility = NInverse + root;

                if (NInverse - root >= 0)
                {
                    impliedVolatility = NInverse - root;
                }
                impliedVolatility /= Math.Sqrt(m_TimeToExpiration);
                return(ImpliedCalculationResultState.ProperResult);
            }
            impliedVolatility = 0.0;
            return(ImpliedCalculationResultState.NoProperResult);
        }
Exemple #7
0
 /// <summary>Gets the implied strike for a specific option price.
 /// </summary>
 /// <param name="optionValue">The value of the option.</param>
 /// <param name="volatility">The volatility.</param>
 /// <param name="impliedStrike">The implied strike (output).</param>
 /// <returns>A value indicating whether <paramref name="impliedStrike"/> contains valid data.</returns>
 public ImpliedCalculationResultState TryGetImpliedStrike(double optionValue, double volatility, out double impliedStrike)
 {
     impliedStrike = m_Forward + volatility * SqrtOfTimeToExpiration * StandardNormalDistribution.GetInverseCdfValue(optionValue / m_DiscountFactor);
     return((Double.IsInfinity(impliedStrike) || Double.IsNaN(impliedStrike)) ? ImpliedCalculationResultState.InputError : ImpliedCalculationResultState.ProperResult);
 }
Exemple #8
0
 /// <summary>Gets the implied volatility for a specific non-discounted option price.
 /// </summary>
 /// <param name="noneDiscountedValue">The value of the option at the time of expiry, thus the price but <b>not</b> discounted to time 0.</param>
 /// <param name="impliedVolatility">The implied volatility (output).</param>
 /// <returns>A value indicating whether <paramref name="impliedVolatility" /> contains valid data.</returns>
 /// <remarks>This method is the inverse function of <see cref="IConstantVolatilityStandardEuropeanOption.GetNoneDiscountedValue(double)" />.</remarks>
 public ImpliedCalculationResultState TryGetImpliedVolatilityOfNonDiscountedValue(double noneDiscountedValue, out double impliedVolatility)
 {
     impliedVolatility = (m_Strike - m_Forward) / (SqrtOfTimeToExpiration * StandardNormalDistribution.GetInverseCdfValue(noneDiscountedValue));
     return((Double.IsInfinity(impliedVolatility) || Double.IsNaN(impliedVolatility)) ? ImpliedCalculationResultState.InputError : ImpliedCalculationResultState.ProperResult);
 }
            /// <summary>Gets the implied Black-Scholes volatility for a given value (price) of a specific call or put option.
            /// </summary>
            /// <param name="theta">A value indicating whether a call (=1) or put (=-1) is given.</param>
            /// <param name="strike">The strike.</param>
            /// <param name="forward">The forward.</param>
            /// <param name="timeToExpiry">The time span between valuation date and expiry date in its <see cref="System.Double"/> representation.</param>
            /// <param name="noneDiscountedValue">The value of the option at the time of expiry, thus the price but <b>not</b> discounted to time 0.</param>
            /// <param name="impliedBlackVolatility">The implied Black volatility (output).</param>
            /// <returns>A value indicating whether <paramref name="impliedBlackVolatility"/> contains valid data.</returns>
            private ImpliedCalculationResultState TryGetPlainVanillaImpliedVolatility(int theta, double strike, double forward, double timeToExpiry, double noneDiscountedValue, out double impliedBlackVolatility)
            {
                impliedBlackVolatility = 0.0;

                /* calculate some constants only once: */
                double x = Math.Log(forward / strike);

                if (Double.IsNaN(x))  // strike <= 0?
                {
                    return(ImpliedCalculationResultState.InputError);
                }

                double beta = noneDiscountedValue / Math.Sqrt(forward * strike);  // normalized value: the given value is already discounted

                /* 1. case: at-the-money option:
                 * Compute the implied vola via inverse commulative distribution function */

                if (Math.Abs(x) < MachineConsts.Epsilon)  // at-the-money option
                {
                    impliedBlackVolatility = -2.0 * StandardNormalDistribution.GetInverseCdfValue(0.5 - 0.5 * beta);
                    return(ImpliedCalculationResultState.ProperResult);
                }

                /* 2. case: in-the-money option:
                 * subtracting the normalized intrinsic value from the normalized value if necessary */

                double expOfXDivTwo = Math.Sqrt(forward / strike);  // =  Math.Exp(x / 2.0);
                double iota         = theta * DoMath.HeavisideFunction(theta * x) * (expOfXDivTwo - 1 / expOfXDivTwo);

                /* the normalized value has to be an element of the interval
                 *           [iota, exp( \theta * x/2)],
                 * otherwise no implied value can be calculated!
                 */
                if (beta < iota)
                {
                    return(ImpliedCalculationResultState.InputError);  // one may sets 'beta = iota;'
                }
                if (beta > ((theta == 1) ? expOfXDivTwo : 1 / expOfXDivTwo))
                {
                    return(ImpliedCalculationResultState.InputError); // one may sets 'beta = ((theta == 1) ? expOfXDivTwo : 1 / expOfXDivTwo);'
                }
                bool isInTheMoneyOption = false;                      // indicates if it is a in-the-money option

                if (theta == -1)
                {
                    if (x < 0)
                    {
                        isInTheMoneyOption = true;
                    }
                }
                else
                {
                    if (x > 0)  // strike < forward
                    {
                        isInTheMoneyOption = true;
                    }
                }
                if (isInTheMoneyOption == true)
                {
                    beta -= iota;
                    theta = 1 - 2 * DoMath.HeavisideFunction(x);

                    /* now consider some out-of-the money option and recalculate the intrinsic value */
                    iota = theta * DoMath.HeavisideFunction(theta * x) * (expOfXDivTwo - 1 / expOfXDivTwo);
                }

                /* 3.) Set initual guess: */
                double bc = NormalizedCallPutValue(theta, x, expOfXDivTwo, Math.Sqrt(2 * Math.Abs(x)));

                /* 3a.) sigmaLow and sigmaHigh are given by formula (3.6)-(3.7) */
                double tempForSigmaHighExp = (theta == 1) ? expOfXDivTwo : 1 / expOfXDivTwo;            // = Math.Exp(theta * x / 2.0)
                double tempForSigmaHighCDF = StandardNormalDistribution.GetCdfValue(-Math.Sqrt(Math.Abs(x) / 2.0));

                double sigmaLow  = Math.Sqrt(2 * x * x / (Math.Abs(x) - 4 * Math.Log((beta - iota) / (bc - iota))));
                double sigmaHigh = -2 * StandardNormalDistribution.GetInverseCdfValue((tempForSigmaHighExp - beta) / (tempForSigmaHighExp - bc) * tempForSigmaHighCDF);

                /* 2b.) calculate the gamma which is given in formula (4.7): */
                double sigmaStar = -2 * StandardNormalDistribution.GetInverseCdfValue(tempForSigmaHighExp / (tempForSigmaHighExp - bc) * tempForSigmaHighCDF);
                double bStar     = NormalizedCallPutValue(theta, x, expOfXDivTwo, sigmaStar);

                double sigmaLowStar  = Math.Sqrt(2 * x * x / (Math.Abs(x) - 4 * Math.Log((bStar - iota) / (bc - iota))));
                double sigmaHighStar = -2 * StandardNormalDistribution.GetInverseCdfValue((tempForSigmaHighExp - bStar) / (tempForSigmaHighExp - bc) * tempForSigmaHighCDF);

                // the weight w^* given by formula (4.8)
                double w = Math.Pow(Math.Min(Math.Max((sigmaStar - sigmaLowStar) / (sigmaHighStar - sigmaLowStar), 0), 1.0), Math.Log(bc / beta) / Math.Log(bc / bStar));

                /* the code based on the 2006 edition of the paper, i.e. using formula (4.1) in "By implication", 24.11.2010:
                 * // double gamma = Math.Log((sigmaStar - sigmaLowStar) / (sigmaHighStar - sigmaLowStar)) / Math.Log(bStar / bc);
                 * // double w = Math.Min(1, Math.Pow(beta / bc, gamma));   // its the weight given by (4.2)
                 *
                 * /* 2c.) now the initial value is given; see formula (4.1):  */
                if (Double.IsNaN(sigmaLow) == false)
                {
                    impliedBlackVolatility = sigmaLow * (1 - w) + sigmaHigh * w;
                }
                else
                {
                    /* it is not hard to see that if \sigma_low is not defined then 'beta/bc' > 1
                     * which implies w = 1.0 */
                    impliedBlackVolatility = sigmaHigh;
                }

                /* 3.) Do the Halley's iteration method: */
                double terminatingCondition = 1.0;

                for (int i = 1; i <= m_MaxNumberOfIterations; i++)
                {
                    /* A.) compute nu^_n(x,\sigma_n,\Theta) via formula (4.10) & (4.13): */

                    // first compute nu_n (without 'hat') via (3.10):
                    double b = NormalizedCallPutValue(theta, x, expOfXDivTwo, impliedBlackVolatility);
                    double impliedBSVolaSquare = impliedBlackVolatility * impliedBlackVolatility;

                    double bDash = MathConsts.OneOverSqrtTwoPi * Math.Exp(-0.5 * x * x / impliedBSVolaSquare - 0.125 * impliedBSVolaSquare);

                    double nuN = 0.0;
                    if (beta < bc)
                    {
                        nuN = Math.Log((beta - iota) / (b - iota)) * Math.Log(b - iota) / Math.Log(beta - iota) * (b - iota) / bDash;
                    }
                    else
                    {
                        nuN = (beta - b) / bDash;
                    }
                    double nuHat = Math.Max(nuN, -impliedBlackVolatility / 2.0);

                    /* B. compute eta^ via formula (4.14) & (4.15) & (4.11): */
                    double etaHat = x * x / (impliedBSVolaSquare * impliedBlackVolatility) - impliedBlackVolatility / 4.0;
                    if (beta < bc)
                    {
                        double logOfbMinusIota = Math.Log(b - iota);
                        etaHat -= (2 + logOfbMinusIota) / logOfbMinusIota * bDash / (b - iota);
                    }
                    etaHat = Math.Max(-0.75, etaHat / 2.0 * nuHat);

                    /* C. next step and store the terminating condition */
                    terminatingCondition   = impliedBlackVolatility; // store the volatility which is used in the step before
                    impliedBlackVolatility = impliedBlackVolatility + Math.Max(nuHat / (1 + etaHat), -impliedBlackVolatility / 2.0);

                    terminatingCondition = Math.Abs(impliedBlackVolatility / terminatingCondition - 1);

                    if (Double.IsNaN(terminatingCondition))
                    {
                        impliedBlackVolatility = Double.NaN;
                        return(ImpliedCalculationResultState.NoProperResult);
                    }
                    if (terminatingCondition < m_Tolerance)
                    {
                        impliedBlackVolatility /= Math.Sqrt(timeToExpiry);
                        return(ImpliedCalculationResultState.ProperResult);
                    }
                }
                impliedBlackVolatility /= Math.Sqrt(timeToExpiry);
                return(ImpliedCalculationResultState.NoProperResult);
            }