Last post: v11 showed 29.3%. Sharpe 1.63. Honest number.
Then I added a Kalman filter.
New complexity. New bias. The system showed 327.53%. Sharpe 13.01. Win rate 94.24%.
I did not publish that number either.
— — —
After the Feb 21 post, I implemented three changes targeting the two root problems identified in the walk-forward: stale hedge ratios (fixed β=1 throughout 2023–2025 as pair relationships drift) and a permissive entry threshold (z=1.5 catches too much noise near the boundary).
Kalman filter dynamic hedge ratios. Replaced the fixed log-price spread with a 1D Kalman filter that estimates β_t at each timestep. A pair where β was 0.90 in 2022 may be 0.65 in 2024. Trading with β=1.0 means the "spread" is tracking β drift, not mean reversion — that explains the 52.96% win rate in OOS.
Raised signal thresholds. Z-score entry from 1.5 → 2.0. At z=1.5, ~13% of random normal observations exceed the threshold. At z=2.0, 4.6%. Raised signal strength and confidence thresholds to match.
CUSUM structural break detection. Before entering any pair, run a Page-CUSUM test on the last 63 days of spread. The quarterly ADF filter runs once — pairs can break mid-quarter. CUSUM catches this in real time.
Backtest: 327.53%. Win rate 94.24%. -0.29% max drawdown. All 19 walk-forward windows profitable. Quant HF fund comparison: +7,813%.
Same pattern as every previous inflated number. Investigated.
— — —
■ New Bias — Kalman β Drift Counted as P&L
The spread is: spread_t = log(p1_t) − β_t × log(p2_t)
The Kalman filter updates β at every timestep. Between trade entry and exit, β drifts.
P&L formula: spread_return = spread_exit − spread_entry
Expanded: [log(p1_exit) − β_exit × log(p2_exit)] − [log(p1_entry) − β_entry × log(p2_entry)]
When β_exit ≠ β_entry, this includes a phantom term: (β_entry − β_exit) × log(p2_exit)
That term has no position P&L. It is purely the filter adapting its estimate. For most stocks, log(price) > 0, so when β increases post-entry (as the filter calibrates), this term is negative — the measured spread falls toward zero. The system records a winning trade. Prices did not move.
The exit condition had the same bug. Exit triggered when Kalman z-score < 0.5. As β drifts, z-score mechanically falls — the pair appears to "revert" without prices moving. 94.24% win rate is exits triggered by β drift, not mean reversion.
Fix: lock β at the entry date. Exit condition and P&L both compute the locked spread — log(p1_t) − β_entry × log(p2_t) — using the entry hedge ratio throughout the trade life. P&L measured as actual price log returns: log(p1_exit/p1_entry) − log(p2_exit/p2_entry).
— — —
■ v12 Final Results (Honest)
→ 29.34% total return (2023-H2 to 2025) → Sharpe 3.10 (v11: 1.63) → Max drawdown −5.88% (v11: −7.97%) → 831 trades (v11: 3,006) → Win rate 61.61% (v11: 52.96%) → Profit factor 1.55 (v11: 1.22)
Return is virtually unchanged from v11. Everything else improved materially. The Kalman filter genuinely identifies better entry points — when P&L is measured against actual price movements, win rate still rises 8.6 percentage points. The β-lock ensures that improvement is real.
— — —
Walk-forward OOS average: +5.41%/quarter (v11: +0.89%). Six times higher. IS→OOS degradation: 72.9% (v11: 95.2%). Still above 50% — still a regime break, not overfit — but substantially improved. W16, which was the worst window in v11 at −5.85%, is now +7.98%. W19 (Jul–Oct 2025) is the new worst window at −9.10%.
Fund comparison at real-world costs: all five institutional profiles negative. Institutional (Norges Bank costs, 1x) at −2.45%. In v11 it was +6.20%. Honest P&L accounting removed that gap. The internal backtest shows 29.34% because its cost model uses ~0.8 bp — cheaper than Norges Bank. At realistic mid-cap spreads (3–10 bp), the signal does not survive costs.
The honest range is unchanged from Feb 21: roughly +2–4% at institutional rates with realistic mid-cap execution, negative at any leverage.
— — —
The full breakdown — v12 changes, Kalman β-drift derivation, corrected P&L, walk-forward window detail, fund comparison — is documented here:
→
Every time the system gets more complex, a new class of problem appears.
The work is always in finding it.