diff --git a/sanguo_backtest/metrics.py b/sanguo_backtest/metrics.py index c3a54d4..cd26a3b 100644 --- a/sanguo_backtest/metrics.py +++ b/sanguo_backtest/metrics.py @@ -69,15 +69,13 @@ def compute_metrics( equity = empyrical.cum_returns(s) bench_curve = empyrical.cum_returns(b) # rolling alpha/beta (expanding window 用于画图,口径由 scalars 保证) - roll_beta = pd.Series(index=s.index, dtype=float) - roll_alpha = pd.Series(index=s.index, dtype=float) - for i in range(len(s)): - sub = aligned.iloc[: i + 1] - if len(sub) >= 2 and sub["b"].var() > 0: - beta = sub["s"].cov(sub["b"]) / sub["b"].var() - alpha = sub["s"].mean() - beta * sub["b"].mean() - roll_beta.iloc[i] = beta - roll_alpha.iloc[i] = alpha * period + # 向量化 expanding (O(n)) 替代原 for 循环 (O(n²))——分钟级 daily_df(万行)原循环卡死 + # (cta_c287f4b5 卡 5min CPU88% 根因)。expanding.cov/.var/.mean 数值与原循环一致 + # (ddof=1);var_b<=0(含单点 NaN)处 beta=NaN,与原 len>=2 且 var>0 守卫等价。 + cov_sb = aligned["s"].expanding().cov(aligned["b"]) + var_b = aligned["b"].expanding().var() + roll_beta = cov_sb / var_b.where(var_b > 0) + roll_alpha = (aligned["s"].expanding().mean() - roll_beta * aligned["b"].expanding().mean()) * period # drawdown: 从峰值回落 (值 <= 0) cummax = equity.cummax() drawdown = (equity - cummax) / cummax