- 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>
81 lines
4.7 KiB
Markdown
81 lines
4.7 KiB
Markdown
# Residual normalization — findings
|
|
|
|
**2026-09-08: closed negative at the signal screen. Keep production unchanged.**
|
|
The proposed normalization did not meet the rule frozen in
|
|
[the experiment protocol](residual-normalization.md), and its point estimates
|
|
did not improve on the current momentum signal. No portfolio replay was run:
|
|
the protocol requires a passing signal screen before that stage.
|
|
|
|
The candidate used the production SPY-adjusted 12-1 return sum divided by sample
|
|
standard deviation of the same 231 daily adjusted returns. All comparisons used
|
|
identical as-of dates and 30-SPY-session forward-return endpoints. No new factor,
|
|
lookback, volatility weight, sizing rule, or exit was tested.
|
|
|
|
| Era | Non-overlapping windows | Current signal, matched coverage | Normalized signal | Difference |
|
|
|---|---:|---:|---:|---:|
|
|
| Full period | 73 | 0.01446 | 0.01284 | -0.00162 |
|
|
| Before 2021 | 30 | 0.00776 | 0.00496 | -0.00280 |
|
|
| From 2021 | 42 | 0.02377 | 0.02259 | -0.00118 |
|
|
|
|
Values are mean **Spearman information coefficients (IC)**: how well signal
|
|
ordering agrees with subsequent stock returns. They are neither Sharpe ratios
|
|
nor trading returns. Higher is better for this positive-momentum hypothesis.
|
|
The candidate needed full-period IC >= 0.03, at least 12 windows, and positive
|
|
mean IC in both eras. It passed the count and era-sign conditions but failed the
|
|
0.03 threshold. The rule was evaluated before rounding.
|
|
|
|
The paired normalized-minus-current IC difference has a 95% moving-block interval
|
|
of **[-0.00496, +0.00156]**. This spans zero: the study does not establish that
|
|
normalization is worse, and it supplies no convincing improvement evidence.
|
|
The mean top-minus-bottom-quintile 30-session return spread also fell, from
|
|
**0.967% to 0.751%**. That is an unconditional diagnostic, not the P&L of the
|
|
long-only, gated, capital-constrained production book.
|
|
|
|
**Coverage and checks**
|
|
|
|
- The snapshot completion manifest matches 4,650 registry tickers, 5,081,073
|
|
OHLCV rows, and 4,144 research-only names. The experiment used the remaining
|
|
506 production-universe registry names, averaging 485.4 paired stocks per
|
|
retained window.
|
|
- Actual retained as-of dates run from **2017-01-06 to 2026-05-01**; the final
|
|
forward endpoint is **2026-06-15**. Later dates cannot necessarily add another
|
|
complete non-overlapping 30-session window. SPY history starts in January
|
|
2016, so a full one-year signal cannot start in July 2016.
|
|
- One retained window crosses the 2021 boundary and belongs to the full-period
|
|
result only, explaining why the era counts sum to 72 rather than 73.
|
|
- The unrestricted production control had **35,441** retained stock/date cells;
|
|
paired control and treatment each had **35,433**. Its mean IC was 0.01447,
|
|
essentially the same as paired control. Coverage restriction did not account
|
|
for the observed result.
|
|
- Numerator parity was checked against the production implementation on each
|
|
eligible evaluated cell. The snapshot and frozen source hashes were unchanged
|
|
across the run. SQLite was opened read-only.
|
|
- Twelve targeted tests passed, including numerical invariance, production
|
|
parity, lookahead exclusion, zero-variance handling, missing-data pairing,
|
|
calendar overlap, and an end-to-end screen check. Ruff passed for the runner
|
|
and its tests. The existing Python runtime was usable outside the filesystem
|
|
sandbox; no environment replacement was needed.
|
|
- A 2026-10-10 code review added four tests (missing prices, file release,
|
|
paired-coverage guard, source ids) with the runner fixes they cover. The
|
|
report was regenerated with the fixed runner; its screen results are
|
|
byte-identical to the original run.
|
|
|
|
**Interpretation and decision**
|
|
|
|
My proposed mechanism was plausible but unsupported by this experiment. Do not
|
|
add normalization to production or search denominator variants on this history.
|
|
This ends this specific experiment, not all possible residual-momentum research.
|
|
|
|
The current signal also falls below 0.03 on this particular long-history screen.
|
|
That cautions against treating its predictive evidence as universally strong;
|
|
it does not by itself measure or invalidate the complete production book. The
|
|
unconditional universe, true session spacing, dates, and return objective differ
|
|
from earlier setup/portfolio studies. Historical constituents are still projected
|
|
backward, this history has already been inspected, and the bootstrap intervals
|
|
remain exploratory. No new CAGR, drawdown, cost-stress, or live result is claimed.
|
|
|
|
**Artifacts:** [machine-readable results](../../reports/residual-normalization-20260908.json),
|
|
[generated tables](../../reports/residual-normalization-20260908.md),
|
|
[runner](../../scripts/run_residual_normalization.py), and
|
|
[tests](../../tests/unit/test_residual_normalization.py).
|