功能

绩效与风险指标

对任意回测计算收益、夏普、索提诺、基准和滚动指标

结果究竟有多好,又是与什么相比?VBT 在 Python 中计算总收益、夏普比率、索提诺比率、卡玛比率、回撤和基准统计等回测绩效指标,既能分析单个投资组合,也能同时分析数千个组合;不仅适用于自身回测,也适用于任意收益序列。

Get a full report for one backtest
data = vbt.YFData.pull("BTC-USD", start="2020-01-01", end="2025-01-01")
fast = data.close.rolling(20).mean()
slow = data.close.rolling(50).mean()
pf = vbt.PF.from_signals(
    data,
    fast.vbt.crossed_above(slow),
    fast.vbt.crossed_below(slow),
    fees=0.001,
)
pf.stats()
Start Index                   2020-01-01 00:00:00+00:00
End Index                     2024-12-31 00:00:00+00:00
Total Duration                       1827 days 00:00:00
Start Value                                       100.0
Min Value                                     95.605003
Max Value                                    787.725311
End Value                                    693.387328
Total Return [%]                             593.387328
Benchmark Return [%]                        1197.596406
Position Coverage [%]                         53.858785
Max Gross Exposure [%]                            100.0
Max Drawdown [%]                              58.610935
Max Drawdown Duration                1126 days 00:00:00
Total Orders                                         37
Total Fees Paid                               15.776306
Total Trades                                         19
Win Rate [%]                                  33.333333
Best Trade [%]                               381.070734
Worst Trade [%]                              -19.099978
Avg Winning Trade [%]                         83.748564
Avg Losing Trade [%]                          -8.434649
Avg Winning Trade Duration             93 days 08:00:00
Avg Losing Trade Duration              27 days 04:00:00
Profit Factor                                  1.754588
Expectancy                                    20.504842
Sharpe Ratio                                   1.083913
Calmar Ratio                                   0.805908
Omega Ratio                                    1.249536
Sortino Ratio                                  1.700246
dtype: object

相同调用也适用于整个网格。这里是 100 组均线参数,每组一行:

Compare selected metrics across 100 backtests
fast_w, slow_w = np.meshgrid(np.arange(5, 55, 5), np.arange(60, 260, 20))
fast = vbt.MA.run(data.close, window=fast_w.ravel(), short_name="fast")
slow = vbt.MA.run(data.close, window=slow_w.ravel(), short_name="slow")
grid_pf = vbt.PF.from_signals(
    data,
    fast.ma_crossed_above(slow),
    fast.ma_crossed_below(slow),
    fees=0.001,
)
metrics = ["total_return", "sharpe_ratio", "max_dd", "total_trades"]
grid_stats = grid_pf.stats(metrics)  
grid_stats.sort_values("Sharpe Ratio", ascending=False).head()
                         Total Return [%]  Sharpe Ratio  Max Drawdown [%]  Total Trades
fast_window slow_window
5           120               1744.634182      1.510302         38.569486             9
            100               1577.778405      1.467615         41.260714            12
25          100               1315.741883      1.405711         40.937689             8
15          100               1241.937564      1.372806         38.948698             9
10          100               1244.107822      1.365325         41.252897            10

表格让您轻松同时比较收益、回撤、交易次数和夏普比率。如需对排名靠前的结果继续测试,请参阅稳健性与过拟合。

收益率

每项指标都从收益率序列开始。对于投资组合,pf.returns 是逐 K 线的价值变化率,会扣除入金和出金影响,避免将它们计入绩效。pf.asset_returns 忽略现金,仅衡量已投资部分。使用 series.vbt.to_returns() 转换价格或价值序列,再通过收益率序列的 vbt.returns 访问器,计算累计、总计和年化收益率、波动率及本页介绍的比率。

年化需要两个数:每根 K 线的时长,以及一年的长度。默认一年为 365 天,适合每天交易的市场。对于每年约交易 252 天的股票,请设置 year_freq,或通过 "auto" 让 VBT 统计每年的 K 线数:

