# Trade analytics (/features/analytics/trade-analytics)

## Expanding trade metrics \[#expanding-trade-metrics]

New in 1.11.0

✅ Regular metrics like MAE and MFE only represent the final point of each trade. But what if you want
to see how these metrics develop during the trade? You can now analyze expanding trade metrics as
DataFrames!

```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()
```

Expanding maximum favorable excursion return projections for BTC-USD trades. [Figure data (JSON)](/assets/figures/features/analysis/expanding-mfe-returns.a7f673138861.json)

## Trade signals \[#trade-signals]

New in 1.8.2

✅ New trade plotting method that separates entry and exit trades into long entries, long exits,
short entries, and short exits. It supports different styles for positions.

```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 with long and short Bollinger Bands strategy trade signals. [Figure data (JSON)](/assets/figures/features/analysis/trade-signals.c1d8660ae356.json)

## Edge ratio \[#edge-ratio]

New in 1.8.1

✅ [Edge ratio](https://www.buildalpha.com/eratio/) is a unique metric for quantifying entry
profitability. Unlike most performance metrics, the edge ratio accounts for both open profits and
losses. This can help you find better trade exits.

```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.  The random strategy should have a similar number of orders to allow comparison.

Running edge ratios for EMA crossover and random BTC-USD strategies. [Figure data (JSON)](/assets/figures/features/analysis/running-edge-ratio.b9bb905dcad9.json)

## Trade history \[#trade-history]

New in 1.8.1

✅ Trade history is a human-readable DataFrame listing orders, extended with useful details about
entry trades, exit trades, and positions.

```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 and MFE \[#mae-and-mfe]

New in 1.3.0

✅ [Maximum Adverse Excursion (MAE)](https://analyzingalpha.com/maximum-adverse-excursion)
helps you see the maximum loss taken during a trade, also known as the maximum drawdown of the
position. [Maximum Favorable Excursion (MFE)](https://analyzingalpha.com/maximum-favorable-excursion)
shows the highest profit reached during a trade. Analyzing MAE and MFE statistics can help you
improve your exit strategies.

```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()
```

Maximum adverse excursion versus return for BTC-USD trades without stop loss. [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()
```

Maximum adverse excursion versus return for BTC-USD trades with stop loss. [Figure data (JSON)](/assets/figures/features/analysis/mae-with-stop-loss.904e9d91750d.json)

## Benchmark \[#benchmark]

New in 1.0.4

✅ The benchmark can now be easily set for your entire portfolio.

```python title="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()
```

Cumulative returns of Microsoft compared with the S\&P 500 benchmark. [Figure data (JSON)](/assets/figures/features/analysis/benchmark.c1d30a1b0d0b.json)
