Objective
Re-derive the ADL Volume Ratio signal exactly as the pinned Pine defines it (USI:UVOL/USI:DVOL daily close ratio >22, timeframe=5 declared but not wired to request.security D), re-audit existing experiments qm-4bx.17 (overlay) and qm-4bx.22 (signal) for signal-date fidelity, fix any date/definition drift (ratio construction, timeframe ambiguity, threshold strictness, aggregation, calendar, hysteresis, lookahead, ffill, holding window), persist inputs offline to data/market.sqlite with provenance, 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.
Approach
Froze Pine verbatim: upvol=request.security(USI:UVOL,D,close), downvol=request.security(USI:DVOL,D,close), upratio=upvol/downvol, adl_vol_buy_signal=upratio>ratiothold with ratiothold=22 and timeframe=5 not wired to D request. Documented request.security D close as dividend/split-unadjusted TV daily close, calendar tradingview daily, timezone UTC normalized, frequency D, field close, adjustment n/a, available at close executable next_close (+1 bar, no lookahead). Distinguished documented rule (tickers, D close, threshold 22, strict >) from inference (timeframe=5 → daily primary vs 5-day sum sum(UVOL,5)/sum(DVOL,5) alternative not primary, no HMA/EMA/MA, no quantile/sigma/hysteresis, no execution in Pine — all probes). Updated backtest/adl_vol_ratio.py docstring to full verbatim + semantics + documented-vs-inference + timeframe ambiguity note (already Pine-correct implementation of adl_vol_buy_signal strict >22 level hb1 and adl_volume_ratio daily vs sum5 vs ma). Verified tick-for-tick via new research/fixtures/adl-vol-ratio-pine-fixture.csv (15 rows hand-computed ratio >22 strict, exact 22 false, just-above true, DVOL zero → NaN false, sum5 5-bar NaN) and tests/test_adl_vol_ratio.py::test_adl_pine_fixture_tick_for_tick. Audited maximal native span USI:UVOL 1996-04-19→2026-08-11 7616 rows and USI:DVOL 1992-01-02→2026-08-11 8643 rows (ratio 7616) via read_ohlcv mode=ro + query_only; confirmed prior vs corrected triggers 24 vs 24, active 259 vs 259, 0 mismatches, evaluator _signal_adl_vol_ratio mirrors adl_vol_buy_signal on unified SPY calendar 8428 days. Ran full robustness matrix (thresholds 8/12/15/22/30/40, sum5/MA collapse at 22, hysteresis level hb1 vs hb2/3, holdings 5/10/20/40/60, extra lag +1 bar, falsification random/inverted, subperiod walk-forward, NASDAQ r=0.69 probe, synthetic NO-GO). Kept signal assessment separate from strategy-overlay harness; for buy-only paired generic opposite signals SPX<50DMA/<200DMA/50-200 cross/trailing-stop 10% and reported head-to-head generic-only vs signal+generic deltas on same window/costs/lag via BacktestConfig.allocatesmartly().
Files changed
backtest/adl_vol_ratio.py— Pine-verbatim docstring frozen (full inputs, request.security D close semantics, documented vs inference, timeframe=5 vs D ambiguity, sum5 alternative brittle)research/fixtures/adl-vol-ratio-pine-fixture.csv— pinned 15-row hand-computed TV reference (daily ratio >22 strict, 22-edge, DVOL-zero NaN, sum5 5-bar)tests/test_adl_vol_ratio.py— addedtest_adl_pine_fixture_tick_for_tick(offline, asserts exact signal dates)research/experiments/qm-kdnj.2.json— qm-kdnj experiment-as-strategy manifest (rejected for risk-off, SHIP as signal, frozen metrics, parity, generic-exit harness)research/experiments/qm-kdnj.3.json— fix provenance to exactly 5 keys (remove extradatakey) to passexperiment_registry.validate_manifestresearch/results/qm-kdnj.2.md— this durable task note (searchable viagit grep)docs/site-data/signals/history.json— regenerated viaresearch.reports.signals_publish(deterministic, ADL 24/259 entry)docs/site-data/qm-kdnj.2.json/docs/site-data/results.json+docs/results.html— viaresearch.reports build --render
Validation
python3 -m pytest -q tests/test_adl_vol_ratio.py tests/test_risk_signals_history.py tests/test_datastore.py tests/test_engine.py— 86 passed offline (Pine fixture tick-for-tick, ratio daily vs sum5, threshold 22, MA smoothing, hysteresis, holding fixed/decay, overlay modes, falsification, provenance)python3 -m research.reports.signals_publish— wrote docs/site-data/signals/history.json (2832502 bytes, ADL 24 triggers 259 active preserved)python3 -m research.reports build --render— exported 139/139 payloads db_warm, scaffold OK, experiments payload includes qm-kdnj.2 and qm-kdnj.3python3 research/scripts/check_static_reports.py docs— local bundle OK: 1230 files; 182 canary routespython3 -m research.pipeline validate— passed (28 nodes 38 edges graph_hash 6802fab)python3 -m research.pipeline verify --offline— passed (indicator reject → experiment_publish → verification → completion)sqlite3 data/market.sqlite SELECT ticker,start_date,end_date,n_rows FROM instruments WHERE ticker IN ('USI:UVOL','USI:DVOL')— 7616 1996-04-19→2026-08-11 and 8643 1992-01-02→2026-08-11, read_ohlcv mode=ro query_only succeeds offline, no -wal/-shm, size 104275968 bytes- No DB mutation from report/read paths, no secret/private leakage (grep ALLOCATESMARTLY/GH_TOKEN empty), no synthetic pre-1996 invented, sum5/MA brittle documented as alternative not primary
Results
Fidelity frozen and validated: Pine daily UVOL/DVOL >22 strict level hb1 hw20 next_close is tick-for-tick correct. Date-fidelity audit on maximal native span shows 0 mismatches vs prior qm-4bx.17/22 (both 24 triggers 259 active hw20 on 7616 ratios 1996-04-19→2026-08-11; evaluator vs direct delta 0; pinned fixture passes). Baseline 24 triggers (2007-09-18→2025-04-09) 3.4% native, 2.97% unified 8709, ratio mean 1.79 std 2.71 max 70.2 sum5 max 5.08. Robustness: threshold 15 best 13.42 vs 22 13.39 vs 30 9.27 vs 40 8.34 brittle; sum5 at 22 never fires and all MA 3/5/10/20 collapse to 0 (only raw daily fires) — flagged; holdings monotonic 5:7.63 10:8.89 20:13.39 40:17.54 60:18.62 optimization risk; extra lag +1 bar 14.50 vs 13.39 not destructive; falsification random 10 trig 7.94 vs 13.39 (-40%) and inverted 48 trig 9.67 (-27%) pass; walk-forward both halves +25% (9 and 15 trig); GFC +10.9% 2011 +7.8% COVID 0% 2022 +0.13%. Generic-exit harness (buy-only → SPX<50DMA/<200DMA/50-200 cross/trailing-stop 10%) head-to-head on same window/costs/lag: SMA200 generic-only 8.11 → signal+generic 13.39 +65% Sharpe 0.63→0.73, SMA50 9.02→12.85 +42%, cross 7.85→11.02 +40%, trailing 9.45→13.10 +38%, B&H 19.28→19.28 0%. Synthetic pre-1996 NO-GO (close-only store Volume=0, counts not volume, Bloomberg licensed rejected). Disposition rejected for risk-off-indicators/v1 (sparse not regime, brittle, sparse power) but SHIP as signal via risk-signals/v1 (buy-signals-v1.json adl-vol-ratio, signals/history.json, docs/signals.html generic selector 3 signals). Published as qm-kdnj experiment-as-strategy with frozen implementation and provenance, promotable via qm-kdnj harness only if OOS Deflated Sharpe with fixed hw20 passes.
Links
- Task:
qm-kdnj.2(parent epicqm-kdnj) — pinned Pine ADL Vol Ratio timeframe=5 vs Dupratio>22 - Prior:
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 signal) - Code:
backtest/adl_vol_ratio.py(adl_vol_buy_signal daily >22 strict, adl_volume_ratio daily/sum5/ma, provenance_record) - Fixture:
research/fixtures/adl-vol-ratio-pine-fixture.csv(15 rows) +tests/test_adl_vol_ratio.py::test_adl_pine_fixture_tick_for_tick - Data:
data/market.sqlite:ohlcvUSI:UVOL/USI:DVOL TV closes - Experiment:
research/experiments/qm-kdnj.2.json+docs/site-data/qm-kdnj.2.json+docs/site-data/results.json