fix(backtest): benchmark全链路透传(API→runner→engine) + metrics分支用_dcfg修cfg=None导致relative_metrics空

This commit is contained in:
2026-07-11 14:27:19 +08:00
parent 5e5a6cf84a
commit f7c2e2eea3
3 changed files with 17 additions and 11 deletions
+2 -1
View File
@@ -75,7 +75,8 @@ async def submit_cta(req: CtaBacktestRequest):
params=req.params,
start=req.start,
end=req.end,
cfg=None
cfg=None,
benchmark=req.benchmark
)
return {"task_id": tid}
+9 -5
View File
@@ -47,7 +47,7 @@ def guess_exchange(symbol: str) -> Exchange:
return Exchange("SSE")
def run_cta_backtest(strategy_class, symbol: str, params: dict, start: str, end: str, cfg, db_path: str) -> BacktestResult:
def run_cta_backtest(strategy_class, symbol: str, params: dict, start: str, end: str, cfg, db_path: str, benchmark: str = "hs300") -> BacktestResult:
"""
Run CTA strategy backtest using vnpy_ctastrategy BacktestingEngine.
@@ -98,6 +98,9 @@ def run_cta_backtest(strategy_class, symbol: str, params: dict, start: str, end:
# Configure vnpy DB → A-share quant_trading.db. Worker process (spawn)
# doesn't inherit main-process SETTINGS, so set before engine.load_data.
# _dcfg is also reused by the metrics branch (benchmark data_paths) since the
# cfg param can be None when called via the API.
_dcfg = None
try:
from vnpy.trader.setting import SETTINGS
from sanguo_data.config import load_config, find_config_path
@@ -129,16 +132,17 @@ def run_cta_backtest(strategy_class, symbol: str, params: dict, start: str, end:
if not isinstance(daily_df.index, pd.DatetimeIndex):
daily_df.index = pd.to_datetime(daily_df.index)
# Get benchmark config (default hs300)
benchmark_key = getattr(cfg, "benchmark", "hs300") if hasattr(cfg, "benchmark") else "hs300"
benchmark_code = BENCHMARK_SYMBOL.get(benchmark_key, "sh000300")
# Get benchmark code (default hs300) + a cfg that has data_paths.
# _dcfg is the loaded config; fall back to the passed cfg if loading failed.
benchmark_code = BENCHMARK_SYMBOL.get(benchmark, "sh000300")
bench_cfg = _dcfg if (_dcfg is not None and hasattr(_dcfg, "data_paths")) else cfg
# Load benchmark data
start_date = start_dt if isinstance(start_dt, datetime) else datetime.strptime(start, "%Y-%m-%d")
end_date = end_dt if isinstance(end_dt, datetime) else datetime.strptime(end, "%Y-%m-%d")
try:
bench_df = read_index_daily(benchmark_code, start_date, end_date, cfg)
bench_df = read_index_daily(benchmark_code, start_date, end_date, bench_cfg)
if bench_df is not None and not bench_df.empty and "close" in bench_df.columns:
# Calculate benchmark daily returns
bench_df["date"] = pd.to_datetime(bench_df["date"])
+6 -5
View File
@@ -30,7 +30,7 @@ class Orchestrator:
await self._on_stage(task_id, stage)
async def submit_cta(self, strategy_class, symbol: str, params: dict,
start: str, end: str, cfg) -> str:
start: str, end: str, cfg, benchmark: str = "hs300") -> str:
"""Submit a CTA backtesting task asynchronously"""
task_id = f"cta_{symbol}_{id(params)}"
self.pool.submit(task_id, "cta")
@@ -40,14 +40,15 @@ class Orchestrator:
params=params,
start=start,
end=end,
cfg=cfg
cfg=cfg,
benchmark=benchmark
)
await self._notify_stage(task_id, "排队中")
spec = self._pending[task_id]
fut: Future = self.pool.submit_work(
task_id, _cta_worker, spec["strategy_class"], spec["symbol"],
spec["params"], spec["start"], spec["end"], spec["cfg"], self.db_path
spec["params"], spec["start"], spec["end"], spec["cfg"], spec["benchmark"], self.db_path
)
task = self.pool.get_task(task_id)
@@ -164,10 +165,10 @@ class Orchestrator:
# Module-level worker functions (must be top-level for ProcessPoolExecutor pickle)
def _cta_worker(strategy_class, symbol: str, params: dict, start: str, end: str, cfg, db_path: str) -> any:
def _cta_worker(strategy_class, symbol: str, params: dict, start: str, end: str, cfg, benchmark: str, db_path: str) -> any:
"""Worker for CTA backtest (lazy import, spawn-friendly)"""
from sanguo_backtest.cta_engine import run_cta_backtest
return run_cta_backtest(strategy_class, symbol, params, start, end, cfg, db_path)
return run_cta_backtest(strategy_class, symbol, params, start, end, cfg, db_path, benchmark=benchmark)
def _opt_worker(strategy_class, symbol: str, grid: dict, start: str, end: str, cfg, db_path: str) -> any: