Objective
For qm-kdnj.2 (Pine USI:UVOL/USI:DVOL request.security D close ratio upratio=upvol/downvol >22 strict level hw20, timeframe=5 input declared but not wired to D in verbatim, 7616 ratios 1996-04-19→2026-08-11, 24 triggers 259 active hw20, 0 date drift vs prior qm-4bx.17/22, persisted tradingview USI:UVOL 7616 rows 1996-04-19→2026-08-11 and USI:DVOL 8643 rows 1992-01-02→2026-08-11 in data/market.sqlite), wire the qm-kdnj generic opposite-signal harness (SPX SMA50/200, 50/200 death-cross, trailing-stop 10% via backtest/generic_signals.py + backtest/adl_vol_ratio.py apply_overlay override) as head-to-head indicator+generic vs generic-only deltas (ΔCAGR/ΔSharpe/ΔMaxDD) on same window/costs/lag (next_close, fee_bps=10). Acceptance: docs/site-data/experiments/qm-kdnj.2.json gains strategy-parity generic harness rows, docs/experiments/qm-kdnj.2.html renders deltas in Research Narrative, offline repro via ensure_universe offline=True, no hand-edited payloads.
Approach
- Froze datastore provenance: instruments USI:UVOL 7616 and USI:DVOL 8643 rows tradingview close_only (Volume=0 per qm-zl9.1), verified
SELECT ticker,start_date,end_date,n_rows FROM instrumentsandread_ohlcvmode=ro query_only succeeds offline, no WAL mutation, 7604 valid intersect bars 1996-04-19→2026-07-24 with SPY 8428 1993-01-29→2026-07-24.
- Reused deterministic
backtest/generic_signals.py(causal UTC-naive, no lookahead):sma_signal(closecross_signal (SMA50 trailing_stop_signal (close < expanding peak*(1-0.10) window None trailing 10% min per qm-kdnj contract), bh_signal(never defensive),generic_weights/all_generic_signalsviavalidate_weights; all boolean daily on SPY closes, one-bar execution viaBacktestConfig.allocatesmartly()next_close 10bps.
- Created
research/scripts/publish_adl_vol_ratio_qm_kdnj2.py(compute_harness/build_manifestoffline): loads SPY+UVOL+DVOL viaensure_universe(["SPY","USI:UVOL","USI:DVOL"], offline=True), loads full UVOL/DVOL viaread_ohlcv→adl_vol_buy_signaldaily threshold 22.0 strict level →apply_holding_window20 fixed, slices to valid_idx (7604), runs paired backtests per generic on aligned window with sameBacktestConfig.allocatesmartly()viageneric_weightsandapply_overlayOR override (long if either generic long or ADL active hw20), computesgeneric_exit_harness(Δ% ΔCAGR ΔSharpe ΔMaxDD) and richergeneric_exit_harness_detail+generic_exit_harness_rowsfor table, emitsresearch/experiments/qm-kdnj.2.jsonvalidated viaexperiment_registry.validate_manifest.
- Updated
research/experiments/qm-kdnj.2.jsonsupplemental to carrygeneric_exit_harnessdict,generic_exit_harness_rows(5 rows sma50/sma200/cross_50_200/trailing_10pct/bh),generic_exit_harness_detailandresearch_narrativeGeneric exit harness entry; window 1996-04-19→2026-07-24 native 7616 ratio etc.
- Existing
research/reports/scaffold.py:render_experiment_detailalready renders harness table fromsupplemental.generic_exit_harness_rows(fallback dict mapping) plus window/note/detail; no scaffold code change needed for ADL — verified table renders afterpython3 -m research.reports scaffoldandbuild --render.
- Added
tests/test_adl_vol_ratio_generic_harness.py(7 tests, offline, no network, no cache mutation):ensure_ohlcv7616/8643 offline, deterministic/causal trailing-stop, ADL signal tick-for-tick threshold strict >22 24/259, harness deltas (SMA200 65.09% etc matching offline compute), manifest + site-data + html contain harness with Δ table, no DB/WAL mutation guard, one-bar lag and costs viaBacktestConfig.
Files changed
research/scripts/publish_adl_vol_ratio_qm_kdnj2.py— new ADL qm-kdnj.2 publisher with harness computation offline via ensure_universe/read_ohlcv, head-to-head generic-only vs signal+generic same window/costs/lag OR override hw20, manifest validationresearch/experiments/qm-kdnj.2.json— experiment manifest research-experiment/v1 id qm-kdnj.2 with metrics/supplemental generic_exit_harness + rows/detail/narrative, equity/benchmark, provenance, limitations, disposition rejected (fidelity verified but brittle/sparse)docs/site-data/experiments/qm-kdnj.2.json— site-data mirror of experiment payload (deterministic via research.reports build)docs/site-data/experiments/index.json— updated index with qm-kdnj.2 harness supplementdocs/experiments/qm-kdnj.2.html— static parity page with Research Narrative generic-exit-harness Δ table (data-test=generic-exit-harness5 rows)docs/experiments.html— experiments listing rebuilttests/test_adl_vol_ratio_generic_harness.py— 7 offline harness tests (ensure_ohlcv, signals causal, ADL threshold, deltas SMA200 65.09% etc, manifest/site/html, no mutation, lag+costs)research/results/qm-ms8n.md— this durable task note (YAML front matter id qm-ms8n, category fallback, newest changelog entry, searchable viagit grep)docs/site-data/results.json,docs/results.html,docs/results/qm-ms8n.html— rebuilt changelog payload viapython3 -m research.reports.results(qm-ms8n newest)
Validation
python3 -m research.scripts.publish_adl_vol_ratio_qm_kdnj2 --write— wrote research/experiments/qm-kdnj.2.json harness 7604 bars 1996-04-19→2026-07-24 SMA200 65.09% (10.01→16.52 CAGR +1.81% Sharpe +0.10), cross 66.32% (15.43→25.66), SMA50 23.43%, trailing 50.70%, BH 0.0% 24 triggers 259 active hw20 offline tradingviewpython3 -m research.reports scaffold— wrote docs/experiments/qm-kdnj.2.html with generic-exit-harness table (section id=generic-exit-harness data-test=generic-exit-harness) 5 rowspython3 -m research.reports build --render— exported 139/139 payloads, wrote docs/site-data/experiments/qm-kdnj.2.json with supplemental harness rowspython3 -m pytest -q tests/test_adl_vol_ratio_generic_harness.py— 7 passed offline via ensure_universe/read_ohlcv offline=True, no DB mutation, deterministic signalspython3 -m pytest -q tests/test_adl_vol_ratio_generic_harness.py tests/test_adl_vol_ratio.py tests/test_datastore.py tests/test_engine.py— 83 passed offline (7 harness + 16 adl fixture +60 datastore/engine) in ~37spython3 -m research.artifacts check --mode full --offline— shared artifacts deterministic and current (11 artifacts)python3 -m research.artifacts build --mode full --offline— built 11 deterministic shared artifactspython3 -m research.pipeline verify --offline— passed (fallback → verification → completion)python3 -m research.pipeline validate— passed (28 nodes 38 edges graph_hash 6802fab)python3 research/scripts/check_static_reports.py docs— local bundle OK: 1249 files; 178 canary routessqlite3 data/market.sqlite SELECT ticker,n_rows,start_date,end_date FROM instruments WHERE ticker IN ('USI:UVOL','USI:DVOL')— USI:UVOL 7616 1996-04-19→2026-08-11, USI:DVOL 8643 1992-01-02→2026-08-11 tradingview, read_ohlcv mode=ro query_only succeeds offline, no -wal/-shmgrep -n generic-exit-harness docs/experiments/qm-kdnj.2.html— section id=generic-exit-harness data-test=generic-exit-harness renders Δ% ΔCAGR ΔSharpe table (SMA50/SMA200/cross/trailing/BH)python3 -c "import json; print(json.load(open('research/experiments/qm-kdnj.2.json'))['supplemental']['generic_exit_harness'])"— harness dict shows SMA200 65.09% 10.01→16.52 etcpython3 -m research.reports.results— rebuilt changelog with qm-ms8n, n_entries 508, zero errors- No DB mutation from read paths (
connect_readonlyquery_only), no secret/private leakage (grep ALLOCATESMARTLY/GH_TOKEN empty), no synthetic pre-1996 history invented, no hand-edited docs/site-data (all via producers)
Results
Datastore provenance frozen: USI:UVOL 7616 rows 1996-04-19→2026-08-11 and USI:DVOL 8643 rows 1992-01-02→2026-08-11 tradingview close_only, persisted in data/market.sqlite:instruments/ohlcv and verified offline via ensure_universe offline=True — ratio 7616 on valid intersect 7604 bars 1996-04-19→2026-07-24 (SPY limit) with 24 triggers /259 active hw20 fixed (3.406% pct_active, 0.8/yr sparse). Generic harness paired each defensive as head-to-head generic-only vs signal+generic on same anchored window with identical costs/lag (BacktestConfig.allocatesmartly next_close 10bps, OR override hw20): SMA200 Δ65.09% (generic_only 10.0058 → signal+generic 16.5190, CAGR 7.93%→9.74% Δ+1.81% Sharpe 0.68→0.78 Δ+0.10, MaxDD -29.4%→-29.4%), SMA50 Δ23.43% (2.8243→3.4858, CAGR 3.50%→4.23% Δ+0.73% Sharpe 0.36→0.41), cross 50/200 Δ66.32% (15.4286→25.6613, CAGR 9.49%→11.36% Δ+1.86% Sharpe 0.71→0.81), trailing-stop 10% Δ50.70% (5.8122→8.7591, CAGR 6.01%→7.46% Δ+1.45% Sharpe 0.55→0.63), B&H Δ0.00% (19.2833→19.2833) verifying no lookahead. Delta table renders in docs/experiments/qm-kdnj.2.html and docs/site-data/experiments/qm-kdnj.2.json; offline harness recomputable without live fetch; experiment remains rejected for risk-off promotion (sparse/brittle) but published as risk-signals/v1 sparse buy signal with frozen Pine semantics.
Links
- Task:
qm-ms8n(parent epicqm-kdnj, sibling harness tasksqm-60x1VIX,qm-0azuFed ROC, discovered-fromqm-u94t) - Pinned Pine:
upvol=request.security(USI:UVOL,D,close); downvol=request.security(USI:DVOL,D,close); upratio=upvol/downvol; adl_vol_buy_signal=upratio>22hw20 fixed verbatim - Prior experiments:
research/findings/adl-vol-ratio-qm-4bx.17.md/.json(NO-SHIP overlay) andresearch/findings/adl-vol-ratio-buy-signal-qm-4bx.22.md/.json(SHIP as risk-signals/v1) - Code:
backtest/generic_signals.py(sma/cross/trailing/bh),backtest/adl_vol_ratio.py(adl_vol_buy_signal + apply_holding_window + apply_overlay override),research/scripts/publish_adl_vol_ratio_qm_kdnj2.py(build_manifest/compute_harness) - Data:
data/market.sqlite:ohlcv/instrumentsUSI:UVOL 7616 + USI:DVOL 8643 rows tradingview close_only; SPY 8428 1993→2026-07-24 - Experiment:
research/experiments/qm-kdnj.2.json+docs/site-data/experiments/qm-kdnj.2.json+docs/experiments/qm-kdnj.2.html(parity page + Research Narrative Δ table) - Tests:
tests/test_adl_vol_ratio_generic_harness.py(7 offline),tests/test_adl_vol_ratio.py(16),tests/test_datastore.py,tests/test_engine.py - Findings:
research/findings/adl-vol-ratio-qm-4bx.17.md(+ qm-kdnj.2 supplemental provenance)