fix: correct the directional movement seeding off-by-one - #111
Open
phmatray wants to merge 1 commit into
Open
Conversation
Adx, Dx, PlusDI and MinusDI seeded one term short, in the same shape as the EMA seed corrected in #105. The C reference uses `while (i-- > 0)`, which runs its body i times; the C# transcription was `while (true) { i--; if (i <= 0) break; }`, which runs it i - 1 times. It showed up two ways, both measured on a 100-bar linear uptrend. The undercount left the cursor short of startIdx, so BegIdx + NBElement came back as 102 over 100 bars - two values claimed for bars that do not exist. Every consumer that maps an output element onto a bar was therefore misaligned by two, silently. All four functions were affected. The ADX seed averaged 13 values while dividing by 14. On a series whose DX is a constant 100 - a perfectly linear trend - ADX must be exactly 100 at every bar, because Wilder's average of a constant is that constant. It read 92.857, which is 100 * 13 / 14, and crept towards 100 without arriving. Adxr is affected transitively; +DI, -DI and DX values were already correct and are unchanged, only their alignment metadata was wrong. The existing AdxTests, DxTests, PlusDITests and MinusDITests assert only RetCode.Success, so none of this was detectable. The four fixtures added here assert numbers: two of them fail against the previous implementation - the alignment invariant and the ADX seed - and two pass either way, held as controls proving the fix leaves the already-correct +DI/-DI/DX values alone. Verified: 0 errors, 1129 tests pass.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Split out of #110 so a semantic change to shipping indicators gets reviewed on its own, with tests, rather than riding inside a fluent-API PR.
Adx,Dx,PlusDIandMinusDIseeded one term short — the same shape as the EMA seed corrected in #105. The C reference useswhile (i-- > 0), which runs its bodyitimes; the C# transcription waswhile (true) { i--; if (i <= 0) break; }, which runs iti - 1times.Two measured symptoms
Both on a 100-bar linear uptrend (
high = 100 + 2i,low = 99 + 2i,close = 99.5 + 2i):1. The output claimed bars that don't exist.
AdxDxPlusDIMinusDITwo values claimed for bars 100 and 101. Any consumer mapping an output element onto a bar — which is the documented contract, and what the samples in #104 do — was misaligned by two, silently.
2. The ADX seed averaged 13 values while dividing by 14.
On this series DX is a constant
100, and Wilder's average of a constant is that constant, so ADX must be exactly 100 at every bar. It read92.857— which is100 × 13/14— then crept toward 100 without arriving:Adxris affected transitively.+DI,-DIandDXvalues were already correct and are unchanged — only their alignment metadata was wrong.Tests
The existing
AdxTests,DxTests,PlusDITests,MinusDITestsassert onlyRetCode.Success, so none of this was detectable by the suite. The four fixtures here assert numbers, and I verified how each behaves against the old code:OutputNeverClaimsMoreBarsThanTheInputHasAdxOnAConstantTrendIsExactlyOneHundredFromItsFirstValueDirectionalIndicatorsSeparateAPureUptrendDirectionalIndicatorsSeparateAPureDowntrendThe two controls are deliberate: they pin
+DI = 80,-DI = 0,DX = 100(all exact and hand-derivable on a linear trend) to prove the fix leaves the already-correct values alone rather than trading one error for another.Verification
Release note
This changes the numeric output of
Adx,Adxr,Dx,PlusDIandMinusDIin a published package. Callers who compensated for the two-bar misalignment will need to stop. Worth calling out in the v4 notes alongside the ATR/EMA/RSI fixes from #105.Once this merges, #110 (fluent API) should be rebased on top — it currently carries these same four files, and its strict alignment guard is what surfaced the defect in the first place.
🤖 Generated with Claude Code