Objective
For qm-kdnj.3 (Pine VIX/VIX3M EMA50/252 vix_ratio_long_ema = ta.ema(vix_data/vix3m_data,252) vix_ratio_short_ema = ta.ema(vix_data/vix3m_data,50) sell = short>long continuous regime, VIX 9215 rows 1990-01-02..2026-08-04 and VIX3M 5032 rows 2006-07-17..2026-07-17 yahoo persisted in data/market.sqlite instruments), wire the generic opposite-signal harness head-to-head deltas as for qm-u94t.1/qm-0azu (SPX <50DMA, <200DMA, 50/200 death-cross, trailing-stop 10% via backtest/generic_signals.py) with datastore provenance already in instruments and offline repro. The delta table must render in the parity page and offline test must pass without live fetch.
Approach
- Froze VIX provenance: ^VIX 9215 and ^VIX3M 5032 daily closes yahoo
yfinance Yahoo chart APIpersisteddata/market.sqlite:ohlcv+instruments(provider yahoo, field Close, frequency D, native spans 1990-01-02 live and 2006-07-17 live) viabacktest.datastoreimport-cache; verifiedcoverage_ohlcvslack 5d andensure_universe([SPY,^VIX,^VIX3M], offline=True)returns intersection 5032 with full 9215 still viaread_ohlcv, UTC-naive sorted unique, one-bar lag via BacktestConfig.allocatesmartly next_close.
- Reused
backtest/generic_signals.py(deterministic, causal): sma_signal, cross_signal, trailing_stop_signal 10% expanding peak, bh_signal via all_generic_signals/generic_weights + validate_weights.
- Created
research/scripts/publish_vix_contango_qm_kdnj3.py(build_manifest, compute_harness offline): loads SPY/^VIX/^VIX3M via ensure_universe offline=True → vix_contango_signals EMA50/252 with SMA-seed (ta.ema alpha 2/(len+1)) → valid 2007-07-17→2026-07-17 4781 bars 43.25% sell active, then for each generic defensive computes paired backtests on same window 2007-07-17→2026-07-17 with BacktestConfig.allocatesmartly (next_close, fee 10bps) via AND blend (cash if either VIX sell or generic defensive; BH blended equals VIX regime alone) and metrics (equity, CAGR, Sharpe, MaxDD, delta%). Emits metrics.generic_exit_harness + supplemental generic_exit_harness_detail/rows + research_narrative and monthly rebased equity.
- Fixed
research/reports/experiments.pydedup: previously findings-based stub for qm-kdnj.3 (slug qm-kdnj-3-vix...) hid registry manifest harness; now registry manifest wins when id exists in registry (skip finding entry if in registry_ids), so supplemental harness renders via /qm-kdnj.3.html.
- Updated
research/reports/scaffold.pyalready renders generic-exit-harness table from supplemental.generic_exit_harness_rows (fallback to dict mapping) with Generic/Generic-only/Signal+Generic Δ%/ΔCAGR/ΔSharpe plus window/note; no hand-edited payloads.
- Added
tests/test_vix_contango_generic_harness.py(7 offline, no network, no DB mutation): ensure_ohlcv 9215/5032, generic signals deterministic/causal, VIX EMA SMA-seed continuous 2007-07-17 and valid 4781, harness deltas (sma200 -52.97% etc, bh negative as VIX-only underperforms), manifest/site-data/html contain harness, no mutation, one-bar lag and costs.
Files changed
research/scripts/publish_vix_contango_qm_kdnj3.py— new offline harness publisher (compute_harness + build_manifest, AND blend, monthly equity)research/experiments/qm-kdnj.3.json— manifest research-experiment/v1 with metrics.generic_exit_harness + supplemental rows/detail/narrative, equity monthly rebased, provenance code includes generic_signalsresearch/reports/experiments.py— prefer registry manifest over finding stub (skip finding if id in registry) so harness renders via qm-kdnj.3.htmldocs/site-data/experiments/qm-kdnj.3.json— site-data mirror (via build_experiments_payload)docs/site-data/experiments/index.json— experiments hub payloaddocs/experiments/qm-kdnj.3.html— new parity page with Research Narrative generic-exit-harness delta table (data-test generic-exit-harness, 5 rows)docs/experiments.html— experiments index (now points to qm-kdnj.3.html)tests/test_vix_contango_generic_harness.py— 7 offline harness testsresearch/results/qm-60x1.md— this durable task note (YAML front matter id qm-60x1, category fallback, searchable via git grep)docs/site-data/results.json,docs/results.html— rebuilt changelog (qm-60x1 newest)
Validation
python3 -m research.scripts.publish_vix_contango_qm_kdnj3 --write— wrote harness window 2007-07-17→2026-07-17 4781 valid 43.25% active, SMA200 4.6786→2.2004 -52.97% CAGR -4.23% Sharpe -0.22 etcpython3 -m research.reports scaffold— wrote docs/experiments/qm-kdnj.3.html with data-test generic-exit-harness table 5 rows sma50/sma200/cross/trailing/bh and delta tablepython3 -m pytest -q tests/test_vix_contango_generic_harness.py— 7 passed offline (9215/5032, EMA seed, harness deltas, manifest/site/html, no DB mutation, next_close 10bps)python3 -m pytest -q tests/test_vix_contango_generic_harness.py tests/test_vix_contango.py tests/test_datastore.py tests/test_engine.py tests/test_fed_bal_roc_generic_harness.py— 81 passedpython research/scripts/check_static_reports.py docs— local bundle OK: 1248 files; 178 canary routespython3 -m research.artifacts check --mode full --offline— shared artifacts are deterministic and current (built 11)python3 -m research.pipeline validate— passed (28 nodes 38 edges)python3 -m research.pipeline verify --offline— passedgrep -c generic-exit-harness docs/experiments/qm-kdnj.3.html— 1 table renderspython3 -c "import json; print(json.load(open('research/experiments/qm-kdnj.3.json'))['supplemental']['generic_exit_harness']['window'])"— 2007-07-17→2026-07-17 same window/costs/lag next_close +10bpssqlite3 data/market.sqlite SELECT ticker,n_rows FROM instruments WHERE ticker IN ('^VIX','^VIX3M')— ^VIX 9215, ^VIX3M 5032- No DB mutation (file_fingerprint unchanged, connect_readonly query_only), no secret leakage
Results
Datastore persistence verified offline: ^VIX 9215 yahoo 1990-01-02..2026-08-04 and ^VIX3M 5032 yahoo 2006-07-17..2026-07-17 in instruments, VIX contango valid 4781 of 5032 after 252 warmup (2007-07-17→2026-07-17, 43.25% sell active). Continuous regime Pine fidelity already frozen (SMA-seed EMA, 31.9% mismatch vs 4-point ensemble fixed) and rejected for economics (CAGR 4.24% vs 10.58% BH). Generic harness paired each defensive head-to-head same window/costs/lag (BacktestConfig.allocatesmartly next_close 10bps, AND blend): SMA200 generic-only 4.6786 → VIX+generic 2.2004 Δ-52.97% (CAGR 8.47%→4.24% Δ-4.23% Sharpe 0.72→0.51 Δ-0.22 MaxDD -20.9%→-22.3%), SMA50 2.2299→1.2856 Δ-42.35%, cross 50/200 4.5627→1.9677 Δ-56.87%, trailing 10% 3.6447→1.3987 Δ-61.62%, BH 6.8080→2.2675 Δ-66.69% (VIX-only) — all negative showing VIX regime adds defensiveness but hurts return vs generic alone on this window, consistent with prior overlay underperformance. Delta table renders in docs/experiments/qm-kdnj.3.html section generic-exit-harness with Δ% ΔCAGR ΔSharpe.
Links
- Task:
qm-60x1(parentqm-u94t, siblingqm-kdnj.3, discovered-from qm-u94t) - Pinned Pine:
vix_ratio_long_ema = ta.ema(vix_data / vix3m_data, 252); vix_ratio_short_ema = ta.ema(vix_data / vix3m_data, 50); sell = short>longverbatim - Code:
backtest/vix_contango.py(ema SMA-seed, vix_contango_signals),backtest/generic_signals.py(all_generic_signals),research/scripts/publish_vix_contango_qm_kdnj3.py(compute_harness/build_manifest),research/reports/experiments.py(registry precedence),research/reports/scaffold.py(harness table) - Data:
data/market.sqlite:ohlcv/instruments^VIX 9215 ^VIX3M 5032 + SPY 8428,backtest/datastore:ensure_universe/read_ohlcvoffline - Experiment:
research/experiments/qm-kdnj.3.json+docs/site-data/experiments/qm-kdnj.3.json+docs/experiments/qm-kdnj.3.html - Tests:
tests/test_vix_contango_generic_harness.py(7),tests/test_vix_contango.py(6) - Findings:
research/findings/vix-contango-pine-audit-qm-kdnj.3.md/.json