# 交易分析 (/zh/features/analytics/trade-analytics)

哪些交易赚了钱，退出做得好吗？VBT 将每笔交易保留为包含入场、退出、利润和价格偏移的记录，让 Python 交易分析能够为单次或数千次回测计算胜率、盈利因子、期望收益、MAE、MFE 和优势比率。

```python title="Check whether a 5% stop would have cut the winners"
>>> 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  # (1)
>>> 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.803366

>>> mae = trades.mae_returns.values  # (2)
>>> 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: float64

>>> trades.plot_mae_returns(column="BTC-USD").show()
```

1.  仅包含已平仓交易。每个资产的最后一笔交易可能仍未平仓。
2.  每笔交易在平仓前达到的最差收益率。

比特币均线交叉交易的最大不利偏移与最终收益率散点图. [Figure data (JSON)](/assets/figures/features/analytics/trade-analytics-mae.23f245cf1a10.json)

20 笔盈利交易中，有 9 笔曾先跌至低于入场价 5% 以上，而它们贡献了总盈利的一半。5% 止损会使它们亏损平仓。散点图为每笔比特币交易展示同样的信息：相对于最终结果，它曾逆着持仓方向走了多远。

## 交易与持仓 \[#trades-and-positions]

同样的订单可以按三种方式分组为交易，每种方式回答不同问题：

| 记录                            | 每条记录对应      | 用途     |
| ----------------------------- | ----------- | ------ |
| `pf.entry_trades`             | 建仓或加仓的订单    | 评估入场   |
| `pf.exit_trades`（`pf.trades`） | 减仓或平仓的订单    | 评估退出   |
| `pf.positions`                | 从建仓到完全平仓的持仓 | 评估完整持仓 |

只有一次入场和退出时，三者相同。分批建仓或平仓时则不同：

