def _configure_plotting(self, strategy: bt.Strategy): datas, inds, obs = strategy.datas, strategy.getindicators( ), strategy.getobservers() for objs in [datas, inds, obs]: for idx, obj in enumerate(objs): self._configure_plotobject(obj, idx, strategy)
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 _blueprint_strategy(self, strategy: bt.Strategy, start=None, end=None, tradingdomain=None, **kwargs) -> None: if not strategy.datas: return self._cur_figurepage.analyzers += [ a for _, a in strategy.analyzers.getitems() ] data_graph, volume_graph = self._build_graph(strategy.datas, strategy.getindicators(), strategy.getobservers(), tradingdomain) start, end = get_strategy_start_end(strategy, start, end) # reset hover container to not mix hovers with other strategies hoverc = HoverContainer( hover_tooltip_config=self.p.scheme.hover_tooltip_config, is_multidata=len(strategy.datas) > 1) figure_envs: List[Figure] = [] for master, slaves in data_graph.items(): plotorder = getattr(master.plotinfo, 'plotorder', 0) figureenv = Figure(strategy, self._cur_figurepage.cds, hoverc, start, end, self.p.scheme, master, plotorder) figureenv.plot(master) for s in slaves: figureenv.plot(s) figure_envs.append(figureenv) for f in figure_envs: f.bfigure.legend.click_policy = self.p.scheme.legend_click f.bfigure.legend.location = self.p.scheme.legend_location f.bfigure.legend.background_fill_color = self.p.scheme.legend_background_color f.bfigure.legend.label_text_color = self.p.scheme.legend_text_color f.bfigure.legend.orientation = self.p.scheme.legend_orientation # link axis for i in range(1, len(figure_envs)): figure_envs[i].bfigure.x_range = figure_envs[0].bfigure.x_range hoverc.apply_hovertips(figure_envs) self._cur_figurepage.figures += figure_envs # volume graphs for v in volume_graph: plotorder = getattr(v.plotinfo, 'plotorder', 0) figureenv = Figure(strategy, self._cur_figurepage.cds, hoverc, start, end, self.p.scheme, v, plotorder) figureenv.plot_volume(v) self._cur_figurepage.figures.append(figureenv)
def list_tradingdomains(self, strategy: bt.Strategy): data_graph, volume_graph = self._build_graph(strategy.datas, strategy.getindicators(), strategy.getobservers()) lgs = list() for master in itertools.chain(data_graph.keys(), volume_graph): lg = FigureEnvelope._resolve_tradingdomain(master) if isinstance(lg, str) and lg not in lgs: lgs.append(lg) return lgs
def list_tradingdomains(self, strategy: bt.Strategy): """Return a list of all trading domains to be found in a strategy.""" data_graph, volume_graph = self._build_graph(strategy.datas, strategy.getindicators(), strategy.getobservers()) lgs = list() for master in itertools.chain(data_graph.keys(), volume_graph): lg = get_tradingdomain(master) if isinstance(lg, str) and lg not in lgs: lgs.append(lg) return lgs
def find_by_plotid(strategy: bt.Strategy, plotid): objs = itertools.chain(strategy.datas, strategy.getindicators(), strategy.getobservers()) founds = [] for obj in objs: if getattr(obj.plotinfo, 'plotid', None) == plotid: founds.append(obj) num_results = len(founds) if num_results == 0: return None elif num_results == 1: return founds[0] else: raise RuntimeError(f'Found multiple objects with plotid "{plotid}"')
def build_master_clock(strategy: bt.Strategy, start: Optional[datetime.datetime] = None, end: Optional[datetime.datetime] = None, ): """Build the master clock which is a clock line that is basically a merged line of all available clocks""" master_clock = [] for obj in itertools.chain(strategy.datas, strategy.getindicators(), strategy.getobservers()): line_clk = get_clock_line(obj).plotrange(start, end) master_clock += line_clk # remove duplicates master_clock = list(dict.fromkeys(master_clock)) master_clock.sort() return master_clock
def _get_strategy(strategy: bt.Strategy) -> str: md = '\n# Strategy\n' md += f'## {strategy.__class__.__name__}\n' md += _get_parameter_table(strategy.params) md += '### Indicators:\n\n' for i in strategy.getindicators(): md += f'{i.__class__.__name__}\n\n' md += _get_parameter_table(i.params) md += 'Source Code:\n' md += f'\n```\n{inspect.getsource(strategy.__class__)}\n```\n\n' return md
def _get_strategy(strategy: bt.Strategy) -> str: md = f'\n# Strategy: {strategy.__class__.__name__}\n' md += _get_parameter_table(strategy.params) md += '## Indicators:\n\n' for i in strategy.getindicators(): md += f'### {i.__class__.__name__} @ {indicator2fullid(i)}\n\n' # md += f'Data: \n' md += _get_parameter_table(i.params) md += 'Source Code:\n' md += '' return md
def _get_strategy(strategy: bt.Strategy, include_src=True) -> str: md = f'\n# Strategy: {strategy.__class__.__name__}\n' md += _get_parameter_table(strategy.params) md += '## Indicators:\n\n' for i in strategy.getindicators(): md += f'### {labelizer.label(i)}\n\n' md += _get_parameter_table(i.params) if include_src: md += 'Source Code:\n' md += f'\n```\n{inspect.getsource(strategy.__class__)}\n```\n\n' return md
def _blueprint_strategy(self, strategy: bt.Strategy, start=None, end=None, tradingdomain=None, **kwargs) -> None: if not strategy.datas: return self._cur_figurepage.analyzers += [ a for _, a in strategy.analyzers.getitems() ] data_graph, volume_graph = self._build_graph(strategy.datas, strategy.getindicators(), strategy.getobservers(), tradingdomain) start, end = Bokeh._get_start_end(strategy, start, end) # reset hover container to not mix hovers with other strategies hoverc = HoverContainer( hover_tooltip_config=self.p.scheme.hover_tooltip_config, is_multidata=len(strategy.datas) > 1) strat_figures = [] for master, slaves in data_graph.items(): plotorder = getattr(master.plotinfo, 'plotorder', 0) figure = FigureEnvelope(strategy, self._cur_figurepage.cds, hoverc, start, end, self.p.scheme, master, plotorder, len(strategy.datas) > 1) figure.plot(master, None) for s in slaves: figure.plot(s, master) strat_figures.append(figure) for f in strat_figures: f.figure.legend.click_policy = self.p.scheme.legend_click f.figure.legend.location = self.p.scheme.legend_location f.figure.legend.background_fill_color = self.p.scheme.legend_background_color f.figure.legend.label_text_color = self.p.scheme.legend_text_color f.figure.legend.orientation = self.p.scheme.legend_orientation # link axis for i in range(1, len(strat_figures)): strat_figures[i].figure.x_range = strat_figures[0].figure.x_range # configure xaxis visibility if self.p.scheme.xaxis_pos == "bottom": for i, f in enumerate(strat_figures): f.figure.xaxis.visible = False if i <= len( strat_figures) else True hoverc.apply_hovertips(strat_figures) self._cur_figurepage.figure_envs += strat_figures # volume graphs for v in volume_graph: plotorder = getattr(v.plotinfo, 'plotorder', 0) figure = FigureEnvelope(strategy, self._cur_figurepage.cds, hoverc, start, end, self.p.scheme, v, plotorder, is_multidata=len(strategy.datas) > 1) figure.plot_volume(v) self._cur_figurepage.figure_envs.append(figure)
def _blueprint_strategy(self, strategy: bt.Strategy, start=None, end=None, **kwargs): if not strategy.datas: return if not len(strategy): return strat_figures = [] self._fp.analyzers = [a for _, a in strategy.analyzers.getitems()] st_dtime = strategy.lines.datetime.plot() if start is None: start = 0 if end is None: end = len(st_dtime) if isinstance(start, datetime.date): start = bisect.bisect_left(st_dtime, bt.date2num(start)) if isinstance(end, datetime.date): end = bisect.bisect_right(st_dtime, bt.date2num(end)) if end < 0: end = len(st_dtime) + 1 + end # -1 = len() -2 = len() - 1 # TODO: using a pandas.DataFrame is desired. On bokeh 0.12.13 this failed cause of this issue: # https://github.com/bokeh/bokeh/issues/7400 strat_clk: array[float] = strategy.lines.datetime.plotrange(start, end) if self._fp.cds is None: # we use timezone of first data 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(0, len(dtline))) self._fp.cds = ColumnDataSource( data=dict(datetime=dtline, index=indices)) self._build_graph(strategy.datas, strategy.getindicators(), strategy.getobservers()) start, end = Bokeh._get_start_end(strategy, start, end) # reset hover container to not mix hovers with other strategies hoverc = HoverContainer() for master, slaves in self._data_graph.items(): plotabove = getattr(master.plotinfo, 'plotabove', False) bf = Figure(strategy, self._fp.cds, hoverc, start, end, self.p.scheme, type(master), plotabove) strat_figures.append(bf) bf.plot(master, strat_clk, None) for s in slaves: bf.plot(s, strat_clk, master) for v in self._volume_graphs: bf = Figure(strategy, self._fp.cds, hoverc, start, end, self.p.scheme) bf.plot_volume(v, strat_clk, 1.0, start, end) # apply legend click policy for f in strat_figures: f.figure.legend.click_policy = self.p.scheme.legend_click for f in strat_figures: f.figure.legend.background_fill_color = self.p.scheme.legend_background_color f.figure.legend.label_text_color = self.p.scheme.legend_text_color # link axis for i in range(1, len(strat_figures)): strat_figures[i].figure.x_range = strat_figures[0].figure.x_range # configure xaxis visibility if self.p.scheme.xaxis_pos == "bottom": for i, f in enumerate(strat_figures): f.figure.xaxis.visible = False if i <= len( strat_figures) else True hoverc.apply_hovertips(strat_figures) self._fp.figures += strat_figures
def _blueprint_strategy(self, strategy: bt.Strategy, start=None, end=None, **kwargs): if not strategy.datas: return if not len(strategy): return self._figurepage.analyzers += [ a for _, a in strategy.analyzers.getitems() ] st_dtime = strategy.lines.datetime.plot() if start is None: start = 0 if end is None: end = len(st_dtime) if isinstance(start, datetime.date): start = bisect.bisect_left(st_dtime, bt.date2num(start)) if isinstance(end, datetime.date): end = bisect.bisect_right(st_dtime, bt.date2num(end)) if end < 0: end = len(st_dtime) + 1 + end # -1 = len() -2 = len() - 1 strat_clk: array[float] = strategy.lines.datetime.plotrange(start, end) if self._figurepage.cds is None: # we use timezone of first data 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(0, len(dtline))) self._figurepage.cds = ColumnDataSource( data=dict(datetime=dtline, index=indices)) data_graph, volume_graph = self._build_graph(strategy.datas, strategy.getindicators(), strategy.getobservers()) start, end = Bokeh._get_start_end(strategy, start, end) # reset hover container to not mix hovers with other strategies hoverc = HoverContainer( hover_tooltip_config=self.p.scheme.hover_tooltip_config, is_multidata=len(strategy.datas) > 1) strat_figures = [] for master, slaves in data_graph.items(): plotorder = getattr(master.plotinfo, 'plotorder', 0) figure = Figure(strategy, self._figurepage.cds, hoverc, start, end, self.p.scheme, master, plotorder, len(strategy.datas) > 1) figure.plot(master, strat_clk, None) for s in slaves: figure.plot(s, strat_clk, master) strat_figures.append(figure) for f in strat_figures: f.figure.legend.click_policy = self.p.scheme.legend_click f.figure.legend.location = self.p.scheme.legend_location f.figure.legend.background_fill_color = self.p.scheme.legend_background_color f.figure.legend.label_text_color = self.p.scheme.legend_text_color f.figure.legend.orientation = self.p.scheme.legend_orientation # link axis for i in range(1, len(strat_figures)): strat_figures[i].figure.x_range = strat_figures[0].figure.x_range # configure xaxis visibility if self.p.scheme.xaxis_pos == "bottom": for i, f in enumerate(strat_figures): f.figure.xaxis.visible = False if i <= len( strat_figures) else True hoverc.apply_hovertips(strat_figures) self._figurepage.figures += strat_figures # volume graphs for v in volume_graph: plotorder = getattr(v.plotinfo, 'plotorder', 0) figure = Figure(strategy, self._figurepage.cds, hoverc, start, end, self.p.scheme, v, plotorder, len(strategy.datas) > 1) figure.plot_volume(v, strat_clk, 1.0) self._figurepage.figures.append(figure)
def _plot_strategy(self, strategy: bt.Strategy, start=None, end=None, **kwargs): if not strategy.datas: return if not len(strategy): return strat_figures = [] # reset hover container to not mix hovers with other strategies hoverc = HoverContainer() for name, a in strategy.analyzers.getitems(): self._fp.analyzers.append((name, a, strategy, None)) # TODO: using a pandas.DataFrame is desired. On bokeh 0.12.13 this failed cause of this issue: # https://github.com/bokeh/bokeh/issues/7400 if self._fp.cds is None: # use datetime line of first data as master datetime. also convert it according to user provided tz dtline = pandas.Series([ bt.num2date(x, strategy.datas[0]._tz) for x in strategy.datas[0].lines.datetime.plotrange( start, end) ], name="DateTime") self._fp.cds = ColumnDataSource(data={'datetime': dtline}) self._build_graph(strategy.datas, strategy.getindicators(), strategy.getobservers()) start, end = Bokeh._get_start_end(strategy, start, end) for master, slaves in self._data_graph.items(): plotabove = getattr(master.plotinfo, 'plotabove', False) bf = Figure(strategy, self._fp.cds, hoverc, start, end, self.p.scheme, type(master), plotabove) strat_figures.append(bf) bf.plot(master, None) for s in slaves: bf.plot(s, master) for v in self._volume_graphs: bf = Figure(strategy, self._fp.cds, hoverc, start, end, self.p.scheme) bf.plot_volume(v, 1.0, start, end) # apply legend click policy for f in strat_figures: f.figure.legend.click_policy = self.p.scheme.legend_click for f in strat_figures: f.figure.legend.background_fill_color = self.p.scheme.legend_background_color f.figure.legend.label_text_color = self.p.scheme.legend_text_color # link axis for i in range(1, len(strat_figures)): strat_figures[i].figure.x_range = strat_figures[0].figure.x_range # configure xaxis visibility if self.p.scheme.xaxis_pos == "bottom": for i, f in enumerate(strat_figures): f.figure.xaxis.visible = False if i <= len( strat_figures) else True hoverc.apply_hovertips(strat_figures) self._fp.figures += strat_figures
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