Plotting code broken after update packages
-
Hello all,
I had a working program running in my pipenv shell untill I updated all dependencies using
pipenv update
Now I get the following error when my code tries to plot the result of a backtest:
Traceback (most recent call last): File "main.py", line 942, in <module> cerebro.plot(b) # Plot Bokeh object File "C:\Users\Craptop\.virtualenvs\FREBT-VbURPJvn\lib\site-packages\backtrader\cerebro.py", line 991, in plot start=start, end=end, use=use) File "C:\Users\Craptop\.virtualenvs\FREBT-VbURPJvn\lib\site-packages\backtrader_plotting\bokeh\bokeh.py", line 511, in plot df: pd.DataFrame = self.build_strategy_data(obj, start, end) File "C:\Users\Craptop\.virtualenvs\FREBT-VbURPJvn\lib\site-packages\backtrader_plotting\bokeh\bokeh.py", line 473, in build_strategy_data for lineidx in range(obj.size()): AttributeError: 'LinesOperation' object has no attribute 'size'
My entire code still works fine, it's just the plotting (I'm using Bokeh) that seems broken. I have been searching for a solution but can't find one. Can anyone point me in the right direction?
Regards,
Frederik -
@fredyellow I'm also having this problem. Any updates?
-
@richard-klein
The following edits seem to have fixed this for me. Admittedly, probably not very robust, but it seems strategy.getindicators() was returning all line objects--not all are indicators. Used "if not hasattr(obj, 'plotinfo')" to ensure we were working with indicators.backtrader_plotting/bokeh/label_resolver.py
def indicator2fullid(ind: bt.Indicator) -> str: """Returns a string listing allZ involved data feeds. Empty string if there is only a single feed in the mix""" names = [] # EDITED if not hasattr(ind, 'plotinfo'): return for x in ind.datas: #EDITED #elif isinstance(x, bt.AbstractDataBase): if isinstance(x, bt.AbstractDataBase): return datatarget2label([x]) elif isinstance(x, bt.LineSeriesStub): # indicator target is one specific line of a datafeed # AFAIK it's not possible to know which line it is so we only add a generic indicator "[L]" return datatarget2label([x._owner]) + '[L]' elif isinstance(x, bt.Indicator): names.append(indicator2label(x)) return f"({','.join(names)})"
backtrader_plotting/bokeh/bokeh.py
def build_strategy_data(self, strategy: bt.Strategy, start: Optional[datetime.datetime] = None, end: Optional[datetime.datetime] = None, num_back: Optional[int] = None, startidx: int = 0): """startidx: index number to write into the dataframe for the index column""" strategydf = pd.DataFrame() start, end = Bokeh._get_start_end(strategy, start, end) strat_clk: array[float] = strategy.lines.datetime.plotrange(start, end) # if patches occured then we see duplicate entries in the strategie clock -> clean them strat_clk = np.unique(strat_clk) if num_back is None: num_back = len(strat_clk) strat_clk = strat_clk[-num_back:] # we use timezone of first data. we might see duplicated timestamps here dtline = [bt.num2date(x, strategy.datas[0]._tz) for x in strat_clk] # add an index line to use as x-axis (instead of datetime axis) to avoid datetime gaps (e.g. weekends) indices = list(range(startidx, startidx + len(dtline))) strategydf['index'] = indices strategydf['datetime'] = dtline for data in strategy.datas: source_id = FigureEnvelope._source_id(data) df_data = convert_to_pandas(strat_clk, data, start, end, source_id) strategydf = strategydf.join(df_data) df_colors = FigureEnvelope.build_color_lines(df_data, self.p.scheme, col_open=source_id + 'open', col_close=source_id + 'close', col_prefix=sourc$ strategydf = strategydf.join(df_colors) for obj in itertools.chain(strategy.getindicators(), strategy.getobservers()): #for lineidx in range(obj.size()): # EDITED if not hasattr(obj, 'plotinfo'): continue ################ for lineidx in range(obj.size()): line = obj.lines[lineidx] source_id = FigureEnvelope._source_id(line) dataline = line.plotrange(start, end) line_clk = get_clock_line(obj).plotrange(start, end) dataline = convert_by_line_clock(dataline, line_clk, strat_clk) strategydf[source_id] = dataline # apply a proper index (should be identical to 'index' column) if strategydf.shape[0] > 0: strategydf.index = indices return strategydf
-
@richard-klein That fixed it! Well done!
What could be the cause of this? -
Now the Bokeh plotting works again, however it seems all of the parameters given in the object constructor are ignored:
b = Bokeh( filename='FREBT.html', # Bokeh plotting object constructor title='FREBT', style='bar', # Candlesticks plot_mode="single", # single / tabs plot_width=1550, # plot_height_data = 500, # See Scheme.py on Github "backtrader_plotting " for all possible params concerning Bokeh layout plot_height_observer = 120, plot_height_indicator = 100, legend_click = "hide", # Hides line clicked on. "Mute" disables this legend_background_transparency = 0.9, tools = "pan,wheel_zoom,box_zoom,reset,save") cerebro.plot(b)
Any ideas?
-
@fredyellow
This works for me.b = Bokeh(style='bar',scheme=Tradimo(),output_mode='save',filename='test.html') cerebro.plot(b)
Maybe try editing the "Tradimo" scheme to include your parameters or create a new scheme and load your parameters via that custom scheme--scheme=custom().
backtrader_plotting/schemes/Tradimo.py
-
I've heard a lot of my admission essay reviews. You know, writing it was plain sailing. Wanna know the way I did it? Click here now.