Welcome to the backtrader_bokeh wiki!

Welcome to the backtrader_bokeh wiki!

Everyone who has used backtrader knows that it's plot backend is Matplotlib. The advantage is that Matplotlib is the default backend of backtrader, but the disadvantage is that Matplotlib is relatively weak in interaction and other aspects. How can the strategy data and analysis results be display in the browser? The answer is Backtrader_Bokeh which combined Backtrader and Bokeh。 Check the example , you can see the plot effect via Backtrader_Bokeh. * just present part of demos, all demos pls run *.py in demos

Backtrader_Bokeh inherited from backtrader_plotting and btplotting. In the meantime, corrected their problems and plan to launch a series of new features more suitable for the quantitative framework backtrader. Welcome to GitHub for attention and discussion.

Telegram Channel: Aui_Say


pip install git+


Backtrader_Bokeh is very easy. You only need to import Backtrader_Bokeh in your Python file as follows. That's all , then you can to get Backtrader_Bokeh brings many benefits, include: 1. Get a powerful backend via Bokeh 2. Fix many bugs of Backtrader through Backtrader_Bokeh's patch, instead of modifying the source code of backtrader 3. A set of easier and clearer Api

from backtrader_bokeh import bt
# import backtraer as bt  # no need anymore, don't do this

There are many ways to use Backtrader_Bokeh. This wiki only introduces three kinds, and you can refer to Demo for more information:

Use Backtrader_Bokeh as analyzers(only Live Mode)

  • Default 80 port:

    from backtrader_bokeh import bt
    cerebro = bt.Cerebro()
    cerebro.adddata(LiveDataStream()) # Note! Data is must Live Data
    cerebro.addanalyzer(bt.analyzers.Live, force_plot_legend=True, autostart=True)
  • If 80 port is not available, you can use other port:

    cerebro.addanalyzer(bt.analyzers.Live, address="localhost", port=8889, force_plot_legend=True, autostart=True)

Use Backtrader_Bokeh as plot object

  • Normal Mode is the backtest which has only a set of strategy's argument:

    from backtrader_bokeh import bt
    plot = bt.Bokeh(style = 'bar', scheme=bt.schemes.Blackly(), force_plot_legend=True) # bt.schemes.Blackly is style of scheme
    cerebro.plot(plot, iplot=False) # if run in Jupter, need to pass 'iplot' argument in there
  • Optstrategy Mode is the backtest which has multi sets of strategy's argument:

    from backtrader_bokeh import bt
    cerebro.optstrategy(MyStrategy, buydate=range(40, 180, 30))   
    result =
    b = bt.Bokeh(style='bar', scheme=bt.schemes.Tradimo(), force_plot_legend=True)
    browser = bt.Opt(b, result, address='localhost', port=8889, autostart= True)

List of Options

First, introduce some functions that need to pass in arguments:

  • Live Mode python cerebro.addanalyzer(...)
  • Normal Mode python bt.Bokeh(...)
  • Optstrategy Mode python bt.Bokeh(...) bt.Opt(...)

This wiki will introduce Backtrader_Bokeh parameters from the following aspects:

  • Type of parameters
  • Definition of Parameter
  • Example. * In addition to special statements, the parameters suitable for bt. Bokeh() are also suitable for cerebro addanalyzer()

Pre knowledge

Back to Backtrader's plot options: * Options affecting the plotting behavior of the entire object * Options affecting the plotting behavior of individual lines * Options affecting the SYSTEM wide plotting options

Backtrader_Bokeh also configures plot's options like above. Backtrader_Bokeh's option inherit most of the Backtrader's option. Besides this, Backtrader_Bokeh has also added a large of options according to Bokeh's needs. In short,Backtrader_Bokeh Options = Backtrader Options + Bokeh Options

