feat: compare legacy and live ranking universes

This commit is contained in:
2026-07-17 17:07:35 +02:00
parent 9800114fc4
commit bbc7383d3a
4 changed files with 528 additions and 218 deletions
+38 -7
View File
@@ -1040,15 +1040,19 @@ def _replay_candidates_for_period(
start_date: date,
cadence: str = DEFAULT_BACKTEST_CADENCE,
include_short_candidates: bool = False,
include_universe_rank_observations: bool = False,
) -> list[dict]:
"""Slim picklable replay used by local event studies.
Unlike the full report worker it skips factor-series construction and only
evaluates setup dates on or after ``start_date``. Long-only remains the
compatibility default. Set ``include_short_candidates`` when the caller
needs the production-faithful cross-sectional ranking universe; shorts can
then contribute to percentiles while the portfolio simulator still trades
only qualified longs.
needs the legacy full-backtest candidate-ranking universe; shorts can then
contribute to those historical percentiles while the portfolio simulator
still trades only qualified longs. ``include_universe_rank_observations``
additionally marks exactly one row per ticker/session for a live-like rank
across tickers rather than across directional setup candidates. If no setup
exists on that session, a non-tradeable rank-only row is emitted.
"""
date_ords, opens, highs, lows, closes, volumes = columns
bars = [
@@ -1079,10 +1083,19 @@ def _replay_candidates_for_period(
)
vol_6m = _realized_vol_6m(window_closes, len(window) - 1)
iso = bars[i].date.isocalendar()
for setup in _window_setups(window, config, activation):
if not include_short_candidates and setup["direction"] != "long":
continue
candidates.append({
raw_momentum = (
window_closes[-22] / window_closes[-253] - 1.0
if len(window_closes) >= 253 and window_closes[-253] > 0
else None
)
setups = [
setup
for setup in _window_setups(window, config, activation)
if include_short_candidates or setup["direction"] == "long"
]
observation_emitted = False
for setup in setups:
candidate = {
"symbol": symbol,
"date": bars[i].date.isoformat(),
"iso_week": (iso[0], iso[1]),
@@ -1101,6 +1114,24 @@ def _replay_candidates_for_period(
"meets_core": setup["meets_core"],
"action": setup["action"],
"risk_level": setup["risk_level"],
}
if include_universe_rank_observations and not observation_emitted:
candidate["_universe_rank_observation"] = True
observation_emitted = True
candidates.append(candidate)
if include_universe_rank_observations and not observation_emitted:
candidates.append({
"symbol": symbol,
"date": bars[i].date.isoformat(),
"iso_week": (iso[0], iso[1]),
"ranking_period": _ranking_period(bars[i].date, cadence),
"direction": "rank_only",
"momentum": raw_momentum,
"residual_momentum": residual_momentum,
"vol_6m": vol_6m,
"meets_core": False,
"_universe_rank_observation": True,
"_rank_only": True,
})
return candidates