From cdf41edf1f33ebd06de302637129e0006b4edbe7 Mon Sep 17 00:00:00 2001 From: ao gong <41768719+ageorge156@users.noreply.github.com> Date: Fri, 21 Aug 2026 21:11:06 +0800 Subject: [PATCH] feat: add unified weight backtest result facade --- src/quant_engine/backtest.py | 45 +++++++++++++++++++++++++++++++++++- 1 file changed, 44 insertions(+), 1 deletion(-) diff --git a/src/quant_engine/backtest.py b/src/quant_engine/backtest.py index e083d36..f7c5e11 100644 --- a/src/quant_engine/backtest.py +++ b/src/quant_engine/backtest.py @@ -34,8 +34,8 @@ print(result.summary()) from __future__ import annotations -from pathlib import Path from collections.abc import Mapping, Sequence +from dataclasses import dataclass import numpy as np import pandas as pd @@ -46,6 +46,26 @@ from quant_engine.metrics import summary as metrics_summary logger = get_logger(__name__) +@dataclass(frozen=True, slots=True, eq=False) +class BacktestResult: + """一次权重回测的稳定结果快照。""" + + nav: pd.Series + returns: pd.Series + weights: pd.DataFrame + benchmark_nav: pd.Series | None = None + + def stats(self, rf: float = 0.0) -> Mapping[str, float]: + """返回标准绩效指标。""" + return metrics_summary(self.returns, rf) + + def benchmark_report(self, rf: float = 0.0) -> pd.DataFrame: + """返回策略与基准的对比报告。""" + if self.benchmark_nav is None: + raise ValueError("benchmark_nav is required for benchmark comparison") + return compare_to_benchmark(self.nav, self.benchmark_nav, rf) + + # ── 调仓表 → 净值 ────────────────────────────────────── @@ -113,6 +133,29 @@ def compute_returns_from_nav(nav: pd.Series) -> pd.Series: return nav.pct_change().fillna(0.0) +def run_weight_backtest( + weights: pd.DataFrame, + stock_returns: pd.DataFrame, + initial_capital: float = 1.0, + tc_rate: float = 0.0, + benchmark_nav: pd.Series | None = None, +) -> BacktestResult: + """执行权重回测并返回隔离于调用方输入的结果快照。""" + weights_snapshot = weights.copy(deep=True) + nav = compute_nav_from_weights( + weights=weights_snapshot, + stock_returns=stock_returns, + initial_capital=initial_capital, + tc_rate=tc_rate, + ) + return BacktestResult( + nav=nav, + returns=compute_returns_from_nav(nav), + weights=weights_snapshot, + benchmark_nav=None if benchmark_nav is None else benchmark_nav.copy(deep=True), + ) + + # ── 调仓工具 ──────────────────────────────────────