Пример #1
0
 /// <summary>Computes the 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(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 CdfNorm(int n, double[] a, double[] y, int startIndexA, int startIndexY)
 {
     for (int j = 0; j < n; j++)
     {
         y[j + startIndexY] = StandardNormalDistribution.GetCdfValue(a[j + startIndexA]);
     }
 }
Пример #2
0
        /// <summary>Gets the implied strike for a specific european straddle.
        /// </summary>
        /// <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="undiscountedValue">The value of the option at the time of expiry, thus the price but <b>not</b> discounted to time 0.</param>
        /// <param name="blackVolatility">The Black volatility.</param>
        /// <param name="initialStrike">The initial strike used as initial guess for the Newton method.</param>
        /// <param name="impliedStrike">The implied strike (output).</param>
        /// <returns>
        ///    Returns a value indicating whether <paramref name="impliedStrike"/> contains valid data.
        /// </returns>
        /// <remarks>This method checks some academic conditions and is based on the Newton approach.
        ///    <para>The implied strike is not unique.</para>
        /// </remarks>
        protected static ImpliedCalculationResultState TryGetImpliedStrike(double forward, double timeToExpiry, double undiscountedValue, double blackVolatility, double initialStrike, out double impliedStrike)
        {
            /* the value of a straddle is given by K-F + 2 * CallValue(K) thus we do it in a simular way as in the case of a call option */
            impliedStrike = initialStrike;
            double optionValueForGivenStrike = undiscountedValue;
            double absUndiscountedValue      = Math.Abs(undiscountedValue);
            double sqrtOfTime = Math.Sqrt(timeToExpiry);

            double dplus, CDFAtdMinus;

            for (int i = 1; i <= MaxNumberIterationImpliedStrike; i++)
            {
                if (impliedStrike < MachineConsts.SuperTinyEpsilon)
                {
                    optionValueForGivenStrike = Math.Abs(forward - impliedStrike);
                    impliedStrike             = forward - undiscountedValue;
                }
                else
                {
                    dplus       = (Math.Log(forward / impliedStrike) + 0.5 * blackVolatility * blackVolatility * timeToExpiry) / (blackVolatility * sqrtOfTime);
                    CDFAtdMinus = StandardNormalDistribution.GetCdfValue(dplus - blackVolatility * sqrtOfTime);

                    optionValueForGivenStrike = impliedStrike - forward + 2 * (forward * StandardNormalDistribution.GetCdfValue(dplus) - impliedStrike * CDFAtdMinus);
                    impliedStrike            += optionValueForGivenStrike * Math.Log(optionValueForGivenStrike / undiscountedValue) / (1 + 2.0 * CDFAtdMinus);
                }
                if (Math.Abs(undiscountedValue - optionValueForGivenStrike) < absUndiscountedValue * MachineConsts.TinyEpsilon)
                {
                    return(ImpliedCalculationResultState.ProperResult);
                }
            }
            return(ImpliedCalculationResultState.NoProperResult);
        }
            /// <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);
            }
