功能
交易分析
通过 MAE、MFE、优势比率、盈利因子和图表分析交易与持仓
哪些交易赚了钱,退出做得好吗?VBT 将每笔交易保留为包含入场、退出、利润和价格偏移的记录,让 Python 交易分析能够为单次或数千次回测计算胜率、盈利因子、期望收益、MAE、MFE 和优势比率。
data = vbt.YFData.pull(["BTC-USD", "ETH-USD"], start="2018-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,
)
trades = pf.trades.status_closed
metrics = ["total_records", "win_rate", "profit_factor", "expectancy", "sqn"]
trades.stats(metrics, agg_func=None) Total Records Win Rate [%] Profit Factor Expectancy SQN
symbol
BTC-USD 26 30.769231 1.632437 13.422132 0.669763
ETH-USD 23 52.173913 1.867036 33.244891 0.803366mae = trades.mae_returns.values
returns = trades.returns.values
pnl = trades.pnl.values
cut = (returns > 0) & (mae < -0.05)
pd.Series({
"winners": (returns > 0).sum(),
"winners that first fell over 5%": cut.sum(),
"their profit": pnl[cut].sum().round(2),
"profit of all winners": pnl[returns > 0].sum().round(2),
})winners 20.00
winners that first fell over 5% 9.00
their profit 1306.13
profit of all winners 2547.29
dtype: float64trades.plot_mae_returns(column="BTC-USD").show()20 笔盈利交易中,有 9 笔曾先跌至低于入场价 5% 以上,而它们贡献了总盈利的一半。5% 止损会使它们亏损平仓。散点图为每笔比特币交易展示同样的信息:相对于最终结果,它曾逆着持仓方向走了多远。
交易与持仓
同样的订单可以按三种方式分组为交易,每种方式回答不同问题:
| 记录 | 每条记录对应 | 用途 |
|---|---|---|
pf.entry_trades | 建仓或加仓的订单 | 评估入场 |
pf.exit_trades(pf.trades) | 减仓或平仓的订单 | 评估退出 |
pf.positions | 从建仓到完全平仓的持仓 | 评估完整持仓 |
只有一次入场和退出时,三者相同。分批建仓或平仓时则不同:
index = pd.date_range("2026-01-01", periods=4)
close = pd.Series([10.0, 11.0, 12.0, 13.0], index=index)
pf = vbt.PF.from_orders(close, size=[1, 1, -1, -1])
cols = ["Entry Index", "Exit Index", "Size", "PnL"]
pf.entry_trades.readable[cols] Entry Index Exit Index Size PnL
0 2026-01-01 2026-01-04 1.0 2.5
1 2026-01-02 2026-01-04 1.0 1.5pf.exit_trades.readable[cols] Entry Index Exit Index Size PnL
0 2026-01-01 2026-01-03 1.0 1.5
1 2026-01-01 2026-01-04 1.0 2.5pf.positions.readable[cols] Entry Index Exit Index Size PnL
0 2026-01-01 2026-01-04 2.0 4.0入场交易使用平均退出价衡量每次入场,退出交易则使用平均入场价衡量每次退出。记录带有输入的列标签,因此参数网格的交易可按参数分组,direction_long 或 direction_short
可选择多头或空头。pf.trade_history
将订单与三种视图连接起来,如下方交易历史功能介绍所示。每个订单占一行,各行携带该订单的入场交易或退出交易利润,因此在上述例子中,PnL 列加总为
8,是实际利润的两倍。请改用三种记录类型中的一种计算统计。
交易指标
trades.stats() 报告胜率、最佳和最差交易、平均盈利和亏损、连续盈利与亏损次数、盈利因子、期望收益及系统质量指数(SQN)。每项指标也都可作为属性访问,例如
trades.profit_factor。
交易收益率是利润相对于入场投入资金的比例,而非相对于账户价值。100 美元持仓赚取 5 美元,无论账户多大,收益率都是 5%。盈利因子、期望收益和 SQN 默认根据利润计算,设置
use_returns=True 则改为根据收益率计算,从而消除仓位规模的影响。
盈利因子是盈利交易总利润除以亏损交易总亏损。数值为 2 表示盈利交易赚到的金额是亏损交易损失的两倍。期望收益是以账户币种计的每笔交易平均利润,因此期望收益为 10,表示选定记录中每笔交易平均赚取 10。结合胜率阅读这些指标,了解盈利交易的规模能否弥补亏损。
对于 R 倍数等需要记录之外信息的指标,只需在记录基础上添加几行代码。止损距离为两个 ATR 时,交易利润除以初始风险即可得到 R 倍数:
stop_dist = 2 * data.run("atr", window=14).atr
pf = vbt.PF.from_signals(
data,
fast.vbt.crossed_above(slow),
fast.vbt.crossed_below(slow),
sl_stop=stop_dist,
delta_format="absolute",
fees=0.001,
)
trades = pf.trades.status_closed
entry_risk = stop_dist.values[trades.entry_idx.values, trades.col.values]
risk = entry_risk * trades.size.values
pd.Series(trades.pnl.values / risk).describe().round(2)count 50.00
mean 3.19
std 14.88
min -1.05
25% -1.02
50% -1.01
75% 1.05
max 77.89
dtype: float64大多数亏损交易在约一个风险单位处止损,少数盈利交易则获得其数倍回报,这是趋势跟随系统的典型特征。
比较多头与空头交易
您可以对交易子集运行相同分析,无需重新运行回测。这里四笔交易使多头和空头具有相同胜率,但结果截然不同:
side_close = pd.Series([100.0, 120.0, 100.0, 105.0, 100.0, 95.0, 100.0, 98.0], index=pd.date_range("2026-01-01", periods=8))
side_pf = vbt.PF.from_orders(side_close, size=[1, -1, -1, 1, 1, -1, -1, 1], init_cash=1000)
side_trades = side_pf.trades.status_closed
side_metrics = ["total_records", "win_rate", "profit_factor", "expectancy"]
pd.DataFrame({
"Long": side_trades.direction_long.stats(side_metrics),
"Short": side_trades.direction_short.stats(side_metrics),
}).round(2) Long Short
Total Records 2 2
Win Rate [%] 50.0 50.0
Profit Factor 4.0 0.4
Expectancy 7.5 -1.5多头交易赚取 20、亏损 5,空头交易则亏损 5、赚取 2。分开观察两侧可以看清利润来源。筛选后的记录仍支持相同交易图表和偏移分析。其他筛选和分组方式,请参阅记录与映射数组。
价格偏移
最大不利偏移(MAE)是交易达到的最差位置,最大有利偏移(MFE)则是最佳位置。两者都有价格单位和收益率形式,并记录发生时的 K 线。与最终结果比较,可以了解止损是否过紧、退出是否错失利润。扩展版本从入场开始逐 K 线跟踪两者,如下方扩展交易指标功能介绍所示;MAE 与 MFE功能介绍则比较使用和不使用止损的投资组合。
利用这些结果选择待测试的止损或目标水平,再通过止损与止盈模拟这些退出。比较新的交易分布和投资组合结果,了解修改对盈利和亏损交易分别产生的影响。
入场质量
优势比率是经波动率调整后的平均 MFE 除以经波动率调整后的平均 MAE。高于 1 表示入场后有利方向的移动通常大于不利方向。运行优势比率对每种持有期进行计算,让您看到入场需要多少根 K 线才能展现优势,并与随机入场比较,如优势比率功能介绍所示。
交易图表
trades.plot() 绘制每笔交易从入场到退出的过程,并按盈亏着色。pf.plot_trade_signals()
分别标记多头、空头的入场和退出,plot_pnl、plot_returns、plot_mae 和 plot_mfe 则以散点图展示交易层面的结果。这些图是标准 Plotly
图形,因此悬停标签可携带任意额外信息,例如入场时的指标值。
✅ 常规 MAE 和 MFE 等指标仅代表每笔交易的最终结果。但如果想了解它们在交易过程中的变化呢?现在可以将扩展交易指标作为 DataFrame 分析!
data = vbt.YFData.pull("BTC-USD")
pf = vbt.PF.from_random_signals(data, n=50, tp_stop=0.5, seed=42)
pf.trades.plot_expanding_mfe_returns().show()✅ 新的交易绘图方法将入场和退出交易细分为多头入场、多头退出、空头入场和空头退出,并支持不同持仓样式。
data = vbt.YFData.pull("BTC-USD")
bb = data.run("bbands")
long_entries = data.hlc3.vbt.crossed_above(bb.upper) & (bb.bandwidth < 0.1)
long_exits = data.hlc3.vbt.crossed_below(bb.upper) & (bb.bandwidth > 0.5)
short_entries = data.hlc3.vbt.crossed_below(bb.lower) & (bb.bandwidth < 0.1)
short_exits = data.hlc3.vbt.crossed_above(bb.lower) & (bb.bandwidth > 0.5)
pf = vbt.PF.from_signals(
data,
long_entries=long_entries,
long_exits=long_exits,
short_entries=short_entries,
short_exits=short_exits
)
pf.plot_trade_signals().show()✅ 优势比率是衡量入场盈利能力的独特指标。与大多数绩效指标不同,优势比率同时考虑未平仓盈亏,帮助您寻找更好的交易退出方式。
data = vbt.YFData.pull("BTC-USD")
fast_ema = data.run("ema", 10, hide_params=True)
slow_ema = data.run("ema", 20, hide_params=True)
entries = fast_ema.real_crossed_above(slow_ema)
exits = fast_ema.real_crossed_below(slow_ema)
pf = vbt.PF.from_signals(data, entries, exits, direction="both")
rand_pf = vbt.PF.from_random_signals(data, n=pf.orders.count() // 2, seed=42)
fig = pf.trades.plot_running_edge_ratio(
trace_kwargs=dict(line_color="limegreen", name="Edge Ratio (S)")
)
fig = rand_pf.trades.plot_running_edge_ratio(
trace_kwargs=dict(line_color="mediumslateblue", name="Edge Ratio (R)"),
fig=fig
)
fig.show()✅ 交易历史是列出订单的人类可读 DataFrame,并扩展了入场交易、退出交易和持仓的实用细节。
data = vbt.YFData.pull(["BTC-USD", "ETH-USD"], missing_index="drop")
pf = vbt.PF.from_random_signals(
data,
n=1,
seed=42,
run_kwargs=dict(hide_params=True),
tp_stop=0.5,
sl_stop=0.1
)
pf.trade_history Order Id Column Signal Index Creation Index \
0 0 BTC-USD 2016-02-20 00:00:00+00:00 2016-02-20 00:00:00+00:00
1 1 BTC-USD 2016-02-20 00:00:00+00:00 2016-06-12 00:00:00+00:00
2 0 ETH-USD 2019-05-25 00:00:00+00:00 2019-05-25 00:00:00+00:00
3 1 ETH-USD 2019-05-25 00:00:00+00:00 2019-07-15 00:00:00+00:00
Fill Index Side Type Stop Type Size Price \
0 2016-02-20 00:00:00+00:00 Buy Market None 0.228747 437.164001
1 2016-06-12 00:00:00+00:00 Sell Market TP 0.228747 655.746002
2 2019-05-25 00:00:00+00:00 Buy Market None 0.397204 251.759872
3 2019-07-15 00:00:00+00:00 Sell Market SL 0.397204 226.583885
Fees PnL Return Direction Status Entry Trade Id Exit Trade Id \
0 0.0 50.0 0.5 Long Closed 0 -1
1 0.0 50.0 0.5 Long Closed -1 0
2 0.0 -10.0 -0.1 Long Closed 0 -1
3 0.0 -10.0 -0.1 Long Closed -1 0
Position Id
0 0
1 0
2 0
3 0✅ 最大不利偏移(MAE)帮助您了解交易期间承受的最大亏损,也称持仓最大回撤。最大有利偏移(MFE)则显示交易期间达到的最高利润。分析 MAE 和 MFE 统计,可以帮助您改进退出策略。
data = vbt.YFData.pull("BTC-USD")
pf = vbt.PF.from_random_signals(data, n=50, seed=42)
pf.trades.plot_mae_returns().show()pf = vbt.PF.from_random_signals(data, n=50, sl_stop=0.1, seed=42)
pf.trades.plot_mae_returns().show()版权所有 © 2021–2026 Oleg Polakow。保留所有权利。
本站内容与文档供使用和评估 VectorBT PRO 以及教育用途。任何其他用途,包括开发或支持竞争产品或服务,均须事先取得书面同意。