Skip to content

Plotting

Any Pandas Series or DataFrame can be plotted via an accessor. There are two main pathways for plotting:

  1. GenericAccessor offering methods tailored specifically for vectorbtpro-typical workflows, and
  2. PXAccessor offering methods parsed from Plotly express.
How to plot a Pandas object
fig = sr_or_df.vbt.plot()  # (1)!

fig = pd.Series(
    np.asarray(y), 
    index=np.asarray(x)
).vbt.scatterplot()  # (2)!
fig = pf.value.vbt.lineplot()  # (3)!
fig = pf.sharpe_ratio.vbt.barplot()  # (4)!
fig = pf.returns.vbt.qqplot()  # (5)!
fig = pf.allocations.vbt.areaplot(line_shape="hv")  # (6)!
fig = pf.returns.vbt.histplot(trace_kwargs=dict(nbinsx=100))  # (7)!

monthly_returns = pf.returns_acc.resample("M").get()
fig = monthly_returns.vbt.boxplot()   # (8)!
fig = monthly_returns.vbt.heatmap()  # (9)!
fig = monthly_returns.vbt.ts_heatmap()  # (10)!

fig = pf.sharpe_ratio.vbt.heatmap(  # (11)!
    x_level="fast_window", 
    y_level="slow_window",
    symmetric=True
)
fig = pf.sharpe_ratio.vbt.heatmap(  # (12)!
    x_level="fast_window", 
    y_level="slow_window",
    slider_level="symbol",
    symmetric=True
)
fig = pf.sharpe_ratio.vbt.volume(  # (13)!
    x_level="timeperiod", 
    y_level="upper_threshold",
    z_level="lower_threshold",
    symmetric=True
)

# ______________________________________________________________

fig = sr_or_df.vbt.px.ecdf()  # (14)!
  1. Create a line plot with visible markers. Each column will become a trace.
  2. Create a scatter plot of a variable x against another variable y. Both must be one-dimensional.
  3. Display equity as a line plot
  4. Compare Sharpe ratio of various symbols or parameter combinations with a bar plot
  5. Create a Q-Q plot to check whether returns are distributed normally. Only one column at a time.
  6. Plot allocations as an area plot
  7. Create a histogram plot of returns with 100 bins (remove nbinsx for auto-binning)
  8. Create a box plot of monthly returns
  9. Create a heatmap of time (y-axis) against monthly returns (x-axis)
  10. Create a heatmap of monthly returns (y-axis) against time (x-axis)
  11. Create a symmetrical heatmap with Sharpe ratios where fast windows are displayed on the x-axis and slow windows are displayed on the y-axis. The Pandas object must be a Series with two index levels.
  12. Create a symmetrical heatmap with Sharpe ratios where fast windows are displayed on the x-axis, slow windows are displayed on the y-axis, and symbols are displayed as a slider. The Pandas object must be a Series with three index levels.
  13. For three to four parameters, create a volume plot instead
  14. Plot an Empirical Cumulative Distribution Plot with Plotly express

+

To plot multiple things over the same figure, get the figure from the first plotting method and pass it to each subsequent one.

Plot equity lines of two portfolios in the same figure
fig = pf1.value.vbt.lineplot()
fig = pf2.value.vbt.lineplot(fig=fig)
fig.show()

The same works to plot multiple columns of a portfolio or other complex object. When plotting a graph with subplots, there's an option to overlay each column automatically.

pf.plot(per_column=True).show()  # (1)!

fig = pf["BTC-USD"].plot(show_legend=False, show_column_label=True)
fig = pf["ETH-USD"].plot(show_legend=False, show_column_label=True, fig=fig)
fig.show()  # (2)!
  1. Plot each column over the same figure
  2. Same as above

+

The default theme can be changed globally in the settings. Available themes are registered under themes in settings.plotting.

Set the dark theme
vbt.settings.set_theme("dark")

+

Trace parameters such as line color and marker shape can be changed with trace_kwargs. Some plotting methods have multiple of such arguments. For allowed parameters, see the Plotly documentation of the respective trace type, for example Scatter for lines.

Change the color of the upper and lower line of a Bollinger Bands indicator
fig = bbands.plot(
    upper_trace_kwargs=dict(line=dict(color="green")),
    lower_trace_kwargs=dict(line=dict(color="red"))
)

+

Layout parameters can be changed by passing them directly to the plot method as variable keyword arguments.

Make the width and height of the plot variable rather than fixed
fig = df.vbt.plot(width=None, height=None)

+