Пример #4
0
 /// <summary>Computes the cumulative normal distribution function values of vector elements [in-place].
 /// </summary>
 /// <param name="n">The number of elements to be calculated.</param>
 /// <param name="a">The input vector a; overwritten by the result of the operation, i.e. N(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 CdfNorm(int n, double[] a)
 {
     for (int j = 0; j < n; j++)
     {
         a[j] = StandardNormalDistribution.GetCdfValue(a[j]);
     }
 }
 /// <summary>Gets the normalized value (price) of a call or put option.
 /// </summary>
 /// <param name="theta">Indicates if a call (=1) or put (=-1) is given.</param>
 /// <param name="x">The logarithm of 'forward / strike'.</param>
 /// <param name="expOfXDivTwo"><paramref name="x"/> divided by two and applied to the exponential function. This
 /// argument will be computed once only (performance reason).</param>
 /// <param name="normalisedVolatility">The normalized volatility, i.e. the volatility divided by the
 /// square root of the time span between valuation and expiry date.</param>
 /// <returns>The normalized value of the call/put option, thus multiplying the result
 /// by the specific discount factor and the square root of forward * strike gives the value of the option.</returns>
 /// <remarks>
 /// The normalized price (value) of the option is given by
 /// <para>
 ///      value / (discount factor * Sqrt(forward * strike))
 /// </para>
 /// thus multiply the result by the discount factor * Sqrt(forward * strike)
 /// to get the correct price.
 /// <para>The implementation is based on 'By implication', Peter Jäckel, formula (2.2)-(2.3).</para>
 /// </remarks>
 private static double NormalizedCallPutValue(int theta, double x, double expOfXDivTwo, double normalisedVolatility)
 {
     if (Math.Abs(x) < MachineConsts.Epsilon)
     {
         return(1 - 2 * StandardNormalDistribution.GetCdfValue(-normalisedVolatility / 2.0));
     }
     return(theta * (expOfXDivTwo * StandardNormalDistribution.GetCdfValue(theta * (x / normalisedVolatility + normalisedVolatility / 2.0))
                     - 1.0 / expOfXDivTwo * StandardNormalDistribution.GetCdfValue(theta * (x / normalisedVolatility - normalisedVolatility / 2.0))));
 }
Пример #6
0
        /// <summary>Gets the price of the option <c>at time of expiry</c>, i.e. not discounted.
        /// </summary>
        /// <param name="volatility">The volatility.</param>
        /// <returns>The value of the option <c>at the time of expiry</c>, thus not discounted. To get the price just multiply the return value with the discount factor.</returns>
        public double GetNoneDiscountedValue(double volatility)
        {
            if ((m_Strike < MachineConsts.Epsilon) || (volatility * volatility * m_TimeToExpiration < MachineConsts.Epsilon))
            {
                return(m_Forward >= m_Strike ? 0.0 : 1.0);
            }
            double d = (m_Strike - m_Forward) / (volatility * SqrtOfTimeToExpiration);

            return(StandardNormalDistribution.GetCdfValue(d));
        }
Пример #7
0
        /// <summary>Gets the forward-delta of the option, i.e. the partial derivative of the option value formula with respect to the forward.
        /// </summary>
        /// <param name="volatility">The volatility.</param>
        /// <returns>The forward-delta of the option, i.e. the partial derivative of the option value formula with respect to the forward.
        /// </returns>
        public double GetForwardDelta(double volatility)
        {
            if ((m_Strike < MachineConsts.Epsilon) || (volatility * volatility * m_TimeToExpiration < MachineConsts.Epsilon))
            {
                return((m_Strike > m_Forward) ? 1 : 0);
            }
            double d = (m_Strike - m_Forward) / (volatility * SqrtOfTimeToExpiration);

            return(-m_DiscountFactor *StandardNormalDistribution.GetCdfValue(d));
        }
Пример #8
0
        /// <summary>Gets the price of the option <c>at time of expiry</c>, i.e. not discounted.
        /// </summary>
        /// <param name="volatility">The volatility.</param>
        /// <returns>The value of the option <c>at the time of expiry</c>, thus not discounted. To get the price just multiply the return value with the discount factor.</returns>
        public double GetNoneDiscountedValue(double volatility)
        {
            if (volatility * volatility * m_TimeToExpiration < MachineConsts.Epsilon)
            {
                return(Math.Max(0, m_Strike - m_Forward));
            }
            double d = (m_Strike - m_Forward) / (volatility * SqrtOfTimeToExpiration);

            return((m_Strike - m_Forward) * StandardNormalDistribution.GetCdfValue(d) + volatility * SqrtOfTimeToExpiration * StandardNormalDistribution.GetPdfValue(d));
        }
