PythonIndicator forwards Update, IsReady, Value and WarmUpPeriod to the python object but never overrides Reset(), so a reset defined in python is never called and the python side keeps its state.
A 3-period SMA that clears its own queue:
class DuckSma(PythonIndicator):
def __init__(self, name, period):
self.Name = name
self.Value = 0
self.WarmUpPeriod = period
self.queue = deque(maxlen=period)
def Update(self, input):
self.queue.appendleft(input.Value)
self.Value = sum(self.queue) / len(self.queue)
return len(self.queue) == self.queue.maxlen
def Reset(self):
self.queue.clear()
self.Value = 0
var indicator = new PythonIndicator(py);
// feed 100, 101, 102, 103, 104
indicator.Reset();
indicator.Update(new IndicatorDataPoint(t, 100m));
Current.Value is 102.33333333333333, the mean of the stale queue, where 100 is expected. The python Reset ran 0 times. IsReady is also True while Samples is 0, because _isReady is set in ComputeNextValue and nothing clears it.
Both wrapping paths behave the same: new PythonIndicator(pyObject), and the TryConvert path QCAlgorithm.WrapPythonIndicator uses.
QCAlgorithm.IndicatorHistory calls indicator.Reset() before replaying history (QCAlgorithm.Indicators.cs:4496), and IndicatorHistory(PyObject, ...) routes python indicators into it, so indicator_history on an indicator that has already seen data returns values mixed with the old state.
PythonIndicatorTests.ResetsProperly overrides the base test and runs it against a C# SimpleMovingAverage(3) instead of the python fixture, which is why this was never caught.
PythonIndicator forwards Update, IsReady, Value and WarmUpPeriod to the python object but never overrides Reset(), so a reset defined in python is never called and the python side keeps its state.
A 3-period SMA that clears its own queue:
Current.Value is 102.33333333333333, the mean of the stale queue, where 100 is expected. The python Reset ran 0 times. IsReady is also True while Samples is 0, because _isReady is set in ComputeNextValue and nothing clears it.
Both wrapping paths behave the same:
new PythonIndicator(pyObject), and the TryConvert path QCAlgorithm.WrapPythonIndicator uses.QCAlgorithm.IndicatorHistory calls indicator.Reset() before replaying history (QCAlgorithm.Indicators.cs:4496), and IndicatorHistory(PyObject, ...) routes python indicators into it, so indicator_history on an indicator that has already seen data returns values mixed with the old state.
PythonIndicatorTests.ResetsProperly overrides the base test and runs it against a C# SimpleMovingAverage(3) instead of the python fixture, which is why this was never caught.