Files
ibkr-quant/tests/test_strategy.py
Gahow Wang d945624601 Switch to overnight_recovery_top10, rebalanced every 10 sessions
Under quant-trading's PIT research engine (S&P 500 members 2006-2026,
10 bp), overnight_recovery_top10 filled at the next close beat the
daily UpCap+MomGap by 10.5%/yr (paired Newey-West t 2.44) at 3x instead
of 29x yearly turnover. UpCap+MomGap was a CN champion there, and its
daily variant was never picked for the US book.

- strategy: overnight-return momentum (12m skip 1m) + distance from the
  126-day low, ported from strategies/robust_momentum.py; top 10 equal
  weight, a held name stays while ranked within 30. Scores and picks
  match RobustMomentumStrategy on the IB bars (25 rebalances, 0 diffs).
- rebalance: every 10 sessions from strategy.anchor (2026-09-29), on
  completed sessions through yesterday's close, MOC at today's close;
  other sessions do nothing and weights drift.
- backtest trades only on rebalance sessions, like the live loop.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-29 18:06:14 +08:00

29 lines
1.1 KiB
Python

import datetime as dt
import pandas as pd
import pytest
from ibkr_quant.strategy import select_with_buffer, sessions_until_rebalance
SCORE = pd.Series({"A": 0.9, "B": 0.8, "C": 0.7, "D": 0.6, "E": 0.5, "F": None})
def test_picks_top_n_equal_weight_without_holdings():
assert select_with_buffer(SCORE, set(), top_n=2, buffer=0.0) == {"A": 0.5, "B": 0.5}
def test_keeps_held_names_within_the_buffer_and_fills_from_the_top():
# top_n * (1 + buffer) = 4: D (rank 3) stays, E (rank 4) and unscored F go.
assert select_with_buffer(SCORE, {"D", "E", "F"}, top_n=2, buffer=1.0) == {"D": 0.5, "A": 0.5}
def test_refuses_too_few_scored_names():
with pytest.raises(ValueError, match="only 5 names"):
select_with_buffer(SCORE, set(), top_n=6, buffer=0.0)
def test_rebalances_on_the_anchor_session_and_every_n_after():
sessions = pd.bdate_range("2026-09-24", periods=12) # anchor 09-29 is the 4th
wait = [sessions_until_rebalance(sessions[: i + 1], dt.date(2026, 9, 29), 5) for i in range(len(sessions))]
assert wait == [None, None, None, 0, 4, 3, 2, 1, 0, 4, 3, 2]