Files
ForexBot2/backtest/research/signal_log_campaign_2026-09.md
T
Harwinder Sekhon c00ed4471b chore: repo hygiene — stale gitignore entries, finished plans, superseded research, doc drift
Removed (all recoverable from git history):
- llm_provider_switch_plan.txt: 2026-08-08 DeepSeek/Anthropic multi-provider plan that
  was never implemented (llm_client.py is DeepSeek-only, no `anthropic` anywhere)
- docs/superpowers/specs/2026-07-21-centralized-config-design.md: spec of the
  config.json migration that merged the same day; its plan sibling was already
  deleted in b936d44. app_config's docstring pointer goes with it.
- scripts/compare_oanda_tiingo.py: Phase 0 recent-tail smoke test, superseded by the
  Phase 1/2 study scripts (result recorded in the test plan)
- backtest/research/fetch_oanda_m15.py + oanda_td_divergence.py + its by-hour CSV:
  the 2026-07-24 Oanda-vs-Twelve-Data study, superseded by the 2026-08-14 feed
  divergence report (which keeps a one-line citation)
- backtest/research/burst_profile.py and hour_analysis.py: superseded by burst_map.py
  and book_report.py
- backtest/research/panel_payload_drytest_2026-08-15.py: one-off live DeepSeek probe;
  its .jsonl evidence stays

Added to git: the three 2026-08-14 research reports (feed divergence, re-pin
verification, signal parity), their CSV, and docs/data_source_change_test_plan.md -
CLAUDE.md cites them as standing evidence and their driver scripts were already
committed. The test plan no longer refers to auto-execution.

Hygiene:
- .gitignore: drop oanda_state.json (daemon removed 2026-09-03) and the vanished
  backtest/trailgrid dirs; ignore .coverage
- docs/architecture_diagram.py only writes the PNG when run as a script (it used
  to plot on import); test box refreshed to 461 tests / 31 modules; PNG regenerated
  (the committed one still drew the removed daemon)
- backtest/instructions_for_backtest.txt: manual-journal head replaced by the
  actual reporting checklist; the 2026-09-06 addendum is unchanged
- doc drift: README/publisher list all six published files; news_summary.php says
  4 h (not 24 h) and drops the pre-id sha1 fallback whose window closed;
  analyze_signal_log lists six lenses and --scan-outcomes; agent_panel cost model
  says 4 requests by default; tiingo comment cites the NY-anchored weekend;
  Module 21 title no longer says "execution lifecycle"

Suite: 461 passed, 0 failed.

Claude-Session: https://claude.ai/code/session_0156YhogLytzj9P2iH1skGbL
2026-09-06 17:01:07 -04:00

