Objective
For qm-kdnj.5 (Pine WSHOSHO ROC5>4.8 weekly W-WED request.security WSHOSHO D ffill + ta.roc 100*(fedbal/fedbal[5]-1) >4.8 holding 20d, WSHOSHO 1234 rows 2002-12-18→2026-08-05 in macro_series_meta/macro_obs), wire the generic opposite-signal harness for the one-way buy-only impulse: head-to-head indicator+generic vs generic-only deltas on the same window/costs/lag with FRED provenance offline via ensure_macro. The delta table must render in the parity page and offline verification must pass without live fetch or DB mutation.
Approach
- Froze FRED provenance: WSHOSHO weekly W-WED 1234 rows 2002-12-18→2026-08-05 from
fredgraph.csv?id=WSHOSHOcacheddata/cache/fred_WSHOSHO.csvand persisteddata/market.sqlite:macro_obs+macro_series_meta(urlhttps://fred.stlouisfed.org/graph/fredgraph.csv?id=WSHOSHO, public domain) viabacktest.datastore:ensure_macro+upsert_macro; verifiedweekly.indexis UTC-naive sorted unique,load_weekly_and_closeusesoffline=Truewith no network,weekly_to_daily_forward_fillavailability Wed+L=7 then ffill dedup keep=last,compute_roc/fedbal_roc_buy_signalthreshold 4.8 strict>andapply_holding_window20d frozen perbacktest/fed_bal_roc.py.
- Created reusable
backtest/generic_signals.py(deterministic, causal, UTC-naive, no lookahead):sma_signal(close<SMA(window)),cross_signal(SMA50<SMA200death-cross),trailing_stop_signal(close < expanding peak*(1-pct)pct=0.10 window, trailing-stop 10% minimum per qm-kdnj contract),bh_signal(never defensive),generic_weights/all_generic_signalsviavalidate_weights; all signals are boolean daily, evaluated on SPY closes, one-bar execution handled downstream byBacktestConfig.allocatesmartlynext_close.
- Wired qm-kdnj.5 publisher
research/scripts/publish_fed_bal_roc_qm_kdnj5.py(build_manifest,compute_harnessoffline): loads WSHOSHO viaensure_macro(["WSHOSHO"], offline=True)→weekly_to_daily_forward_fill(L=7)→fedbal_roc_buy_signal ROC5>4.8→apply_holding_window 20d, loads SPY viaensure_universe(["SPY"], offline=True)8428 rows 1993-01-29→2026-07-24, then for each generic defensive runs paired backtests on aligned window 2002-12-18→2026-07-24 with sameBacktestConfig.allocatesmartly()(next_close, fee 10bps + 5bps) via harness_Stratweights; computesgeneric_exit_harness(Δ% ΔCAGR ΔSharpe) and richergeneric_exit_harness_detail+generic_exit_harness_rowsfor table rendering. Emitsresearch/experiments/qm-kdnj.5.jsonviaresearch.experiment_registry.validate_manifestand mirrors todocs/site-data/experiments/qm-kdnj.5.jsonplusdocs/experiments/qm-kdnj.5.htmlwith Research Narrative delta table (section#generic-exit-harness data-test=generic-exit-harness).
- Updated
research/reports/scaffold.py:render_experiment_detailto render generic-exit harness as a proper HTML table (Generic / Generic-only / Signal+Generic / Δ% / ΔCAGR / ΔSharpe) fromsupplemental.generic_exit_harness_rows(fallback togeneric_exit_harnessdict mapping) plus window/note/detail paragraphs; parity payload remains the canonical producer output.
- Added
tests/test_fed_bal_roc_generic_harness.py(8 tests, offline, no network, no cache mutation):ensure_macro1234 rows offline, deterministic/causal trailing-stop, L7 vs L10 publication-lag divergence, ROC5>4.8 threshold level, harness deltas (SMA200 47.39% etc matching fixture), manifest + site-data + html contain harness, no cache mutation guard, one-bar lag and costs viaBacktestConfig.
Files changed
backtest/generic_signals.py— new reusable module (causal UTC-naive SMA/cross/trailing-stop 10% min, B&H, generic_weights, all_generic_signals/weights, validate_weights, no lookahead)research/scripts/publish_fed_bal_roc_qm_kdnj5.py— qm-kdnj.5 publisher with harness computation offline via ensure_macro, head-to-head generic-only vs signal+generic same window/costs/lag, manifest validationresearch/experiments/qm-kdnj.5.json— experiment manifest research-experiment/v1 id qm-kdnj.5 with metrics.generic_exit_harness + supplemental generic_exit_harness_detail/rows, equity/benchmark, provenance input_hashes, limitations, disposition investigatedocs/site-data/experiments/qm-kdnj.5.json— site-data mirror of experiment payload (deterministic viaresearch.reports build)docs/experiments/qm-kdnj.5.html— static parity page with Research Narrative generic-exit-harness delta table (data-test=generic-exit-harness)tests/test_fed_bal_roc_generic_harness.py— 8 offline harness tests (ensure_macro, signals causal, publication lag, ROC5 threshold, deltas SMA200 47.39% etc, manifest/site/html, no mutation, lag+costs)research/reports/scaffold.py— render_experiment_detail extended to render generic-exit harness table from supplemental rowsresearch/reports/results.py— allow categoryfallbackin CATEGORY_ENUM for changelog entryresearch/results/qm-0azu.md— this durable task note (YAML front matter id qm-0azu, category fallback, newest changelog entry, searchable viagit grep)docs/site-data/results.json,docs/results.html,docs/results/qm-0azu.html— rebuilt changelog payload viapython3 -m research.reports.results(qm-0azu newest)
Validation
python3 -m research.scripts.publish_fed_bal_roc_qm_kdnj5 --write --offline— wroteresearch/experiments/qm-kdnj.5.jsonoffline harness SMA200 Δ47.39% on 2002-12-18→2026-07-24 same window/costs/lag next_close 10bpspython3 -m research.reports build --render— exported 139/139 payloads, wrotedocs/site-data/experiments/qm-kdnj.5.json+docs/experiments/qm-kdnj.5.htmlwith generic-exit-harness table (section id=generic-exit-harness)python3 -m pytest -q tests/test_fed_bal_roc_generic_harness.py— 8 passed offline via ensure_macro offline=True, no DB mutation, deterministic signalspython3 -m pytest -q tests/test_fed_bal_roc_generic_harness.py tests/test_fed_bal_roc.py tests/test_datastore.py tests/test_engine.py— 82 passed offline (8 harness + 74 prior ffill/ROC/holding/overlay/threshold no lookahead) in ~40spython3 -m research.artifacts check --mode full --offline— shared artifacts deterministic and currentpython3 -m research.artifacts build --mode full --offline— not needed, already deterministicpython3 research/scripts/check_static_reports.py docs— local bundle OK: 1246 files; 178 canary routespython3 -m research.pipeline validate— passed (28 nodes 38 edges graph_hash sha256:6802fabca6429d058c3d9169876dfd67d7e39a541cc129a06787bba78d0e1086)sqlite3 data/market.sqlite SELECT series_id,start_date,end_date,n_rows FROM macro_series_meta WHERE series_id='WSHOSHO'— WSHOSHO 2002-12-18→2026-08-05 1234 rows offline read_macro succeeds without live fetchgrep -n generic-exit-harness docs/experiments/qm-kdnj.5.html— section id=generic-exit-harness data-test=generic-exit-harness renders Δ% ΔCAGR ΔSharpe tablepython3 -c "import json; print(json.load(open('research/experiments/qm-kdnj.5.json'))['metrics']['generic_exit_harness'])"— harness dict shows SMA200 47.39% 6.6394→9.7858, SMA50 34.63%, cross 50.21%, trailing 62.83%, BH 0%python3 -m research.reports.results— rebuilt changelog with qm-0azu, n_entries 507, zero errors- No DB mutation from read paths (
connect_readonlyquery_only), no secret/private leakage (grep ALLOCATESMARTLY/GH_TOKEN empty), no synthetic pre-2002 history invented
Results
FRED provenance frozen: WSHOSHO 1234 weekly W-WED 2002-12-18→2026-08-05 (public domain via FRED H.4.1 fredgraph.csv), persisted in data/market.sqlite:macro_obs/macro_series_meta and verified offline via ensure_macro(offline=True) — weekly_to_daily ffill L7 + ROC5>4.8 + hw20 yields 57 triggers / 186 active days (3.14%, 2.42/yr) with no lookahead. Generic harness paired each defensive as head-to-head generic-only vs signal+generic on the same anchored window 2002-12-18→2026-07-24 with identical costs/lag (BacktestConfig.allocatesmartly() next_close 10bps): SMA200 Δ47.39% (generic_only 6.6394 → signal+generic 9.7858, CAGR +1.80% Sharpe +0.09, MaxDD -21.5%→-27.3%), SMA50 Δ34.63% (2.8471→3.8330), cross 50/200 Δ50.21% (8.5526→12.8467), trailing-stop 10% Δ62.83% (6.4074→10.4332, CAGR +2.26% Sharpe +0.12), B&H Δ0.00% (12.6866→12.6866) verifying no lookahead. Detailed table is rendered deterministically in docs/experiments/qm-kdnj.5.html#generic-exit-harness from supplemental.generic_exit_harness_rows and persisted in the experiment payload metrics.generic_exit_harness. Offline verification: 8-test harness passes, 82 focused tests pass, 74 prior Fed-ROC tests remain green, artifacts deterministic, static bundle OK, pipeline validates. Disposition remains investigate — not a promotable risk-indicator or catalog strategy, but the sparse buy-only signal now has parity+generic-baseline persistence for qm-u94t closure. No private leakage, no DB mutation.
Links
- Task:
qm-0azu(parent epicqm-kdnj, siblingqm-kdnj.5, discovered-fromqm-u94t) - Pinned Pine:
fedbal = request.security("WSHOSHO","D",close); ta.roc(fedbal,5) >4.8(verbatim in findings) - Prior experiments:
research/findings/fed-bal-roc-qm-4bx.19.md/.json(NO-SHIP overlay) andresearch/findings/fed-bal-roc-buy-signal-qm-4bx.21.md/.json(SHIP as risk-signals/v1) - Code:
backtest/generic_signals.py(sma/cross/trailing/bh),backtest/fed_bal_roc.py(weekly_to_daily_forward_fill + compute_roc + fedbal_roc_buy_signal + apply_holding_window),research/scripts/publish_fed_bal_roc_qm_kdnj5.py(build_manifest/compute_harness),research/reports/scaffold.py(generic-exit-harness table) - Data:
data/market.sqlite:macro_obs/macro_series_metaWSHOSHO 1234 + SPY 8428 rows;data/cache/fred_WSHOSHO.csvFRED provenance - Experiment:
research/experiments/qm-kdnj.5.json+docs/site-data/experiments/qm-kdnj.5.json+docs/experiments/qm-kdnj.5.html(parity page + Research Narrative delta table) - Tests:
tests/test_fed_bal_roc_generic_harness.py(8 offline),tests/test_fed_bal_roc.py(74),tests/test_datastore.py,tests/test_engine.py - Findings:
research/findings/fed-bal-roc-qm-4bx.19.md(+ qm-kdnj.5 supplemental provenance)