Fix snapshot refresh: enable auto-rebuild, fix date-range edge cases, log incremental failures

- registry.json: change _ftb_fix_v2 from manual_only to auto_full_rebuild so
  backtest auto-refreshes when snapshot doesn't cover the requested period
- run.py: return [] (not all_trading_days fallback) when parking cap pushes
  requested_end before requested_start, preventing silent wrong-date-range runs
- run.py: allow 1-trading-day lag tolerance in parking cap so a single lagging
  symbol (e.g. QQQM shortly after close) doesn't cap the whole simulation
- backtest_sim.py: log incremental_update_failed_falling_back warning so
  silent fallback to full rebuild is visible in direct-mode logs

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
main
I Luk Kim 4 months ago
parent 24e88cb74f
commit fcf379c759

@ -1788,6 +1788,16 @@ class BacktestRunner:
if requested_start <= requested_end:
return get_trading_days(requested_start, requested_end)
# requested_start > requested_end: parking cap pushed end before start
# (e.g. user requested start=2026-04-27 but data only covers to 2026-04-24).
# Do NOT fall through to all_trading_days() — that silently runs on the
# lookback-extended store (e.g. March 17April 24 due to lookback_entry_enabled).
logger.warning(
"backtest_start_beyond_available_data",
requested_start=requested_start.isoformat(),
effective_end=requested_end.isoformat(),
)
return []
if not self.config.get_strategy_engines():
return self.store.all_trading_days()
include_reaction_dates = any(
@ -8338,6 +8348,16 @@ def _extend_store_to_requested_window(
_parking_last_dates.append(max(_sym_dates))
if _parking_last_dates:
_parking_cap = min(_parking_last_dates)
_parking_best = max(_parking_last_dates)
# Allow up to 1 trading-day lag per symbol (extras like QQQM can lag Oracle
# by a few minutes after market close, leaving a None for the most recent bar).
# If the straggler is only 1 trading day behind the best-covered symbol, use
# the best date so the simulation isn't unnecessarily capped.
if _parking_cap < _parking_best:
from libs.backtest.calendar import get_trading_days as _gtd
_lag_days = max(0, len(_gtd(_parking_cap, _parking_best)) - 1)
if _lag_days <= 1:
_parking_cap = _parking_best
current_end = getattr(store, "_requested_end_date", end_date)
if _parking_cap < current_end:
setattr(store, "_requested_end_date", _parking_cap)

@ -447,6 +447,11 @@ async def _refresh_snapshot(
try:
await incremental_update_canonical_snapshot(snapshot_id)
except Exception as inc_exc:
logger.warning(
"incremental_update_failed_falling_back",
snapshot_id=snapshot_id,
error=str(inc_exc),
)
if console:
console.print(f" [yellow]Incremental failed ({inc_exc}), falling back to full rebuild...[/]")
await build_canonical_snapshot(snapshot_id, manual=manual)

@ -97,8 +97,8 @@
]
},
"midlarge-liquid-long-v1_bucketfix_full_audit_canonical_ftb_fix_v2": {
"purpose": "test_ftb_fix",
"refresh_policy": "manual_only",
"purpose": "main_ftb_fix",
"refresh_policy": "auto_full_rebuild",
"universe_profile": "midlarge-liquid-long-v1",
"label_version": "label-2.0.0",
"start_date": "2022-03-01",

Loading…
Cancel
Save