A plot with multiple subplots can be constructed with vbt.make_subplots(), which takes the same arguments as Plotly.

Create two subplots - one per row
fig = vbt.make_subplots(rows=2, cols=1)

+

Most plotting methods accept the argument add_trace_kwargs (see Figure.add_trace), which can be used to specify which subplot to plot the traces over.

Plot the first and second DataFrame over the first and second subplot respectively
df1.vbt.plot(add_trace_kwargs=dict(row=1, col=1), fig=fig)
df2.vbt.plot(add_trace_kwargs=dict(row=2, col=1), fig=fig)

Note

The provided figure fig must be created with vbt.make_subplots().


+

Traces with two different scales but similar time scale can also be plotted next to each other by creating a secondary y-axis.

Plot the first and second DataFrame on the first and second y-axis respectively
fig = vbt.make_subplots(specs=[[{"secondary_y": True}]])
df1.vbt.plot(add_trace_kwargs=dict(secondary_y=False), fig=fig)
df2.vbt.plot(add_trace_kwargs=dict(secondary_y=True), fig=fig)

+

The figure can be changed manually after creation. Below, 0 is the index of the trace in the figure.

Retrospectively change the title and the markers of the scatter plot
fig = df.vbt.scatterplot()
fig.layout.title.text = "Scatter"
fig.data[0].marker.line.width = 4
fig.data[0].marker.line.color = "black"

Note

A plotting method can add multiple traces to the figure.


+

Settings related to plotting can be defined or changed globally in settings.plotting.

Change the background of any figure to black
vbt.settings["plotting"]["layout"]["paper_bgcolor"] = "rgb(0,0,0)"
vbt.settings["plotting"]["layout"]["plot_bgcolor"] = "rgb(0,0,0)"
vbt.settings["plotting"]["layout"]["template"] = "vbt_dark"
Same by registering an own theme
import plotly.io as pio
import plotly.graph_objects as go

pio.templates["my_black"] = go.layout.Template(
    layout_paper_bgcolor="rgb(0,0,0)",
    layout_plot_bgcolor="rgb(0,0,0)",
)
vbt.settings["plotting"]["layout"]["template"] = "vbt_dark+my_black"

+

Usually Plotly displays a homogeneous datetime index including time gaps such as non-business hours and weekends. To skip the gaps, we can use the rangebreaks property.

Skip non-business hours and weekends
fig = df.vbt.plot()
fig.update_xaxes(
    rangebreaks=[
        dict(bounds=['sat', 'mon']),
        dict(bounds=[16, 9.5], pattern='hour'),
        # (1)!
    ]
)
  1. Pass dict(values=df.isnull().all(axis=1).index) to also skip NaN rows, or any other index

Note

Make sure that your data has the correct timezone to apply the above approach.

Skip all gaps automatically
fig = df.vbt.plot()
fig.auto_rangebreaks()  # (1)!
fig.auto_rangebreaks(freq="D")  # (2)!

# ______________________________________________________________

vbt.settings.plotting.auto_rangebreaks = True
vbt.settings.plotting.auto_rangebreaks = dict(freq="D")

# ______________________________________________________________

def pre_show_func(fig):
    fig.auto_rangebreaks(freq="D")

vbt.settings.plotting.pre_show_func = pre_show_func  # (4)!
fig = df.vbt.plot()
fig.show()  # (5)!
  1. Skip gaps based on the recognized frequency of the index
  2. Skip gaps based on any other frequency. For example, use the daily frequency to skip weekends when the recognized frequency are business days.
  3. Skip NaN rows, or provide any other index to skip
  4. Run auto-rangebreaks before showing any figure
  5. This requires the method show() to be called

Note

The above approach works only on figures produced by VBT methods.


+

To display a figure on an interactive HTML page, see Interactive HTML Export.

Save the figure to an HTML file
fig.write_html("fig.html")
Save multiple figures to the same HTML file
with open("fig.html", "a") as f:
    f.write(fig1.to_html(full_html=False))
    f.write(fig2.to_html(full_html=False))
    f.write(fig3.to_html(full_html=False))

+

To display a figure in a separate browser tab, see Renderers.

Make browser the default renderer
import plotly.io as pio
pio.renderers.default = "browser"

+

If a figure takes too much time to display, maybe the amount of data is the problem? If this is the case, plotly-resampler may come to the rescue to resample any (primarily scatter) data on the fly.

Enable plotly-resampler globally
vbt.settings.plotting.use_resampler = True

Another approach is by selecting a date range of particular interest.

Display one year of data
fig = fig.select_range(start="2023", end="2024")