fix(backtest): address the second review of the breakeven and book-config work
Deploy / lint (push) Successful in 14s
Deploy / test (push) Successful in 2m0s
Deploy / deploy (push) Successful in 48s

- The breakeven study grades at the shadow book's capacity AND risk per
  trade, and labels each with where it came from. A missing setting row
  now reads "shadow_book default" instead of crediting the setting; the
  committed report said shadow_book_capacity though the snapshot has no
  such row.
- _production_book_config reads strategies through the same
  _portfolio_monitor_strategies() hook as the monitor.
- breakeven_gate_reset is rejected when there is no post-stop episode or
  cooldown for it to start, and the study skips its diagnostic arm for a
  book without one, so a production config change cannot abort the run.
- identical_to_control is renamed ties_control_on_graded_metrics and needs
  real values: be_2r ties on every graded metric but still changed 2 trades.
- _BookConfig drops an unused field and hashes by identity; the min-R:R
  sweep swaps its gate in with dataclasses.replace instead of passing it.
- New tests cover the parity guard's success path, the no-config fallback
  and the gate-reset check.

The breakeven report is regenerated: every row, diagnostic row and the
parity check are identical; only the labels, the risk fields and the
renamed flag changed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
dennisthiessenandClaude Opus 5.5 committed 2026-10-10 21:41:44 +02:00
1 parent 7a9b3ad38b
commit 231372fb3a
5 files changed
+168 -88

No files matched your search

+14 -11
View File
@@ -75,9 +75,11 @@ BACKTEST_BREAKEVEN_STUDY=1 python scripts/run_backtest_snapshot.py \
--out reports/backtest-20261009-breakeven-stop.json
```
The graded capacity is read from the snapshot's `shadow_book_capacity` setting.
`prod.sqlite` has no such row, so the shadow book's default of 15 applies; the
report records it as `capacity` / `capacity_source`.
The graded capacity and risk per trade come from the shadow book's config in the
database the run reads (`shadow_book_capacity`, `shadow_book_risk_pct`).
`prod.sqlite` has neither row, so the shadow book's defaults apply: 15
positions and 1% risk. The report records each value with its source
(`capacity_source`, `risk_source`), which reads `shadow_book default` here.
## Result — no change; `be_1r` fails, `be_2r` passes only by being the production exit
@@ -201,11 +203,12 @@ and live still agree.
- `breakeven_r` must be positive and finite, and is rejected for exit policies
without an ATR trail. Before, a non-positive value moved the stop to entry on
the first bar, and other policies ignored the setting silently.
- Each verdict now records `identical_to_control`, which is true for `be_2r`. A
pass that is only ties can no longer read as a finding.
- The graded capacity comes from the shadow book's capacity config (the
`shadow_book_capacity` setting in the database the run reads, or its default
of 15 when the row is missing) instead of the `SIM_MAX_POSITIONS` constant. On
a snapshot that is the snapshot's copy of the setting, not the current live
value ([Reproducing](#reproducing)). `capacity_source` reads
`shadow_book_capacity` in both cases.
- Each verdict now records `ties_control_on_graded_metrics`, which is true for
`be_2r`. A pass that is only ties can no longer read as a finding. The flag
compares the graded metrics only: `be_2r` still changed 2 trades.
- The graded capacity and risk per trade come from the shadow book's config
instead of the `SIM_MAX_POSITIONS` constant and the entry variant's fixed 1%.
On a snapshot that is the snapshot's copy of the settings, not the current
live values, and a missing row is labeled as the default it fell back to
([Reproducing](#reproducing)). In this run both are defaults, equal to the
values the first run used.