Objective
Replicate Zweig Breadth Thrust - The RecessionALERT Redux from data/recessionalert.sqlite page https://recessionalert.com/zweig-breadth-thrust-redux/ and any matching fixture sheet. Spreadsheet/DB fixtures are the source of truth. Never invent thresholds. Never live-crawl. Fail closed on missing disclosed rules. Acceptance: offline runner/spec reproduces the disclosed Zweig-redux signal from SHA-verified fixtures, or a fail-closed gap report. No live site access.
Approach
- Verified page cache in
data/recessionalert.sqlite:https://recessionalert.com/zweig-breadth-thrust-redux/sha2566cc53cedbef312323ba69e787639a2b4d5a0dc2eee5e72c371431b9dd42f66d793502 bytes fetched 2026-08-22T23:44:07Z. The page discloses the classic Zweig rule (10-day EMA of NYSE Advances/(Advances+Declines) from below 0.40 to above 0.615 within 10 trading days) and describes the Redux ZBT-A variant only qualitatively as stricter lower + relaxed upper + extended window (~4× frequency, 13 chart signals to 2013) without numeric disclosure; a copyright gate hides the actuarial table. A rapid ZBT-B variant is partially disclosed as retaining 0.40 lower, relaxing 0.615 upper, 12-day window.
- Inventoried offline workbook assets. Six cached xlsx assets were enumerated;
WeeklyData.xlsxsha256fc2be5f1fc2a2e38949ae566f6e8cb4699a95562975a498fb5a53510cc40d67c(8209462 bytes) sheetSP500 BREADTH DATAcontains the calculatedZBT BUYcol 33 (15 signals),ZBT-B BUYcol 34 (89),ZBT-A BUYcol 35 (12) for 2003-01-02→2026-08-14 (5942 rows). The Redux machine-readable span is 2004-05-21→2022-10-04 (12 markers). No other workbook contains Zweig thresholds; formulas are absent (calculated values only). This matches the Great Trough snapshot pattern for exact replay.
- Built offline runner
research/recessionalert/zweig_breadth_thrust.py(verify_page_sha256, verify_workbook_sha256, load_zweig, fixture_summary, check_causality, gap_report) that recomputes both SHAs before parsing, validates headers DATE/SP500/ZBT BUY/ZBT-B BUY/ZBT-A BUY at cols 0/1/33/34/35, and normalizes workbook 0/1 to boolean Redux impulses without inventing thresholds. Runner verified: 5942 rows, Redux 12, classic 15, rapid 89. Causality receiptpython3 -m research.recessionalert.zweig_breadth_thrust --check-causality --jsonshows +1-bar edge retained at all 37/83/119/150 horizons vs unconditional baseline and +2-bar extra lag stable.
- Froze definition in
research/findings/specs/recessionalert-zweig-breadth-thrust-redux.json: buy-only Zweig Redux, classic disclosed as above, Redux replayed exact from ZBT-A BUY, rapid documented as 12-day, NYSE daily-close cadence, next-session (+1 bar) execution, no disclosed exit, 20-session viewer window only. Fail-closed gaps list exact ZBT-A/B numeric thresholds, proprietary EMA construction, pre-2004/pre-1945 history, and revision policy.
- Validated causally: forward S&P 500 close-to-close returns at 37/83/119/150 trading days with +1 bar. Redux beats unconditional at every horizon (37d mean 0.018 vs 0.012 win 66.7% vs 66.5%, 83d 0.045 vs 0.027 win 83.3% vs 70.3%, 119d 0.074 vs 0.039, 150d 0.102 vs 0.049). Extra lag T+2 is stable and stronger (83d 0.054 vs 0.027). Subperiod 10 pre-2020 vs 2 post-2020 is documented but not gated due to small post sample; adjacent variants confirm tradeoff (classic 15-signal 83d 0.078 vs Redux 0.045 vs rapid 89-signal 0.035). No failures beyond small-n caveat; negative-adjacent evidence retained.
- Promoted to Other indicators (not AS risk-off). Added registry entry
research/fixtures/buy-signals-v1.jsonsignalrecessionalert-zweig-breadth-thrust-reduxfilterzweig_breadth_thrustseriesZWEIG_REDUX_Athreshold 0.5 holding 20 fixed with full provenance, gaps, and validation. Added evaluator_signal_zweig_breadth_thrustinresearch/risk_signals_history.pyand loader inresearch/reports/signals_publish.pythat replays the SHA-verified daily Redux fixture viaload_zweig. Rebuiltdocs/site-data/signals/history.json(7 signals, 12 Redux trigger dates) viapython3 -m research.reports.signals_publish --offlineandpython3 -m research.reports scaffoldwhich wrotedocs/other-indicators.htmlwith 7 indicators andrecessionalert-zweig-breadth-thrust-reduxcard.
- Wrote this changelog entry and rebuilt
docs/site-data/results.json,docs/results.html,docs/results/qm-09tp.20.htmlviapython3 -m research.reports.results. Ranpython3 research/scripts/check_static_reports.py docsandpython3 -m research.artifacts check --mode full --offline(rebuild on drift).
Files / code / data changed
research/recessionalert/zweig_breadth_thrust.py— offline snapshot runner (verify_page_sha256, verify_workbook_sha256, load_zweig, fixture_summary, check_causality, gap_report; 5942 rows, 12/15/89 markers)research/findings/specs/recessionalert-zweig-breadth-thrust-redux.json— frozen Redux disclosure, classic 0.40/0.615/10d, replay mapping, gaps, and fixture expectationsresearch/fixtures/buy-signals-v1.json— addedrecessionalert-zweig-breadth-thrust-redux(7 signals, title updated to include Zweig Redux plus Great Trough, SPF Anxious and USMLEI gauges)research/risk_signals_history.py— added_signal_zweig_breadth_thrustand FILTER_EVALUATORSzweig_breadth_thrust/zweig/zbt/recessionalert_zweig_breadth_thrust*, plusis_zweigbranch inevaluate_signal(direct boolean impulse, daily, fail-closed on missing fixture)research/reports/signals_publish.py— loads Zweig daily inload_live_inputsunder aliasesZWEIG_REDUX_A/ZWEIG_CLASSIC/ZWEIG_RAPID_B/RECESSIONALERT_ZBT_A/ZBT_A_BUYvia SHA-verifiedload_zweig, supports--offlineread-only SPY boundarydocs/site-data/signals/history.json— 7 signals, zweig-redux present with 12 trigger dates 2004-05-21…2022-10-04docs/other-indicators.html/docs/other.html/docs/site-data/home.json— 7 indicators, zweig card with playground overlayresearch/results/qm-09tp.20.md— this durable task note (id qm-09tp.20)docs/site-data/results.json,docs/results.html,docs/results/qm-09tp.20.html— rebuilt changelog (newest entry qm-09tp.20)tests/test_zweig_breadth_thrust.py— runner/spec, corrupt-cache, registry replay, outcome causality, and publication tests (8 tests)data/recessionalert.sqlite(page/workbook cache) — fixtures (read-only, not rewritten)
Validation
python3 -m research.recessionalert.zweig_breadth_thrust— exit 0, 5942 rows 2003-01-02→2026-08-14, Redux 12 / classic 15 / rapid 89python3 -m research.recessionalert.zweig_breadth_thrust --check-causality --json— passed (monotonic daily, 12 Redux markers, 37/83/119/150 vs baseline as above, extra lag T+2 stable, pre10 post2 split, late-input immutable, no invented thresholds)python3 -m pytest -q tests/test_zweig_breadth_thrust.py— 8 passed (SHA snapshot, markers match workbook 0/1, next-bar outcomes beat baseline and retain at T+2, gap report never claims ZBT-A threshold reconstruction, corrupt cache fails closed, spec freezes classic 0.40/0.615/10d and workbook replay, registry replays 12 exact markers, publication includes 7-signal history and 7-indicator page)python3 -c "from research.risk_signals_history import load_buy_signals_registry; r=load_buy_signals_registry(); assert 'recessionalert-zweig-breadth-thrust-redux' in [s['signal_id'] for s in r['signals']]"— 7 signalspython3 -m research.reports.signals_publish --offline— wrotedocs/site-data/signals/history.jsonwith 7 signals, zweig-redux present with 12 datespython3 -m research.reports scaffold— wrotedocs/other-indicators.htmlwith 7 indicators, badge 7, card presentpython3 -m research.reports.results— rewrotedocs/site-data/results.jsonwithqm-09tp.20newest,docs/results/qm-09tp.20.htmlexistspython3 research/scripts/check_static_reports.py docs— passedpython3 -m research.artifacts check --mode full --offline— passed (or rebuilt)- No live crawl, no
data/market.sqlitemutation, no secrets printed, no hand-edit of generated HTML/canary
Results
Reproduced the disclosed Zweig Breadth Thrust Redux signal offline from SHA-verified fixtures without inventing numeric thresholds. Published as a playable Other-indicators signal recessionalert-zweig-breadth-thrust-redux with 12 exact Redux markers 2004-05-21→2022-10-04, +1-bar causality, and browser-visible overlay on docs/other-indicators.html. Classic 0.40/0.615/10d remains the disclosed anchor while Redux is workbook-replayed; validation shows consistent edge vs unconditional baseline (small-n caveat retained) and extra-lag stability. Changelog entry research/results/qm-09tp.20.md is searchable via git grep qm-09tp.20.
Links
- Task: qm-09tp.20
- Page: https://recessionalert.com/zweig-breadth-thrust-redux/ (cached sha256:6cc53cedbef312323ba69e787639a2b4d5a0dc2eee5e72c371431b9dd42f66d7 93502 bytes)
- Workbook: https://recessionalert.com/excel-data/ WeeklyData.xlsx sha256:fc2be5f1fc2a2e38949ae566f6e8cb4699a95562975a498fb5a53510cc40d67c sheet SP500 BREADTH DATA cols 33/34/35
- Runner:
research/recessionalert/zweig_breadth_thrust.py - Spec:
research/findings/specs/recessionalert-zweig-breadth-thrust-redux.json - Registry:
research/fixtures/buy-signals-v1.json - History:
docs/site-data/signals/history.json - Page:
docs/other-indicators.html