Add S/R v2 research and validation harness

This commit is contained in:
2026-07-12 21:15:18 +02:00
parent 57ac1d2cdd
commit 19b81c169d
19 changed files with 1575 additions and 117 deletions
+47 -8
View File
@@ -13,14 +13,24 @@ net-positive is the open question, tracked below.
## 1. How the levels are built today
`app/services/sr_service.py::detect_sr_levels`, over **all stored history**
(`query_ohlcv` with no date range — 5 years / ~1260 daily bars per ticker):
> **Update (2026-07-12 detector rewrite):** several gaps below were addressed in
> `sr_service` / `indicator_service` — close-bin VP, local-peak HVN, POC/VAH/VAL
> as candidates, LVN dropped from S/R, pivot prominence + lookbacks, rejection-
> weighted recency strength, ATR-adaptive merge, hard cap, round numbers. The
> table documents the *pre-rewrite* failure modes measured on the snapshot; keep
> it for historical context. Re-measure density on prod after deploy if gate
> rates shift.
1. Candidates = volume-profile **HVN and LVN** bins + **pivot** swing highs/lows.
2. Strength = share of bars that "touched" the level, scaled so ~20% of bars → 100.
3. Nearby levels merged within 0.5%; tagged `support` if below spot, else `resistance`.
`app/services/sr_service.py::detect_sr_levels` (post-rewrite):
### Where that departs from best practice
1. Candidates = VP **POC / VAH / VAL / local HVN peaks** (lookback 252) +
**prominent** swing pivots (lookback 504) + nearby **round numbers**.
2. Strength = rejection-weighted touches on last 252 bars with recency decay
(pass-throughs down-weighted); method base + confluence on merge.
3. Nearby levels merged with **ATR-adaptive** tolerance (clamped ~0.41.5%);
capped (~16, interleaved S/R); tagged `support` if below spot, else `resistance`.
### Where the pre-rewrite detector departed from best practice
Measured on `backtest_snapshots/prod.sqlite` (AAPL, 1261 bars, spot $308.63):
@@ -35,8 +45,8 @@ Measured on `backtest_snapshots/prod.sqlite` (AAPL, 1261 bars, spot $308.63):
| **Round-number levels absent** — the mechanism with the best empirical support ([Osler 2000](https://papers.ssrn.com/sol3/papers.cfm?abstract_id=888805)). | not implemented |
Not a defect: the pivot `window=2` is the standard 5-bar Williams fractal. What it
lacks is a **prominence filter** — AAPL yields 338 pivots over 1261 bars, one every
~3.7 bars.
lacked pre-rewrite is a **prominence filter** — AAPL yielded 338 pivots over 1261
bars, one every ~3.7 bars.
### The structural problem: resistance famine
@@ -383,6 +393,35 @@ Note `--allow-spawn` is required on Windows: `_mp_context()` has no `fork`/
large, consistent across five nested windows — and still didn't survive a holdout.
Nested lookbacks are not out-of-sample. Split by entry date before believing anything.
## 7. S/R v2 research harness (implementation started 2026-07-12)
The detector rewrite is decomposed into causal, research-only arms. The live
scanner does not read `BACKTEST_SR_VARIANT`; these switches exist only in the
offline snapshot harness:
| arm | behavior |
|---|---|
| `production_control` | deployed detector plus legacy 1.5 primary selection |
| `rr_aligned_control` | deployed detector; primary selection uses activation `min_rr` |
| `rewrite` | rewritten detector with activation-aligned primary selection |
| `soft_zones` | rewrite plus max-strength/confluence zone aggregation |
| `confirmed_rounds` | soft zones; standalone rounds need two rejection clusters |
| `gate_v2` | confirmed rounds plus uncapped gate evidence |
Detector evidence (`sources`, rejection count, last rejection age) stays in the
pure backtest objects. It is deliberately not migrated into the production DB
schema until a variant passes validation.
Run the training matrix with `--entry-end 2024-06-30 --sr-audit`, choose one arm,
and record that lock before running exactly control and the locked arm with
`--entry-start 2024-07-01`. Use `scripts/compare_sr_variants.py` to produce the
paired cohort CSV and summary JSON.
The post-2024 interval has informed earlier research, so this is validation rather
than a pristine holdout; do not sweep variants on it. No deployment follows
automatically. A lower validation Sharpe or higher drawdown remains a no-ship
result even when CAGR rises.
**Next runs, if picked back up:**
- A **per-name target model** for clear-air setups instead of a constant k×ATR. This