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=source_id) strategydf = strategydf.join(df_colors) for obj in itertools.chain(strategy.getindicators(), strategy.getobservers()): 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
def plot_volume(self, obj, alpha, extra_axis=False): src_prefix = sanitize_source_name(obj._name) df = convert_to_pandas(obj, self._start, self._end) if len(nanfilt(df.volume)) == 0: return colorup = convert_color(self._scheme.volup) colordown = convert_color(self._scheme.voldown) is_up = df.close > df.open colors = [colorup if x else colordown for x in is_up] self._add_to_cds(df.volume, src_prefix + 'volume') self._add_to_cds(colors, src_prefix + 'volume_colors') kwargs = { 'fill_alpha': alpha, 'line_alpha': alpha, 'name': 'Volume', 'legend': 'Volume' } ax_formatter = NumeralTickFormatter(format='0.000 a') if extra_axis: self.figure.extra_y_ranges = {'axvol': DataRange1d()} adapt_yranges(self.figure.extra_y_ranges['axvol'], df.volume) self.figure.extra_y_ranges['axvol'].end /= self._scheme.volscaling ax_color = colorup ax = LinearAxis(y_range_name="axvol", formatter=ax_formatter, axis_label_text_color=ax_color, axis_line_color=ax_color, major_label_text_color=ax_color, major_tick_line_color=ax_color, minor_tick_line_color=ax_color) self.figure.add_layout(ax, 'left') kwargs['y_range_name'] = "axvol" else: self.figure.yaxis.formatter = ax_formatter adapt_yranges(self.figure.y_range, df.volume) self.figure.y_range.end /= self._scheme.volscaling self.figure.vbar('datetime', get_bar_length_ms(obj) * 0.7, src_prefix + 'volume', 0, source=self._cds, fill_color=src_prefix + 'volume_colors', line_color="black", **kwargs) self._hoverc.add_hovertip("Volume", f"@{src_prefix}volume{{(0.00 a)}}")
def plot_volume(self, data: bt.feeds.DataBase, strat_clk: array, alpha, extra_axis=False): source_id = Figure._source_id(data) df = convert_to_pandas(strat_clk, data, self._start, self._end) if len(nanfilt(df.volume)) == 0: return colorup = convert_color(self._scheme.volup) colordown = convert_color(self._scheme.voldown) is_up = df.close > df.open colors = [colorup if x else colordown for x in is_up] self._add_to_cds(df.volume, f'{source_id}volume') self._add_to_cds(colors, f'{source_id}volume_colors') kwargs = {'fill_alpha': alpha, 'line_alpha': alpha, 'name': 'Volume', 'legend': 'Volume'} ax_formatter = NumeralTickFormatter(format=self._scheme.number_format) if extra_axis: self.figure.extra_y_ranges = {'axvol': DataRange1d()} adapt_yranges(self.figure.extra_y_ranges['axvol'], df.volume) self.figure.extra_y_ranges['axvol'].end /= self._scheme.volscaling ax_color = colorup ax = LinearAxis(y_range_name="axvol", formatter=ax_formatter, axis_label_text_color=ax_color, axis_line_color=ax_color, major_label_text_color=ax_color, major_tick_line_color=ax_color, minor_tick_line_color=ax_color) self.figure.add_layout(ax, 'left') kwargs['y_range_name'] = "axvol" else: self.figure.yaxis.formatter = ax_formatter adapt_yranges(self.figure.y_range, df.volume) self.figure.y_range.end /= self._scheme.volscaling self.figure.vbar('index', get_bar_width(), f'{source_id}volume', 0, source=self._cds, fill_color=f'{source_id}volume_colors', line_color="black", **kwargs) hover_target = None if self._scheme.merge_data_hovers else self.figure self._hoverc.add_hovertip("Volume", f"@{source_id}volume{{({self._scheme.number_format})}}", hover_target)
def plot_data(self, data: bt.AbstractDataBase, strat_clk: array = None): source_id = Figure._source_id(data) title = sanitize_source_name(label_resolver.datatarget2label([data])) # append to title self._figure_append_title(title) df = convert_to_pandas(strat_clk, data, self._start, self._end) # configure colors colorup = convert_color(self._scheme.barup) colordown = convert_color(self._scheme.bardown) colorup_wick = convert_color(self._scheme.barup_wick) colordown_wick = convert_color(self._scheme.bardown_wick) colorup_outline = convert_color(self._scheme.barup_outline) colordown_outline = convert_color(self._scheme.bardown_outline) is_up = df.close > df.open self._add_to_cds(df.open, source_id + 'open') self._add_to_cds(df.high, source_id + 'high') self._add_to_cds(df.low, source_id + 'low') self._add_to_cds(df.close, source_id + 'close') self._add_to_cds([colorup if x else colordown for x in is_up], source_id + 'colors_bars') self._add_to_cds( [colorup_wick if x else colordown_wick for x in is_up], source_id + 'colors_wicks') self._add_to_cds( [colorup_outline if x else colordown_outline for x in is_up], source_id + 'colors_outline') if self._scheme.style == 'line': if data.plotinfo.plotmaster is None: color = convert_color(self._scheme.loc) else: self._nextcolor(data.plotinfo.plotmaster) color = convert_color(self._color(data.plotinfo.plotmaster)) renderer = self.figure.line('index', source_id + 'close', source=self._cds, line_color=color, legend=title) self._set_single_hover_renderer(renderer) self._hoverc.add_hovertip("Close", f"@{source_id}close", data) elif self._scheme.style == 'bar': self.figure.segment('index', source_id + 'high', 'index', source_id + 'low', source=self._cds, color=source_id + 'colors_wicks', legend_label=title) renderer = self.figure.vbar( 'index', get_bar_width(), source_id + 'open', source_id + 'close', source=self._cds, fill_color=source_id + 'colors_bars', line_color=source_id + 'colors_outline', legend_label=title, ) self._set_single_hover_renderer(renderer) self._hoverc.add_hovertip( "Open", f"@{source_id}open{{{self._scheme.number_format}}}", data) self._hoverc.add_hovertip( "High", f"@{source_id}high{{{self._scheme.number_format}}}", data) self._hoverc.add_hovertip( "Low", f"@{source_id}low{{{self._scheme.number_format}}}", data) self._hoverc.add_hovertip( "Close", f"@{source_id}close{{{self._scheme.number_format}}}", data) else: raise Exception(f"Unsupported style '{self._scheme.style}'") adapt_yranges(self.figure.y_range, df.low, df.high) # check if we have to plot volume overlay if self._scheme.volume and self._scheme.voloverlay: self.plot_volume(data, strat_clk, self._scheme.voltrans, True)
def plot_data(self, data: bt.feeds.DataBase, master): source_id = Figure._source_id(data) title = sanitize_source_name(data._name) if len(data._env.strats) > 1: title += f" ({get_strategy_label(self._strategy)})" # append to title self._figure_append_title(title) df = convert_to_pandas(data, self._start, self._end) # configure colors colorup = convert_color(self._scheme.barup) colordown = convert_color(self._scheme.bardown) colorup_wick = convert_color(self._scheme.barup_wick) colordown_wick = convert_color(self._scheme.bardown_wick) colorup_outline = convert_color(self._scheme.barup_outline) colordown_outline = convert_color(self._scheme.bardown_outline) is_up = df.close > df.open self._add_to_cds(df.open, source_id + 'open') self._add_to_cds(df.high, source_id + 'high') self._add_to_cds(df.low, source_id + 'low') self._add_to_cds(df.close, source_id + 'close') self._add_to_cds([colorup if x else colordown for x in is_up], source_id + 'colors_bars') self._add_to_cds( [colorup_wick if x else colordown_wick for x in is_up], source_id + 'colors_wicks') self._add_to_cds( [colorup_outline if x else colordown_outline for x in is_up], source_id + 'colors_outline') if self._scheme.style == 'line': if data.plotinfo.plotmaster is None: color = convert_color(self._scheme.loc) else: self._nextcolor(data.plotinfo.plotmaster) color = convert_color(self._color(data.plotinfo.plotmaster)) self.figure.line('datetime', source_id + 'close', source=self._cds, line_color=color, legend=data._name) elif self._scheme.style == 'bar': self.figure.segment('datetime', source_id + 'high', 'datetime', source_id + 'low', source=self._cds, color=source_id + 'colors_wicks', legend=data._name) renderer = self.figure.vbar('datetime', get_bar_length_ms(data) * 0.7, source_id + 'open', source_id + 'close', source=self._cds, fill_color=source_id + 'colors_bars', line_color=source_id + 'colors_outline') self._set_single_hover_renderer(renderer) self._hoverc.add_hovertip("Open", f"@{source_id}open{{0,0.000}}") self._hoverc.add_hovertip("High", f"@{source_id}high{{0,0.000}}") self._hoverc.add_hovertip("Low", f"@{source_id}low{{0,0.000}}") self._hoverc.add_hovertip("Close", f"@{source_id}close{{0,0.000}}") else: raise Exception(f"Unsupported style '{self._scheme.style}'") adapt_yranges(self.figure.y_range, df.low, df.high) # check if we have to plot volume overlay if self._scheme.volume and self._scheme.voloverlay: self.plot_volume(data, self._scheme.voltrans, True)
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() master_clock = build_master_clock(strategy, start, end) start, end = get_strategy_start_end(strategy, start, end) if num_back is None: num_back = len(master_clock) master_clock = master_clock[-num_back:] strategydf['master_clock'] = master_clock # we use timezone of first data. we might see duplicated timestamps here dtline = [bt.num2date(x, strategy.datas[0]._tz) for x in master_clock] # 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 = get_source_id(data) df_data = convert_to_pandas(master_clock, data, start, end, source_id) strategydf = strategydf.join(df_data) df_colors = Figure.build_color_lines(df_data, self.p.scheme, col_open=source_id + 'open', col_close=source_id + 'close', col_prefix=source_id) strategydf = strategydf.join(df_colors) for obj in itertools.chain(strategy.getindicators(), strategy.getobservers()): for lineidx, line, source_id in get_lines(obj): dataline = line.plotrange(start, end) plottype = get_plottype(obj, lineidx) line_clk = get_clock_line(obj).plotrange(start, end) dataline = convert_to_master_clock( dataline, line_clk, master_clock, forward_fill=plottype == PlotType.LINE) strategydf[source_id] = dataline # now iterate again over indicators to calculate area plots (_fill_gt / _fill_lt) for ind in strategy.getindicators(): for lineidx, line, source_id in get_lines(ind): for fattr, _, y2, _, _, fop in get_ind_areas(ind, lineidx): if fop is None: continue # we only need to take care when operator is used if isinstance(y2, int): # scalar value pass elif isinstance(y2, str): y2 = strategydf[y2] else: raise RuntimeError('Unexpected type') dataline_pd = pd.Series(strategydf[source_id]) lineid = source_id + fattr strategydf[lineid] = dataline_pd.where( fop(dataline_pd, y2), y2).to_numpy() # apply a proper index (should be identical to 'index' column) if strategydf.shape[0] > 0: strategydf.index = indices return strategydf