183 lines
13 KiB
Markdown
Raw 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.
# Signal-log audit → prediction-quality campaign — 2026-09-03
**Source evidence:** `logs/signals.db` 2026-08-13 → 2026-09-03 (14,683 rows, candle
snapshots from 08-18), replayed with the repo's own advisory outcome model
(`analyze_signal_log.py --stored-candles`; the replay reproduces
`advisory_outcome.py` to 5e-17 on the 49 panel-seeded rows), cross-checked
against the pinned Oanda books (`backtest/oanda_*_065*.csv`).
## Live-log findings that motivated the campaign
| View (3 weeks, $0.65 rt) | n | WR | R | PF |
|---|---|---|---|---|
| every unique setup event | 144 | 67% | +37.8 | 1.93 |
| opportunities (first per tf+direction, 60 min) | 66 | 67% | +18.3 | 1.92 |
| one-position-per-TF (5-bar cooldown) | 32 | 62% | +6.6 | 1.53 |
| direction BUY / SELL | 121 / 23 | 70% / 48% | +43.2 / −5.4 | 2.53 / 0.56 |
- Two rally days (08-19, 08-21) contribute essentially all of the R.
- SELL is negative in every view; in the books SELL is the weakest bucket on
every TF (H1 SELL −16.7R/5yr PF 0.77, −11.5R OOS PF 0.30; M15 SELL −11.7R OOS;
M5 SELL in `transitioning` −7.9R/12mo PF 0.21, −5.2R/5yr PF 0.49).
- Bollinger is anti-predictive on all four TFs (forward-return hit 0.26–0.50 at
12 bars) and "opposes" 93% of taken book trades; divergence is inverted on
M15/H1 live and trades where it opposed outperform in the books.
- Refuted live hypotheses (books disagree): ADX<20 gate, exhaustion gate,
NEAR-EMA200 gate, EXTREME-volatility gate, capping SL/ATR.
## Method (rule 1)
Harness: `backtest/gpu_backtest.py --pair XAUUSD --spread 0.65` on the CPU
backend (CuPy present but the GPU was not reachable; the M5 12mo control
reproduces the pinned `oanda_m5_12mo_065_nopb_20260814.csv` trade-for-trade
except the two trades on the end date, which `--end` excludes). Every candidate
is compared with a CONTROL produced on the same harness, never with the pinned
book directly. Windows (cache start = start − 62 days):
| window | `--start` | `--end` |
|---|---|---|
| 12mo (M15/H1) | 2025-08-04 | 2026-08-04 |
| 12mo (M5 canonical) | 2025-08-14 | 2026-08-14 |
| 5yr | 2021-08-01 | 2026-08-04 |
| OOS | 2024-06-01 | 2025-03-30 |
Acceptance: positive total R in both 12mo halves, PF ≥ 1.3, OOS not worse
than control, and a candidate that removes a net-positive bucket for a PF-only
gain is rejected (big-candle precedent). Runner:
`<scratchpad>/campaign/run_campaign.sh`; summary: `summarize.py` (scratchpad
tooling, not retained — both shipped gates are reproduced by a plain pair-factory
run at HEAD; see the parity recipe in `sizing_campaign_2026-09.md`).
Candidates (all `--set`, no code):
- **B1 SELL gating** — H1 `xau_strategy.sell_min_quality=[]` (block all H1
shorts); M5 `xau_strategy.sell_blocked_regimes=["transitioning"]`; M15
`xau_strategy.sell_min_quality=["B"]`.
- **B2 divergence off** — `technical.disabled_indicators=["divergence"]` on
M15 and H1 (M5 divergence-down was rejected 2026-08-04; not re-run).
- **B3 Bollinger weight 0 in trending regimes** — full
`technical.regime_weights_override` with bollinger 0.0 in
trending_up/trending_down/trending, ranging/transitioning unchanged; M5, M15, H1.
## Results
Every control reproduced its pinned book: M15 12mo (`ctl_m15_12mo`) is exact
at 397/+73.93R/PF 1.53; all three H1 controls are exact; the M5 12mo control
(`base_m5_12mo`) is exact except for the two trades that fall on the end
date, which `--end` excludes on this harness (see Method above).
| book | n | R | PF | DD | H1 | H2 | sellN | sellR | ΔR vs control | ΔPF |
|---|---|---|---|---|---|---|---|---|---|---|
| b1_h1_nosell_12mo | 91 | +22.2 | 1.79 | 4.1 | +20.6 | +1.6 | 0 | +0.0 | -1.8 | +0.25 |
| b1_h1_nosell_5yr | 335 | +49.1 | 1.46 | 13.2 | -5.0 | +54.1 | 0 | +0.0 | +16.7 | +0.28 |
| b1_h1_nosell_oos | 78 | +16.7 | 1.74 | 3.6 | +7.3 | +9.4 | 0 | +0.0 | +11.5 | +0.61 |
| b1_m15_sellB_12mo | 341 | +74.6 | 1.62 | 10.8 | +68.4 | +6.2 | 82 | +9.1 | +0.7 | +0.09 |
| b1_m15_sellB_5yr | 1378 | +119.1 | 1.22 | 41.4 | -21.3 | +140.3 | 335 | +3.7 | -8.9 | +0.02 |
| b1_m15_sellB_oos | 272 | +47.1 | 1.51 | 13.8 | +12.3 | +34.8 | 59 | -3.4 | +8.3 | +0.15 |
| b1_m5_selltrans_12mo | 557 | +112.2 | 1.56 | 9.9 | +55.9 | +56.4 | 75 | +14.8 | +7.9 | +0.06 |
| b1_m5_selltrans_5yr | 889 | +133.7 | 1.41 | 12.8 | -8.3 | +142.0 | 88 | +9.0 | +5.3 | +0.03 |
| b1_m5_selltrans_oos | 86 | +9.4 | 1.33 | 8.2 | -1.4 | +10.9 | 4 | +1.5 | +0.0 | +0.00 |
| b2_h1_nodiv_12mo | 149 | +28.6 | 1.62 | 6.5 | +23.6 | +5.0 | 49 | +5.5 | +4.6 | +0.07 |
| b2_h1_nodiv_5yr | 567 | +40.5 | 1.20 | 14.1 | -5.2 | +45.6 | 203 | -18.2 | +8.1 | +0.02 |
| b2_h1_nodiv_oos | 119 | +7.6 | 1.17 | 7.7 | -1.9 | +9.5 | 31 | -14.3 | +2.4 | +0.04 |
| b2_m15_nodiv_12mo | 439 | +81.3 | 1.51 | 13.1 | +72.3 | +8.9 | 147 | +7.0 | +7.4 | -0.02 |
| b2_m15_nodiv_5yr | 1771 | +139.9 | 1.20 | 47.4 | -16.2 | +156.1 | 578 | +11.7 | +11.9 | -0.01 |
| b2_m15_nodiv_oos | 341 | +52.3 | 1.43 | 17.1 | +15.6 | +36.7 | 93 | -10.0 | +13.5 | +0.07 |
| b3_h1_bb0_12mo | 154 | +31.2 | 1.64 | 9.4 | +22.8 | +8.4 | 52 | +6.7 | +7.3 | +0.09 |
| b3_h1_bb0_5yr | 583 | +37.3 | 1.18 | 18.3 | -7.4 | +44.7 | 206 | -20.4 | +4.9 | +0.00 |
| b3_h1_bb0_oos | 122 | +5.6 | 1.12 | 8.7 | -2.0 | +7.6 | 32 | -13.8 | +0.4 | -0.01 |
| b3_m15_bb0_12mo | 439 | +49.9 | 1.29 | 19.4 | +61.6 | -11.8 | 151 | +0.3 | -24.0 | -0.23 |
| b3_m15_bb0_5yr | 1790 | +126.6 | 1.18 | 42.3 | -1.8 | +128.5 | 592 | +17.4 | -1.3 | -0.03 |
| b3_m15_bb0_oos | 345 | +55.5 | 1.47 | 12.4 | +18.2 | +37.3 | 94 | -8.4 | +16.7 | +0.11 |
| b3_m5_bb0_12mo | 621 | +84.0 | 1.35 | 16.6 | +49.3 | +34.7 | 104 | +9.7 | -20.4 | -0.14 |
| b3_m5_bb0_5yr | 987 | +113.0 | 1.31 | 15.3 | -7.7 | +120.6 | 134 | +12.0 | -15.5 | -0.08 |
| b3_m5_bb0_oos | 93 | +10.6 | 1.35 | 5.6 | -2.9 | +13.5 | 9 | +3.7 | +1.2 | +0.01 |
| b12_h1_nosell_nodiv_12mo | 100 | +23.1 | 1.75 | 4.6 | +21.8 | +1.3 | 0 | +0.0 | -0.8 | +0.20 |
| b12_h1_nosell_nodiv_5yr | 364 | +58.7 | 1.52 | 12.9 | -4.1 | +62.8 | 0 | +0.0 | +26.3 | +0.34 |
| b12_h1_nosell_nodiv_oos | 88 | +21.9 | 1.92 | 4.8 | +9.4 | +12.5 | 0 | +0.0 | +16.8 | +0.79 |
| base_m5_12mo | 575 | +104.3 | 1.50 | 10.7 | +50.8 | +53.5 | 93 | +6.9 | — | — |
| ctl_h1_12mo | 138 | +24.0 | 1.55 | 8.0 | +19.4 | +4.6 | 47 | +1.8 | — | — |
| ctl_h1_5yr | 515 | +32.4 | 1.18 | 15.2 | -5.9 | +38.3 | 180 | -16.7 | — | — |
| ctl_h1_oos | 104 | +5.1 | 1.13 | 7.5 | -0.9 | +6.1 | 26 | -11.5 | — | — |
| ctl_m15_12mo | 397 | +73.9 | 1.53 | 10.8 | +66.7 | +7.2 | 138 | +8.5 | — | — |
| ctl_m15_5yr | 1578 | +128.0 | 1.21 | 35.8 | -3.4 | +131.4 | 535 | +12.6 | — | — |
| ctl_m15_oos | 299 | +38.8 | 1.36 | 15.5 | +5.9 | +32.9 | 86 | -11.7 | — | — |
| ctl_m5_5yr | 911 | +128.4 | 1.39 | 12.8 | -8.3 | +136.7 | 110 | +3.8 | — | — |
| ctl_m5_oos | 86 | +9.4 | 1.33 | 8.2 | -1.4 | +10.9 | 4 | +1.5 | — | — |
Raw table (n / R / PF / WR% / DD / H1 / H2 / PF-half-1 / PF-half-2 /
sellN / sellR / ΔR / ΔPF / Δn vs control; control and `base_m5_12mo`
rows carry no delta):
```
book n R PF WR% DD H1 H2 PF1 PF2 sellN sellR | ΔR ΔPF Δn
b1_h1_nosell_12mo 91 +22.2 1.79 62.6 4.1 +20.6 +1.6 2.10 1.18 0 +0.0 | -1.8 +0.25 -47
b1_h1_nosell_5yr 335 +49.1 1.46 58.2 13.2 -5.0 +54.1 0.87 1.78 0 +0.0 | +16.7 +0.28 -180
b1_h1_nosell_oos 78 +16.7 1.74 62.8 3.6 +7.3 +9.4 1.67 1.81 0 +0.0 | +11.5 +0.61 -26
b1_m15_sellB_12mo 341 +74.6 1.62 62.8 10.8 +68.4 +6.2 2.15 1.10 82 +9.1 | +0.7 +0.09 -56
b1_m15_sellB_5yr 1378 +119.1 1.22 57.0 41.4 -21.3 +140.3 0.91 1.46 335 +3.7 | -8.9 +0.02 -200
b1_m15_sellB_oos 272 +47.1 1.51 60.7 13.8 +12.3 +34.8 1.24 1.85 59 -3.4 | +8.3 +0.15 -27
b1_m5_selltrans_12mo 557 +112.2 1.56 53.7 9.9 +55.9 +56.4 1.54 1.58 75 +14.8 | +7.9 +0.06 -18
b1_m5_selltrans_5yr 889 +133.7 1.41 51.5 12.8 -8.3 +142.0 0.84 1.52 88 +9.0 | +5.3 +0.03 -22
b1_m5_selltrans_oos 86 +9.4 1.33 53.5 8.2 -1.4 +10.9 0.91 1.95 4 +1.5 | +0.0 +0.00 +0
b2_h1_nodiv_12mo 149 +28.6 1.62 62.4 6.5 +23.6 +5.0 1.93 1.24 49 +5.5 | +4.6 +0.07 +11
b2_h1_nodiv_5yr 567 +40.5 1.20 56.3 14.1 -5.2 +45.6 0.93 1.37 203 -18.2 | +8.1 +0.02 +52
b2_h1_nodiv_oos 119 +7.6 1.17 56.3 7.7 -1.9 +9.5 0.91 1.44 31 -14.3 | +2.4 +0.04 +15
b2_m15_nodiv_12mo 439 +81.3 1.51 60.8 13.1 +72.3 +8.9 1.90 1.11 147 +7.0 | +7.4 -0.02 +42
b2_m15_nodiv_5yr 1771 +139.9 1.20 55.8 47.4 -16.2 +156.1 0.94 1.39 578 +11.7 | +11.9 -0.01 +193
b2_m15_nodiv_oos 341 +52.3 1.43 59.2 17.1 +15.6 +36.7 1.23 1.68 93 -10.0 | +13.5 +0.07 +42
b3_h1_bb0_12mo 154 +31.2 1.64 60.4 9.4 +22.8 +8.4 1.89 1.36 52 +6.7 | +7.3 +0.09 +16
b3_h1_bb0_5yr 583 +37.3 1.18 54.5 18.3 -7.4 +44.7 0.90 1.35 206 -20.4 | +4.9 +0.00 +68
b3_h1_bb0_oos 122 +5.6 1.12 55.7 8.7 -2.0 +7.6 0.91 1.33 32 -13.8 | +0.4 -0.01 +18
b3_m15_bb0_12mo 439 +49.9 1.29 59.0 19.4 +61.6 -11.8 1.77 0.87 151 +0.3 | -24.0 -0.23 +42
b3_m15_bb0_5yr 1790 +126.6 1.18 56.1 42.3 -1.8 +128.5 0.99 1.31 592 +17.4 | -1.3 -0.03 +212
b3_m15_bb0_oos 345 +55.5 1.47 61.2 12.4 +18.2 +37.3 1.29 1.66 94 -8.4 | +16.7 +0.11 +46
b3_m5_bb0_12mo 621 +84.0 1.35 51.4 16.6 +49.3 +34.7 1.43 1.28 104 +9.7 | -20.4 -0.14 +46
b3_m5_bb0_5yr 987 +113.0 1.31 50.7 15.3 -7.7 +120.6 0.85 1.38 134 +12.0 | -15.5 -0.08 +76
b3_m5_bb0_oos 93 +10.6 1.35 54.8 5.6 -2.9 +13.5 0.85 2.17 9 +3.7 | +1.2 +0.01 +7
b12_h1_nosell_nodiv_12mo 100 +23.1 1.75 63.0 4.6 +21.8 +1.3 2.00 1.15 0 +0.0 | -0.8 +0.20 -38
b12_h1_nosell_nodiv_5yr 364 +58.7 1.52 59.6 12.9 -4.1 +62.8 0.90 1.87 0 +0.0 | +26.3 +0.34 -151
b12_h1_nosell_nodiv_oos 88 +21.9 1.92 63.6 4.8 +9.4 +12.5 1.93 1.92 0 +0.0 | +16.8 +0.79 -16
base_m5_12mo 575 +104.3 1.50 52.9 10.7 +50.8 +53.5 1.46 1.53 93 +6.9
ctl_h1_12mo 138 +24.0 1.55 60.9 8.0 +19.4 +4.6 1.84 1.22 47 +1.8
ctl_h1_5yr 515 +32.4 1.18 55.7 15.2 -5.9 +38.3 0.91 1.34 180 -16.7
ctl_h1_oos 104 +5.1 1.13 56.7 7.5 -0.9 +6.1 0.95 1.32 26 -11.5
ctl_m15_12mo 397 +73.9 1.53 62.2 10.8 +66.7 +7.2 1.95 1.10 138 +8.5
ctl_m15_5yr 1578 +128.0 1.21 56.5 35.8 -3.4 +131.4 0.99 1.37 535 +12.6
ctl_m15_oos 299 +38.8 1.36 59.2 15.5 +5.9 +32.9 1.10 1.66 86 -11.7
ctl_m5_5yr 911 +128.4 1.39 51.3 12.8 -8.3 +136.7 0.84 1.48 110 +3.8
ctl_m5_oos 86 +9.4 1.33 53.5 8.2 -1.4 +10.9 0.91 1.95 4 +1.5
```
## Decisions
- **SHIPPED** — M5 `xau_strategy.sell_blocked_regimes=["transitioning"]`
(b1_m5_selltrans): 12mo +7.9R, 5yr +5.3R, OOS unchanged (no such trades
in that window); positive in both 12mo halves on every window. H1
`xau_strategy.sell_min_quality=[]` (b1_h1_nosell, all H1 shorts blocked):
5yr +16.7R and OOS +11.5R more than pay for the 12mo cost of -1.8R; H1
shorts were -16.7R/5yr and -11.5R/OOS in the control. Both land in
`pair_signals/xauusd.py`.
- **REJECTED** — M15 `sell_min_quality=["B"]` (b1_m15_sellB): 5yr -8.9R
with max DD widening 35.8 -> 41.4R for a marginal +0.7R on 12mo; fails
the drawdown clause. All three B3 Bollinger-weight-0-in-trending-regimes
runs (b3_h1_bb0, b3_m15_bb0, b3_m5_bb0): negative on M5 (-20.4R/12mo,
-15.5R/5yr) and M15 (-24.0R/12mo, PF 1.29 -> still below the 12mo H2
going negative at -11.8R); H1's B3 5yr gain (+4.9R) doesn't clear the
bar once M5/M15 fail outright — Bollinger's constant-oppose vote stays
a load-bearing quality bar, consistent with the 2026-08-04 audit.
- **DECLINED** — B2-M15 (`technical.disabled_indicators=["divergence"]`
on M15, b2_m15_nodiv): R improves on every window (+7.4/+11.9/+13.5R)
but 5yr max DD widens 35.8 -> 47.4R while PF stays flat (1.21 -> 1.20) —
more risk for no risk-adjusted gain; owner call.
- **PARKED** — B2-H1 (`technical.disabled_indicators=["divergence"]` on
H1, b2_h1_nodiv, standalone: +4.6/+8.1/+2.4R, PF 1.62/1.20/1.17). The
combined run with the H1 no-short override (b12_h1_nosell_nodiv) passes
rule 1 and beats the no-shorts override alone on both 5yr and OOS: 12mo
100/+23.1R PF 1.75 (halves +21.8/+1.3), 5yr 364/+58.7R PF 1.52, OOS
88/+21.9R PF 1.92. Not shipped anyway: it would route through
`technical.disabled_indicators`, which CLAUDE.md rule 8 reserves for
experiments, not production paths. Owner decision; recommended vehicle
is an H1-only `regime_weights_override` with divergence weight 0 plus a
fresh validation run under that mechanism.