```python title="Split two buys and two sells into trades and 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.5

>>> pf.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.5

>>> pf.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`
将订单与三种视图连接起来，如下方[交易历史](#trade-history)功能介绍所示。每个订单占一行，各行携带该订单的入场交易或退出交易利润，因此在上述例子中，`PnL` 列加总为
8，是实际利润的两倍。请改用三种记录类型中的一种计算统计。

## 交易指标 \[#trade-metrics]

`trades.stats()` 报告胜率、最佳和最差交易、平均盈利和亏损、连续盈利与亏损次数、盈利因子、期望收益及系统质量指数（SQN）。每项指标也都可作为属性访问，例如
`trades.profit_factor`。

交易收益率是利润相对于入场投入资金的比例，而非相对于账户价值。100 美元持仓赚取 5 美元，无论账户多大，收益率都是 5%。盈利因子、期望收益和 SQN 默认根据利润计算，设置
`use_returns=True` 则改为根据收益率计算，从而消除仓位规模的影响。

盈利因子是盈利交易总利润除以亏损交易总亏损。数值为 2 表示盈利交易赚到的金额是亏损交易损失的两倍。期望收益是以账户币种计的每笔交易平均利润，因此期望收益为 10，表示选定记录中每笔交易平均赚取
10。结合胜率阅读这些指标，了解盈利交易的规模能否弥补亏损。

对于 R 倍数等需要记录之外信息的指标，只需在记录基础上添加几行代码。止损距离为两个 ATR 时，交易利润除以初始风险即可得到 R 倍数：

```python title="Express trade results as multiples of the initial risk"
>>> 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
```

大多数亏损交易在约一个风险单位处止损，少数盈利交易则获得其数倍回报，这是趋势跟随系统的典型特征。

## 比较多头与空头交易 \[#compare-long-and-short-trades]

您可以对交易子集运行相同分析，无需重新运行回测。这里四笔交易使多头和空头具有相同胜率，但结果截然不同：

```python title="Compare both sides of a strategy from one simulation"
>>> 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。分开观察两侧可以看清利润来源。筛选后的记录仍支持相同交易图表和偏移分析。其他筛选和分组方式，请参阅[记录与映射数组](/zh/features/analytics/records-and-mapped-arrays/)。

## 价格偏移 \[#excursions]

最大不利偏移（MAE）是交易达到的最差位置，最大有利偏移（MFE）则是最佳位置。两者都有价格单位和收益率形式，并记录发生时的 K
线。与最终结果比较，可以了解止损是否过紧、退出是否错失利润。扩展版本从入场开始逐 K
线跟踪两者，如下方[扩展交易指标](#expanding-trade-metrics)功能介绍所示；[MAE 与 MFE](#mae-and-mfe)功能介绍则比较使用和不使用止损的投资组合。

利用这些结果选择待测试的止损或目标水平，再通过[止损与止盈](/zh/features/backtesting/stop-loss-and-take-profit/)模拟这些退出。比较新的交易分布和投资组合结果，了解修改对盈利和亏损交易分别产生的影响。

## 入场质量 \[#entry-quality]

优势比率是经波动率调整后的平均 MFE 除以经波动率调整后的平均 MAE。高于 1 表示入场后有利方向的移动通常大于不利方向。运行优势比率对每种持有期进行计算，让您看到入场需要多少根 K
线才能展现优势，并与随机入场比较，如[优势比率](#edge-ratio)功能介绍所示。

## 交易图表 \[#trade-plots]

`trades.plot()` 绘制每笔交易从入场到退出的过程，并按盈亏着色。`pf.plot_trade_signals()`
分别标记多头、空头的入场和退出，`plot_pnl`、`plot_returns`、`plot_mae` 和 `plot_mfe` 则以散点图展示交易层面的结果。这些图是标准 Plotly
图形，因此悬停标签可携带任意额外信息，例如入场时的指标值。

## 扩展交易指标 \[#expanding-trade-metrics]

新增于 1.11.0

✅ 常规 MAE 和 MFE 等指标仅代表每笔交易的最终结果。但如果想了解它们在交易过程中的变化呢？现在可以将扩展交易指标作为 DataFrame 分析！

```python title="Visualize the expanding MFE using projections"
>>> 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()
```

BTC-USD 交易的扩展最大有利偏移收益率投影. [Figure data (JSON)](/assets/figures/features/analysis/expanding-mfe-returns.a7f673138861.json)

## 交易信号 \[#trade-signals]

新增于 1.8.2

✅ 新的交易绘图方法将入场和退出交易细分为多头入场、多头退出、空头入场和空头退出，并支持不同持仓样式。

```python title="Plot trade signals of a Bollinger Bands strategy"
>>> 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()
```

BTC-USD OHLC 与布林带策略的多空交易信号. [Figure data (JSON)](/assets/figures/features/analysis/trade-signals.c1d8660ae356.json)

## 优势比率 \[#edge-ratio]

新增于 1.8.1

✅ [优势比率](https://www.buildalpha.com/eratio/)是衡量入场盈利能力的独特指标。与大多数绩效指标不同，优势比率同时考虑未平仓盈亏，帮助您寻找更好的交易退出方式。

```python title="Compare the edge ratio of an EMA crossover to a random strategy"
>>> 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)  # (1)
>>> 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()
```

1.  随机策略应有相近的订单数量，才能进行比较。

EMA 交叉策略与随机 BTC-USD 策略的运行优势比率. [Figure data (JSON)](/assets/figures/features/analysis/running-edge-ratio.b9bb905dcad9.json)

## 交易历史 \[#trade-history]

新增于 1.8.1

✅ 交易历史是列出订单的人类可读 DataFrame，并扩展了入场交易、退出交易和持仓的实用细节。

```python title="Get the trade history of a random portfolio with one signal"
>>> 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-and-mfe]

新增于 1.3.0

✅
[最大不利偏移（MAE）](https://analyzingalpha.com/maximum-adverse-excursion)帮助您了解交易期间承受的最大亏损，也称持仓最大回撤。[最大有利偏移（MFE）](https://analyzingalpha.com/maximum-favorable-excursion)则显示交易期间达到的最高利润。分析
MAE 和 MFE 统计，可以帮助您改进退出策略。

```python title="Analyze the MAE of a random portfolio without SL"
>>> data = vbt.YFData.pull("BTC-USD")
>>> pf = vbt.PF.from_random_signals(data, n=50, seed=42)
>>> pf.trades.plot_mae_returns().show()
```

不使用止损的 BTC-USD 交易：最大不利偏移与收益率. [Figure data (JSON)](/assets/figures/features/analysis/mae-without-stop-loss.45e6b1c85f4d.json)

```python title="Analyze the MAE of a random portfolio with SL"
>>> pf = vbt.PF.from_random_signals(data, n=50, sl_stop=0.1, seed=42)
>>> pf.trades.plot_mae_returns().show()
```

使用止损的 BTC-USD 交易：最大不利偏移与收益率. [Figure data (JSON)](/assets/figures/features/analysis/mae-with-stop-loss.904e9d91750d.json)