Пример #9
0
        /// <summary>Gets the strike-delta of the option, i.e. the partial derivative of the option value formula with respect to the strike.
        /// </summary>
        /// <param name="volatility">The volatility.</param>
        /// <returns>The strike-delta of the option, i.e. the partial derivative of the option value formula with respect to the strike.</returns>
        public double GetStrikeDelta(double volatility)
        {
            if (volatility * volatility * m_TimeToExpiration < MachineConsts.Epsilon)
            {
                return(0.0);
            }
            double d = (m_Forward - m_Strike) / (volatility * SqrtOfTimeToExpiration);

            return(m_DiscountFactor * (1.0 - 2.0 * StandardNormalDistribution.GetCdfValue(d)));
        }
Пример #10
0
        /// <summary>Gets the forward-delta of the option, i.e. the partial derivative of the option value formula with respect to the forward.
        /// </summary>
        /// <param name="volatility">The volatility.</param>
        /// <returns>The forward-delta of the option, i.e. the partial derivative of the option value formula with respect to the forward.</returns>
        public double GetForwardDelta(double volatility)
        {
            if ((m_Strike < MachineConsts.Epsilon) || (volatility * volatility * m_TimeToExpiration < MachineConsts.Epsilon))
            {
                return((m_Forward >= m_Strike) ? 1 : -1);
            }
            double dplus = (LogOfMoneyness + 0.5 * volatility * volatility * m_TimeToExpiration) / (volatility * SqrtOfTimeToExpiration);

            return(m_DiscountFactor * (2.0 * StandardNormalDistribution.GetCdfValue(dplus) - 1.0));
        }
Пример #11
0
        /// <summary>Gets the strike-delta of the option, i.e. the partial derivative of the option value formula with
        /// respect to the strike.
        /// </summary>
        /// <param name="volatility">The volatility.</param>
        /// <returns>The strike-delta of the option, i.e. the partial derivative of the option value formula with respect to the strike.</returns>
        public double GetStrikeDelta(double volatility)
        {
            if ((m_Strike < MachineConsts.Epsilon) || (volatility * volatility * m_TimeToExpiration < MachineConsts.Epsilon))
            {
                return(0.0);
            }
            double dminus = (LogOfMoneyness - 0.5 * volatility * volatility * m_TimeToExpiration) / (volatility * SqrtOfTimeToExpiration);

            return(-m_DiscountFactor *StandardNormalDistribution.GetCdfValue(dminus));
        }
        /// <summary>Gets the price of the option <c>at time of expiry</c>, i.e. not discounted.
        /// </summary>
        /// <param name="volatility">The volatility.</param>
        /// <returns>The value of the option <c>at the time of expiry</c>, thus not discounted. To get the price just multiply the return value with the discount factor.
        /// </returns>
        public double GetNoneDiscountedValue(double volatility)
        {
            /* mind numerical traps: */
            if ((m_Strike < MachineConsts.Epsilon) || (volatility * volatility * m_TimeToExpiration < MachineConsts.Epsilon))
            {
                return(m_Forward <= m_Strike ? 1.0 : 0.0);
            }
            double dminus = (LogOfMoneyness - 0.5 * volatility * volatility * m_TimeToExpiration) / (volatility * SqrtOfTimeToExpiration);

            return(StandardNormalDistribution.GetCdfValue(-dminus));
        }
Пример #13
0
        /// <summary>Gets the price of the option <c>at time of expiry</c>, i.e. not discounted.
        /// </summary>
        /// <param name="volatility">The volatility.</param>
        /// <returns>The value of the option <c>at the time of expiry</c>, thus not discounted. To get the price just multiply the return value with the discount factor.</returns>
        public double GetNoneDiscountedValue(double volatility)
        {
            if ((m_Strike < MachineConsts.Epsilon) || (volatility * volatility * m_TimeToExpiration < MachineConsts.Epsilon))
            {
                return(Math.Max(0, m_Strike - m_Forward));
            }
            double dplus  = (LogOfMoneyness + 0.5 * volatility * volatility * m_TimeToExpiration) / (volatility * SqrtOfTimeToExpiration);
            double dminus = dplus - volatility * SqrtOfTimeToExpiration;

            return(m_Strike * StandardNormalDistribution.GetCdfValue(-dminus) - m_Forward * StandardNormalDistribution.GetCdfValue(-dplus));
        }
