Quant Buffet API
指标 API
compute_metrics:全部输出字段、精确公式与基准相对统计量。
`backtest.metrics.compute_metrics` 把净值曲线转换成实验室界面上展示的统计量。
签名
python
metrics = compute_metrics(
result.equity, # pd.Series, required, positional
benchmark=spy_buy_and_hold, # optional pd.Series, reindexed + ffilled for you
risk_free=0.0, # annualised, subtracted before Sharpe
trades_count=len(result.trades),
)
# keys: start, end, years, start_equity, end_equity, total_return, cagr,
# volatility, sharpe, sortino, max_drawdown, calmar, daily_win_rate,
# trades (+ benchmark_total_return, benchmark_cagr, alpha, beta)| 参数 | 类型 | 默认值 | 备注 |
|---|---|---|---|
equity | pd.Series | — | 位置参数。会 dropna 并转为 float。 |
benchmark | pd.Series | None | None | 仅关键字。会按 equity 重建索引并前向填充。 |
risk_free | float | 0.0 | 仅关键字。年化值,内部换算为日频。 |
trades_count | int | 0 | 仅关键字。原样写入输出。 |
核心输出字段
| 键 | 类型 | 计算方式 |
|---|---|---|
start、end | str | 净值首末日期,格式 YYYY-MM-DD。 |
years | float | 跨越天数 / 365.25,保留 2 位小数。 |
start_equity、end_equity | float | 首末净值。 |
total_return | float | end / start − 1,未年化。 |
cagr | float | (end / start) ** (1 / years) − 1。 |
volatility | float | 日收益标准差(ddof=1)× √年化系数。 |
sharpe | float | 超额收益均值 / 超额收益标准差 × √年化系数。 |
sortino | float | 年化收益均值 / 年化下行标准差。 |
max_drawdown | float | equity / equity.cummax() − 1 的最小值,为负。 |
calmar | float | cagr / abs(max_drawdown);从未回撤则为 0.0。 |
daily_win_rate | float | 收益为正的交易日占比。 |
trades | int | 原样回显 trades_count。 |
年化系数
模块并未硬编码 252,而是从你自己的索引推断:len(index) / (跨越天数 / 365.25)。纯 ETF 面板约得到 252;含周末行的加密面板约得到 365。这让 Sharpe 在不同资产类型间可比,但也意味着二者混合的面板会落在中间值。
| 面板内容 | 约等于 |
|---|---|
| 仅美国 ETF | 约 252 |
| 仅加密货币 | 约 365 |
| ETF + 加密混合 | 约 365(加密行主导日历) |
| 少于 2 行 | 252(兜底值) |
基准相关字段
仅当提供了 benchmark 且对齐后仍有至少 6 行数据时才会输出:
| 键 | 计算方式 |
|---|---|
benchmark_total_return | 基准末值 / 初值 − 1。 |
benchmark_cagr | 用你的 years 年化,以便同口径比较。 |
beta | 日收益上的 cov(策略, 基准) / var(基准)。 |
alpha | (策略均值 − beta × 基准均值) × 年化系数 —— 年化 Jensen alpha。 |
python
# How the lab builds its benchmark: buy-and-hold, scaled to your starting equity
bench_sym = "SPY" if "SPY" in prices.columns else ASSETS[0]
spy = prices[bench_sym].reindex(result.equity.index).ffill()
buy_hold = float(result.equity.iloc[0]) * (spy / spy.iloc[0])
metrics = compute_metrics(result.equity, benchmark=buy_hold,
trades_count=len(result.trades))边界情况
| 情形 | 结果 |
|---|---|
| 净值点少于 5 个 | 只返回 {"error": "insufficient equity points"} |
| 收益方差为零(从未交易) | sharpe 与 sortino 返回 0.0,而非除零 |
| 没有下跌日 | sortino 为 0.0,因为下行标准差为零 |
| 净值从未低于前高 | max_drawdown 与 calmar 均为 0.0 |
| 基准对齐后不足 6 行 | 完全不输出基准相关字段 |
界面如何格式化这些值
| 界面标签 | 指标键 | 格式 |
|---|---|---|
| 年化收益 | cagr | 百分比,2 位小数 |
| 波动率 | volatility | 百分比,2 位小数 |
| Sharpe 比率 | sharpe | 数值,2 位小数 |
| Sortino 比率 | sortino | 数值,2 位小数 |
| 最大回撤 | max_drawdown | 百分比,2 位小数(为负) |
| Beta | beta | 数值,2 位小数 |
| Alpha | alpha | 百分比,2 位小数 |
| 胜率 | daily_win_rate | 百分比,2 位小数 |