feat: add selectable daily backtest cadence

This commit is contained in:
2026-07-17 14:41:24 +02:00
parent bc50ba9136
commit 65a462271c
12 changed files with 550 additions and 37 deletions
+37 -10
View File
@@ -37,8 +37,10 @@ from app.services import fundamental_service, ingestion_service, sentiment_servi
from app.services.alert_service import dispatch_alerts
from app.services.backtest_service import (
BACKTEST_TARGET_MODELS,
DEFAULT_BACKTEST_CADENCE,
PRODUCTION_GTL_TARGET_MODEL,
run_and_store as run_backtest_and_store,
validate_backtest_cadence,
validate_backtest_target_model,
)
from app.services.benchmark_service import refresh_benchmark_prices
@@ -112,6 +114,7 @@ def _idle_runtime() -> dict[str, object]:
_job_runtime: dict[str, dict[str, object]] = {name: _idle_runtime() for name in _JOB_NAMES}
_next_backtest_target_model = PRODUCTION_GTL_TARGET_MODEL
_next_backtest_cadence = DEFAULT_BACKTEST_CADENCE
# ---------------------------------------------------------------------------
@@ -119,23 +122,44 @@ _next_backtest_target_model = PRODUCTION_GTL_TARGET_MODEL
# ---------------------------------------------------------------------------
def queue_backtest_target_model(target_model: str | None) -> str:
"""Select the model for the next manual backtest run only.
def queue_backtest_options(
target_model: str | None,
cadence: str | None,
) -> tuple[str, str]:
"""Select model and cadence for the next manual backtest run only.
Scheduled runs and subsequent manual runs return to the production GTL.
Scheduled and subsequent manual runs return to production GTL at the
resource-safe weekly cadence.
"""
global _next_backtest_target_model
selected = validate_backtest_target_model(
global _next_backtest_target_model, _next_backtest_cadence
selected_model = validate_backtest_target_model(
target_model or PRODUCTION_GTL_TARGET_MODEL
)
_next_backtest_target_model = selected
selected_cadence = validate_backtest_cadence(
cadence or DEFAULT_BACKTEST_CADENCE
)
_next_backtest_target_model = selected_model
_next_backtest_cadence = selected_cadence
return selected_model, selected_cadence
def queue_backtest_target_model(target_model: str | None) -> str:
"""Compatibility wrapper for callers selecting only the target model."""
selected, _ = queue_backtest_options(target_model, DEFAULT_BACKTEST_CADENCE)
return selected
def _consume_backtest_options() -> tuple[str, str]:
global _next_backtest_target_model, _next_backtest_cadence
selected = (_next_backtest_target_model, _next_backtest_cadence)
_next_backtest_target_model = PRODUCTION_GTL_TARGET_MODEL
_next_backtest_cadence = DEFAULT_BACKTEST_CADENCE
return selected
def _consume_backtest_target_model() -> str:
global _next_backtest_target_model
selected = _next_backtest_target_model
_next_backtest_target_model = PRODUCTION_GTL_TARGET_MODEL
"""Compatibility wrapper consuming all queued one-run options."""
selected, _ = _consume_backtest_options()
return selected
@@ -1028,12 +1052,13 @@ async def compute_regime_monitor() -> None:
async def run_backtest_job() -> None:
"""Replay the price-derived engine over history and cache the report."""
job_name = "backtest"
target_model = _consume_backtest_target_model()
target_model, cadence = _consume_backtest_options()
_log_event(
logging.INFO,
"job_start",
job=job_name,
target_model=target_model,
cadence=cadence,
)
_runtime_start(job_name)
@@ -1051,6 +1076,7 @@ async def run_backtest_job() -> None:
db,
_on_progress,
target_model=target_model,
cadence=cadence,
)
_runtime_finish(
@@ -1058,6 +1084,7 @@ async def run_backtest_job() -> None:
processed=report.get("tickers", 0), total=report.get("tickers", 0),
message=(
f"{BACKTEST_TARGET_MODELS[target_model]}: "
f"{cadence} cadence, "
f"{report.get('candidates', 0)} setups, "
f"{report.get('qualified', 0)} qualified"
),