-
-
Notifications
You must be signed in to change notification settings - Fork 5.2k
Call the python reset from PythonIndicator.Reset #9698
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -128,6 +128,23 @@ public override bool IsReady | |
| /// </summary> | ||
| public int WarmUpPeriod { get; protected set; } | ||
|
|
||
| /// <summary> | ||
| /// Resets this indicator to its initial state | ||
| /// </summary> | ||
| public override void Reset() | ||
| { | ||
| // GetMethod throws when the attribute is absent, and returns null when it is CSharp | ||
| if (_indicatorWrapper != null && _indicatorWrapper.HasAttr(nameof(Reset))) | ||
| { | ||
| using (Py.GIL()) | ||
| { | ||
| _indicatorWrapper.GetMethod(nameof(Reset), pythonOnly: true)?.Invoke().Dispose(); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. For inheriting classes
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Confirmed, and it is on the normal path. I counted the depth in python rather than reading the crash. The test increments a counter on entry to The ~995 frames you saw are that same loop without the cap.
|
||
| } | ||
| } | ||
| _isReady = false; | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If the python
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Confirmed. Both are in a |
||
| base.Reset(); | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Computes the next value of this indicator from the given state | ||
| /// </summary> | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -69,6 +69,10 @@ def __init__(self, name, period): | |
| count = len(self.queue) | ||
| self.{(SnakeCase ? "value" : "Value")} = np.sum(self.queue) / count | ||
| return count == self.queue.maxlen | ||
|
|
||
| def {(SnakeCase ? "reset" : "Reset")}(self): | ||
| self.queue.clear() | ||
| self.{(SnakeCase ? "value" : "Value")} = 0 | ||
| " | ||
| ); | ||
| var indicator = module.GetAttr("CustomSimpleMovingAverage") | ||
|
|
@@ -165,6 +169,23 @@ public void IsReadyAfterPeriodUpdates() | |
| Assert.IsTrue(sma.IsReady); | ||
| } | ||
|
|
||
| [Test] | ||
| public void ResetClearsTheStateHeldInPython() | ||
| { | ||
| var indicator = CreateIndicator(); | ||
| var reference = new DateTime(2024, 1, 1); | ||
|
|
||
| for (var i = 0; i < 3; i++) | ||
| { | ||
| indicator.Update(new IndicatorDataPoint(reference.AddDays(i), 100m + i)); | ||
| } | ||
|
|
||
| indicator.Reset(); | ||
| indicator.Update(new IndicatorDataPoint(reference, 100m)); | ||
|
|
||
| Assert.AreEqual(100m, indicator.Current.Value); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Also assert
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sharper than it first looked, because the assertion would have been vacuous as well as missing. The indicator has period 14 and the test fed it three points, so it was never ready, and asserting It feeds 20 now and asserts ready first. Dropping |
||
| } | ||
|
|
||
| [Test] | ||
| public override void ResetsProperly() | ||
| { | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
HasAttris snake-case tolerant butGetPythonMethodfalls back to PascalCase-onlyGetAttr("Reset")— soself.reset = False(non-method, noReset) passes the guard then throws an uncaughtAttributeError; worked before this PR. Also, a non-bound-method callable (self.Reset = lambda: ...) is silently skipped. Suggest resolving the reset method once inSetIndicator, like_pythonIsReadyProperty— also removes the per-callHasAttrGIL round-trip and avoids cachingnullunder"Reset"in_pythonMethods(keyed by name only, ignorespythonOnly).There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed the way you suggested, and the shape is not new here:
AlgorithmPythonWrapperresolvesOnDataandOnMarginCallinto fields at construction.Resolution happens once in
SetIndicatorthroughGetPythonMethodWithChecks, snake-case first and PascalCase second, both behindHasAttr. Soself.reset = Falsenow resolves to null rather than reachingGetAttr("Reset"), andnullno longer lands in_pythonMethodsunder a key that ignorespythonOnly. The per-call round trip goes with it.The non-bound callable I have left alone, deliberately.
That
<class 'method'>test insideGetPythonMethodis doing real work: it is what separates a python override from the inherited C# binding. Accept any callable and every inheriting class starts looking like it overridesreset. Two wrappers call the helper directly andBasePythonWrapperroutes everyInvokeMethodthrough it, so I would rather widen it on its own.One consequence of resolving once, since you were the one who flagged the caching: repeated
SetIndicatornow has something to release.GetIndicatorAsManagedObjectcalls it from twenty sites inIndicatorExtensionswith no caching, unlikeWrapPythonIndicatorwhich keys off the handle, so the field is disposed before it is replaced. I stopped there.AlgorithmPythonWrapperreleases its equivalents inDispose, butPythonIndicatorhas noDisposeand does not release_instanceor_indicatorWrappereither, and adding one is a lifetime contract rather than a fix.