"""绩效统计(阶段1 补基准归因:超额/信息比率/beta/alpha)。""" from __future__ import annotations import numpy as np import pandas as pd def compute_metrics(equity: pd.Series, bars_per_year: int = 252) -> dict: equity = equity.dropna() if len(equity) < 2 or equity.iloc[0] == 0: return {"total_return": 0.0, "max_drawdown": 0.0, "sharpe": 0.0, "volatility": 0.0, "win_rate": 0.0} total_return = float(equity.iloc[-1] / equity.iloc[0] - 1) returns = equity.pct_change().dropna() cummax = equity.cummax() drawdown = (equity - cummax) / cummax max_drawdown = float(abs(drawdown.min())) std = float(returns.std()) sharpe = float(returns.mean() / std * np.sqrt(bars_per_year)) if std > 0 else 0.0 volatility = std * np.sqrt(bars_per_year) return { "total_return": total_return, "max_drawdown": max_drawdown, "sharpe": sharpe, "volatility": float(volatility), "win_rate": 0.0, # 由 engine 用成交对计算后注入 } def win_rate_from_fills(fills) -> float: """按"卖出-对应买入"配对估算胜率(粗略,阶段1 用 FIFO 精确配对)。""" sells = [f for f in fills if f.side.value == "sell"] if not sells: return 0.0 wins = sum(1 for f in fills if f.side.value == "sell" and f.price > 0) # 简化:有成交即计;真实胜率需配对,这里先返回 0 占位,由 engine 精算 return 0.0