Files
ForexBot2/backtest/research/cycle_2026-09-10.md
Harwinder Sekhon be72f3f350 research: improvement cycle 1 ledger (baseline, cost ladder, C1 reject, C2 plateau, seeding parity)
backtest/research/cycle_2026-09-10.md: pre-registered rules, HEAD baseline
on the 3-year window (three pinned MD5s reproduced), cost ladder and
concentration facts, bucket screen (one cycle-2 candidate: M15 grade-A
SELLs), C1 H1 divergence->0 REJECTED, C2 M15 RSI-multiplier ladder =
stable plateau, seeding-depth measurement and the two shipped parity
fixes. CLAUDE.md: live fetch depth, backtester pad, 501 tests, the
2026-09-10 state entry; corrected the docs note (no PDF builds exist)
and recorded pyarrow as a dev-venv-only dependency.

Claude-Session: https://claude.ai/code/session_01NQDjW8rXYrJR4JDWMWKUij
2026-09-10 08:29:14 -04:00

288 lines
20 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Improvement cycle 1 — 2026-09-10 (HEAD baseline, robustness challenge, seeding parity)
## Purpose
First cycle under the owner's standing brief (evidence-driven incremental improvement of
the existing XAU/USD system). No new strategy idea is tested. The cycle (1) re-establishes
the baseline at HEAD and proves the harness reproduces the pinned books, (2) challenges the
current edge — cost sensitivity, concentration, and the parameter neighbourhood of the M15
RSI × 0.5 change shipped 2026-09-09, (3) reruns the one parked candidate with prior rule-1
evidence (H1 divergence off) through the production vehicle it now has, and (4) measures
and fixes a live-vs-backtest indicator-seeding gap (H4 fetches 380 bars, D1 300, while the
backtester seeds from its whole cached history). Everything under "Pre-registered" was
written before the corresponding run.
## Harness
`venv/bin/python backtest/gpu_backtest.py --pair XAUUSD --spread 0.65 --workers 12
--primary-tf {5min|15min|1h|4h}`, pair-factory config (no `--quality/--strategies/
--confirm-tfs`). Windows: **W3** 2023-09-05→2026-09-05 (halves at 2025-03-05; last-12-months
slice from 2025-09-05 reported descriptively), **W2** 2024-09-05→2026-09-05 (halves at
2025-09-05), **OOS** 2024-06-01→2025-03-30. Code: git `2ac7301` (branch `sql-integration`).
Backend: NumPy CPU (CuPy imports but has no CUDA toolkit on this box). Bars: cached
`backtest/cache/oanda_XAU_USD_{5min,15min,1h,4h}_20230705_20260905.parquet`,
`_1d_20221110_20260905`, `_{5min,15min,1h}_20240705_20260905`, `_{…}_20240331_20250330`.
Outputs: `backtest/research/cycle_2026-09-10/` (gitignored). Every candidate is compared
with a CONTROL produced on this harness in this session. `--set
technical.regime_weights_override=<JSON>` replaces the WHOLE table, so each candidate
passes the complete table with one indicator's multipliers changed.
## Pre-registered rules (written 2026-09-10 before any run)
### Step 0 — reproducibility gate
The HEAD factory run must reproduce the ledger's MD5s: M5 W2
`29120a1ee32979431d062f210d41a9d1`, M15 W2 `d185f099482a07f29502cb5a371a9bbf`, M15 OOS
`6f8f24b49597ced28f50a19e08c3472f`. A mismatch stops the cycle until explained.
### Step 1 — diagnostics (descriptive; flags fixed in advance)
- Cost ladder $0.65 / 0.80 / 1.00 / 1.30 on W3 per TF. Red flag for a TF: at $0.80 PF < 1.3
or either W3 half ≤ 0.
- Concentration/stability: share of total R in the top 5% of trades, % of calendar months
positive, longest losing streak, max DD and R per calendar year. No action threshold.
- Bucket screen over the recorded W3 trades (direction, quality, regime, strategy,
entry bar-close hour, weekday, `us_holiday`, `ev_entry_within_30m_hi_usd`,
`ev_hi_usd_during_hold`, year/quarter; plus M15/H4 SELL-in-transitioning and holiday
trades). A bucket becomes a cycle-2 candidate ONLY if n ≥ 30, R < 0 in each of the three
12-month slices AND in OOS, and a mechanism can be stated. No gate is built this cycle.
### C1 — H1 divergence multiplier → 0 in every regime (H1-only)
Vehicle: `--set technical.regime_weights_override=<default table with divergence 0.0 in
all five regimes>`, primary 1h, W3 + OOS, control = the Step-0 HEAD run.
PASS only if ALL: (a) ΔR ≥ +5R on W3; (b) ΔR > 0 in both W3 halves; (c) PF ≥ 1.3 and not
more than 0.05 below control; (d) max DD ≤ control + 2R; (e) trade count within ±25%;
(f) OOS ΔR ≥ 0. Last-12-months slice reported, not a clause. No rescue, no regime-family
fine-tuning after seeing the result. A pass is a shipping PROPOSAL for the owner.
### C2 — M15 RSI multiplier neighbourhood (stability of the live change)
Ladder k ∈ {0, 0.25, 0.5 (HEAD), 0.75, 1.0 (pre-2026-09-09), 1.5}, full table with
rsi × k, on W2, OOS and W3. Reading, with G(k) = R(k) − R(1.0) on W2: **stable plateau** if
G(0.25) ≥ 0.5·G(0.5) and G(0.75) ≥ 0.5·G(0.5) and the OOS deltas at 0.25 and 0.75 are ≥ 0;
**spike** if either neighbour retains < 50% of G(0.5). Outcome is a report to the owner;
no different k is proposed whatever the result.
### Step 3 — seeding-depth parity
3a: on the deepest cached H4/D1 frames (H1 as control), sampled bars over the last two
years, compare `TechnicalEngine.analyze()` on the trailing N bars with the full-history
result; `MIN_SEED_BARS` = smallest N with zero indicator-signal flips on both H4 and D1.
3b: live per-TF fetch floor raised to that depth (no book changes; the backtester is the
reference). 3c: backtester slow-TF pad raised to the same depth, shipped only if the M5
and M15 W3 books stay byte-identical and the H1/H4 differences are confined to the
window-start seeding zone; the H1/H4 re-pin is the owner's decision.
## Results
(filled in as runs complete)
### Step 0 — HEAD baseline (W3, $0.65, factory config; recorded with `--record`)
| TF | trades | total R | WR | PF | max DD | half 1 (→2025-03-05) | half 2 | last 12 mo | SELL bucket | book MD5 |
|---|---|---|---|---|---|---|---|---|---|---|
| M5 | 870 | +153.64 | 53.3% | 1.50 | 10.1 | 136 / +16.73 / PF 1.36 | 734 / +136.91 / 1.52 | 593 / +119.65 / 1.57 | 100 / +8.95 / 1.25 | `c2722f9830528658b53854f6bae61b8c` |
| M15 | 1273 | +186.61 | 59.4% | 1.39 | 19.3 | 556 / +75.87 / 1.37 | 717 / +110.74 / 1.41 | 474 / +81.04 / 1.47 | 404 / −15.18 / 0.91 | `b2cfa9e2e6cd3997f4d5ca9b49cab90e` |
| H1 | 253 | +70.02 | 64.4% | 1.98 | 4.4 | 115 / +26.26 / 1.79 | 138 / +43.76 / 2.15 | 89 / +24.62 / 1.96 | 0 (all shorts blocked) | `b99111132d9ecb80c2998ed5aee2f3ea` |
| H4 | 114 | +21.68 | 57.9% | 1.59 | 5.4 | 52 / +4.91 / 1.26 | 62 / +16.77 / 1.94 | 46 / +12.34 / 1.79 | 20 / −0.97 / 0.89 | `4508e7a3402e008ba1d93805094cd981` |
These equal the numbers CLAUDE.md records for HEAD (M5 870/+153.6/1.50, M15 1273/+186.61/1.39,
H1 253/+70.0/1.98, H4 114/+21.7/1.59). Engine / gate fingerprints from the manifests:
M5 `f28b1a8c409d`/`bc0991492340`, M15 `536f84340ea0`/`b2920991cdb8` (new since the RSI
override), H1 `6efb6d365f01`/`0b67d738dc15`, H4 `ed96f258b10e`/`0c3b51f063f9`.
`replay_scorecard.py --check` on the four recorded runs: see `step0.out`.
Per calendar year (R / PF): M5 2023 +2.4/1.47, 2024 **+1.0/1.02** (102 trades — a flat
year), 2025 +40.5/1.34, 2026 +109.7/1.77; M15 +13.2/1.40, +52.9/1.36, +77.3/1.43,
+43.2/1.37; H1 +5.5/2.60, +14.7/1.58, +33.7/2.11, +16.1/2.35; H4 +1.2/1.28, +4.6/1.39,
+7.7/1.74, +8.2/1.78.
### Step 3a — seeding-depth measurement (`seed_depth.out`)
Deepest Oanda caches (H4 12,419 bars, D1 2,617, H1 45,914; 2018→2026-08-06), bars sampled
every 12th (H4, 267 bars) / 2nd (D1, 313) / 48th (H1, 247) over the last two years;
`TechnicalEngine.analyze()` on the trailing N bars vs on the full history; identical
results with raw frames and with frames cleaned by `validate_and_clean` (the frame-wide
σ of the outlier filter did not add a single flip).
| TF | N = live depth today | bars with ≥1 indicator flip | which | max composite Δ | N = 600 … 5000 |
|---|---|---|---|---|---|
| H4 | 380 | 2 / 267 | ema_trend | 4.66 | 0 flips at every depth |
| D1 | 300 | 1 / 313 | ema_trend | 9.47 | 0 flips at every depth |
| H1 | 1460 (control) | 0 / 247 | — | 0.00 | 0 |
Only the 200-bar EMA trend reading is affected, as the `adjust=False` seeding predicts;
composite-signal and regime never flipped in the sample. Pre-registered pick = smallest
zero-flip depth = 600; shipped floor = **1000** (seed weight e^-10, a ×40 margin on a
sample of a few hundred bars; costs nothing — one request either way). The 2026-08-04
audit's "~2% of H4 bars" was measured on a different sample; this sample says 0.75%
(H4) and 0.3% (D1) of bars, with composite errors up to 9.5 points on the ones that flip.
### Step 1 — concentration, stability, bucket screen (W3 recorded books, `diag_w3_head.txt`)
| TF | top 5% of trades carry | months positive | longest losing streak |
|---|---|---|---|
| M5 | 94% of total R (44 trades, +144R of +154R) | 62% (23/37) | 8 |
| M15 | 96% (64 trades, +179R of +187R) | 68% (25/37) | 9 |
| H1 | 48% (13 trades) | 71% (25/35) | 4 |
| H4 | 80% (6 trades) | 50% (15/30) | 6 |
The intraday edge is a fat right tail: trailing exits let a few dozen runners pay for
everything else (M5 2024 was flat over 102 trades). H1 is the only timeframe whose edge is
spread across its book.
Bucket screen (pre-registered criterion: n ≥ 30 and R < 0 in each 12-month slice y1/y2/y3
AND in the OOS slice of the same book). Exactly one bucket qualifies (besides the trivial
`exit_reason = SL`):
- **M15 SELL grade A**: 230 trades, −14.4R, PF 0.86, y1 −7.4 (67) / y2 −0.4 (80) /
y3 −6.5 (83) / OOS −6.9 (62). Context: M15 SELL overall 404 / −15.2R / PF 0.91 (SELL
max DD 36R on its own curve); SELL|B 160 / −5.5R (y3 +4.6 — not qualifying);
SELL|A+ 14 / +4.7R; SELL|trending_down 301 / −10.9R (y2 +0.9 — not qualifying).
This is the same bucket the 2026-09-03 campaign tested as `sell_min_quality=["B"]`
(allow only B-grade M15 shorts) and REJECTED: 12mo +0.7R, 5-yr −8.9R with max DD 35.8 →
41.4R, OOS +8.3R — A-grade M15 shorts paid in 2021–2023. Under the owner's ≤3-year
window they lose in every slice. Recorded as the ONE cycle-2 candidate from this screen
(M15 `sell_min_quality` excluding "A" via the factory), to be run against a control with
the 3-yr DD clause; the 5-yr verdict is the fact on the other side of the scale. Not
built this cycle (rule: no gate from the screen).
- Not qualifying / no evidence: holiday-day trades are POSITIVE on M15 (27 / +10.6R) and
small everywhere; entries within 30 min after a high-impact USD release are positive
(M15 114 / +9.3R); M15 SELL-in-transitioning (the gate-6c bucket) 75 / −2.2R with y2
and y3 positive; M15 `vol_regime = high` 291 / +4.1R / PF 1.03 (y3 +12.6); hour 7 UTC
−4.0R (OOS +2.4). M5/H1/H4 produced no qualifying bucket.
### Step 0 — reproducibility gate: PASS
| book | MD5 at HEAD | ledger MD5 | result |
|---|---|---|---|
| M5 W2 | `29120a1ee32979431d062f210d41a9d1` | same | equal |
| M15 W2 | `d185f099482a07f29502cb5a371a9bbf` | same | equal |
| M15 OOS | `6f8f24b49597ced28f50a19e08c3472f` | same | equal |
| H1 OOS (new control, no prior MD5) | `c02eebc9066916d7637bbf5796993f87` | — | recorded |
`replay_scorecard.py --check`: all eight invariants hold on every recorded W3 run.
### Step 3b — live floor shipped (commit `9942736`) and verified on the live feed
`BaseDataProvider.MIN_LIVE_BARS = 1000`, applied in a new pure helper
`_live_outputsize(timeframe, lookback_days)`; requested depths now M5 5000, M15 5000,
H1 1460 (unchanged), **H4 1000 (was 380), D1 1000 (was 300)**. The backtester never calls
`fetch_ohlcv`, so no book changes. Tests: new Module 16 depth test, Module 21 cache test
updated from 300 to 1000; suite **500 passed**.
Live verification 2026-09-10 (real Oanda fetch, same latest closed bar, engine on the
frame cleaned exactly as live does):
- H4 (bar 2026-09-10 04:00 UTC): 380 / 1000 / 5000 bars → identical readings
(composite +15.03, ranging) — no flip on today's bar.
- D1 (bar 2026-09-09): **300 bars → ema_trend SELL, composite −9.53; 1000 and 5000 bars →
ema_trend NEUTRAL, composite −8.87.** The old depth was mis-reading the daily trend
today; the new floor agrees with the converged value.
### Step 1 — cost ladder (W3, same trade lists; `--spread` only re-prices; mechanics check: the H1 $1.00 total predicted from the $0.65 book (−0.35 $/trade ÷ risk) equals the rerun to the cent)
| TF | $0.65 R / PF / DD | $0.80 R / PF / DD (halves) | $1.00 R / PF / DD | $1.30 R / PF / DD | red flag ($0.80: PF < 1.3 or a half ≤ 0)? |
|---|---|---|---|---|---|
| M5 | +153.6 / 1.50 / 10.1 | +145.6 / 1.46 / 11.0 (+14.9 / +130.7) | +134.9 / 1.42 / 12.2 | +118.8 / 1.36 / 14.1 | no |
| M15 | +186.6 / 1.39 / 19.3 | +176.4 / 1.37 / 19.9 (+70.0 / +106.4) | +162.8 / 1.34 / 20.8 | +142.4 / 1.29 / 22.6 | no (PF dips under 1.3 only at $1.30) |
| H1 | +70.0 / 1.98 / 4.4 | +68.9 / 1.96 / 4.5 (+25.6 / +43.2) | +67.3 / 1.93 / 4.5 | +65.0 / 1.89 / 4.7 | no |
| H4 | +21.7 / 1.59 / 5.4 | +21.3 / 1.57 / 5.5 (+4.7 / +16.6) | +20.8 / 1.56 / 5.7 | +20.1 / 1.53 / 6.0 | no |
Doubling the assumed round-trip cost ($0.65 → $1.30) costs M5 23% and M15 24% of total R
and H1/H4 about 7%; the H1 book is the least cost-sensitive because its stops are the
widest in dollars. The M5 first half is thin at every price (+8.7R at $1.30 over 136
trades), which is the 2024 flat year again, not a cost effect.
### C1 — H1 divergence multiplier → 0 in every regime: **FAIL (a, b, c) — closed**
| book | trades | total R | ΔR | half 1 | half 2 | PF | max DD | last 12 mo | OOS |
|---|---|---|---|---|---|---|---|---|---|
| H1 control (HEAD, W3) | 253 | +70.02 | | +26.26 | +43.76 | 1.98 | 4.4 | +24.62 (89) | 78 / +16.68 / PF 1.74 |
| H1 divergence 0 (W3) | 280 | +71.01 | **+0.99** | +28.70 (+2.4) | +42.30 (**−1.5**) | **1.89** | 4.8 | +23.09 (102) | 88 / +21.91 / PF 1.92 (+5.2) |
Clauses: (a) ΔR +1.0 < +5 FAIL; (b) second half negative FAIL; (c) PF 1.98 → 1.89, drop
0.09 > 0.05 FAIL; (d) DD +0.4 pass; (e) trades +10.7% pass; (f) OOS +5.2 pass. Calendar
years: 2024 +14.7 → +19.1, 2025 +33.7 → +34.1, 2026 +16.1 → +12.4 — the gain sits in
2024, the most recent year gives it back. The OOS candidate book (88 / +21.91 / PF 1.92)
is exactly the 2026-09-03 campaign's `b12_h1_nosell_nodiv_oos` under
`disabled_indicators` — so a 0.0 regime multiplier is mechanically the same as disabling
the indicator for the composite (engine skips only base weight 0, but an effective weight
of 0 adds nothing to score, weight sum or confidence); the only residual difference is
display (reasons text still lists the vote). The parked candidate's earlier evidence
(5-yr +9.6R over no-shorts) was carried by 2021–2023; on the owner's 3-year window it is
noise with a worse profit factor. Not shipped; do not re-propose without new years.
### C2 — M15 RSI multiplier neighbourhood: **STABLE PLATEAU** (the 2026-09-09 ship stands)
Full regime table with the RSI multiplier × k (k = 1.0 is the pre-2026-09-09 table,
k = 0.5 is HEAD; every other point is a `--set` run on the same harness).
| k | W2 trades / R / PF / DD (halves) | G(k) = R − R(1.0) on W2 | OOS trades / R / PF / DD | ΔOOS | W3 trades / R / PF / DD (halves) |
|---|---|---|---|---|---|
| 0 (RSI off) | 1091 / +145.46 / 1.35 / 18.5 (+74.1 / +71.3) | +27.8 | 418 / +51.89 / 1.34 / 19.5 | +11.3 | 1490 / +188.14 / 1.33 / 22.7 (+69.3 / +118.8) |
| 0.25 | 1008 / +139.93 / 1.37 / 20.1 (+66.1 / +73.9) | +22.3 (78% of G(0.5)) | 390 / +59.55 / 1.44 / 17.3 | +19.0 | 1374 / +187.58 / 1.36 / 21.4 (+77.7 / +109.9) |
| **0.5 (HEAD)** | 931 / +146.14 / 1.43 / 16.1 (+66.1 / +80.0) | **+28.5** | 355 / +60.67 / 1.49 / 17.1 | +20.1 | 1273 / +186.61 / 1.39 / 19.3 (+75.9 / +110.7) |
| 0.75 | 854 / +137.83 / 1.45 / 14.9 (+47.6 / +90.3) | +20.2 (71%) | 326 / +55.56 / 1.49 / 15.6 | +15.0 | 1176 / +178.81 / 1.42 / 17.2 (+66.9 / +111.9) |
| 1.0 (old) | 793 / +117.62 / 1.41 / 14.8 (+38.2 / +79.4) | 0 | 300 / +40.59 / 1.38 / 15.5 | 0 | 1092 / +151.90 / 1.38 / 15.5 (+55.7 / +96.2) |
| 1.5 | 676 / +110.17 / 1.46 / 14.2 (+39.4 / +70.7) | −7.5 | 266 / +37.27 / 1.39 / 18.8 | −3.3 | 949 / +136.20 / 1.39 / 18.8 (+42.6 / +93.6) |
Reading (rule fixed in advance): G(0.25) = 78% and G(0.75) = 71% of G(0.5), both ≥ 50%;
OOS deltas +19.0 and +15.0, both ≥ 0 → **stable plateau, not a spike**. The response is
monotone in k on every window (R falls as RSI's weight rises; trade count falls ~90 per
0.25 step), PF peaks in the 0.5–0.75 band, and drawdown grows as k → 0 (RSI off has the
same R as HEAD with PF 1.35 and DD 22.7 on W3). The shipped 0.5 sits inside a broad
region, on its PF/DD-favourable side. Under this rule no other k is proposed.
### Step 3c — backtester history pad targets the same 1000-bar seed (`_history_pad_days`)
Change: the per-TF warm-up pad now targets `BaseDataProvider.MIN_LIVE_BARS` (1000 bars,
converted to calendar days with 7/5) instead of 210 bars, so a window's first bars are
seeded like the rest of the window and like live. Intraday pads are unchanged (62 days
already hold ≥ 1488 bars), so M5/M15/H1 keep their cache windows; H4 now pads 234 days
(was 62 → 372 bars) and D1 1400 days (was 299 → ~210 bars for a 200-bar EMA). New caches
`oanda_XAU_USD_4h_20230114_20260905.parquet` (5,826 bars) and
`_1d_20191105_20260905.parquet` (2,124 bars); the first H4 chunk (2023-01-14, a Saturday)
is legitimately empty. The raw-path fetch for trade resolution now reuses the primary
timeframe's own padded start (one cache file per TF, as before). Test: Module 21
"history pad seeds slow TFs like live"; suite **501 passed**.
W3 books before → after, trade-by-trade (`bookdiff.py` with the recorded runs):
| TF | trades / R before | trades / R after | trades added / removed / R changed | what actually differs |
|---|---|---|---|---|
| H1 | 253 / +70.02 | 253 / +70.02 | 0 / 0 / 0 | ONE confirmation reading: 2023-09-20 18:00 (15 days into the window), H4 ema_trend STRONG_BUY → BUY (value 1.0 → 0.5, confidence 90 → 65); that trade's blended `score` moves 0.21; nothing else in 5,761 recorded indicator rows |
| H4 | 114 / +21.68 | 114 / +21.68 | 0 / 0 / 0 | no signal flips; 20 D1 rows (ADX/RSI/MACD Wilder recursions) differ at the 1e-6…1e-9 level, all Oct–Dec 2023; exported floats differ by ≤ 1.7e-10 (score ≤ 1.4e-9, R ≤ 1e-15) |
Both differences sit in the window-start seeding zone, exactly where the deeper seed was
meant to act, and neither moves a trade. The canonical H1 book therefore needs NO re-pin
(trade content identical); only the CSV bytes differ (H1 `3b45d12bade823178b267c13bc698458`
vs `b99111132d9ecb80c2998ed5aee2f3ea`, H4 `e75adae13c839b15736f69c0114e6aa5` vs
`4508e7a3402e008ba1d93805094cd981`), which the parity recipe must state.
M5 and M15 on the patched harness: **byte-identical** to Step 0 (M5
`c2722f9830528658b53854f6bae61b8c`, M15 `b2cfa9e2e6cd3997f4d5ca9b49cab90e`) — the pad
change did not leak into the intraday books. Shipped (commit below); no re-pin needed.
## Verdicts
| item | decision | one-line reason |
|---|---|---|
| Step 0 reproducibility | PASS | three ledger MD5s reproduced at HEAD; scorecard invariants hold |
| Cost ladder | no red flag | every TF keeps PF ≥ 1.3 with positive halves at $0.80; edge survives $1.00 on all four |
| Concentration | fact recorded | M5/M15: top 5% of trades carry 94–96% of R; H1 48% |
| Bucket screen | one cycle-2 candidate | M15 grade-A SELLs (230 / −14.4R, negative in y1, y2, y3 and OOS); 5-yr history says the opposite — run it against a control with the DD clause before believing it |
| C1 H1 divergence → 0 | **REJECT** | ΔR +1.0 (< +5), second half −1.5R, PF 1.98 → 1.89; OOS-only gain |
| C2 M15 RSI ladder | stable plateau | neighbours keep 78% / 71% of the shipped gain, OOS positive at both; ship stands |
| 3a/3b live seeding floor | **ACCEPT, shipped** (`9942736`) | measured flips at 380/300 bars, none at ≥ 600; live D1 flipped on 2026-09-10 at the old depth; books untouched |
| 3c backtester pad | **ACCEPT, shipped** | no trade moved in any book; M5/M15 byte-identical; H1/H4 differ only in window-start numerics |
## What NOT to re-propose (added to the ledger's closed list)
- H1 divergence weight 0 (any vehicle) without new years of data.
- M15 RSI multiplier other than 0.5 on the strength of a single-point rerun; the ladder is on record.
- Cost-driven gates: the books are not cost-fragile at $0.80–$1.00.
## Shipped
- `9942736` providers: `MIN_LIVE_BARS = 1000` live floor + `_live_outputsize` (Module 16
test; Module 21 expectation 300 → 1000). Suite 500.
- backtester: `_history_pad_days` (62 / 234 / 1400 days for intraday / H4 / D1), raw-path
fetch reuses the primary's start (Module 21 test). Suite 501. Live H1/H4 scans and the
books now share the same seeding depth; pinned H1/H4 CSVs are trade-identical but no
longer byte-reproducible (float noise in exported indicator columns — same status the
M5/M15 pins have had since 2026-09-07).
- No signal-core change, no docs change (nothing user-visible moved), both news gating
flags untouched.