Wiki
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
Installation
pip install git+https://github.com/iniself/backtrader_bokeh
Quickstart
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.addstrategy(MyStrategy) cerebro.adddata(LiveDataStream()) # Note! Data is must Live Data cerebro.addanalyzer(bt.analyzers.Live, force_plot_legend=True, autostart=True) cerebro.run()
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 = cerebro.run(optreturn=False) b = bt.Bokeh(style='bar', scheme=bt.schemes.Tradimo(), force_plot_legend=True) browser = bt.Opt(b, result, address='localhost', port=8889, autostart= True) browser.start()
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 forcerebro 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
style
str
Controls the type of display of the main plot:
Single
only shows the line chart of closing price,bar
orcandle
shows the bar chart including opening price, closing price, highest price and lowest price. * sincev0.0.7
, you can customize the style of each data like following:data = bt.feeds.YahooFinanceCSVData(...) data.plotinfo.plotstyle = 'bar'
bt.Bokeh(style='bar')
- scheme
object
- Plot Scheme. There are currently two schemes: blackly (dark theme) and tradimo (light theme)
bt.Bokeh(scheme=bt.schemes.Blackly())
- 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')
- output_mode
str
- only Normal Mode:
save
: save the file without opening the browsershow
: save the file and opening the browsermemory
: do not save the file, but return to the model
- use_default_tabs
bool
- If
true
, the default web tabs will be added bt.Bokeh(use_default_tabs=False)
- 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])
- show_headline
bool
- Headline show or not show
bt.Bokeh(show_headline=False)
- headline
str
- Change headline content. Default is "Backtrader Backtesting Results"
bt.Bokeh(headline='Your backtrader')
- 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)
- 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 FeedDATA-OBS
: Add the Data feed info to the ObserverIND-OBS
: Add the Indicators info to the Observer- ……
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( subplot=False, plotname='sm5 indicator' ) } BacktraderBokeh(plotconfig=plotconfig)
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 a.pnl.gross.total 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) browser.start()
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' # https://docs.bokeh.org/en/latest/docs/reference/models/formatters.html#bokeh.models.formatters.DatetimeTickFormatter self.hovertool_timeformat = '%F %R' self.number_format = '0,0[.]00[000000]' self.number_format_volume = '0.00 a' # https://docs.bokeh.org/en/latest/docs/reference/models/formatters.html 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()
orbt. bokeh()
as arguments. Or you can pass into the construct function of the scheme classpython 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:
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) .... cerebro.addstrategy(MyStrategy)
Pass in argument:
class MyStrategy(bt.Strategy): def __init__(self): self.sma5 = bt.indicators.SMA(period=15, plotname = "MySMA") .... cerebro.addstrategy(MyStrategy)
class MyStrategy(bt.Strategy): def __init__(self): self.sma5 = bt.indicators.SMA(period=15) self.sma5.plotinfo.plotname = "MySMA" .... cerebro.addstrategy(MyStrategy)
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) self.sma5.plotinfo.plotid='sm5' .... plotconfig = { 'id:sm5': dict( plotname='MySMA' ) } cerebro.addstrategy(MyStrategy) b = bt.Bokeh(plotconfig=plotconfig) cerebro.plot(b)
Example of Object-Wide plotting options * just part of plotinfo,more plotinfo and plotlines pls refer Backtrader :
- plot
bool
- Whether the object has to be plotted
plot=True
- 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
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)
- plotname
str
- Name to use on the chart instead of the class name. As in the example above mysma instead of SimpleMovingAverage
plotname='somename'
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 cerebro.addobserver(MyBroker)
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
- 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)
- 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)
- port
int
- Web port. The default is
80
port,If80
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
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.