← Results index

Re-audit S&P 500 breadth-low experiment — Pine HMA20 8.5/0 crossover fidelity

2026-08-18T19:30:00Z Experiment completed Re-derived Pine S&P 500 breadth-low HMA20 8.5/0 (request.security S5FI D close, ta.hma WMA steps 2*WMA10-WMA20 via WMA4, ta.crossover prev<=8.5 & curr>8.5 and ta.crossunder prev>=0 & curr<0 OR independent) tick-for-tick and verified prior qm-4bx.18/23 is already Pine-correct for execution (SPY-aligned 18 triggers 224 active hw20 matches prior; Pine-native 19 triggers 239 active includes Thanksgiving bar 2008-11-27 that SPY calendar shifts 27→28 plus Jan28 HMA drift — 10.5% calendar delta not definition drift); audited on maximal native 2006-12-29→2026-08-11 the sparse buy-only signal (0.92/yr, 4.68% SPY-aligned) is +73% vs SMA200 and walk-forward both halves +29%/+33% but brittle (HMA-only, mega 0 ±1 drops 2.2, holding 60d 12.58 dominates 20d 8.01) and 0% vs B&H, rejected for risk-off promotion and published as qm-kdnj experiment with parity metrics and generic-exit harness.

Objective

Re-derive the S&P 500 breadth-low signal exactly as the pinned Pine defines it (s5fi=request.security(S5FI,D,close), s5fi_hma=ta.hma(s5fi,20), crossover(hma,8.5) or crossunder(hma,0)), re-audit existing experiments qm-4bx.18 (spx-breadth-low overlay) and qm-4bx.23 (s5fi-breadth-buy-signal) for signal-date fidelity, fix any date/definition drift (ta.hma WMA steps, EMA/ROC/cum confusion, crossover vs crossunder independence, request.security daily alignment, one-bar execution lag off-by-one, ffill, EMA seed), persist inputs offline to data/market.sqlite with provenance via research/input_provenance.py, and republish under the qm-kdnj experiment-as-strategy contract with strategy parity plus notebook narrative and generic-exit harness for the one-way buy signal. Do not re-tune thresholds.

Approach

Froze Pine verbatim: s5fi = request.security("S5FI","D",close), s5fi_hma = ta.hma(s5fi,20), s5fi_crossover = ta.crossover(s5fi_hma,8.5), s5fi_megalow_hit = ta.crossunder(s5fi_hma,0), spx_breadth_buy_signal = crossover or crossunder. Documented request.security D close as TradingView daily close (calendar tradingview daily, timezone UTC, UTC-naive sorted unique, trading days only, available_at close, next_close execution +1 bar, no lookahead), ta.hma(20)=WMA(2<em>WMA(source,10)-WMA(source,20),4) with WMA(n)=sum i</em>source / sum i, half=20//2=10 sqrt=round(sqrt(20))=4 minimal inference (integer half, Pine round sqrt, NaN until 20 bars, no EMA/Wilder smoothing), ta.crossover=prev<=8.5 & curr>8.5 strict > on current, ta.crossunder=prev>=0 & curr<0 strict < on current, OR independent (mega crossunder fires even without low crossover), input.float defaults 8.5/0 documented vs inferred (WMA rounding, NaN prefix frozen to match backtest/spx_breadth_low.py observable behavior and verified on fixture). Distinguished documented rule from inference; never invented thresholds.

Re-derived signal function: verified backtest/spx_breadth_low.py already matches Pine tick-for-tick — wma()/hma()/compute_ma() use same WMA definition, spx_breadth_buy_signal(...,crossover,absolute) uses ma.shift(1)<=low & ma>low and ma.shift(1)>=mega & ma<mega OR — tick-for-tick on hand-computed checks (e.g., 2008-01-25 hma 8.22 → 2008-01-28 hma 9.57 cross true, 2008-10-09 0.568 → 2008-10-10 -1.48 crossunder true). Evaluator _signal_s5fi_hma in research/risk_signals_history.py mirrors it via same module with RiskSignalsInputs injection; evaluator vs direct delta 0. Thanksgiving 2008-11-27 bar present in S5FI native (15.35 duplicate) but absent in SPY calendar causes HMA path drift and trigger shift 27→28; SPY-aligned history payload (daily reindex to SPY calendar) yields 18 triggers vs native 19 (Jan28 extra) — 10.5% calendar delta, not HMA/crossover bug. No code fix required; audit quantifies delta and documents calendar alignment as execution-correct.

Files changed

Validation

Results

Fidelity frozen and validated: HMA20 definition WMA(2*WMA10-WMA20,4) and crossover prev<=8.5 & curr>8.5 / crossunder prev>=0 & curr<0 are tick-for-tick correct; mega crossunder independent from low crossover; request.security D close daily alignment and one-bar next_close lag preserved; calendar holiday gap (Thanksgiving 2008-11-27 bar in S5FI native vs SPY execution calendar) explains 19 native vs 18 SPY-aligned triggers — prior audit already correct for execution. Baseline SPY-aligned 18 triggers 224 active 4.68% hw20 fixed; native 19 triggers 239 active 4.82%; 0.92-0.97/yr sparse buy-only impulse, not persistent regime. Robustness: only HMA works (SMA20 4.54 EMA20 4.80 collapse), mega 0 brittle (±1 drops to 5.79), low10 9.55 narrowly beats 8.5, q0.10 9.82 beats absolute but not robust, holdings monotonic 5→5.41 10→7.02 20→8.01 40→10.20 60→12.58 optimization risk frozen at 20. Extra lag +1 bar -2.6% (8.01→7.807) not destructive validates no lookahead. Falsification passes (random 0 trig =base 4.64, inverted high 0 trig 4.66 <<8.01). Data alternative synthetic breadth50 18 triggers 5.88 >base but risk-off-indicators/v1 (brittle + sparse + zero vs B&H rescue-only) — publish as qm-kdnj experiment with frozen Pine semantics and generic-exit harness; retained as risk-signals/v1 SHIP as signal in buy-signals-v1.json/signals/history.json/docs/signals.html.

Links

Source artifact: research/results/qm-kdnj.1.md