System and Scheme Options

  1. style

    • str
    • Controls the type of display of the main plot: Single only shows the line chart of closing price, bar or candle shows the bar chart including opening price, closing price, highest price and lowest price. * since v0.0.7, you can customize the style of each data like following:

      data = bt.feeds.YahooFinanceCSVData(...)
      data.plotinfo.plotstyle = 'bar'      
    • bt.Bokeh(style='bar')
  2. scheme
    • object
    • Plot Scheme. There are currently two schemes: blackly (dark theme) and tradimo (light theme)
    • bt.Bokeh(scheme=bt.schemes.Blackly())
  3. filename
    • str
    • In Normal Mode, the specified file name is used instead of the Backtrader_Bokeh default temporary file name. * This option is only applicable to static web pages, so it is invalid in "Live Mode" and "Optstrategy Mode"
    • bt.Bokeh(filename='yourfile.html')
  4. output_mode
    • str
    • only Normal Mode:
      save: save the file without opening the browser
      show: save the file and opening the browser
      memory: do not save the file, but return to the model
  5. use_default_tabs
    • bool
    • If true, the default web tabs will be added
    • bt.Bokeh(use_default_tabs=False)
  6. tabs
    • list
    • Tabs you want to add in the web page. only be effectual when use_default_tabs=False
    • python bt.Bokeh(tabs=[bt.tabs.AnalyzerTab])
  7. show_headline
    • bool
    • Headline show or not show
    • bt.Bokeh(show_headline=False)
  8. headline
    • str
    • Change headline content. Default is "Backtrader Backtesting Results"
    • bt.Bokeh(headline='Your backtrader')
  9. force_plot_legend
    • bool
    • If True, all legends will be forced to plot. * Set to true when legends dont be ploted
    • bt.Bokeh(force_plot_legend=True)
  10. hover_tooltip_config
    • str
      • Decide what is included in the tooltip. When this parameter is not passed in, tooltip is default (data, indicators, observer) . For example, data feed will display time, opening price, closing price, highest price, lowest price and trading volume. But if you want to display additional info, you need this option
      • IND-DATA: Add the indicators info to the tooltip in the figure of main Data Feed
      • DATA-OBS: Add the Data feed info to the Observer
      • IND-OBS: Add the Indicators info to the Observer
      • ……
  11. plotconfig

    • dict
    • Object-Wide plotting options (detail on Object-Wide plotting options)。Backtrader_Bokeh's plotconfig is equivalent to Plotting - Backtrader
    • plotconfig = {
        'id: sm5': dict(
            plotname='sm5 indicator'
  12. usercolumns

    • dict
    • Custom columns can be added to the results list to display special attributes of the results. To use it, you need to pass a dictionary, where the key is the label of the column, and the value is an callable value, which needs an optimization result to calculate the attribute. This option is only applicable to Optstrategy Mode
    • def get_pnl_gross(strats):
        a = strats[0].analyzers.tradeanalyzer.get_analysis()
        return if 'pnl' in a else 0
      b = bt.Bokeh(style='bar', scheme=bt.schemes.Tradimo())
      browser = bt.Opt(b, result, usercolumns=dict(pnl=get_pnl_gross), sortcolumn='pnl', sortasc=False)
  13. Other Scheme Options

    •   def _set_params(self):
            self.multiple_tabs = False
            self.show_headline = True
            self.headline = ''
            self.hover_tooltip_config = ''
            self.barup_wick = self.barup
            self.bardown_wick = self.bardown
            self.barup_outline = self.barup
            self.bardown_outline = self.bardown
            self.crosshair_line_color = '#999999'
            self.legend_background_color = '#3C3F41'
            self.legend_text_color = 'lightgrey'
            self.legend_location = 'top_left'
            self.legend_orientation = 'vertical'
            self.loc = 'lightgray'
            self.background_fill = '#222222'
            self.body_background_color = 'white'
            self.border_fill = '#3C3F41'
            self.legend_click = 'hide'  # or 'mute'
            self.axis_line_color = 'darkgrey'
            self.tick_line_color = self.axis_line_color
            self.grid_line_color = '#444444'
            self.axis_text_color = 'lightgrey'
            self.plot_title_text_color = 'darkgrey'
            self.axis_label_text_color = 'darkgrey'
            self.tag_pre_background_color = 'lightgrey'
            self.tag_pre_text_color = 'black'
            self.xaxis_pos = 'all'  # 'all' or 'bottom'
            self.table_color_even = '#404040'
            self.table_color_odd = '#333333'
            self.table_header_color = '#7a7a7a'
            # Plot a title above the plot figure
            self.plot_title = True
            # Number of columns on the analyzer tab
            self.analyzer_tab_num_cols = 1
            # Number of columns on the metadata tab
            self.metadata_tab_num_cols = 3
            # Sizing mode for plot figures
            self.plot_sizing_mode = 'scale_width'
            # Aspect ratios for different figure types
            self.data_aspectratio = 2.5
            self.vol_aspectratio = 5.0
            self.obs_aspectratio = 5.0
            self.ind_aspectratio = 5.0
            # output backend mode ("canvas", "svg", "webgl")
            self.output_backend = 'canvas'
            self.toolbar_location = 'right'
            self.tooltip_background_color = '#4C4F51'
            self.tooltip_text_label_color = '#848EFF'
            self.tooltip_text_value_color = '#aaaaaa'
            self.tab_active_background_color = '#333333'
            self.tab_active_color = '#4C4F51'
            self.text_color = 'lightgrey'
            self.hovertool_timeformat = '%F %R'
            self.number_format = '0,0[.]00[000000]'
            self.number_format_volume = '0.00 a'
            self.axis_tickformat_days = '%d %b %R'
            self.axis_tickformat_hourmin = '%H:%M:%S'
            self.axis_tickformat_hours = '%d %b %R'
            self.axis_tickformat_minsec = '%H:%M:%S'
            self.axis_tickformat_minutes = '%H:%M'
            self.axis_tickformat_months = '%d/%m/%y'
            self.axis_tickformat_seconds = '%H:%M:%S'
            self.axis_tickformat_years = '%Y %b'
            # used to add padding on the y-axis for all data except volume
            self.y_range_padding = 0.5
            # position of y axis for volume
            self.vol_axis_location = 'right'
    • Scheme Options can be directly pass into cerebro Addanalyzer() or bt. bokeh() as arguments. Or you can pass into the construct function of the scheme class
      • python bt.Bokeh(overtool_timeformat='%F %R:%S')
      • python bt.Bokeh(scheme=bt.schemes.Blackly(overtool_timeformat='%F %R:%S'))

Object-Wide plotting options

It has been said in pre knowledge, Object-Wide plotting options are the settings of plotinfo and plotlines for each object (such as an indicator). There are three ways to configure this option in backtrader:

  1. Inheriting:

    class MY_SMA(bt.indicators.SMA):
      params = (('barplot', True), ('bardist', 0.02))
      plotinfo = dict(
        plotname = "MySMA"
      plotlines = dict(
    class MyStrategy(bt.Strategy):
     def __init__(self):
       self.sma5 = MY_SMA(period=15)
  2. Pass in argument:

    class MyStrategy(bt.Strategy):
     def __init__(self):
        self.sma5 = bt.indicators.SMA(period=15, plotname = "MySMA")
    class MyStrategy(bt.Strategy):
     def __init__(self):
        self.sma5 = bt.indicators.SMA(period=15)
        self.sma5.plotinfo.plotname = "MySMA"
  3. Backtrader_Bokeh adds a way to handle all Object-Wide plotting options in one place:

    class MyStrategy(bt.Strategy):
     def __init__(self):
        self.sma5 = bt.indicators.SMA(period=15)
    plotconfig = {
       'id:sm5': dict(
    b = bt.Bokeh(plotconfig=plotconfig)

    Example of Object-Wide plotting options * just part of plotinfo,more plotinfo and plotlines pls refer Backtrader :

  4. plot
    • bool
    • Whether the object has to be plotted
    • plot=True
  5. subplot
    • bool
    • Whether to plot along the data or in an independent subchart. Moving Averages are an example of plotting over the data. Stochastic and RSI are examples of things plotted in a subchart on a different scale
    • subplot=True
  6. plotmaster

    • object
    • An Indicator/Observer has a master which is the data on which is working. In some cases plotting it with a different master may be wished needed

    • class MyStrategy(bt.Strategy): def __init__(self): self.sma5 = bt.indicators.SMA(period=5, subplot=True) self.sma10 = bt.indicators.SMA(period=10, plotmaster=sma5)
  7. plotname
    • str
    • Name to use on the chart instead of the class name. As in the example above mysma instead of SimpleMovingAverage
    • plotname='somename'
  8. plotorder

    • int
    • The smaller of number, the plot is more upper on the page. Default all 0
      * The following code will let the Observer be ploted above Data Feed (stock price, trading volume and other main charts)
    • class MyBroker(bt.observers.Broker):
       def __init__(self):
           self.plotinfo.plotorder = 5
  9. The other Object-Wide plotting options

    • plotinfo = dict(plot=True, subplot=True, plotname='', plotorder=0, plotlinelabels=False, # whether to plot the names of the individudal lines along the data in the legend on the chart when subplot=False plotlinevalues=True, # controls whether the legend for the lines in indicators and observers has the last plotted value. Can be controlled on a per-line basis with _plotvalue for each line plotvaluetags=True, # controls whether a value tag with the last value is plotted on the right hand side of the line. Can be controlled on a per-line basis with _plotvaluetag for each line plotymargin=0.0, # margin to add to the top and bottom of individual subcharts on the graph. It is a percentage but 1 based. For example: 0.05 -> 5% plothlines=[a,b,...], # an iterable containing values (within the scale) at which horizontal lines have to be plotted plotyticks=[], # an iterable containing values (within the scale) at which value ticks have to specifically be placed on the scale plotyhlines=[a,b,...], # an iterable containing values (within the scale) at which horizontal lines have to be plotted plotforce=False, # sometimes and thus the complex process of matching data feeds to indicators and bla, bla, bla … a custom indicator may fail to plot. This is a last resort mechanism to try to enforce plotting plotmaster=None, # an Indicator/Observer has a master which is the data on which is working. In some cases plotting it with a different master may be wished needed plotylimited=True, # currently only applies to data feeds. If True (default), other lines on the data plot don’t change the scale. Example: Bollinger Bands (top and bottom) may be far away from the actual absolute minimum/maximum of the data feed. With \plotlimited=True, those bands remain out of the chart, because the data controls the scaling. If set toFalse`, the bands affects the y-scale and become visible on the chart )
    • Please try different options by yourself

Browser option

  1. autostart
    • bool
    • If true, open browser automatically. * Suitable for Optstrategy Mode and Live Mode, because browser can't be open automatically under the two Modes
    • bt.Opt(autostrart=True) (Optstrategy Mode), cerebro.addanalyzer(autostrart=True) (Live Mode)
  2. address
    • str
    • Hosts address. If run Backtrader_Bokeh locally, the configuration is "localhost" :
    • bt.Opt(address='localhost', port=8889 (Optstrategy Mode), cerebro.addanalyzer(address='localhost', port=9889) (Live Mode)
  3. port
    • int
    • Web port. The default is 80 port,If 80 port is not available or want to share the plotting to public, you can change the default port:
    • bt.Opt(address='localhost', port=8889 (Optstrategy Mode), cerebro.addanalyzer(address='localhost', port=9889) (Live Mode)


Datadomains are basically used to group entities that belong together to plot them together on one page. A datadomain is a single string. All entities having an identical string belong to the same datadomain.

Per default each data feed creates one datadomain which is derived from its _name attribute. All entities that are based on this data (like e.g. indiators) will inherit the datadomain of that data. So by default each data and its corresponding indicators and others entities do form a separate datadomain.

Datadomain values can be manually overridden though to change the automtically created grouping. This is done by passing the parameter datadomain to the initializer of an entity.