Plotting Custom Graphs in Vectorbt

Plotting Custom Graphs in Vectorbt

VectorBt (Vbt) is great for doing speedy backtesting. But us mere mortals find it difficult to glean much from multi-dimensional arrays. So it might be worth throwing out a few graphs.

VectorBt uses Plotly as it's plotting framework. So if you've got experience with how Plotly figures work and how graphs are constructed, you'll have an easier time when it comes to plotting in Vbt.

If you're looking for hyper-parameter optimisation and 2d or 3d heatmaps, you'll want to try our other articles. In this article we're largely going to restrict ourselves to various bar charts, scatter charts, etc.

Defining our Strategy

I've drawn up a simple strategy here that we can draw data from in our plotting. It just takes the past three days of 1-minute candles, and tests a simple strategy where we enter on an rsi < 30 and exit on rsi > 60.

 1>>> import vectorbt as vbt
 2>>> import datetime
 3>>> 
 4>>> now = datetime.datetime.now()
 5>>> before = now - datetime.timedelta(days=3)
 6>>> btc_price = vbt.YFData.download(
 7...     "BTC-USD", 
 8...     missing_index='drop',
 9...     interval='1m',
10...     start=before.timestamp(),
11...     end=now.timestamp()
12...     ).get('Close')
13
14>>> 
15>>> rsi = vbt.RSI.run(btc_price, window=21)
16>>>
17>>> entries = rsi.rsi_crossed_above(60)
18>>> exits = rsi.rsi_crossed_below(30)
19>>> 
20>>> pf = vbt.Portfolio.from_signals(btc_price, entries, exits)

Built-in Portfolio Plots

There's quite the selection of built-in plots that we can use, the most basic being

1>>> pf.plot.show()

A basic vectorbt dashboard

That's probably good enough for an initial look at a strategy. But we can dig deeper. There's also

1>>> pf.plot_positions().show()

A plot showing our profit and loss on each trade

Which shows us all the trades that were taken and draws convenient rectangles showing the delta of the trade to give a quick overview of how well it's performing.

We also have

1>>> pf.plot_drawdowns().show()

A plot of the drawdowns during our backtest

Which shows the draw-downs in various trades and helps us visualize our win rate and risk.

If you're willing to dig through the code, you can find more of these built-in plotting functions here.

Mixing and Matching Built-in Plots

If you'd like to plot more than one of the built-in plots to create a dashboard of sorts, you can edit the subplots parameter inside pf.plot.

1pf.plot(subplots = [
2... 'drawdowns',
3... 'trades']).show()

A composite dashboard of drawdowns and trades

There's a full list of names for the built-in charts on the docs.

Creating Custom Charts

At this point we can start thinking about creating custom charts. To start with we'll keep things simple and just stick to one chart. The easiest way to get started with this is to pick a plotting function from the docs and have at it.

We've got all the normal chart types that you get in Plotly. scatter charts, bar graphs, box plots, etc. Anything that can be done in Plotly can be done in Vbt if you get the right syntax.

Let's plot our RSI values

 1fig = pf.plot(subplots = [ 
 2...     ('RSI', dict(
 3...         title = 'RSI',
 4...         yaxis_kwargs=dict(title='RSI'),
 5...         check_is_not_grouped=True,
 6...         ))
 7...     ],
 8...     make_subplots_kwargs = dict(rows=5,cols=1,shared_xaxes='all')
 9... )
10>>> scatter_rsi = vbt.plotting.Scatter(
11...         data = rsi.rsi,
12...         x_labels = rsi.rsi.index,
13...         trace_names = ["RSI"],
14...         add_trace_kwargs=dict(row=2, col=1),
15...         fig=fig
16...             )
17>>> fig.show()

A custom plotly chart showing the rsi value over time

Note that we selected the second row to put our RSI in. That's because I'm going to insert another chart above it in a second. It might look a little wonky for now.

You'll notice that there are various options like yaxis_kwargs. This allows us to pass extra parameters that we'd normally specify in the equivalent plotly functions. You can experiment with this to change attributes like line color, thickness, etc.

In order to add another graph above our RSI, let us use the built-in graph orders.

 1fig = pf.plot(subplots = [ 
 2...     'orders',
 3...     ('RSI', dict(
 4...         title = 'RSI',
 5...         yaxis_kwargs=dict(title='RSI'),
 6...         check_is_not_grouped=True,
 7...         ))
 8...     ],
 9...     make_subplots_kwargs = dict(rows=5,cols=1,shared_xaxes='all')
10... )
11>>> scatter_rsi = vbt.plotting.Scatter(
12...         data = rsi.rsi,
13...         x_labels = rsi.rsi.index,
14...         trace_names = ["RSI"],
15...         add_trace_kwargs=dict(row=2, col=1),
16...         fig=fig
17...             )
18>>> fig.show()

A composite dashboard of our trades as well as the RSI over time

Note the order in which they appear in subplots, as it's the same order that they appear on the dashboard, and you'll have to set the row and column accordingly.

Much like any other Plotly figure, we can also add lines and other shapes to our graphs.

To give a simple example, let's add some horizontal lines for our RSI entry and exit points

 1>>> fig = fig.add_hline(  y=70,
 2...                 line_color="#858480",
 3...                 row = 2,
 4...                 col = 1,
 5...                 line_width = 5
 6... )
 7>>> fig = fig.add_hline(  y=30,
 8...                 line_color="#858480",
 9...                 row = 2,
10...                 col = 1,
11...                 line_width = 5
12... )
13>>> fig.show()

A composite dashboard showing the trades and the RSI over time with custom lines drawn on the graph

And there we have our final product! Not the prettiest thing in the world, but I leave it in your capable hands to edit the color-scheme as you require.

Video Tutorial

If you'd prefer a video tutorial, you can check out this free course on my youtube channel here: