You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
momentum.rsi saturates at 100 for the rest of the series after one null in the input #30
Wilder's recursive average never recovers from a null in the input, so avg_loss stays at zero and the ratio saturates at exactly 100.0 for the remainder of the series.
clean 0.0000 median 49.55
one null 0.8750 median 100.00
One null at index 500 of 4000 turns 87.5% of the remaining output into exactly 100.0.
It is not a scale or a sign problem: scaling closes by 1e-6, or demeaning them so the series
crosses zero, both leave the implementation agreeing with a hand-computed Wilder RSI to two
decimals (57.08 against 57.04). Only a null triggers it.
Observed downstream
A caller computing a 24h and 72h RSI on a perpetual-futures premium index, whose input carried 537
nulls, emitted exactly 100.0 on 79,661 of 100,812 rows (79.0%), median 100.0. A hand-computed
Wilder RSI on the same series has median 49.68 and never reaches 100; the series has 3,283 up moves
against 3,267 down moves, so a saturated oscillator is not a property of the data. That caller has
since been changed to compute the oscillator locally, so nothing ships broken today.
Suggested direction
Wilder's average has to restart, or return null, on the far side of a gap. Returning null for
windows that span a null is consistent with every other rolling primitive in the package.
adx, cci, stochastic and any other recursive smoother in the same module have the same shape
and should be checked before this is closed.
Observed in a shipped artifact, not only in a reproduction
cme_futures/features/financial.parquet carries this defect in production output. Of its 30
products, exactly one has any rsi_14 at 100.0, and it has a lot:
The trigger is a single null. 03_financial_features.py:197 nulls any non-positive price, and
CL settled negative on 2020-04-20. Rebuilding the notebook's own frame and recomputing rsi("adj_close", period=14).over(["product", "position"]) against the released ml4t-engineer 0.1.0b10:
CL adj_close nulls after the >0 gate : 1 of 11,606 (2020-04-20)
recomputed rsi_14 ==100.0 share : 12.7378%
first saturated : 2020-04-20
last saturated : 2025-12-31
One null, and five and a half years of the series is affected. Against this branch:
fixed rsi_14 ==100.0 share : 0.0000% median 50.80
rows re-seeding after the gap : 15 (the null plus 14 warmup observations)
That is the third independent instance, and the second in shipped output:
where
input nulls
saturated share
crypto_perps_fundingpremium_rsi_24h/72h
537
79.0% of 100,812 rows, median 100.0
cme_futuresrsi_14, CL
1
12.59% of the product, all of it after the null
synthetic, one null at index 500 of 4,000
1
87.5%
The two production cases differ in how the null arises - a vendor gap in the crypto premium
index, a price the notebook nulls because it went negative - and neither is exotic. Any series
that can carry a hole for any reason is exposed.
Tracked downstream as ml4t/agent-workspace#359, which is now attributed to this issue.
Activity
stefan-jansen commentedon Aug 9, 2026
Observed in a shipped artifact, not only in a reproduction
cme_futures/features/financial.parquetcarries this defect in production output. Of its 30products, exactly one has any
rsi_14at100.0, and it has a lot:The trigger is a single null.
03_financial_features.py:197nulls any non-positive price, andCL settled negative on 2020-04-20. Rebuilding the notebook's own frame and recomputing
rsi("adj_close", period=14).over(["product", "position"])against the releasedml4t-engineer 0.1.0b10:One null, and five and a half years of the series is affected. Against this branch:
That is the third independent instance, and the second in shipped output:
crypto_perps_fundingpremium_rsi_24h/72hcme_futuresrsi_14, CLThe two production cases differ in how the null arises - a vendor gap in the crypto premium
index, a price the notebook nulls because it went negative - and neither is exotic. Any series
that can carry a hole for any reason is exposed.
Tracked downstream as
ml4t/agent-workspace#359, which is now attributed to this issue.