Annualize stock returns with the right year length
spy = vbt.YFData.pull("SPY", start="2020-01-01", end="2025-01-01")
rets = spy.close.vbt.to_returns()

print(round(rets.vbt.returns(freq="1D").sharpe_ratio(), 3))
0.892
print(round(rets.vbt.returns(freq="1D", year_freq="252 days").sharpe_ratio(), 3))
0.741
print(round(rets.vbt.returns(freq="1D", year_freq="auto").sharpe_ratio(), 3))
0.741

可通过 vbt.settings.returns.year_freq 为一次会话统一设置。

分析外部权益曲线

您可以对其他回测工具导出的权益曲线,或已在 Pandas 中算好的收益率使用相同指标。下面是一个没有入金或出金的小型账户价值序列:

Measure returns from an equity curve without creating a portfolio
equity = pd.Series([102.0, 101.0, 104.0, 103.0, 106.0], index=pd.date_range("2026-01-05", periods=5))
external_returns = equity.vbt.to_returns(init_value=100)
external_acc = external_returns.vbt.returns(freq="1D", year_freq="252 days")
pd.Series({
    "Total return [%]": external_acc.total() * 100,
    "Max drawdown [%]": external_acc.max_drawdown() * 100,
}).round(2)
Total return [%]    6.00
Max drawdown [%]   -0.98
dtype: float64

起始价值 100 包含第一天涨至 102 的收益。最终价值为 106,总收益率因此为 6%。同一访问器也适用于由多个收益率序列组成的 DataFrame,因此无需重建交易即可比较多种策略结果。

风险调整比率

夏普、索提诺、卡玛、欧米伽和信息比率,以及风险价值、条件风险价值、尾部比率、偏度和峰度,都既可作为具有默认值的属性使用,也可作为接受参数的方法调用,例如 pf.get_sharpe_ratio(risk_free=...)。

问题查看哪些指标
策略相对于收益波动获得了多少收益?夏普比率
相对于下行波动获得了多少收益?索提诺比率和下行风险
年化收益与最差回撤相比如何?卡玛比率
最差的单根 K 线收益是什么样?风险价值和条件风险价值

单次亏损的深度、持续时间和恢复情况,请参阅回撤分析。

夏普比率是单根 K 线超额收益的均值除以标准差,再乘以每年 K 线数的平方根。它并非年化收益率除以年化波动率;后者先复利计算,会得到不同数值。

设置年化无风险利率

传入年利率,例如 pf.get_sharpe_ratio(risk_free=0.04) 表示 4%。VBT 根据配置的频率和年长度,将其转换为每根 K 线的收益率。对于变化的利率,请先从策略收益率中减去对齐的逐 K 线无风险收益率序列,再以 risk_free=0 计算夏普比率。

没有持仓的 K 线收益率为零,这些零仍参与计算,因为持币等待也是策略的一部分。如果只想衡量在场时间,请先将这些收益设为 NaN,再计算比率。单笔交易的收益率不能替代夏普比率中的逐 K 线收益率,因为交易持续时间不同,无法直接年化。

基准比较

每个投资组合都有基准,默认是买入并持有交易资产,也可通过 bm_close 指定其他价格序列,如下方基准功能介绍所示。随后收益率访问器即可比较两者:

Compare the crossover strategy with holding Bitcoin
ret_acc = pf.returns_acc
pd.Series({
    "alpha": ret_acc.alpha(),
    "beta": ret_acc.beta(),
    "information ratio": ret_acc.information_ratio(),
    "up capture": ret_acc.up_capture_ratio(),
    "down capture": ret_acc.down_capture_ratio(),
}).round(3)
alpha                0.142
beta                 0.491
information ratio   -0.027
up capture           0.127
down capture         0.883
dtype: float64

策略的比特币市场敞口约为持有的一半。相对直接持有,它捕获的上涨很少,却承受了大部分下跌,因此收益只有一半。