Пример #14
0
        /// <summary>Gets the price of the option <c>at time of expiry</c>, i.e. not discounted.
        /// </summary>
        /// <param name="volatility">The volatility.</param>
        /// <returns>The value of the option <c>at the time of expiry</c>, thus not discounted. To get the price just multiply the return value with the discount factor.</returns>
        public double GetNoneDiscountedValue(double volatility)
        {
            /* mind numerical traps: */
            if ((m_Strike < MachineConsts.Epsilon) || (volatility * volatility * m_TimeToExpiration < MachineConsts.Epsilon))
            {
                return(Math.Max(0, m_Forward - m_Strike));
            }
            double dplus  = LogOfMoneyness / (volatility * SqrtOfTimeToExpiration) + 0.5 * volatility * SqrtOfTimeToExpiration;
            double dminus = dplus - volatility * SqrtOfTimeToExpiration;

            return(m_Forward * StandardNormalDistribution.GetCdfValue(dplus) - m_Strike * StandardNormalDistribution.GetCdfValue(dminus));
        }
Пример #15
0
        /// <summary>Gets the price of the option <c>at time of expiry</c>, i.e. not discounted.
        /// </summary>
        /// <param name="volatility">The volatility.</param>
        /// <returns>The value of the option <c>at the time of expiry</c>, thus not discounted. To get the price just multiply the return value with the discount factor.</returns>
        public double GetNoneDiscountedValue(double volatility)
        {
            double forwardMinusStrike = m_Forward - m_Strike;

            if (volatility * volatility * m_TimeToExpiration < MachineConsts.Epsilon)
            {
                return(Math.Max(0, forwardMinusStrike));
            }
            double d = forwardMinusStrike / (volatility * SqrtOfTimeToExpiration);

            return(forwardMinusStrike * StandardNormalDistribution.GetCdfValue(d) + volatility * SqrtOfTimeToExpiration * StandardNormalDistribution.GetPdfValue(d));
        }
Пример #16
0
        /// <summary>Gets the price of the option <c>at time of expiry</c>, i.e. not discounted.
        /// </summary>
        /// <param name="volatility">The volatility.</param>
        /// <returns>The value of the option <c>at the time of expiry</c>, thus not discounted. To get
        /// the price just multiply the return value with the discount factor.
        /// </returns>
        public double GetNoneDiscountedValue(double volatility)
        {
            if ((m_Strike < MachineConsts.Epsilon) || (volatility * volatility * m_TimeToExpiration < MachineConsts.Epsilon))
            {
                return(Math.Abs(m_Strike - m_Forward));
            }
            double dplus  = (LogOfMoneyness + 0.5 * volatility * volatility * m_TimeToExpiration) / (volatility * SqrtOfTimeToExpiration);
            double dminus = dplus - volatility * SqrtOfTimeToExpiration;

            /* the value is given by: (K: strike, f: forward), use N(-t) = 1-N(t)
             *   discfactor * [ f * N(d+) - K * N(d-) + K * N(-d-) - f * N(-d+) ], i.e. call + put value
             *   = discfactor * [ f * N(d+) - K * N(d-) + K* (1-N(d-)) - f * (1-N(d+))
             *   = discfactor * [2 * f * N(d+) - 2 * K * N(d-) + K -f] */
            return(m_Strike - m_Forward + 2.0 * m_Forward * StandardNormalDistribution.GetCdfValue(dplus) - 2.0 * m_Strike * StandardNormalDistribution.GetCdfValue(dminus));
        }
