# Interactive charts (/features/analytics/interactive-charts)

How do you see what a strategy actually did? VBT plots backtests, prices, indicators, signals, and
parameter results as interactive Plotly charts in Python: zoom into a single trade, hover for exact
values, and combine any of them in one figure.

```python title="Plot a backtest with orders, trade P&L, a custom RSI panel, and drawdowns"
>>> data = vbt.YFData.pull("BTC-USD", start="2023-01-01", end="2024-01-01")
>>> rsi = data.run("rsi", window=14).rsi
>>> pf = vbt.PF.from_signals(data, rsi.vbt.crossed_below(30), rsi.vbt.crossed_above(70))

>>> def plot_rsi(pf, rsi, add_trace_kwargs=None, fig=None):  # (1)
...     rsi.rename("RSI").vbt.plot(add_trace_kwargs=add_trace_kwargs, fig=fig)

>>> pf.plot(
...     subplots=[
...         "orders",
...         "trade_pnl",
...         ("rsi", dict(title="RSI", plot_func=plot_rsi, rsi=rsi)),
...         "drawdowns",
...     ],
...     settings=dict(bm_returns=False),
... ).show()
```

1.  A custom panel is a function that draws into the subplot it is given.

Bitcoin RSI strategy chart for 2023 with buy and sell orders, trade profit and loss, the RSI indicator, and drawdowns. [Figure data (JSON)](/assets/figures/features/analytics/interactive-charts-portfolio.82d6e04cdee9.json)

## Portfolio and trade charts \[#portfolio-and-trade-charts]

`pf.plot()` draws a dashboard from named subplots. Built-in subplots include `orders`, `trades`,
`trade_pnl`, `trade_signals`, `cumulative_returns`, `drawdowns`, `underwater`, `gross_exposure`,
`allocations`, `cash`, `assets`, and `value`, and each can be configured through `subplot_settings`.
Every subplot also exists as its own method, such as `pf.plot_cumulative_returns()`,
`pf.plot_underwater()`, or `pf.plot_allocations()` for multi-asset weights over time.

Records plot themselves too: `pf.trades.plot()` shows each trade from entry to exit with its profit,
and `pf.drawdowns.plot(top_n=5)` shades the largest drawdowns. For returns, a calendar heatmap of
monthly returns is one line: `pf.returns_acc.resample("M").ts_heatmap()`.

## Price, indicator, and signal charts \[#price-indicator-and-signal-charts]

Data objects plot as candlesticks or OHLC bars with volume. Indicators and signals are added to the
same figure by passing `fig`:

```python title="Overlay moving averages and crossover signals on candlesticks"
>>> sub = data.loc["2023-04-01":"2023-08-31"]
>>> fast = sub.close.rolling(10).mean()
>>> slow = sub.close.rolling(30).mean()
>>> entries = fast.vbt.crossed_above(slow)
>>> exits = fast.vbt.crossed_below(slow)

>>> fig = sub.plot(plot_volume=False)
>>> fast.rename("SMA 10").vbt.plot(fig=fig)
>>> slow.rename("SMA 30").vbt.plot(fig=fig)
>>> entries.vbt.signals.plot_as_entries(fast, fig=fig)  # (1)
>>> exits.vbt.signals.plot_as_exits(fast, fig=fig)
>>> fig.show()
```

1.  Draw each entry as a marker on the fast moving average.

Bitcoin candlestick chart from April to August 2023 with 10 and 30 day moving averages and crossover entry and exit markers. [Figure data (JSON)](/assets/figures/features/analytics/interactive-charts-candlestick.5b9b028ce41e.json)

Indicators built with VBT have a `plot` method with sensible defaults, and TA-Lib indicators plot
according to their output types, for example as lines or histograms.

## Heatmaps and other generic plots \[#heatmaps-and-other-generic-plots]

Any Series or DataFrame gets plotting methods through its accessor: line, scatter, bar, histogram,
box, heatmap, and 3D volume plots. A parameter grid with three parameters becomes a heatmap with a
slider for the third:

