Add local backtest snapshot runner
Deploy / lint (push) Successful in 8s
Deploy / test (push) Successful in 1m22s
Deploy / deploy (push) Successful in 43s

This commit is contained in:
2026-07-03 18:34:07 +02:00
parent 14327ab25a
commit 66ef0564c1
5 changed files with 380 additions and 12 deletions
+31 -12
View File
@@ -791,6 +791,23 @@ def _backtest_worker_count() -> int:
return max(1, min(configured, cpu - 1))
def _offline_snapshot_mode() -> bool:
return os.getenv("BACKTEST_SNAPSHOT_OFFLINE", "").strip().lower() in {"1", "true", "yes", "on"}
async def _load_benchmark_closes_for_backtest(
db: AsyncSession, *, days: int | None = None, refresh: bool = True
) -> dict[date, float]:
from app.services.benchmark_service import load_benchmark_closes, refresh_benchmark_prices
if refresh and not _offline_snapshot_mode():
if days is None:
await refresh_benchmark_prices(db)
else:
await refresh_benchmark_prices(db, days=days)
return await load_benchmark_closes(db)
def _mp_context():
"""A start method safe to use from the threaded asyncio server: ``forkserver``
(workers forked from a clean, single-threaded server — avoids the
@@ -800,6 +817,12 @@ def _mp_context():
for method in ("forkserver", "fork"):
if method in methods:
return multiprocessing.get_context(method)
if (
_offline_snapshot_mode()
and os.getenv("BACKTEST_ALLOW_SPAWN", "").strip().lower() in {"1", "true", "yes", "on"}
and "spawn" in methods
):
return multiprocessing.get_context("spawn")
return None
@@ -1568,10 +1591,9 @@ async def run_backtest(
# emitted and the rest of the report remains valid.
benchmark_closes: dict[date, float] = {}
try:
from app.services.benchmark_service import load_benchmark_closes, refresh_benchmark_prices
await refresh_benchmark_prices(db, days=settings.ohlcv_history_days + 365)
benchmark_closes = await load_benchmark_closes(db)
benchmark_closes = await _load_benchmark_closes_for_backtest(
db, days=settings.ohlcv_history_days + 365
)
except Exception:
logger.exception("Benchmark load for residual momentum failed")
@@ -1693,16 +1715,13 @@ async def run_backtest(
spy_closes: dict | None = None
try:
from app.services.benchmark_service import (
load_benchmark_closes,
refresh_benchmark_prices,
)
oldest = min((cols[0][0] for cols in price_columns.values()), default=None)
if oldest is not None:
days_needed = None
if oldest is not None and not _offline_snapshot_mode():
days_needed = (date.today() - date.fromordinal(oldest)).days + 30
await refresh_benchmark_prices(db, days=days_needed)
spy_closes = await load_benchmark_closes(db)
spy_closes = await _load_benchmark_closes_for_backtest(
db, days=days_needed, refresh=oldest is not None
)
except Exception:
logger.exception("Benchmark load for the portfolio sim failed")