diff --git a/Indicators/ImpliedVolatility.cs b/Indicators/ImpliedVolatility.cs index 7690fd847aa8..72523c297398 100644 --- a/Indicators/ImpliedVolatility.cs +++ b/Indicators/ImpliedVolatility.cs @@ -309,17 +309,18 @@ protected override decimal ComputeNextValue(IndicatorDataPoint input) private decimal TheoreticalPrice(decimal volatility, decimal spotPrice, decimal strikePrice, decimal timeTillExpiry, decimal riskFreeRate, decimal dividendYield, OptionRight optionType, OptionPricingModelType optionModel = OptionPricingModelType.BlackScholes) { - switch (optionModel) + if (timeTillExpiry <= 0m) { - // Binomial model also follows BSM process (log-normal) - case OptionPricingModelType.BinomialCoxRossRubinstein: - return OptionGreekIndicatorsHelper.CRRTheoreticalPrice(volatility, spotPrice, strikePrice, timeTillExpiry, riskFreeRate, dividendYield, optionType); - case OptionPricingModelType.ForwardTree: - return OptionGreekIndicatorsHelper.ForwardTreeTheoreticalPrice(volatility, spotPrice, strikePrice, timeTillExpiry, riskFreeRate, dividendYield, optionType); - case OptionPricingModelType.BlackScholes: - default: - return OptionGreekIndicatorsHelper.BlackTheoreticalPrice(volatility, spotPrice, strikePrice, timeTillExpiry, riskFreeRate, dividendYield, optionType); + return 0m; } + + return optionModel switch + { + // Binomial model also follows BSM process (log-normal) + OptionPricingModelType.BinomialCoxRossRubinstein => OptionGreekIndicatorsHelper.CRRTheoreticalPrice(volatility, spotPrice, strikePrice, timeTillExpiry, riskFreeRate, dividendYield, optionType), + OptionPricingModelType.ForwardTree => OptionGreekIndicatorsHelper.ForwardTreeTheoreticalPrice(volatility, spotPrice, strikePrice, timeTillExpiry, riskFreeRate, dividendYield, optionType), + _ => OptionGreekIndicatorsHelper.BlackTheoreticalPrice(volatility, spotPrice, strikePrice, timeTillExpiry, riskFreeRate, dividendYield, optionType), + }; } /// diff --git a/Indicators/OptionGreekIndicatorBase.cs b/Indicators/OptionGreekIndicatorBase.cs index a6de80420e9c..3c8aaac7b57c 100644 --- a/Indicators/OptionGreekIndicatorBase.cs +++ b/Indicators/OptionGreekIndicatorBase.cs @@ -174,8 +174,8 @@ protected override decimal ComputeNextValue(IndicatorDataPoint input) RiskFreeRate.Update(time, _riskFreeInterestRateModel.GetInterestRate(time)); DividendYield.Update(time, _dividendYieldModel.GetDividendYield(time)); - var timeTillExpiry = Convert.ToDecimal((Expiry - time).TotalDays) / 365m; - _greekValue = CalculateGreek(timeTillExpiry); + var timeTillExpiry = Convert.ToDecimal((Expiry - time).TotalDays / 365); + _greekValue = timeTillExpiry < 0 ? 0 : CalculateGreek(timeTillExpiry); } return _greekValue; diff --git a/Indicators/OptionGreekIndicatorsHelper.cs b/Indicators/OptionGreekIndicatorsHelper.cs index 9176c7156953..e094d03dfa48 100644 --- a/Indicators/OptionGreekIndicatorsHelper.cs +++ b/Indicators/OptionGreekIndicatorsHelper.cs @@ -58,7 +58,7 @@ public static decimal BlackTheoreticalPrice(decimal volatility, decimal spotPric internal static decimal CalculateD1(decimal spotPrice, decimal strikePrice, decimal timeToExpiration, decimal riskFreeRate, decimal dividendYield, decimal volatility) { var numerator = DecimalMath(Math.Log, spotPrice / strikePrice) + (riskFreeRate - dividendYield + 0.5m * volatility * volatility) * timeToExpiration; - var denominator = volatility * DecimalMath(Math.Sqrt, timeToExpiration); + var denominator = volatility * DecimalMath(Math.Sqrt, Math.Max(0m, timeToExpiration)); if (denominator == 0m) { // return a random variable large enough to produce normal probability density close to 1 @@ -69,7 +69,7 @@ internal static decimal CalculateD1(decimal spotPrice, decimal strikePrice, deci internal static decimal CalculateD2(decimal d1, decimal volatility, decimal timeToExpiration) { - return d1 - volatility * DecimalMath(Math.Sqrt, timeToExpiration); + return d1 - volatility * DecimalMath(Math.Sqrt, Math.Max(0m, timeToExpiration)); } // Reference: https://en.wikipedia.org/wiki/Binomial_options_pricing_model#Step_1:_Create_the_binomial_price_tree diff --git a/Tests/Indicators/OptionBaseIndicatorTests.cs b/Tests/Indicators/OptionBaseIndicatorTests.cs index 9030a45056de..b563375bb676 100644 --- a/Tests/Indicators/OptionBaseIndicatorTests.cs +++ b/Tests/Indicators/OptionBaseIndicatorTests.cs @@ -106,6 +106,20 @@ protected void RunTestIndicator(Symbol call, Symbol put, OptionIndicatorBase cal Assert.AreEqual(expected, (double)putIndicator.Current.Value, acceptance); } + [Test] + public void ZeroGreeksIfExpired() + { + var indicator = CreateIndicator(); + var date = new DateTime(2099, 1, 1); // date that the option must be expired already + var price = 500m; + var optionPrice = 10m; + + indicator.Update(new IndicatorDataPoint(_symbol, date, optionPrice)); + indicator.Update(new IndicatorDataPoint(_underlying, date, price)); + + Assert.AreEqual(0m, indicator.Current.Value); + } + [Test] public override void ResetsProperly() {