```python title="Explore three RSI strategy parameters with a heatmap and a slider"
>>> btc = vbt.YFData.pull("BTC-USD", start="2020-01-01", end="2025-01-01")
>>> rsi = btc.run("rsi", window=14).rsi

>>> @vbt.parameterized(merge_func="concat")  # (1)
... def sharpe(lower, upper, sl_stop):
...     pf = vbt.PF.from_signals(
...         btc,
...         rsi.vbt.crossed_below(lower),
...         rsi.vbt.crossed_above(upper),
...         sl_stop=sl_stop,
...     )
...     return pf.sharpe_ratio

>>> sharpes = sharpe(
...     vbt.Param([20, 25, 30, 35, 40]),
...     vbt.Param([60, 65, 70, 75, 80]),
...     vbt.Param([0.05, 0.1, 0.2]),
... )
>>> sharpes.vbt.heatmap(x_level="lower", y_level="upper", slider_level="sl_stop").show()
```

1.  Run the function for every combination of the parameters and concatenate the 75 results.

Heatmap of Sharpe ratios for RSI entry and exit thresholds with a slider for the stop loss level. [Figure data (JSON)](/assets/figures/features/analytics/interactive-charts-heatmap-slider.53955f7134c5.json)

When Plotly Express has a chart you need, call it from the accessor, for example
`df.vbt.px.parallel_coordinates()`. Cross-validation splits have their own plots that show the
training and test periods of every split.

## Customization \[#customization]

Every plotting method takes `trace_kwargs` for the trace, `add_trace_kwargs` to place it in a
subplot, and layout keywords for the figure. For example,
`slow.vbt.plot(trace_kwargs=dict(line=dict(color="orange", dash="dash")), fig=fig)` draws one dashed
orange line. `vbt.make_subplots` creates figures with several panels, and since the result is a
regular Plotly figure, everything Plotly offers still works afterwards.

Themes and defaults live in `vbt.settings.plotting`, for example `vbt.settings.set_theme("dark")`
for dark charts. For markets that close overnight or on weekends, `fig.auto_rangebreaks(freq="1D")`
adds Plotly range breaks for the missing time, so candles sit next to each other instead of leaving
gaps.

## Charts that update with new data \[#charts-that-update-with-new-data]

You can update the data in existing Plotly traces while keeping the figure's layout and styling.
Connect those updates to a `DataUpdater` to refresh a candlestick chart as new bars arrive, or
update lines for prices and indicators in a notebook widget. This is useful for watching the latest
part of a series without creating a new chart each time.

The [Data pipelines](/features/data/financial-data-pipelines/) page covers scheduled data updates,
and [Live simulation](/features/backtesting/live-simulation/) covers continuing a portfolio on new
bars. You can also save successive chart images as frames in a GIF with `imageio` to replay how a
chart developed.

## Large data and output \[#large-data-and-output]

A minute chart over several years has millions of points, which a browser draws slowly. With
`plotly-resampler` installed, set `vbt.settings.plotting["use_resampler"] = True` and charts load a
downsampled view that refines as you zoom. Alternatively, select a shorter range before plotting, or
resample the portfolio first: `pf.resample("1D")` returns a daily portfolio with the same total
return, and its charts draw a point per day.

## Share interactive charts and export images \[#share-interactive-charts-and-export-images]

Figures show interactively in notebooks and browsers with `fig.show()`, as static images with
`fig.show_png()` or `fig.show_svg()`, and save to files with `fig.write_image()` or
`fig.write_html()`. Static images are rendered by Plotly's `kaleido` package, so install it first,
and update it if an export hangs. The renderer can be set per figure or globally.

An HTML export keeps zoom, hover labels, and legend controls, so someone else can explore the
results in a browser without installing Python or VBT. Plotly includes the chart data and its
JavaScript library in the file by default. See
[Plotly's HTML export guide](https://plotly.com/python/interactive-html-export/) for saving a figure
or combining several charts in one page. Use PNG or SVG when you need a fixed image for a report or
presentation.


## Related pages

*   [Technical indicators](/features/indicators/technical-indicators/): Run TA-Lib, pandas-ta, ta, SMC, WorldQuant Alphas, and native indicators at scale
*   [Trading signals](/features/indicators/trading-signals/): Generate, combine, clean, and analyze entry and exit signals before backtesting
*   [Backtesting engine](/features/backtesting/backtesting-engine/): Simulate orders, signals, and callbacks across many assets and parameters at once
*   [Multidimensional research](/features/tooling/multidimensional-research/): Run assets, parameters, and strategies as labeled columns, then index and stack them