/// <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)))); }
/// <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 d = (m_Strike - m_Forward) / (volatility * SqrtOfTimeToExpiration); return(m_DiscountFactor * StandardNormalDistribution.GetPdfValue(d) / (volatility * SqrtOfTimeToExpiration)); }
/// <summary>Gets the theta of the option, i.e. the partial derivative of the option value formula with respect to the time to maturity. /// </summary> /// <param name="volatility">The volatility.</param> /// <returns>The theta of the option, i.e. the partial derivative of the option value formula with respect to the time to expiry.</returns> public double GetTheta(double volatility) { if (volatility * volatility * m_TimeToExpiration < MachineConsts.Epsilon) { return(0.0); } double d = (m_Forward - m_Strike) / (volatility * SqrtOfTimeToExpiration); return(m_DiscountFactor * 0.5 * StandardNormalDistribution.GetPdfValue(d) * d / m_TimeToExpiration); }
/// <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.GetPdfValue(-dminus) / (volatility * SqrtOfTimeToExpiration * m_Strike)); }
/// <summary>Gets the (forward-)vanna of the option, i.e. the partial derivative of the option value formual with respect /// to the forward and with respect to the volatility, i.e. '\partial\sigma \partial F'. /// </summary> /// <param name="volatility">The volatility.</param> /// <returns>The (forward-)vanna of the option, i.e. the partial derivative of the option value formual with respect /// to the forward and with respect to the volatility, i.e. '\partial\sigma \partial F'. /// </returns> public double GetForwardVanna(double volatility) { if (volatility * volatility * m_TimeToExpiration < MachineConsts.Epsilon) { return(0); } double d = (m_Strike - m_Forward) / (volatility * SqrtOfTimeToExpiration); return(m_DiscountFactor * 2.0 * d * StandardNormalDistribution.GetPdfValue(d) / volatility); }
/// <summary>Gets the vega of the option, i.e. the partial derivative of the option value formula with respect to the volatility. /// </summary> /// <param name="volatility">The volatility.</param> /// <returns>The vega of the option, i.e. the partial derivative of the option value formula with respect to the volatility.</returns> public double GetVega(double volatility) { if ((m_Strike < MachineConsts.Epsilon) || (volatility * volatility * m_TimeToExpiration < MachineConsts.Epsilon)) { return(m_DiscountFactor * (m_Strike - m_Forward)); } double dplus = (LogOfMoneyness + 0.5 * volatility * volatility * m_TimeToExpiration) / (volatility * SqrtOfTimeToExpiration); return(m_DiscountFactor * m_Forward * SqrtOfTimeToExpiration * StandardNormalDistribution.GetPdfValue(dplus)); }
/// <summary>Gets the (forward-)vanna of the option, i.e. the partial derivative of the option value formual with respect /// to the forward and with respect to the volatility, i.e. '\partial\sigma \partial F'. /// </summary> /// <param name="volatility">The volatility.</param> /// <returns>The (forward-)vanna of the option, i.e. the partial derivative of the option value formual with respect /// to the forward and with respect to the volatility, i.e. '\partial\sigma \partial F'. /// </returns> public double GetForwardVanna(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); return(2.0 * m_DiscountFactor * StandardNormalDistribution.GetPdfValue(dplus) * (SqrtOfTimeToExpiration - dplus / volatility)); }
/// <summary>Gets the forward-gamma of the option, i.e. the second partial derivative of the option value formula with respect to the forward. /// </summary> /// <param name="volatility">The volatility.</param> /// <returns>The forward-gamma of the option, i.e. the second partial derivative of the option value formula with respect to the forward.</returns> /// <remarks>The initial value of the underlying is equal to the forward times the discount factor.</remarks> public double GetForwardGamma(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); // is just two times the gamma of an call/put option return(2.0 * m_DiscountFactor * StandardNormalDistribution.GetPdfValue(dplus) / (volatility * SqrtOfTimeToExpiration * m_Forward)); }
/// <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)))); }
/// <summary>Gets the theta of the option, i.e. the partial derivative of the option value formula with respect to the time to maturity. /// </summary> /// <param name="volatility">The volatility.</param> /// <returns>The theta of the option, i.e. the partial derivative of the option value formula with respect to the time to expiry.</returns> public double GetTheta(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; double tempTerm1 = 0.25 * volatility / SqrtOfTimeToExpiration; double tempTerm2 = LogOfMoneyness / (2.0 * volatility * m_TimeToExpiration * SqrtOfTimeToExpiration); return(2.0 * m_DiscountFactor * (m_Forward * StandardNormalDistribution.GetPdfValue(dplus) * (tempTerm1 - tempTerm2) + m_Strike * StandardNormalDistribution.GetPdfValue(dminus) * (tempTerm1 + tempTerm2))); }
/// <summary>Gets the volga of the option, i.e. the second partial derivative of the option value formula with respect to the volatility. /// </summary> /// <param name="volatility">The volatility.</param> /// <returns>The volga of the option, i.e. the second partial derivative of the option value formula with respect to the volatility.</returns> public double GetVolga(double volatility) { if ((m_Strike < MachineConsts.Epsilon) || (volatility * volatility * m_TimeToExpiration < MachineConsts.Epsilon)) { return(0.0); } double volaSquare = volatility * volatility; double dminus = (LogOfMoneyness - 0.5 * volaSquare * m_TimeToExpiration) / (volatility * SqrtOfTimeToExpiration); double densityAtDminus = StandardNormalDistribution.GetPdfValue(dminus); // compute the derivative of \phi(d_), where \phi is the density of the standard normal distribution: double densityDerivativeAtDminus = dminus * densityAtDminus * (0.5 * SqrtOfTimeToExpiration + LogOfMoneyness / (SqrtOfTimeToExpiration * volaSquare)); return(m_DiscountFactor * (0.5 * SqrtOfTimeToExpiration * densityDerivativeAtDminus + (densityDerivativeAtDminus * volatility - densityAtDminus - densityAtDminus) * LogOfMoneyness / (SqrtOfTimeToExpiration * volaSquare * volatility))); }
/// <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 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); }
/// <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)); }
/// <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)); }
/// <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))); }
/// <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); }