滚动指标

单个数值会掩盖绩效随时间的变化。每种比率都有基于 K 线窗口的滚动版本:

Plot a rolling one-year Sharpe ratio against the benchmark
rolling = pd.DataFrame({
    "Strategy": pf.returns_acc.rolling_sharpe_ratio(365),
    "Benchmark": pf.bm_returns.vbt.returns(freq="1D").rolling_sharpe_ratio(365),
})
rolling.vbt.plot().show()
2020 至 2024 年比特币均线策略和持有比特币的滚动一年夏普比率 图表数据(JSON)

归因与市场状态

分组收益率是其合计价值的收益率,而非成员收益率之和。如需按资产拆分,请用前一根 K 线上每列价值占分组价值的比例作为权重,现金也包含在内。若要比较市场状态,可为每根 K 线标注状态,按标签对收益率分组,再通过收益率访问器为各组计算指标:

Compare Sharpe ratios in uptrends and downtrends
sma200 = data.close.rolling(200).mean()
regime = pd.Series(
    np.where(data.close > sma200, "uptrend", "downtrend"),
    index=data.index,
    name="regime",
)[sma200.notna()]  

def sharpe(returns):
    return returns.vbt.returns(freq="1D").sharpe_ratio()

pd.DataFrame({
    "bars": regime.value_counts(),
    "strategy": pf.returns.groupby(regime).apply(sharpe),
    "buy and hold": pf.bm_returns.groupby(regime).apply(sharpe),
}).round(2)
           bars  strategy  buy and hold
regime
downtrend   617      -1.3         -1.14
uptrend    1011       2.0          2.67

该策略在上涨趋势中盈利,在下跌趋势中亏损,而直接持有在两种状态下的夏普比率都更高。这样的拆分显示规则是否在市场本身的趋势之外增添了价值。

统计报告

stats 根据指标列表构建报告。选择所需指标,也能加快大网格的处理;还可在内置指标旁添加自己的指标:

Add a custom metric to the report
pf.stats([
    "total_return",
    "sharpe_ratio",
    ("trades_per_year", dict(
        title="Trades per Year",
        calc_func=lambda self: self.trades.count() / (len(self.wrapper.index) / 365),
    )),
])
Total Return [%]    593.387328
Sharpe Ratio          1.083913
Trades per Year        3.79584
dtype: object

报告中的持续时间按 K 线数乘以频率计算,而非日历时间。分布在九个日历日中的四根日 K 线,报告总持续时间为 4 天,因此数据中不存在的周末和节假日不会计入。

设置会传递给每项指标,例如 pf.stats(settings=dict(use_asset_returns=True)) 在资产收益率上计算收益指标。收益率(returns_stats)、交易、回撤和订单也都有相同类型的报告;分组投资组合的 stats 按组报告。对于极大的网格,可使用 vbt.chunked 按列分块并行计算 stats。

QuantStats

安装 QuantStats 后,pf.qs 公开其函数、图表和报告,并已填入投资组合的收益率、基准和频率。例如,pf.qs.sharpe() 返回 QuantStats 版本的夏普比率,pf.qs.html_report() 构建完整绩效报告。适配器也会转换适用于 QuantStats 的无风险利率和年化设置。

基准

✅ 现在可以轻松为整个投资组合设置基准。

Compare Microsoft to S&P 500
data = vbt.YFData.pull(["SPY", "MSFT"], start="2010", missing_columns="drop")

pf = vbt.PF.from_holding(
    close=data.data["MSFT"]["Close"],
    bm_close=data.data["SPY"]["Close"]
)
pf.plot_cumulative_returns().show()
微软股票与标准普尔 500 基准的累计收益比较 图表数据(JSON)

版权所有 © 2021–2026 Oleg Polakow。保留所有权利。

本站内容与文档供使用和评估 VectorBT PRO 以及教育用途。任何其他用途,包括开发或支持竞争产品或服务,均须事先取得书面同意。