Skip to content

Commit

Permalink
Solve divide by zero exception in McGinleyDynamic indicator (#8397)
Browse files Browse the repository at this point in the history
* Add SafeDivision() method

* Add assertions

* Nit changes

* Nit change
  • Loading branch information
Marinovsky authored Nov 14, 2024
1 parent 5c5c527 commit f2da037
Show file tree
Hide file tree
Showing 2 changed files with 49 additions and 7 deletions.
18 changes: 11 additions & 7 deletions Indicators/McGinleyDynamic.cs
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ public class McGinleyDynamic : WindowIndicator<IndicatorDataPoint>, IIndicatorWa
public McGinleyDynamic(string name, int period)
: base(name, period)
{
if (period == 0) throw new ArgumentException("Period can not be zero");
_period = period;
_rollingSum = new Sum(name + "_Sum", period);
}
Expand All @@ -77,18 +78,21 @@ protected override decimal ComputeNextValue(IReadOnlyWindow<IndicatorDataPoint>
{
return 0;
}
else if (Samples == _period)

if (Samples == _period)
{
return _rollingSum.Current.Value / _period;
}
else

if (Current.Value == 0 || input.Value == 0)
{
if ((Current.Value == 0) || (input.Value == 0) || (_period == 0))
{
return Current.Value;
}
return Current.Value + (input.Value - Current.Value) / (_period * (decimal)Math.Pow((double)(input.Value / Current.Value), 4.0));
return Current.Value;
}

var ratioValue = (double)input.Value.SafeDivision(Current.Value, 0);
if (ratioValue == 0) return Current.Value;
var denominator = _period * (decimal)Math.Pow(ratioValue, 4.0);
return Current.Value + (input.Value - Current.Value).SafeDivision(denominator, 0);
}

/// <summary>
Expand Down
38 changes: 38 additions & 0 deletions Tests/Indicators/McGinleyDynamicTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
using System;
using NUnit.Framework;
using QuantConnect.Indicators;
using System.Collections.Generic;

namespace QuantConnect.Tests.Indicators
{
Expand Down Expand Up @@ -62,5 +63,42 @@ public override void ResetsProperly()
indicator.Update(new DateTime(2024, 7, 9, 0, 3, 0), 2.0m);
Assert.AreEqual(indicator.Current.Value, 2.0m);
}

[Test]
public override void WorksWithLowValues()
{
var indicator = new McGinleyDynamic("test", 10);

var startDate = new DateTime(2020, 6, 4);
var dataPoints = new List<IndicatorDataPoint>()
{
new IndicatorDataPoint(startDate, 0m),
new IndicatorDataPoint(startDate.AddDays(1), 0m),
new IndicatorDataPoint(startDate.AddDays(2), 0m),
new IndicatorDataPoint(startDate.AddDays(3), 0m),
new IndicatorDataPoint(startDate.AddDays(4), 3.27743794800m),
new IndicatorDataPoint(startDate.AddDays(5), 7.46527532600m),
new IndicatorDataPoint(startDate.AddDays(6), 2.54419732600m),
new IndicatorDataPoint(startDate.AddDays(7), 0m),
new IndicatorDataPoint(startDate.AddDays(8), 0m),
new IndicatorDataPoint(startDate.AddDays(9), 0.71847738800m),
new IndicatorDataPoint(startDate.AddDays(10), 0m),
new IndicatorDataPoint(startDate.AddDays(11), 1.86016748400m),
new IndicatorDataPoint(startDate.AddDays(12), 0.45273917600m),
new IndicatorDataPoint(startDate.AddDays(13), 0m),
new IndicatorDataPoint(startDate.AddDays(14), 1.80111454800m),
new IndicatorDataPoint(startDate.AddDays(15), 0m),
new IndicatorDataPoint(startDate.AddDays(16), 2.74596152400m),
new IndicatorDataPoint(startDate.AddDays(17), 0m),
new IndicatorDataPoint(startDate.AddDays(18), 0m),
new IndicatorDataPoint(startDate.AddDays(19), 0m),
new IndicatorDataPoint(startDate.AddDays(20), 0.84642541600m),
};

for (int i=0; i < 21; i++)
{
Assert.DoesNotThrow(() => indicator.Update(dataPoints[i]));
}
}
}
}

0 comments on commit f2da037

Please sign in to comment.