def plot(self, column=None, levels=(30, 70), percent_k_trace_kwargs=None, percent_d_trace_kwargs=None, shape_kwargs=None, add_trace_kwargs=None, xref='x', yref='y', fig=None, **layout_kwargs): # pragma: no cover """Plot `STOCH.percent_k` and `STOCH.percent_d`. Args: column (str): Name of the column to plot. levels (tuple): Two extremes: bottom and top. percent_k_trace_kwargs (dict): Keyword arguments passed to `plotly.graph_objects.Scatter` for `STOCH.percent_k`. percent_d_trace_kwargs (dict): Keyword arguments passed to `plotly.graph_objects.Scatter` for `STOCH.percent_d`. shape_kwargs (dict): Keyword arguments passed to `plotly.graph_objects.Figure.add_shape` for zone between levels. add_trace_kwargs (dict): Keyword arguments passed to `add_trace`. xref (str): X coordinate axis. yref (str): Y coordinate axis. fig (plotly.graph_objects.Figure): Figure to add traces to. **layout_kwargs: Keyword arguments for layout. ## Example ```python-repl >>> vbt.STOCH.run(ohlcv['High'], ohlcv['Low'], ohlcv['Close']).plot() ``` ![](/vectorbt/docs/img/STOCH.png) """ self_col = self.select_series(column=column) if fig is None: fig = FigureWidget() default_layout = dict() default_layout['yaxis' + yref[1:]] = dict(range=[-5, 105]) fig.update_layout(**default_layout) fig.update_layout(**layout_kwargs) if percent_k_trace_kwargs is None: percent_k_trace_kwargs = {} if percent_d_trace_kwargs is None: percent_d_trace_kwargs = {} if shape_kwargs is None: shape_kwargs = {} percent_k_trace_kwargs = merge_dicts(dict(name='%K'), percent_k_trace_kwargs) percent_d_trace_kwargs = merge_dicts(dict(name='%D'), percent_d_trace_kwargs) fig = self_col.percent_k.vbt.plot(trace_kwargs=percent_k_trace_kwargs, add_trace_kwargs=add_trace_kwargs, fig=fig) fig = self_col.percent_d.vbt.plot(trace_kwargs=percent_d_trace_kwargs, add_trace_kwargs=add_trace_kwargs, fig=fig) # Plot levels # Fill void between levels shape_kwargs = merge_dicts( dict( type="rect", xref=xref, yref=yref, x0=self_col.percent_k.index[0], y0=levels[0], x1=self_col.percent_k.index[-1], y1=levels[1], fillcolor="purple", opacity=0.2, layer="below", line_width=0, ), shape_kwargs) fig.add_shape(**shape_kwargs) return fig
def plot(self, column=None, plot_close=True, plot_zones=True, close_trace_kwargs=None, entry_trace_kwargs=None, exit_trace_kwargs=None, exit_profit_trace_kwargs=None, exit_loss_trace_kwargs=None, active_trace_kwargs=None, profit_shape_kwargs=None, loss_shape_kwargs=None, add_trace_kwargs=None, xref='x', yref='y', fig=None, **layout_kwargs): # pragma: no cover """Plot orders. Args: column (str): Name of the column to plot. plot_close (bool): Whether to plot `Trades.close`. plot_zones (bool): Whether to plot zones. Set to False if there are many trades within one position. close_trace_kwargs (dict): Keyword arguments passed to `plotly.graph_objects.Scatter` for `Trades.close`. entry_trace_kwargs (dict): Keyword arguments passed to `plotly.graph_objects.Scatter` for "Entry" markers. exit_trace_kwargs (dict): Keyword arguments passed to `plotly.graph_objects.Scatter` for "Exit" markers. exit_profit_trace_kwargs (dict): Keyword arguments passed to `plotly.graph_objects.Scatter` for "Exit - Profit" markers. exit_loss_trace_kwargs (dict): Keyword arguments passed to `plotly.graph_objects.Scatter` for "Exit - Loss" markers. active_trace_kwargs (dict): Keyword arguments passed to `plotly.graph_objects.Scatter` for "Active" markers. profit_shape_kwargs (dict): Keyword arguments passed to `plotly.graph_objects.Figure.add_shape` for profit zones. loss_shape_kwargs (dict): Keyword arguments passed to `plotly.graph_objects.Figure.add_shape` for loss zones. add_trace_kwargs (dict): Keyword arguments passed to `add_trace`. xref (str): X coordinate axis. yref (str): Y coordinate axis. fig (plotly.graph_objects.Figure): Figure to add traces to. **layout_kwargs: Keyword arguments for layout. ## Example ```python-repl >>> trades.plot() ``` ![](/vectorbt/docs/img/trades_plot.png)""" from vectorbt.settings import color_schema, contrast_color_schema self_col = self.select_series(column=column, group_by=False) if close_trace_kwargs is None: close_trace_kwargs = {} close_trace_kwargs = merge_dicts( dict(line_color=color_schema['blue'], name='Close' if self_col.wrapper.name is None else self_col.wrapper.name), close_trace_kwargs) if entry_trace_kwargs is None: entry_trace_kwargs = {} if exit_trace_kwargs is None: exit_trace_kwargs = {} if exit_profit_trace_kwargs is None: exit_profit_trace_kwargs = {} if exit_loss_trace_kwargs is None: exit_loss_trace_kwargs = {} if active_trace_kwargs is None: active_trace_kwargs = {} if profit_shape_kwargs is None: profit_shape_kwargs = {} if loss_shape_kwargs is None: loss_shape_kwargs = {} if add_trace_kwargs is None: add_trace_kwargs = {} if fig is None: fig = FigureWidget() fig.update_layout(**layout_kwargs) # Plot close if plot_close: fig = self_col.close.vbt.plot(trace_kwargs=close_trace_kwargs, add_trace_kwargs=add_trace_kwargs, fig=fig) if len(self_col.values) > 0: # Extract information _id = self_col.values['id'] _id_str = 'Trade Id' if self.trade_type == TradeType.Trade else 'Position Id' size = self_col.values['size'] entry_idx = self_col.values['entry_idx'] entry_price = self_col.values['entry_price'] entry_fees = self_col.values['entry_fees'] exit_idx = self_col.values['exit_idx'] exit_price = self_col.values['exit_price'] exit_fees = self_col.values['exit_fees'] pnl = self_col.values['pnl'] ret = self_col.values['return'] direction_value_map = to_value_map(TradeDirection) direction = self_col.values['direction'] direction = np.vectorize(lambda x: str(direction_value_map[x]))( direction) status = self_col.values['status'] def get_duration_str(from_idx, to_idx): if isinstance(self_col.wrapper.index, DatetimeTypes): duration = self_col.wrapper.index[ to_idx] - self_col.wrapper.index[from_idx] elif self_col.wrapper.freq is not None: duration = self_col.wrapper.to_time_units(to_idx - from_idx) else: duration = to_idx - from_idx return np.vectorize(str)(duration) duration = get_duration_str(entry_idx, exit_idx) if len(entry_idx) > 0: # Plot Entry markers entry_customdata = np.stack( (_id, size, entry_fees, direction, *((self_col.values['position_id'], ) if self.trade_type == TradeType.Trade else ())), axis=1) entry_scatter = go.Scatter( x=self_col.wrapper.index[entry_idx], y=entry_price, mode='markers', marker=dict(symbol='square', color=contrast_color_schema['blue'], size=7, line=dict(width=1, color=adjust_lightness( contrast_color_schema['blue']))), name='Entry', customdata=entry_customdata, hovertemplate=_id_str + ": %{customdata[0]}" "<br>Date: %{x}" "<br>Avg. Price: %{y}" "<br>Size: %{customdata[1]:.6f}" "<br>Fees: %{customdata[2]:.6f}" "<br>Direction: %{customdata[3]}" + ("<br>Position Id: %{customdata[4]}" if self.trade_type == TradeType.Trade else '')) entry_scatter.update(**entry_trace_kwargs) fig.add_trace(entry_scatter, **add_trace_kwargs) # Plot end markers def _plot_end_markers(mask, name, color, kwargs): if np.any(mask): customdata = np.stack( (_id[mask], duration[mask], size[mask], exit_fees[mask], pnl[mask], ret[mask], direction[mask], *((self_col.values['position_id'][mask], ) if self.trade_type == TradeType.Trade else ())), axis=1) scatter = go.Scatter( x=self_col.wrapper.index[exit_idx[mask]], y=exit_price[mask], mode='markers', marker=dict(symbol='square', color=color, size=7, line=dict(width=1, color=adjust_lightness(color))), name=name, customdata=customdata, hovertemplate=_id_str + ": %{customdata[0]}" "<br>Date: %{x}" "<br>Duration: %{customdata[1]}" "<br>Avg. Price: %{y}" "<br>Size: %{customdata[2]:.6f}" "<br>Fees: %{customdata[3]:.6f}" "<br>PnL: %{customdata[4]:.6f}" "<br>Return: %{customdata[5]:.2%}" "<br>Direction: %{customdata[6]}" + ("<br>Position Id: %{customdata[7]}" if self.trade_type == TradeType.Trade else '')) scatter.update(**kwargs) fig.add_trace(scatter, **add_trace_kwargs) # Plot Exit markers _plot_end_markers((status == TradeStatus.Closed) & (pnl == 0.), 'Exit', contrast_color_schema['gray'], exit_trace_kwargs) # Plot Exit - Profit markers _plot_end_markers((status == TradeStatus.Closed) & (pnl > 0.), 'Exit - Profit', contrast_color_schema['green'], exit_profit_trace_kwargs) # Plot Exit - Loss markers _plot_end_markers((status == TradeStatus.Closed) & (pnl < 0.), 'Exit - Loss', contrast_color_schema['red'], exit_loss_trace_kwargs) # Plot Active markers _plot_end_markers(status == TradeStatus.Open, 'Active', contrast_color_schema['orange'], active_trace_kwargs) if plot_zones: profit_mask = pnl > 0. if np.any(profit_mask): # Plot profit zones for i in np.flatnonzero(profit_mask): fig.add_shape(**merge_dicts( dict( type="rect", xref=xref, yref=yref, x0=self_col.wrapper.index[entry_idx[i]], y0=entry_price[i], x1=self_col.wrapper.index[exit_idx[i]], y1=exit_price[i], fillcolor='green', opacity=0.2, layer="below", line_width=0, ), profit_shape_kwargs)) loss_mask = pnl < 0. if np.any(loss_mask): # Plot loss zones for i in np.flatnonzero(loss_mask): fig.add_shape(**merge_dicts( dict( type="rect", xref=xref, yref=yref, x0=self_col.wrapper.index[entry_idx[i]], y0=entry_price[i], x1=self_col.wrapper.index[exit_idx[i]], y1=exit_price[i], fillcolor='red', opacity=0.2, layer="below", line_width=0, ), loss_shape_kwargs)) return fig
def plot_cum_returns(self, benchmark_rets=None, start_value=1, fill_to_benchmark=False, main_kwargs=None, benchmark_kwargs=None, hline_shape_kwargs=None, add_trace_kwargs=None, xref='x', yref='y', fig=None, **layout_kwargs): # pragma: no cover """Plot cumulative returns. Args: benchmark_rets (array_like): Benchmark return to compare returns against. Will broadcast per element. start_value (float): The starting returns. fill_to_benchmark (bool): Whether to fill between main and benchmark, or between main and `start_value`. main_kwargs (dict): Keyword arguments passed to `vectorbt.generic.accessors.GenericSRAccessor.plot` for main. benchmark_kwargs (dict): Keyword arguments passed to `vectorbt.generic.accessors.GenericSRAccessor.plot` for benchmark. hline_shape_kwargs (dict): Keyword arguments passed to `plotly.graph_objects.Figure.add_shape` for `start_value` line. add_trace_kwargs (dict): Keyword arguments passed to `add_trace`. xref (str): X coordinate axis. yref (str): Y coordinate axis. fig (plotly.graph_objects.Figure): Figure to add traces to. **layout_kwargs: Keyword arguments for layout. ## Example ```python-repl >>> import pandas as pd >>> import numpy as np >>> np.random.seed(0) >>> rets = pd.Series(np.random.uniform(-0.05, 0.05, size=100)) >>> benchmark_rets = pd.Series(np.random.uniform(-0.05, 0.05, size=100)) >>> rets.vbt.returns.plot_cum_returns(benchmark_rets=benchmark_rets) ``` ![](/vectorbt/docs/img/plot_cum_returns.png) """ from vectorbt.settings import color_schema if fig is None: fig = FigureWidget() fig.update_layout(**layout_kwargs) x_domain = [0, 1] xaxis = 'xaxis' + xref[1:] if xaxis in fig.layout: if 'domain' in fig.layout[xaxis]: if fig.layout[xaxis]['domain'] is not None: x_domain = fig.layout[xaxis]['domain'] fill_to_benchmark = fill_to_benchmark and benchmark_rets is not None if benchmark_rets is not None: # Plot benchmark benchmark_rets = reshape_fns.broadcast_to(benchmark_rets, self._obj) if benchmark_kwargs is None: benchmark_kwargs = {} benchmark_kwargs = merge_dicts(dict( trace_kwargs=dict( line_color=color_schema['gray'], name='Benchmark' ) ), benchmark_kwargs) benchmark_cumrets = benchmark_rets.vbt.returns.cumulative(start_value=start_value) benchmark_cumrets.vbt.plot(**benchmark_kwargs, add_trace_kwargs=add_trace_kwargs, fig=fig) else: benchmark_cumrets = None # Plot main if main_kwargs is None: main_kwargs = {} main_kwargs = merge_dicts(dict( trace_kwargs=dict( line_color=color_schema['purple'], ), other_trace_kwargs='hidden' ), main_kwargs) cumrets = self.cumulative(start_value=start_value) if fill_to_benchmark: cumrets.vbt.plot_against(benchmark_cumrets, **main_kwargs, add_trace_kwargs=add_trace_kwargs, fig=fig) else: cumrets.vbt.plot_against(start_value, **main_kwargs, add_trace_kwargs=add_trace_kwargs, fig=fig) # Plot hline if hline_shape_kwargs is None: hline_shape_kwargs = {} fig.add_shape(**merge_dicts(dict( type='line', xref="paper", yref=yref, x0=x_domain[0], y0=start_value, x1=x_domain[1], y1=start_value, line=dict( color="gray", dash="dash", ) ), hline_shape_kwargs)) return fig
def plot_pnl(self, column=None, marker_size_range=[7, 14], opacity_range=[0.75, 0.9], closed_profit_trace_kwargs=None, closed_loss_trace_kwargs=None, open_trace_kwargs=None, hline_shape_kwargs=None, add_trace_kwargs=None, xref='x', yref='y', fig=None, **layout_kwargs): # pragma: no cover """Plot trade PnL. Args: column (str): Name of the column to plot. closed_profit_trace_kwargs (dict): Keyword arguments passed to `plotly.graph_objects.Scatter` for "Closed - Profit" markers. closed_loss_trace_kwargs (dict): Keyword arguments passed to `plotly.graph_objects.Scatter` for "Closed - Loss" markers. open_trace_kwargs (dict): Keyword arguments passed to `plotly.graph_objects.Scatter` for "Open" markers. hline_shape_kwargs (dict): Keyword arguments passed to `plotly.graph_objects.Figure.add_shape` for zeroline. add_trace_kwargs (dict): Keyword arguments passed to `add_trace`. xref (str): X coordinate axis. yref (str): Y coordinate axis. fig (plotly.graph_objects.Figure): Figure to add traces to. **layout_kwargs: Keyword arguments for layout. ## Example ```python-repl >>> trades.plot_pnl() ``` ![](/vectorbt/docs/img/trades_plot_pnl.png) """ from vectorbt.settings import contrast_color_schema self_col = self.select_series(column=column, group_by=False) if closed_profit_trace_kwargs is None: closed_profit_trace_kwargs = {} if closed_loss_trace_kwargs is None: closed_loss_trace_kwargs = {} if open_trace_kwargs is None: open_trace_kwargs = {} if hline_shape_kwargs is None: hline_shape_kwargs = {} if add_trace_kwargs is None: add_trace_kwargs = {} marker_size_range = tuple(marker_size_range) if fig is None: fig = FigureWidget() fig.update_layout(**layout_kwargs) x_domain = [0, 1] xaxis = 'xaxis' + xref[1:] if xaxis in fig.layout: if 'domain' in fig.layout[xaxis]: if fig.layout[xaxis]['domain'] is not None: x_domain = fig.layout[xaxis]['domain'] if len(self_col.values) > 0: # Extract information _id = self.values['id'] _id_str = 'Trade Id' if self.trade_type == TradeType.Trade else 'Position Id' exit_idx = self.values['exit_idx'] pnl = self.values['pnl'] returns = self.values['return'] status = self.values['status'] neutral_mask = pnl == 0 profit_mask = pnl > 0 loss_mask = pnl < 0 marker_size = min_rel_rescale(np.abs(returns), marker_size_range) opacity = max_rel_rescale(np.abs(returns), opacity_range) open_mask = status == TradeStatus.Open closed_profit_mask = (~open_mask) & profit_mask closed_loss_mask = (~open_mask) & loss_mask open_mask &= ~neutral_mask if np.any(closed_profit_mask): # Plot Profit markers profit_scatter = go.Scatter( x=self_col.wrapper.index[exit_idx[closed_profit_mask]], y=pnl[closed_profit_mask], mode='markers', marker=dict( symbol='circle', color=contrast_color_schema['green'], size=marker_size[closed_profit_mask], opacity=opacity[closed_profit_mask], line=dict(width=1, color=adjust_lightness( contrast_color_schema['green'])), ), name='Closed - Profit', customdata=np.stack( (_id[closed_profit_mask], returns[closed_profit_mask]), axis=1), hovertemplate=_id_str + ": %{customdata[0]}" "<br>Date: %{x}" "<br>PnL: %{y}" "<br>Return: %{customdata[1]:.2%}") profit_scatter.update(**closed_profit_trace_kwargs) fig.add_trace(profit_scatter, **add_trace_kwargs) if np.any(closed_loss_mask): # Plot Loss markers loss_scatter = go.Scatter( x=self_col.wrapper.index[exit_idx[closed_loss_mask]], y=pnl[closed_loss_mask], mode='markers', marker=dict(symbol='circle', color=contrast_color_schema['red'], size=marker_size[closed_loss_mask], opacity=opacity[closed_loss_mask], line=dict(width=1, color=adjust_lightness( contrast_color_schema['red']))), name='Closed - Loss', customdata=np.stack( (_id[closed_loss_mask], returns[closed_loss_mask]), axis=1), hovertemplate=_id_str + ": %{customdata[0]}" "<br>Date: %{x}" "<br>PnL: %{y}" "<br>Return: %{customdata[1]:.2%}") loss_scatter.update(**closed_loss_trace_kwargs) fig.add_trace(loss_scatter, **add_trace_kwargs) if np.any(open_mask): # Plot Active markers active_scatter = go.Scatter( x=self_col.wrapper.index[exit_idx[open_mask]], y=pnl[open_mask], mode='markers', marker=dict(symbol='circle', color=contrast_color_schema['orange'], size=marker_size[open_mask], opacity=opacity[open_mask], line=dict( width=1, color=adjust_lightness( contrast_color_schema['orange']))), name='Open', customdata=np.stack((_id[open_mask], returns[open_mask]), axis=1), hovertemplate=_id_str + ": %{customdata[0]}" "<br>Date: %{x}" "<br>PnL: %{y}" "<br>Return: %{customdata[1]:.2%}") active_scatter.update(**open_trace_kwargs) fig.add_trace(active_scatter, **add_trace_kwargs) # Plot zeroline fig.add_shape(**merge_dicts( dict(type='line', xref="paper", yref=yref, x0=x_domain[0], y0=0, x1=x_domain[1], y1=0, line=dict( color="gray", dash="dash", )), hline_shape_kwargs)) return fig
def plot(self, column=None, top_n=5, plot_ts=True, plot_zones=True, ts_trace_kwargs=None, peak_trace_kwargs=None, valley_trace_kwargs=None, recovery_trace_kwargs=None, active_trace_kwargs=None, ptv_shape_kwargs=None, vtr_shape_kwargs=None, active_shape_kwargs=None, add_trace_kwargs=None, xref='x', yref='y', fig=None, **layout_kwargs): # pragma: no cover """Plot drawdowns over `Drawdowns.ts`. Args: column (str): Name of the column to plot. top_n (int): Filter top N drawdown records by maximum drawdown. plot_ts (bool): Whether to plot time series. plot_zones (bool): Whether to plot zones. ts_trace_kwargs (dict): Keyword arguments passed to `plotly.graph_objects.Scatter` for time series. peak_trace_kwargs (dict): Keyword arguments passed to `plotly.graph_objects.Scatter` for peak values. valley_trace_kwargs (dict): Keyword arguments passed to `plotly.graph_objects.Scatter` for valley values. recovery_trace_kwargs (dict): Keyword arguments passed to `plotly.graph_objects.Scatter` for recovery values. active_trace_kwargs (dict): Keyword arguments passed to `plotly.graph_objects.Scatter` for active recovery values. ptv_shape_kwargs (dict): Keyword arguments passed to `plotly.graph_objects.Figure.add_shape` for PtV zones. vtr_shape_kwargs (dict): Keyword arguments passed to `plotly.graph_objects.Figure.add_shape` for VtR zones. active_shape_kwargs (dict): Keyword arguments passed to `plotly.graph_objects.Figure.add_shape` for active VtR zones. add_trace_kwargs (dict): Keyword arguments passed to `add_trace`. xref (str): X coordinate axis. yref (str): Y coordinate axis. fig (plotly.graph_objects.Figure): Figure to add traces to. **layout_kwargs: Keyword arguments for layout. ## Example ```python-repl >>> import vectorbt as vbt >>> import pandas as pd >>> ts = pd.Series([1, 2, 1, 2, 3, 2, 1, 2]) >>> vbt.Drawdowns.from_ts(ts, freq='1 days').plot() ``` ![](/vectorbt/docs/img/drawdowns_plot.png) """ from vectorbt.settings import color_schema, contrast_color_schema self_col = self.select_series(column=column, group_by=False) if top_n is not None: # Drawdowns is negative, thus top_n becomes bottom_n self_col = self_col.filter_by_mask( self_col.drawdown.bottom_n_mask(top_n)) if ts_trace_kwargs is None: ts_trace_kwargs = {} ts_trace_kwargs = merge_dicts(dict(line_color=color_schema['blue']), ts_trace_kwargs) if peak_trace_kwargs is None: peak_trace_kwargs = {} if valley_trace_kwargs is None: valley_trace_kwargs = {} if recovery_trace_kwargs is None: recovery_trace_kwargs = {} if active_trace_kwargs is None: active_trace_kwargs = {} if ptv_shape_kwargs is None: ptv_shape_kwargs = {} if vtr_shape_kwargs is None: vtr_shape_kwargs = {} if active_shape_kwargs is None: active_shape_kwargs = {} if add_trace_kwargs is None: add_trace_kwargs = {} if fig is None: fig = FigureWidget() fig.update_layout(**layout_kwargs) y_domain = [0, 1] yaxis = 'yaxis' + yref[1:] if yaxis in fig.layout: if 'domain' in fig.layout[yaxis]: if fig.layout[yaxis]['domain'] is not None: y_domain = fig.layout[yaxis]['domain'] if plot_ts: fig = self_col.ts.vbt.plot(trace_kwargs=ts_trace_kwargs, add_trace_kwargs=add_trace_kwargs, fig=fig) if len(self_col.values) > 0: # Extract information _id = self_col.values['id'] start_idx = self_col.values['start_idx'] valley_idx = self_col.values['valley_idx'] end_idx = self_col.values['end_idx'] status = self_col.values['status'] start_val = self_col.ts.values[start_idx] valley_val = self_col.ts.values[valley_idx] end_val = self_col.ts.values[end_idx] def get_duration_str(from_idx, to_idx): if isinstance(self_col.wrapper.index, DatetimeTypes): duration = self_col.wrapper.index[ to_idx] - self_col.wrapper.index[from_idx] elif self_col.wrapper.freq is not None: duration = self_col.wrapper.to_time_units(to_idx - from_idx) else: duration = to_idx - from_idx return np.vectorize(str)(duration) # Plot peak markers peak_mask = start_idx != np.roll( end_idx, 1) # peak and recovery at same time -> recovery wins if np.any(peak_mask): peak_customdata = _id[peak_mask][:, None] peak_scatter = go.Scatter( x=self_col.ts.index[start_idx[peak_mask]], y=start_val[peak_mask], mode='markers', marker=dict(symbol='diamond', color=contrast_color_schema['blue'], size=7, line=dict(width=1, color=adjust_lightness( contrast_color_schema['blue']))), name='Peak', customdata=peak_customdata, hovertemplate="Drawdown Id: %{customdata[0]}" "<br>Date: %{x}" "<br>Price: %{y}") peak_scatter.update(**peak_trace_kwargs) fig.add_trace(peak_scatter, **add_trace_kwargs) recovery_mask = status == DrawdownStatus.Recovered if np.any(recovery_mask): # Plot valley markers valley_drawdown = ( valley_val[recovery_mask] - start_val[recovery_mask]) / start_val[recovery_mask] valley_duration = get_duration_str(start_idx[recovery_mask], valley_idx[recovery_mask]) valley_customdata = np.stack( (_id[recovery_mask], valley_drawdown, valley_duration), axis=1) valley_scatter = go.Scatter( x=self_col.ts.index[valley_idx[recovery_mask]], y=valley_val[recovery_mask], mode='markers', marker=dict(symbol='diamond', color=contrast_color_schema['red'], size=7, line=dict(width=1, color=adjust_lightness( contrast_color_schema['red']))), name='Valley', customdata=valley_customdata, hovertemplate="Drawdown Id: %{customdata[0]}" "<br>Date: %{x}" "<br>Price: %{y}" "<br>Drawdown: %{customdata[1]:.2%}" "<br>Duration: %{customdata[2]}") valley_scatter.update(**valley_trace_kwargs) fig.add_trace(valley_scatter, **add_trace_kwargs) if plot_zones: # Plot drawdown zones for i in np.flatnonzero(recovery_mask): fig.add_shape(**merge_dicts( dict( type="rect", xref=xref, yref="paper", x0=self_col.ts.index[start_idx[i]], y0=y_domain[0], x1=self_col.ts.index[valley_idx[i]], y1=y_domain[1], fillcolor='red', opacity=0.2, layer="below", line_width=0, ), ptv_shape_kwargs)) # Plot recovery markers recovery_return = ( end_val[recovery_mask] - valley_val[recovery_mask]) / valley_val[recovery_mask] recovery_duration = get_duration_str(valley_idx[recovery_mask], end_idx[recovery_mask]) recovery_customdata = np.stack( (_id[recovery_mask], recovery_return, recovery_duration), axis=1) recovery_scatter = go.Scatter( x=self_col.ts.index[end_idx[recovery_mask]], y=end_val[recovery_mask], mode='markers', marker=dict(symbol='diamond', color=contrast_color_schema['green'], size=7, line=dict( width=1, color=adjust_lightness( contrast_color_schema['green']))), name='Recovery/Peak', customdata=recovery_customdata, hovertemplate="Drawdown Id: %{customdata[0]}" "<br>Date: %{x}" "<br>Price: %{y}" "<br>Return: %{customdata[1]:.2%}" "<br>Duration: %{customdata[2]}") recovery_scatter.update(**recovery_trace_kwargs) fig.add_trace(recovery_scatter, **add_trace_kwargs) if plot_zones: # Plot recovery zones for i in np.flatnonzero(recovery_mask): fig.add_shape(**merge_dicts( dict( type="rect", xref=xref, yref="paper", x0=self_col.ts.index[valley_idx[i]], y0=y_domain[0], x1=self_col.ts.index[end_idx[i]], y1=y_domain[1], fillcolor='green', opacity=0.2, layer="below", line_width=0, ), vtr_shape_kwargs)) # Plot active markers active_mask = ~recovery_mask if np.any(active_mask): active_drawdown = ( valley_val[active_mask] - start_val[active_mask]) / start_val[active_mask] active_duration = get_duration_str(valley_idx[active_mask], end_idx[active_mask]) active_customdata = np.stack( (_id[active_mask], active_drawdown, active_duration), axis=1) active_scatter = go.Scatter( x=self_col.ts.index[end_idx[active_mask]], y=end_val[active_mask], mode='markers', marker=dict(symbol='diamond', color=contrast_color_schema['orange'], size=7, line=dict( width=1, color=adjust_lightness( contrast_color_schema['orange']))), name='Active', customdata=active_customdata, hovertemplate="Drawdown Id: %{customdata[0]}" "<br>Date: %{x}" "<br>Price: %{y}" "<br>Drawdown: %{customdata[1]:.2%}" "<br>Duration: %{customdata[2]}") active_scatter.update(**active_trace_kwargs) fig.add_trace(active_scatter, **add_trace_kwargs) if plot_zones: # Plot active drawdown zones for i in np.flatnonzero(active_mask): fig.add_shape(**merge_dicts( dict( type="rect", xref=xref, yref="paper", x0=self_col.ts.index[start_idx[i]], y0=y_domain[0], x1=self_col.ts.index[end_idx[i]], y1=y_domain[1], fillcolor='orange', opacity=0.2, layer="below", line_width=0, ), active_shape_kwargs)) return fig