Пример #17
0
            /// <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)
            {
                /* Initial value is taken from rational approximation of a straddle option. The algorithm converges with quaratic order of convergence, therefore
                 * for an arbitrary point for which the algorithm is well-defined. Because of the good initial point, often it is one iteration required only. */

                double v0;
                double sqrtOfTime = Math.Sqrt(timeToExpiry);

                if (RationalApproximation.TryGetStraddleImpliedVolatility(strike - forward, sqrtOfTime, 2.0 * c0 * forward + strike - forward, out value) == ImpliedCalculationResultState.ProperResult)
                {
                    v0 = value * sqrtOfTime / forward;
                }
                else
                {
                    v0 = 0.1 * sqrtOfTime / forward;  // default if approximation fails.
                }

                var x = 1 - strike / forward;

                if (x > 0) // "in-out" duality
                {
                    c0 = c0 - x;
                    x  = -x;
                }
                var oneOverOnePlusW = 1.0 / (1 + m_RelaxationParameter);

                var v = v0;

                for (int j = 0; j < m_MaxNumberOfIterations; j++)
                {
                    var    n = StandardNormalDistribution.GetPdfValue(x / v);
                    var    N = StandardNormalDistribution.GetCdfValue(x / v);
                    double c = v * n + x * N;  // undiscounted normalized option value

                    v = (c0 - x * N + m_RelaxationParameter * v * n) * oneOverOnePlusW / n;

                    if (Math.Abs(c - c0) < m_Tolerance)
                    {
                        value = forward * v / sqrtOfTime;  // 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 / sqrtOfTime;
                return(ImpliedCalculationResultState.NoProperResult);
            }
        /// <summary>Gets the implied strike for a given value (price) and Black-Scholes volatility of a specific european call or put option.
        /// </summary>
        /// <param name="theta">A value indicating whether a call (=1) or put (=-1) is given.</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="blackVolatility">The Black volatility.</param>
        /// <param name="sqrtOfTime">The square root of <paramref name="timeToExpiry"/>.</param>
        /// <param name="impliedStrike">The implied strike (output).</param>
        /// <returns>Returns a value indicating whether <paramref name="impliedStrike"/> contains valid data.</returns>
        /// <remarks>The implementation is based on the Newton approach.</remarks>
        protected static ImpliedCalculationResultState TryGetPlainVanillaImpliedStrike(int theta, double forward, double timeToExpiry, double noneDiscountedValue, double blackVolatility, double sqrtOfTime, out double impliedStrike)
        {
            /* we apply Newtons method to the function
             *     H(K) = ln( f(K) / c),
             * where f(K) is the (undiscounted) value of the option and c is the given undiscounted value
             * of the option. It holds H'(K) = f'(K)/f(K) and Newtons method gives
             *
             *      K_{n+1} = K_n - H(K_n)/H'(K_n) = K_n - ln(f(K)/c) * f(K) / f'(K).
             */

            impliedStrike = forward;
            double absOfValueAtExpiry        = Math.Abs(noneDiscountedValue);
            double optionValueForGivenStrike = noneDiscountedValue;

            double dplus, CDFAtThetaTimesdMinus;

            for (int i = 1; i <= MaxNumberIterationImpliedStrike; i++)
            {
                if (impliedStrike < MachineConsts.SuperTinyEpsilon)
                {
                    optionValueForGivenStrike = forward + impliedStrike;
                    impliedStrike             = forward - theta * noneDiscountedValue;
                }
                else
                {
                    dplus = (Math.Log(forward / impliedStrike) + 0.5 * blackVolatility * blackVolatility * timeToExpiry) / (blackVolatility * sqrtOfTime);
                    CDFAtThetaTimesdMinus = StandardNormalDistribution.GetCdfValue(theta * (dplus - blackVolatility * sqrtOfTime));

                    optionValueForGivenStrike = (theta * forward * StandardNormalDistribution.GetCdfValue(theta * dplus) - theta * impliedStrike * CDFAtThetaTimesdMinus);
                    impliedStrike            += theta * optionValueForGivenStrike * Math.Log(optionValueForGivenStrike / noneDiscountedValue) / CDFAtThetaTimesdMinus;
                }
                if (Math.Abs(noneDiscountedValue - optionValueForGivenStrike) < absOfValueAtExpiry * MachineConsts.TinyEpsilon)
                {
                    return(ImpliedCalculationResultState.ProperResult);
                }
            }
            return(ImpliedCalculationResultState.NoProperResult);
        }
        /// <summary>Gets the implied strike for a given value (price) and Bachelier (=Normal Black) volatility of a specific european call or put option.
        /// </summary>
        /// <param name="theta">A value indicating whether a call (=1) or put (=-1) is given.</param>
        /// <param name="forward">The forward.</param>
        /// <param name="undiscountedValue">The value of the option at the time of expiry, thus the price but <b>not</b> discounted to time 0.</param>
        /// <param name="bachelierVolatility">The Bachelier (=Normal Black) volatility.</param>
        /// <param name="sqrtOfTime">The square root of the time span between valuation date and expiry date.</param>
        /// <param name="impliedStrike">The implied strike (output), input represents the initial value of the Newton-Method.</param>
        /// <returns>Returns a value indicating whether <paramref name="impliedStrike"/> contains valid data.
        /// </returns>
        /// <remarks>The implementation is based on the Newton approach and the forward is used as initial guess.</remarks>
        protected static ImpliedCalculationResultState TryGetPlainVanillaImpliedStrike(int theta, double forward, double undiscountedValue, double bachelierVolatility, double sqrtOfTime, ref double impliedStrike)
        {
            /* we apply Newtons method to the function
             *     H(K) = ln( f(K) / c),
             * where f(K) is the (undiscounted) value of the option and c is the given undiscounted value
             * of the option. It holds H'(K) = f'(K)/f(K) and Newtons method gives
             *
             *      K_{n+1} = K_n - H(K_n)/H'(K_n) = K_n - ln(f(K)/c) * f(K) / f'(K).
             */
            double absOfValueAtExpiry        = Math.Abs(undiscountedValue);
            double optionValueForGivenStrike = undiscountedValue;

            double d, CDF_Theta_d;

            for (int i = 1; i <= MaxNumberIterationImpliedStrike; i++)
            {
                if (impliedStrike < MachineConsts.SuperTinyEpsilon)
                {
                    optionValueForGivenStrike = forward + impliedStrike;
                    impliedStrike             = forward - theta * undiscountedValue;
                }
                else
                {
                    d           = (forward - impliedStrike) / (bachelierVolatility * sqrtOfTime);
                    CDF_Theta_d = StandardNormalDistribution.GetCdfValue(theta * d);
                    optionValueForGivenStrike = theta * (forward - impliedStrike) * CDF_Theta_d + bachelierVolatility * sqrtOfTime * StandardNormalDistribution.GetPdfValue(theta * d);

                    impliedStrike += theta * optionValueForGivenStrike * Math.Log(optionValueForGivenStrike / undiscountedValue) / CDF_Theta_d;
                }
                if (Math.Abs(undiscountedValue - optionValueForGivenStrike) < absOfValueAtExpiry * MachineConsts.TinyEpsilon)
                {
                    return(ImpliedCalculationResultState.ProperResult);
                }
            }
            return(ImpliedCalculationResultState.NoProperResult);
        }
            /// <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);
            }
Пример #21
0
        /// <summary>Gets the price of the option <c>at time of expiry</c>, i.e. not discounted.
        /// </summary>
        /// <param name="volatility">The volatility.</param>
        /// <returns>The value of the option <c>at the time of expiry</c>, thus not discounted. To get the price just multiply the return value with the discount factor.</returns>
        public double GetNoneDiscountedValue(double volatility)
        {
            if (volatility * volatility * m_TimeToExpiration < MachineConsts.Epsilon)
            {
                return(Math.Abs(m_Strike - m_Forward));
            }
            // here, we use the fact N(-d) = 1 - N(d):
            double d = (m_Forward - m_Strike) / (volatility * SqrtOfTimeToExpiration);

            return(2.0 * (volatility * SqrtOfTimeToExpiration * StandardNormalDistribution.GetPdfValue(d) + (m_Strike - m_Forward) * (0.5 - StandardNormalDistribution.GetCdfValue(d))));
        }
Пример #22
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>
        /// <remarks>This method is based on Newtons method where the initial guess is equal to the approximation given in
        ///   <para>How close are the option pricing formulas of Bachelier and Black-Merton-Scholes, W. Schachermayer, J. Teichmann.</para>
        /// <para>The implied strike is not unique for straddle options.</para>
        /// </remarks>
        public ImpliedCalculationResultState TryGetImpliedStrike(double optionValue, double volatility, out double impliedStrike)
        {
            double undiscountedValue = optionValue / m_DiscountFactor;

            if (m_TimeToExpiration < MachineConsts.Epsilon)
            {
                impliedStrike = m_Forward - undiscountedValue;
                return(ImpliedCalculationResultState.ProperResult);
            }

            /* compute initial guess:
             *    Straddle(m) = C(m) + P(m) \approx 2 * a + m^2/( 2 pi * a),
             * see eqn.(3.4)&(3.5), where the left hand side is already given.
             */
            double a = volatility * SqrtOfTimeToExpiration / MathConsts.SqrtTwoPi;
            double m = Math.Sqrt(Math.Abs((undiscountedValue - 2 * a) * MathConsts.TwoPi * a));

            impliedStrike = m_Forward + m;

            /* we apply Newtons method to the function
             *     H(K) = ln( f(K) / c),
             * where f(K) is the (undiscounted) value of the option and c is the given undiscounted value
             * of the option. It holds H'(K) = f'(K)/f(K) and Newtons method gives
             *
             *      K_{n+1} = K_n - H(K_n)/H'(K_n) = K_n - ln(f(K)/c) * f(K) / f'(K),
             *
             * here f'(K) = 1 - 2 * N(d) which will be equal to 0, if K=forward.
             */

            double absOfValueAtExpiry        = Math.Abs(undiscountedValue);
            double optionValueForGivenStrike = undiscountedValue;
            double sqrtOfTime = SqrtOfTimeToExpiration;

            double d, CDF_d;

            for (int i = 1; i <= MaxNumberIterationImpliedStrike; i++)
            {
                d     = (m_Forward - impliedStrike) / (volatility * sqrtOfTime);
                CDF_d = StandardNormalDistribution.GetCdfValue(d);
                optionValueForGivenStrike = 2.0 * (volatility * sqrtOfTime * StandardNormalDistribution.GetPdfValue(d) + (impliedStrike - m_Forward) * (0.5 - CDF_d));

                if (Math.Abs(d) < MachineConsts.Epsilon) // forward = strike and f'(K) = 0, i.e. denominator = 0
                {
                    if (Math.Abs(undiscountedValue - optionValueForGivenStrike) < absOfValueAtExpiry * MachineConsts.TinyEpsilon)
                    {
                        return(ImpliedCalculationResultState.ProperResult);
                    }

                    /* here, we do a better approximation for the initial guess of the implied strike,
                     * i.e. straddle = C(m) + P(m) = 2 a + m^2/(2Pi a) - m^4/(48 * Pi^2 a^3), we use
                     * a root-finding approach the these polynomials and we use the root 'm' (= strike - forward)
                     * where Straddle(m) - undiscountedValue is minimal with respect to each root.
                     */
                    IRealPolynomial polynomial = Polynomial.Real.Create(2 * a, 0, 1.0 / (a * MathConsts.TwoPi), 0, -1.0 / (a * a * a * 48 * MathConsts.PiSquared));
                    List <double>   roots      = new List <double>();
                    polynomial.GetRealRoots(roots, Polynomial.RootFinder.LaguerreApproach.StandardPolishing);

                    if (roots.Count == 0)
                    {
                        throw new ArithmeticException("Straddle approximation polynomial of degree 4 has no real root.");
                    }
                    else
                    {
                        impliedStrike = m_Forward + roots[0];
                        double minDistance = Math.Abs(undiscountedValue - GetUndiscountedOptionValue(roots[0], volatility, sqrtOfTime));
                        for (int j = 1; j < roots.Count; j++)
                        {
                            double distance = Math.Abs(undiscountedValue - GetUndiscountedOptionValue(roots[j], volatility, sqrtOfTime));
                            if (distance < minDistance)
                            {
                                impliedStrike = m_Forward + roots[j];
                                minDistance   = distance;
                            }
                        }
                    }
                }
                else
                {
                    impliedStrike -= optionValueForGivenStrike * Math.Log(optionValueForGivenStrike / undiscountedValue) / (1 - 2 * CDF_d);
                }
                if (Math.Abs(undiscountedValue - optionValueForGivenStrike) < absOfValueAtExpiry * MachineConsts.TinyEpsilon)
                {
                    return(ImpliedCalculationResultState.ProperResult);
                }
            }
            return(ImpliedCalculationResultState.NoProperResult);
        }
Пример #23
0
        /// <summary>Gets the strike-delta of the option, i.e. the partial derivative of the option value formula with
        /// respect to the strike.
        /// </summary>
        /// <param name="volatility">The volatility.</param>
        /// <returns>The strike-delta of the option, i.e. the partial derivative of the option value formula with respect to the strike.</returns>
        public double GetStrikeDelta(double volatility)
        {
            if ((m_Strike < MachineConsts.Epsilon) || (volatility * volatility * m_TimeToExpiration < MachineConsts.Epsilon))
            {
                return(0.0);
            }
            double dplus  = (LogOfMoneyness + 0.5 * volatility * volatility * m_TimeToExpiration) / (volatility * SqrtOfTimeToExpiration);
            double dminus = dplus - volatility * SqrtOfTimeToExpiration;

            return(m_DiscountFactor * (-2.0 * m_Forward / (volatility * SqrtOfTimeToExpiration * m_Strike) * StandardNormalDistribution.GetPdfValue(dplus) - (2 * StandardNormalDistribution.GetCdfValue(dminus) - 1) + 2.0 * StandardNormalDistribution.GetPdfValue(dminus) / (volatility * SqrtOfTimeToExpiration)));
        }
Пример #24
0
        /// <summary>Gets the undiscounted option value using the Bachelier (=Normal Black) model thus the value <c>at expiry</c> of the option.
        /// </summary>
        /// <param name="strikeMinusForward">'Strike minus forward'.</param>
        /// <param name="volatility">The Bachelier (=Normal Black) volatility.</param>
        /// <param name="sqrtOfTime">The square root of the <see cref="System.Double"/> representation of the time span between valuation date and expiry date.</param>
        /// <returns>The <b>undiscounted</b> Bachelier (=Normal Black) price of the call option, thus the value <c>at time of expiry</c>, i.e.
        /// to get the price just multiply the return value with the discount factor.</returns>
        private static double GetUndiscountedOptionValue(double strikeMinusForward, double volatility, double sqrtOfTime)
        {
            // here, we use the fact N(-d) = 1 - N(d):
            double d = -strikeMinusForward / (volatility * sqrtOfTime);

            return(2.0 * (volatility * sqrtOfTime * StandardNormalDistribution.GetPdfValue(d) + strikeMinusForward * (0.5 - StandardNormalDistribution.GetCdfValue(d))));
        }