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
research/experiments/qm-kdnj.1.json— new qm-kdnj experiment-as-strategy manifest (rejected for risk-off, SHIP as signal, frozen Pine semantics, native 19 vs SPY 18 triggers, metrics parity, generic-exit harness, limitations, supplemental Pine verbatim/HMA semantics)research/results/qm-kdnj.1.md— this durable task note (searchable viagit grep)research/experiments/qm-kdnj.2.json/qm-kdnj.3.jsonalready present — this task addsqm-kdnj.1.jsonalongside them for experiments payload paritydocs/site-data/signals/history.json— already containss5fi-breadth18 triggers SPY-aligned rebuilt deterministically viaRiskSignalsInputsdaily reindex frommarket.sqlite(offline reproducible)research/fixtures/buy-signals-v1.json— already containss5fi-breadthregistry entry (provenance metadata_only, native 2006-12-29 4956 rows, synthetic 1999-03-05 6896, NO-GO pre-2006/1998)- No change to
backtest/spx_breadth_low.py— verified Pine-correct; no synthetic history invented
Validation
python3 -m pytest -q tests/test_spx_breadth_low.py tests/test_risk_signals_history.py tests/test_datastore.py— 74 passed offline (HMA/WMA/sma/ema/wilder, spx_breadth_buy_signal crossover/level/hysteresis, quantile/sigma expanding-only, holding fixed/decay/retrigger, reconstruct_breadth, calibrate, overlay modes, falsification, provenance, engine)python3 -m pytest -q tests/test_datastore.py— 51 passed mode=ro query_onlypython3 -m research.reports build --render— exported 139/139 payloads db_warm, experiments payload includesqm-kdnj.1/qm-kdnj.2/qm-kdnj.3with parity metrics; signals/history.json rebuiltpython3 research/scripts/check_static_reports.py docs— local bundle OK: 1233 files; 182 canary routes; public docs contain no private datasqlite3 data/market.sqlite SELECT ticker,start_date,end_date,n_rows FROM instruments WHERE ticker='INDEX:S5FI'— INDEX:S5FI 2006-12-29→2026-08-11 4956 rows tradingview, __SYNTH_SPXBREADTH50__ 1999-03-05→2026-08-04 6896 rows, SPY 1993-01-29→2026-07-24 8428 rows;read_ohlcvmode=ro query_only succeeds offline, no -wal/-shm, size 100M- Date-fidelity audit: native 19 triggers [2008-01-28, 2008-10-10, 2008-11-27, 2009-03-12, 2010-06-04, 2010-07-09, 2011-08-10, 2011-08-26, 2015-09-09, 2016-01-26, 2018-12-28, 2019-01-09, 2020-03-16, 2020-04-08, 2022-06-22, 2022-06-29, 2022-09-29, 2022-10-07, 2023-10-09] vs SPY-aligned 18 (drops Jan28, shifts Nov27→28) — 2 mismatches 10.5% holiday-driven, evaluator vs direct delta 0, no HMA/crossover/lookahead/ffill/EMA-seed bug; prior qm-4bx.18/23 18 execution-correct
- Generic-exit harness head-to-head same window 2007-01-01→2026-08-04 next_close 10bps: SMA200 generic 4.64 → signal+generic 8.01 +73%, SMA50 5.12→7.45 +45%, 50/200 cross 4.92→7.1 +44%, trailing 10% 5.03→7.28 +44%, 60/40 4.82→5.345 +11%, B&H 7.47→7.47 0% — separate signal assessment retained
- No DB mutation from report/read paths, no secret/private leakage (
grepALLOCATESMARTLY/GH_TOKEN empty), no synthetic pre-2006 S5FI invented beyond ETF proxy calibration
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-signals/v1 SHIP as signal in buy-signals-v1.json/signals/history.json/docs/signals.html.
Links
- Task:
qm-kdnj.1(parent epicqm-kdnj) — pinned Pine HMA20 8.5/0 breadth low - Prior:
research/findings/spx-breadth-low-qm-4bx.18.md/.json(NO-SHIP overlay) andresearch/findings/s5fi-breadth-buy-signal-qm-4bx.23.md/.json(SHIP as signal) - Code:
backtest/spx_breadth_low.py(spx_breadth_buy_signal crossover OR crossunder, hma/wma, provenance_record) - Signal:
research/fixtures/buy-signals-v1.jsons5fi-breadth +research/risk_signals_history.py::_signal_s5fi_hma+docs/site-data/signals/history.json(18 triggers SPY-aligned) - Data:
data/market.sqlite:ohlcvINDEX:S5FI 4956 rows + __SYNTH_SPXBREADTH50__ 6896 rows - Experiment:
research/experiments/qm-kdnj.1.json+docs/site-data/experiments.json+docs/site-data/results.json+docs/results.html