# Trading signals (/features/indicators/trading-signals)

## Signal unraveling \[#signal-unraveling]

New in v2024.2.22

✅ To backtest each signal individually, you can now "unravel" each signal, or each pair of entry and exit
signals, into its own column. This creates a wide, two-dimensional mask that, when backtested, returns
performance metrics for each signal rather than for the entire column.

```python title="For each signal, create a separate position with own stop orders"
>>> data = vbt.YFData.pull("BTC-USD")
>>> fast_sma = data.run("talib_func:sma", timeperiod=20)  # (1)
>>> slow_sma = data.run("talib_func:sma", timeperiod=50)
>>> entries = fast_sma.vbt.crossed_above(slow_sma)
>>> exits = fast_sma.vbt.crossed_below(slow_sma)
>>> entries, exits = entries.vbt.signals.unravel_between(exits, relation="anychain")  # (2)
>>> pf = vbt.PF.from_signals(
...     data,
...     long_entries=entries,
...     short_entries=exits,
...     size=100,  # (3)
...     size_type="value",
...     init_cash="auto",  # (4)
...     tp_stop=0.2,
...     sl_stop=0.1,
...     group_by=vbt.ExceptLevel("signal"),  # (5)
...     cash_sharing=True
... )
>>> pf.positions.returns.to_pd(ignore_index=True).vbt.barplot(
...     trace_kwargs=dict(marker=dict(colorscale="Spectral"))
... ).show()  # (6)
```

1.  Run a TA-Lib function faster without building an indicator: `"talib_func:sma"` vs `"talib:sma"`.
2.  Place each pair of entry->exit and exit->entry signals in a separate column.
3.  Order $100 worth of the asset.
4.  Simulate with infinite cash.
5.  Combine all columns (positions) under the same asset into a single portfolio.
6.  Show position returns by signal index.

Position returns for individually unraveled BTC-USD signals. [Figure data (JSON)](/assets/figures/features/indicators/signal-unraveling.5e7c83dd018b.json)

## Signal detection \[#signal-detection]

New in 1.8.0

✅ VBT includes an indicator that uses a robust peak detection algorithm based on z-scores.
This indicator can be used to identify outbreaks and outliers in any time series data.

```python title="Detect sudden changes in the bandwidth of a Bollinger Bands indicator"
>>> data = vbt.YFData.pull("BTC-USD")
>>> fig = vbt.make_subplots(rows=2, cols=1, shared_xaxes=True)
>>> bbands = data.run("bbands")
>>> bbands.loc["2022"].plot(add_trace_kwargs=dict(row=1, col=1), fig=fig)
>>> sigdet = vbt.SIGDET.run(bbands.bandwidth, factor=5)
>>> sigdet.loc["2022"].plot(add_trace_kwargs=dict(row=2, col=1), fig=fig)
>>> fig.show()
```

BTC-USD Bollinger Bands and detected bandwidth changes during 2022. [Figure data (JSON)](/assets/figures/features/indicators/signal-detection.af030867491c.json)

## Pivot detection \[#pivot-detection]

New in 1.7.1

✅ The pivot detection indicator is a tool for finding when the price trend is reversing. By identifying
support and resistance areas, it helps spot significant price changes while filtering out short-term
fluctuations and reducing noise. It works simply: a peak is registered when the price jumps above
one threshold, and a valley is recorded when the price falls below another. Another advantage is
that, unlike the [regular Zig Zag indicator](https://www.investopedia.com/ask/answers/030415/what-zig-zag-indicator-formula-and-how-it-calculated.asp),
which tends to look ahead, our indicator only returns confirmed pivot points and is safe to use in backtesting.

```python title="Plot the last pivot value"
>>> data = vbt.YFData.pull("BTC-USD", start="2020", end="2023")
>>> fig = data.plot(plot_volume=False)
>>> pivot_info = data.run("pivotinfo", up_th=1.0, down_th=0.5)
>>> pivot_info.plot(fig=fig, conf_value_trace_kwargs=dict(visible=False))
>>> fig.show()
```

BTC-USD OHLC with confirmed pivot values from 2020 through 2022. [Figure data (JSON)](/assets/figures/features/indicators/pivot-detection.0678a96251d4.json)

## Robust crossovers \[#robust-crossovers]

New in 1.0.0

✅ Crossovers are now robust to NaNs.

```python title="Remove a bunch of data points and plot the crossovers"
>>> data = vbt.YFData.pull("BTC-USD", start="2022-01", end="2022-03")
>>> fast_sma = vbt.talib("SMA").run(data.close, vbt.Default(5)).real
>>> slow_sma = vbt.talib("SMA").run(data.close, vbt.Default(10)).real
>>> np.random.seed(42)
>>> fast_sma.iloc[np.random.choice(np.arange(len(fast_sma)), 5)] = np.nan
>>> slow_sma.iloc[np.random.choice(np.arange(len(slow_sma)), 5)] = np.nan
>>> crossed_above = fast_sma.vbt.crossed_above(slow_sma, skipna=True)
>>> crossed_below = fast_sma.vbt.crossed_below(slow_sma, skipna=True)

>>> fig = fast_sma.rename("Fast SMA").vbt.lineplot()
>>> slow_sma.rename("Slow SMA").vbt.lineplot(fig=fig)
>>> crossed_above.vbt.signals.plot_as_entries(fast_sma, fig=fig)
>>> crossed_below.vbt.signals.plot_as_exits(fast_sma, fig=fig)
>>> fig.show()
```

Robust fast and slow BTC-USD moving-average crossovers with missing data. [Figure data (JSON)](/assets/figures/features/indicators/resilient-crossovers.64a086686925.json)
