feat: add residual momentum to signal-edge backtest
Adds a research-only 12-1 residual momentum signal to the cross-sectional signal-evaluation harness. The signal estimates benchmark beta over the 12-1 formation window and ranks cumulative stock return minus beta-adjusted benchmark return; it only appears when benchmark closes are available. No production qualification behavior changes. The Backtest signal table labels the new row as 12-1 residual momentum. Tests cover benchmark-gated emission and beta removal while keeping stock-specific drift. Verification: 453 backend tests pass, ruff check app/ clean, frontend npm run build clean. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -60,8 +60,9 @@ def test_quintile_spread_none_when_too_few():
|
||||
def test_signal_values_momentum_and_trend():
|
||||
# Steadily rising series so every lookback is positive and trend is above SMA.
|
||||
closes = [100.0 * (1.01 ** k) for k in range(300)]
|
||||
dates = [date(2024, 1, 1) + timedelta(days=k) for k in range(300)]
|
||||
i = 299
|
||||
vals = bt._signal_values(closes, closes, i)
|
||||
vals = bt._signal_values(dates, closes, closes, i)
|
||||
assert vals["mom_12_1"] > 0 # up over the 12→1 month window
|
||||
assert vals["trend_200"] > 0 # price above its 200-bar SMA in an uptrend
|
||||
# 12-1 momentum skips the last month: close[i-21] / close[i-252] - 1
|
||||
@@ -73,7 +74,8 @@ def test_signal_values_momentum_and_trend():
|
||||
|
||||
def test_signal_values_drops_signals_without_enough_history():
|
||||
closes = [100.0 + k for k in range(80)] # only 80 bars
|
||||
vals = bt._signal_values(closes, closes, 79)
|
||||
dates = [date(2024, 1, 1) + timedelta(days=k) for k in range(80)]
|
||||
vals = bt._signal_values(dates, closes, closes, 79)
|
||||
assert "mom_3_1" in vals # needs 63 bars of lookback — present
|
||||
assert "mom_6_1" not in vals # needs 126 — absent
|
||||
assert "mom_12_1" not in vals # needs 252 — absent
|
||||
|
||||
Reference in New Issue
Block a user