Ejemplo n.º 1
0
            def plot_func(self, *args, _px_func_name: str = px_func_name,
                          _px_func: tp.Callable = px_func, **kwargs) -> tp.BaseFigure:
                from vectorbt.settings import layout

                layout_kwargs = dict(
                    template=kwargs.pop('template', layout['template']),
                    width=kwargs.pop('width', layout['width']),
                    height=kwargs.pop('height', layout['height'])
                )
                # Fix category_orders
                if 'color' in kwargs:
                    if isinstance(kwargs['color'], str):
                        if isinstance(self._obj, pd.DataFrame):
                            if kwargs['color'] in self._obj.columns:
                                category_orders = dict()
                                category_orders[kwargs['color']] = sorted(self._obj[kwargs['color']].unique())
                                kwargs = merge_dicts(dict(category_orders=category_orders), kwargs)

                # Fix Series name
                obj = self._obj.copy(deep=False)
                if isinstance(obj, pd.Series):
                    if obj.name is not None:
                        obj = obj.rename(str(obj.name))
                else:
                    obj.columns = clean_labels(obj.columns)
                obj.index = clean_labels(obj.index)

                if _px_func_name == 'imshow':
                    return make_figure(_px_func(
                        obj.vbt.to_2d_array(), *args, **layout_kwargs, **kwargs
                    ), layout=layout_kwargs)
                return make_figure(_px_func(
                    obj, *args, **layout_kwargs, **kwargs
                ), layout=layout_kwargs)
Ejemplo n.º 2
0
    def plot(self,
             column: tp.Optional[tp.Label] = None,
             plot_close: bool = True,
             close_trace_kwargs: tp.KwargsLike = None,
             ma_trace_kwargs: tp.KwargsLike = None,
             add_trace_kwargs: tp.KwargsLike = None,
             fig: tp.Optional[tp.BaseFigure] = None,
             **layout_kwargs) -> tp.BaseFigure:  # pragma: no cover
        """Plot `MA.ma` against `MA.close`.

        Args:
            column (str): Name of the column to plot.
            plot_close (bool): Whether to plot `MA.close`.
            close_trace_kwargs (dict): Keyword arguments passed to `plotly.graph_objects.Scatter` for `MA.close`.
            ma_trace_kwargs (dict): Keyword arguments passed to `plotly.graph_objects.Scatter` for `MA.ma`.
            add_trace_kwargs (dict): Keyword arguments passed to `add_trace`.
            fig (Figure or FigureWidget): Figure to add traces to.
            **layout_kwargs: Keyword arguments for layout.

        ## Example

        ```python-repl
        >>> vbt.MA.run(ohlcv['Close'], 10).plot()
        ```

        ![](/docs/img/MA.svg)
        """
        from vectorbt._settings import settings
        plotting_cfg = settings['plotting']

        self_col = self.select_one(column=column)

        if fig is None:
            fig = make_figure()
        fig.update_layout(**layout_kwargs)

        if close_trace_kwargs is None:
            close_trace_kwargs = {}
        if ma_trace_kwargs is None:
            ma_trace_kwargs = {}
        close_trace_kwargs = merge_dicts(dict(
            name='Close',
            line=dict(
                color=plotting_cfg['color_schema']['blue']
            )
        ), close_trace_kwargs)
        ma_trace_kwargs = merge_dicts(dict(
            name='MA'
        ), ma_trace_kwargs)

        if plot_close:
            fig = self_col.close.vbt.plot(
                trace_kwargs=close_trace_kwargs,
                add_trace_kwargs=add_trace_kwargs, fig=fig)
        fig = self_col.ma.vbt.plot(
            trace_kwargs=ma_trace_kwargs,
            add_trace_kwargs=add_trace_kwargs, fig=fig)

        return fig
Ejemplo n.º 3
0
    def plot(self,
             column: tp.Optional[tp.Label] = None,
             tr_trace_kwargs: tp.KwargsLike = None,
             atr_trace_kwargs: tp.KwargsLike = None,
             add_trace_kwargs: tp.KwargsLike = None,
             fig: tp.Optional[tp.BaseFigure] = None,
             **layout_kwargs) -> tp.BaseFigure:  # pragma: no cover
        """Plot `ATR.tr` and `ATR.atr`.

        Args:
            column (str): Name of the column to plot.
            tr_trace_kwargs (dict): Keyword arguments passed to `plotly.graph_objects.Scatter` for `ATR.tr`.
            atr_trace_kwargs (dict): Keyword arguments passed to `plotly.graph_objects.Scatter` for `ATR.atr`.
            add_trace_kwargs (dict): Keyword arguments passed to `add_trace`.
            fig (Figure or FigureWidget): Figure to add traces to.
            **layout_kwargs: Keyword arguments for layout.

        ## Example

        ```python-repl
        >>> vbt.ATR.run(ohlcv['High'], ohlcv['Low'], ohlcv['Close'], 10).plot()
        ```

        ![](/docs/img/ATR.svg)
        """
        self_col = self.select_one(column=column)

        if fig is None:
            fig = make_figure()
        fig.update_layout(**layout_kwargs)

        if tr_trace_kwargs is None:
            tr_trace_kwargs = {}
        if atr_trace_kwargs is None:
            atr_trace_kwargs = {}
        tr_trace_kwargs = merge_dicts(dict(
            name='TR'
        ), tr_trace_kwargs)
        atr_trace_kwargs = merge_dicts(dict(
            name='ATR'
        ), atr_trace_kwargs)

        fig = self_col.tr.vbt.plot(
            trace_kwargs=tr_trace_kwargs,
            add_trace_kwargs=add_trace_kwargs, fig=fig)
        fig = self_col.atr.vbt.plot(
            trace_kwargs=atr_trace_kwargs,
            add_trace_kwargs=add_trace_kwargs, fig=fig)

        return fig
Ejemplo n.º 4
0
    def plot(self,
             column: tp.Optional[tp.Label] = None,
             mstd_trace_kwargs: tp.KwargsLike = None,
             add_trace_kwargs: tp.KwargsLike = None,
             fig: tp.Optional[tp.BaseFigure] = None,
             **layout_kwargs) -> tp.BaseFigure:  # pragma: no cover
        """Plot `MSTD.mstd`.

        Args:
            column (str): Name of the column to plot.
            mstd_trace_kwargs (dict): Keyword arguments passed to `plotly.graph_objects.Scatter` for `MSTD.mstd`.
            add_trace_kwargs (dict): Keyword arguments passed to `add_trace`.
            fig (Figure or FigureWidget): Figure to add traces to.
            **layout_kwargs: Keyword arguments for layout.

        ## Example

        ```python-repl
        >>> vbt.MSTD.run(ohlcv['Close'], 10).plot()
        ```

        ![](/vectorbt/docs/img/MSTD.svg)
        """
        self_col = self.select_one(column=column)

        if fig is None:
            fig = make_figure()
        fig.update_layout(**layout_kwargs)

        if mstd_trace_kwargs is None:
            mstd_trace_kwargs = {}
        mstd_trace_kwargs = merge_dicts(dict(
            name='MSTD'
        ), mstd_trace_kwargs)

        fig = self_col.mstd.vbt.plot(
            trace_kwargs=mstd_trace_kwargs,
            add_trace_kwargs=add_trace_kwargs, fig=fig)

        return fig
Ejemplo n.º 5
0
    def plot(self,
             column: tp.Optional[tp.Label] = None,
             obv_trace_kwargs: tp.KwargsLike = None,
             add_trace_kwargs: tp.KwargsLike = None,
             fig: tp.Optional[tp.BaseFigure] = None,
             **layout_kwargs) -> tp.BaseFigure:  # pragma: no cover
        """Plot `OBV.obv`.

        Args:
            column (str): Name of the column to plot.
            obv_trace_kwargs (dict): Keyword arguments passed to `plotly.graph_objects.Scatter` for `OBV.obv`.
            add_trace_kwargs (dict): Keyword arguments passed to `add_trace`.
            fig (Figure or FigureWidget): Figure to add traces to.
            **layout_kwargs: Keyword arguments for layout.

        ## Example

        ```py
        >>> vbt.OBV.run(ohlcv['Close'], ohlcv['Volume']).plot()
        ```

        ![](/docs/img/OBV.svg)
        """
        self_col = self.select_one(column=column)

        if fig is None:
            fig = make_figure()
        fig.update_layout(**layout_kwargs)

        if obv_trace_kwargs is None:
            obv_trace_kwargs = {}
        obv_trace_kwargs = merge_dicts(dict(
            name='OBV'
        ), obv_trace_kwargs)

        fig = self_col.obv.vbt.plot(
            trace_kwargs=obv_trace_kwargs,
            add_trace_kwargs=add_trace_kwargs, fig=fig)

        return fig
Ejemplo n.º 6
0
    def plot_cum_returns(self,
                         benchmark_rets: tp.Optional[tp.ArrayLike] = None,
                         start_value: float = 1,
                         fill_to_benchmark: bool = False,
                         main_kwargs: tp.KwargsLike = None,
                         benchmark_kwargs: tp.KwargsLike = None,
                         hline_shape_kwargs: tp.KwargsLike = None,
                         add_trace_kwargs: tp.KwargsLike = None,
                         xref: str = 'x',
                         yref: str = 'y',
                         fig: tp.Optional[tp.BaseFigure] = None,
                         **layout_kwargs) -> tp.BaseFigure:  # 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 (Figure or FigureWidget): 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.svg)
        """
        from vectorbt._settings import settings
        plotting_cfg = settings['plotting']

        if fig is None:
            fig = make_figure()
        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 = broadcast_to(benchmark_rets, self._obj)
            if benchmark_kwargs is None:
                benchmark_kwargs = {}
            benchmark_kwargs = merge_dicts(
                dict(trace_kwargs=dict(line_color=plotting_cfg['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=plotting_cfg['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
Ejemplo n.º 7
0
    def plot(self,
             plot_type: tp.Union[None, str, tp.BaseTraceType] = None,
             display_volume: bool = True,
             ohlc_kwargs: tp.KwargsLike = None,
             volume_kwargs: tp.KwargsLike = None,
             ohlc_add_trace_kwargs: tp.KwargsLike = None,
             volume_add_trace_kwargs: tp.KwargsLike = None,
             fig: tp.Optional[tp.BaseFigure] = None,
             **layout_kwargs) -> tp.BaseFigure:
        """Plot OHLCV data.

        Args:
            plot_type: Either 'OHLC', 'Candlestick' or Plotly trace.

                Pass None to use the default.
            display_volume (bool): If True, displays volume as bar chart.
            ohlc_kwargs (dict): Keyword arguments passed to `plot_type`.
            volume_kwargs (dict): Keyword arguments passed to `plotly.graph_objects.Bar`.
            ohlc_add_trace_kwargs (dict): Keyword arguments passed to `add_trace` for OHLC.
            volume_add_trace_kwargs (dict): Keyword arguments passed to `add_trace` for volume.
            fig (Figure or FigureWidget): Figure to add traces to.
            **layout_kwargs: Keyword arguments for layout.

        ## Example

        ```python-repl
        >>> import vectorbt as vbt

        >>> vbt.YFData.download("BTC-USD").get().vbt.ohlcv.plot()
        ```

        ![](/vectorbt/docs/img/ohlcv.svg)
        """
        from vectorbt.settings import ohlcv, color_schema

        if ohlc_kwargs is None:
            ohlc_kwargs = {}
        if volume_kwargs is None:
            volume_kwargs = {}
        if ohlc_add_trace_kwargs is None:
            ohlc_add_trace_kwargs = {}
        if volume_add_trace_kwargs is None:
            volume_add_trace_kwargs = {}
        if display_volume:
            ohlc_add_trace_kwargs = merge_dicts(dict(row=1, col=1),
                                                ohlc_add_trace_kwargs)
            volume_add_trace_kwargs = merge_dicts(dict(row=2, col=1),
                                                  volume_add_trace_kwargs)

        column_names = ohlcv[
            'column_names'] if self._column_names is None else self._column_names
        open = self._obj[column_names['open']]
        high = self._obj[column_names['high']]
        low = self._obj[column_names['low']]
        close = self._obj[column_names['close']]

        # Set up figure
        if fig is None:
            if display_volume:
                fig = make_subplots(rows=2,
                                    cols=1,
                                    shared_xaxes=True,
                                    vertical_spacing=0,
                                    row_heights=[0.7, 0.3])
            else:
                fig = make_figure()
            fig.update_layout(showlegend=True,
                              xaxis=dict(rangeslider_visible=False,
                                         showgrid=True),
                              yaxis=dict(showgrid=True))
            if display_volume:
                fig.update_layout(xaxis2=dict(showgrid=True),
                                  yaxis2=dict(showgrid=True),
                                  bargap=0)
        fig.update_layout(**layout_kwargs)
        if plot_type is None:
            plot_type = ohlcv['plot_type']
        if isinstance(plot_type, str):
            if plot_type.lower() == 'ohlc':
                plot_type = 'OHLC'
                plot_obj = go.Ohlc
            elif plot_type.lower() == 'candlestick':
                plot_type = 'Candlestick'
                plot_obj = go.Candlestick
            else:
                raise ValueError(
                    "Plot type can be either 'OHLC' or 'Candlestick'")
        else:
            plot_obj = plot_type
        ohlc = plot_obj(x=self.wrapper.index,
                        open=open,
                        high=high,
                        low=low,
                        close=close,
                        name=plot_type,
                        increasing_line_color=color_schema['increasing'],
                        decreasing_line_color=color_schema['decreasing'])
        ohlc.update(**ohlc_kwargs)
        fig.add_trace(ohlc, **ohlc_add_trace_kwargs)

        if display_volume:
            volume = self._obj[column_names['volume']]

            marker_colors = np.empty(volume.shape, dtype=object)
            marker_colors[(close.values -
                           open.values) > 0] = color_schema['increasing']
            marker_colors[(close.values -
                           open.values) == 0] = color_schema['gray']
            marker_colors[(close.values -
                           open.values) < 0] = color_schema['decreasing']
            volume_bar = go.Bar(x=self.wrapper.index,
                                y=volume,
                                marker=dict(color=marker_colors, line_width=0),
                                opacity=0.5,
                                name='Volume')
            volume_bar.update(**volume_kwargs)
            fig.add_trace(volume_bar, **volume_add_trace_kwargs)

        return fig
Ejemplo n.º 8
0
    def __init__(self,
                 data: tp.Optional[tp.ArrayLike] = None,
                 x_labels: tp.Optional[tp.Labels] = None,
                 y_labels: tp.Optional[tp.Labels] = None,
                 z_labels: tp.Optional[tp.Labels] = None,
                 trace_kwargs: tp.KwargsLike = None,
                 add_trace_kwargs: tp.KwargsLike = None,
                 scene_name: str = 'scene',
                 fig: tp.Optional[tp.BaseFigure] = None,
                 **layout_kwargs) -> None:
        """Create a volume plot.

        Args:
            data (array_like): Data in any format that can be converted to NumPy.

                Must be a 3-dim array.
            x_labels (array_like): X-axis labels.
            y_labels (array_like): Y-axis labels.
            z_labels (array_like): Z-axis labels.
            trace_kwargs (dict): Keyword arguments passed to `plotly.graph_objects.Volume`.
            add_trace_kwargs (dict): Keyword arguments passed to `add_trace`.
            scene_name (str): Reference to the 3D scene.
            fig (Figure or FigureWidget): Figure to add traces to.
            **layout_kwargs: Keyword arguments for layout.

        !!! note
            Figure widgets have currently problems displaying NaNs.
            Use `.show()` method for rendering.

        ## Example

        ```python-repl
        >>> import vectorbt as vbt
        >>> import numpy as np

        >>> volume = vbt.plotting.Volume(
        ...     data=np.random.randint(1, 10, size=(3, 3, 3)),
        ...     x_labels=['a', 'b', 'c'],
        ...     y_labels=['d', 'e', 'f'],
        ...     z_labels=['g', 'h', 'i']
        ... )
        >>> volume.fig
        ```

        ![](/vectorbt/docs/img/Volume.svg)
        """
        Configured.__init__(self,
                            data=data,
                            x_labels=x_labels,
                            y_labels=y_labels,
                            z_labels=z_labels,
                            trace_kwargs=trace_kwargs,
                            add_trace_kwargs=add_trace_kwargs,
                            scene_name=scene_name,
                            fig=fig,
                            **layout_kwargs)

        from vectorbt._settings import settings
        layout_cfg = settings['plotting']['layout']

        if trace_kwargs is None:
            trace_kwargs = {}
        if add_trace_kwargs is None:
            add_trace_kwargs = {}
        if data is None:
            if x_labels is None or y_labels is None or z_labels is None:
                raise ValueError(
                    "At least x_labels, y_labels and z_labels must be passed")
            x_len = len(x_labels)
            y_len = len(y_labels)
            z_len = len(z_labels)
        else:
            checks.assert_ndim(data, 3)
            data = np.asarray(data)
            x_len, y_len, z_len = data.shape
        if x_labels is None:
            x_labels = np.arange(x_len)
        else:
            x_labels = clean_labels(x_labels)
        if y_labels is None:
            y_labels = np.arange(y_len)
        else:
            y_labels = clean_labels(y_labels)
        if z_labels is None:
            z_labels = np.arange(z_len)
        else:
            z_labels = clean_labels(z_labels)
        x_labels = np.asarray(x_labels)
        y_labels = np.asarray(y_labels)
        z_labels = np.asarray(z_labels)

        if fig is None:
            fig = make_figure()
            if 'width' in layout_cfg:
                # Calculate nice width and height
                fig.update_layout(width=layout_cfg['width'],
                                  height=0.7 * layout_cfg['width'])

        # Non-numeric data types are not supported by go.Volume, so use ticktext
        # Note: Currently plotly displays the entire tick array, in future versions it will be more sensible
        more_layout = dict()
        if not np.issubdtype(x_labels.dtype, np.number):
            x_ticktext = x_labels
            x_labels = np.arange(x_len)
            more_layout[scene_name] = dict(xaxis=dict(
                ticktext=x_ticktext, tickvals=x_labels, tickmode='array'))
        if not np.issubdtype(y_labels.dtype, np.number):
            y_ticktext = y_labels
            y_labels = np.arange(y_len)
            more_layout[scene_name] = dict(yaxis=dict(
                ticktext=y_ticktext, tickvals=y_labels, tickmode='array'))
        if not np.issubdtype(z_labels.dtype, np.number):
            z_ticktext = z_labels
            z_labels = np.arange(z_len)
            more_layout[scene_name] = dict(zaxis=dict(
                ticktext=z_ticktext, tickvals=z_labels, tickmode='array'))
        fig.update_layout(**more_layout)
        fig.update_layout(**layout_kwargs)

        # Arrays must have the same length as the flattened data array
        x = np.repeat(x_labels, len(y_labels) * len(z_labels))
        y = np.tile(np.repeat(y_labels, len(z_labels)), len(x_labels))
        z = np.tile(z_labels, len(x_labels) * len(y_labels))

        volume = go.Volume(
            x=x,
            y=y,
            z=z,
            opacity=0.2,
            surface_count=15,  # keep low for big data
            colorscale='Plasma')
        volume.update(**trace_kwargs)
        fig.add_trace(volume, **add_trace_kwargs)

        TraceUpdater.__init__(self, fig, (fig.data[-1], ))

        if data is not None:
            self.update(data)
Ejemplo n.º 9
0
    def plot(self,
             column: tp.Optional[tp.Label] = None,
             levels: tp.Tuple[float, float] = (30, 70),
             rsi_trace_kwargs: tp.KwargsLike = None,
             add_trace_kwargs: tp.KwargsLike = None,
             xref: str = 'x', 
             yref: str = 'y',
             fig: tp.Optional[tp.BaseFigure] = None,
             **layout_kwargs) -> tp.BaseFigure:  # pragma: no cover
        """Plot `RSI.rsi`.

        Args:
            column (str): Name of the column to plot.
            levels (tuple): Two extremes: bottom and top.
            rsi_trace_kwargs (dict): Keyword arguments passed to `plotly.graph_objects.Scatter` for `RSI.rsi`.
            add_trace_kwargs (dict): Keyword arguments passed to `add_trace`.
            xref (str): X coordinate axis.
            yref (str): Y coordinate axis.
            fig (Figure or FigureWidget): Figure to add traces to.
            **layout_kwargs: Keyword arguments for layout.

        ## Example

        ```python-repl
        >>> vbt.RSI.run(ohlcv['Close']).plot()
        ```

        ![](/vectorbt/docs/img/RSI.svg)
        """
        self_col = self.select_series(column=column)

        if fig is None:
            fig = make_figure()
        default_layout = dict()
        default_layout['yaxis' + yref[1:]] = dict(range=[-5, 105])
        fig.update_layout(**default_layout)
        fig.update_layout(**layout_kwargs)

        if rsi_trace_kwargs is None:
            rsi_trace_kwargs = {}
        rsi_trace_kwargs = merge_dicts(dict(
            name='RSI'
        ), rsi_trace_kwargs)

        fig = self_col.rsi.vbt.plot(
            trace_kwargs=rsi_trace_kwargs,
            add_trace_kwargs=add_trace_kwargs, fig=fig)

        # Fill void between levels
        fig.add_shape(
            type="rect",
            xref=xref,
            yref=yref,
            x0=self_col.rsi.index[0],
            y0=levels[0],
            x1=self_col.rsi.index[-1],
            y1=levels[1],
            fillcolor="purple",
            opacity=0.2,
            layer="below",
            line_width=0,
        )

        return fig
Ejemplo n.º 10
0
    def plot(self,
             column: tp.Optional[tp.Label] = None,
             macd_trace_kwargs: tp.KwargsLike = None,
             signal_trace_kwargs: tp.KwargsLike = None,
             hist_trace_kwargs: tp.KwargsLike = None,
             add_trace_kwargs: tp.KwargsLike = None,
             fig: tp.Optional[tp.BaseFigure] = None,
             **layout_kwargs) -> tp.BaseFigure:  # pragma: no cover
        """Plot `MACD.macd`, `MACD.signal` and `MACD.hist`.

        Args:
            column (str): Name of the column to plot.
            macd_trace_kwargs (dict): Keyword arguments passed to `plotly.graph_objects.Scatter` for `MACD.macd`.
            signal_trace_kwargs (dict): Keyword arguments passed to `plotly.graph_objects.Scatter` for `MACD.signal`.
            hist_trace_kwargs (dict): Keyword arguments passed to `plotly.graph_objects.Bar` for `MACD.hist`.
            add_trace_kwargs (dict): Keyword arguments passed to `add_trace`.
            fig (Figure or FigureWidget): Figure to add traces to.
            **layout_kwargs: Keyword arguments for layout.

        ## Example

        ```python-repl
        >>> vbt.MACD.run(ohlcv['Close']).plot()
        ```

        ![](/docs/img/MACD.svg)"""
        self_col = self.select_one(column=column)

        if fig is None:
            fig = make_figure()
            fig.update_layout(bargap=0)
        fig.update_layout(**layout_kwargs)

        if macd_trace_kwargs is None:
            macd_trace_kwargs = {}
        if signal_trace_kwargs is None:
            signal_trace_kwargs = {}
        if hist_trace_kwargs is None:
            hist_trace_kwargs = {}
        macd_trace_kwargs = merge_dicts(dict(
            name='MACD'
        ), macd_trace_kwargs)
        signal_trace_kwargs = merge_dicts(dict(
            name='Signal'
        ), signal_trace_kwargs)
        hist_trace_kwargs = merge_dicts(dict(name='Histogram'), hist_trace_kwargs)

        fig = self_col.macd.vbt.plot(
            trace_kwargs=macd_trace_kwargs,
            add_trace_kwargs=add_trace_kwargs, fig=fig)
        fig = self_col.signal.vbt.plot(
            trace_kwargs=signal_trace_kwargs,
            add_trace_kwargs=add_trace_kwargs, fig=fig)

        # Plot hist
        hist = self_col.hist.values
        hist_diff = generic_nb.diff_1d_nb(hist)
        marker_colors = np.full(hist.shape, adjust_opacity('silver', 0.75), dtype=object)
        marker_colors[(hist > 0) & (hist_diff > 0)] = adjust_opacity('green', 0.75)
        marker_colors[(hist > 0) & (hist_diff <= 0)] = adjust_opacity('lightgreen', 0.75)
        marker_colors[(hist < 0) & (hist_diff < 0)] = adjust_opacity('red', 0.75)
        marker_colors[(hist < 0) & (hist_diff >= 0)] = adjust_opacity('lightcoral', 0.75)

        hist_bar = go.Bar(
            x=self_col.hist.index,
            y=self_col.hist.values,
            marker_color=marker_colors,
            marker_line_width=0
        )
        hist_bar.update(**hist_trace_kwargs)
        if add_trace_kwargs is None:
            add_trace_kwargs = {}
        fig.add_trace(hist_bar, **add_trace_kwargs)

        return fig
Ejemplo n.º 11
0
    def __init__(self,
                 data: tp.Optional[tp.ArrayLike] = None,
                 trace_names: tp.TraceNames = None,
                 horizontal: bool = False,
                 remove_nan: bool = True,
                 from_quantile: tp.Optional[float] = None,
                 to_quantile: tp.Optional[float] = None,
                 trace_kwargs: tp.KwargsLikeSequence = None,
                 add_trace_kwargs: tp.KwargsLike = None,
                 fig: tp.Optional[tp.BaseFigure] = None,
                 **layout_kwargs) -> None:
        """Create a histogram plot.

        Args:
            data (array_like): Data in any format that can be converted to NumPy.

                Must be of shape (any, `trace_names`).
            trace_names (str or list of str): Trace names, corresponding to columns in pandas.
            horizontal (bool): Plot horizontally.
            remove_nan (bool): Whether to remove NaN values.
            from_quantile (float): Filter out data points before this quantile.

                Should be in range `[0, 1]`.
            to_quantile (float): Filter out data points after this quantile.

                Should be in range `[0, 1]`.
            trace_kwargs (dict or list of dict): Keyword arguments passed to `plotly.graph_objects.Histogram`.

                Can be specified per trace as a sequence of dicts.
            add_trace_kwargs (dict): Keyword arguments passed to `add_trace`.
            fig (Figure or FigureWidget): Figure to add traces to.
            **layout_kwargs: Keyword arguments for layout.

        ## Example

        ```python-repl
        >>> import vectorbt as vbt

        >>> hist = vbt.plotting.Histogram(
        ...     data=[[1, 2], [3, 4], [2, 1]],
        ...     trace_names=['a', 'b']
        ... )
        >>> hist.fig
        ```
        ![](/vectorbt/docs/img/Histogram.svg)
        """
        Configured.__init__(self,
                            data=data,
                            trace_names=trace_names,
                            horizontal=horizontal,
                            remove_nan=remove_nan,
                            from_quantile=from_quantile,
                            to_quantile=to_quantile,
                            trace_kwargs=trace_kwargs,
                            add_trace_kwargs=add_trace_kwargs,
                            fig=fig,
                            **layout_kwargs)

        if trace_kwargs is None:
            trace_kwargs = {}
        if add_trace_kwargs is None:
            add_trace_kwargs = {}
        if data is None:
            if trace_names is None:
                raise ValueError("At least trace_names must be passed")
        if trace_names is None:
            data = reshape_fns.to_2d(data)
            trace_names = [None] * data.shape[1]
        if isinstance(trace_names, str):
            trace_names = [trace_names]

        if fig is None:
            fig = make_figure()
            fig.update_layout(barmode='overlay')
        fig.update_layout(**layout_kwargs)

        for i, trace_name in enumerate(trace_names):
            _trace_kwargs = resolve_dict(trace_kwargs, i=i)
            trace_name = _trace_kwargs.pop('name', trace_name)
            if trace_name is not None:
                trace_name = str(trace_name)
            hist = go.Histogram(opacity=0.75 if len(trace_names) > 1 else 1,
                                name=trace_name,
                                showlegend=trace_name is not None)
            hist.update(**_trace_kwargs)
            fig.add_trace(hist, **add_trace_kwargs)

        TraceUpdater.__init__(self, fig, fig.data[-len(trace_names):])
        self.horizontal = horizontal
        self.remove_nan = remove_nan
        self.from_quantile = from_quantile
        self.to_quantile = to_quantile

        if data is not None:
            self.update(data)
Ejemplo n.º 12
0
    def __init__(self,
                 data: tp.Optional[tp.ArrayLike] = None,
                 trace_names: tp.TraceNames = None,
                 x_labels: tp.Optional[tp.Labels] = None,
                 trace_kwargs: tp.KwargsLikeSequence = None,
                 add_trace_kwargs: tp.KwargsLike = None,
                 fig: tp.Optional[tp.BaseFigure] = None,
                 **layout_kwargs) -> None:
        """Create a scatter plot.

        Args:
            data (array_like): Data in any format that can be converted to NumPy.

                Must be of shape (`x_labels`, `trace_names`).
            trace_names (str or list of str): Trace names, corresponding to columns in pandas.
            x_labels (array_like): X-axis labels, corresponding to index in pandas.
            trace_kwargs (dict or list of dict): Keyword arguments passed to `plotly.graph_objects.Scatter`.

                Can be specified per trace as a sequence of dicts.
            add_trace_kwargs (dict): Keyword arguments passed to `add_trace`.
            fig (Figure or FigureWidget): Figure to add traces to.
            **layout_kwargs: Keyword arguments for layout.

        ## Example

        ```python-repl
        >>> import vectorbt as vbt

        >>> scatter = vbt.plotting.Scatter(
        ...     data=[[1, 2], [3, 4]],
        ...     trace_names=['a', 'b'],
        ...     x_labels=['x', 'y']
        ... )
        >>> scatter.fig
        ```
        ![](/vectorbt/docs/img/Scatter.svg)
        """
        Configured.__init__(self,
                            data=data,
                            trace_names=trace_names,
                            x_labels=x_labels,
                            trace_kwargs=trace_kwargs,
                            add_trace_kwargs=add_trace_kwargs,
                            fig=fig,
                            **layout_kwargs)

        if trace_kwargs is None:
            trace_kwargs = {}
        if add_trace_kwargs is None:
            add_trace_kwargs = {}
        if data is None:
            if trace_names is None:
                raise ValueError("At least trace_names must be passed")
        if trace_names is None:
            data = reshape_fns.to_2d(data)
            trace_names = [None] * data.shape[1]
        if isinstance(trace_names, str):
            trace_names = [trace_names]
        if x_labels is not None:
            x_labels = clean_labels(x_labels)

        if fig is None:
            fig = make_figure()
        fig.update_layout(**layout_kwargs)

        for i, trace_name in enumerate(trace_names):
            _trace_kwargs = resolve_dict(trace_kwargs, i=i)
            trace_name = _trace_kwargs.pop('name', trace_name)
            if trace_name is not None:
                trace_name = str(trace_name)
            scatter = go.Scatter(x=x_labels,
                                 name=trace_name,
                                 showlegend=trace_name is not None)
            scatter.update(**_trace_kwargs)
            fig.add_trace(scatter, **add_trace_kwargs)

        TraceUpdater.__init__(self, fig, fig.data[-len(trace_names):])

        if data is not None:
            self.update(data)
Ejemplo n.º 13
0
    def plot(self,
             column: tp.Optional[tp.Label] = None,
             top_n: int = 5,
             plot_zones: bool = True,
             ts_trace_kwargs: tp.KwargsLike = None,
             start_trace_kwargs: tp.KwargsLike = None,
             end_trace_kwargs: tp.KwargsLike = None,
             open_shape_kwargs: tp.KwargsLike = None,
             closed_shape_kwargs: tp.KwargsLike = None,
             add_trace_kwargs: tp.KwargsLike = None,
             xref: str = 'x',
             yref: str = 'y',
             fig: tp.Optional[tp.BaseFigure] = None,
             **layout_kwargs) -> tp.BaseFigure:  # pragma: no cover
        """Plot ranges.

        Args:
            column (str): Name of the column to plot.
            top_n (int): Filter top N range records by maximum duration.
            plot_zones (bool): Whether to plot zones.
            ts_trace_kwargs (dict): Keyword arguments passed to `plotly.graph_objects.Scatter` for `Ranges.ts`.
            start_trace_kwargs (dict): Keyword arguments passed to `plotly.graph_objects.Scatter` for start values.
            end_trace_kwargs (dict): Keyword arguments passed to `plotly.graph_objects.Scatter` for end values.
            open_shape_kwargs (dict): Keyword arguments passed to `plotly.graph_objects.Figure.add_shape` for open zones.
            closed_shape_kwargs (dict): Keyword arguments passed to `plotly.graph_objects.Figure.add_shape` for closed zones.
            add_trace_kwargs (dict): Keyword arguments passed to `add_trace`.
            xref (str): X coordinate axis.
            yref (str): Y coordinate axis.
            fig (Figure or FigureWidget): Figure to add traces to.
            **layout_kwargs: Keyword arguments for layout.

        ## Example

        ```python-repl
        >>> import vectorbt as vbt
        >>> from datetime import datetime, timedelta
        >>> import pandas as pd

        >>> price = pd.Series([1, 2, 1, 2, 3, 2, 1, 2], name='Price')
        >>> price.index = [datetime(2020, 1, 1) + timedelta(days=i) for i in range(len(price))]
        >>> vbt.Ranges.from_ts(price >= 2, wrapper_kwargs=dict(freq='1 day')).plot()
        ```

        ![](/docs/img/ranges_plot.svg)
        """
        from vectorbt._settings import settings
        plotting_cfg = settings['plotting']

        self_col = self.select_one(column=column, group_by=False)
        if top_n is not None:
            self_col = self_col.apply_mask(self_col.duration.top_n_mask(top_n))

        if ts_trace_kwargs is None:
            ts_trace_kwargs = {}
        ts_trace_kwargs = merge_dicts(
            dict(line=dict(color=plotting_cfg['color_schema']['blue'])),
            ts_trace_kwargs)
        if start_trace_kwargs is None:
            start_trace_kwargs = {}
        if end_trace_kwargs is None:
            end_trace_kwargs = {}
        if open_shape_kwargs is None:
            open_shape_kwargs = {}
        if closed_shape_kwargs is None:
            closed_shape_kwargs = {}
        if add_trace_kwargs is None:
            add_trace_kwargs = {}

        if fig is None:
            fig = make_figure()
        fig.update_layout(**layout_kwargs)
        y_domain = get_domain(yref, fig)

        if self_col.ts is not None:
            fig = self_col.ts.vbt.plot(trace_kwargs=ts_trace_kwargs,
                                       add_trace_kwargs=add_trace_kwargs,
                                       fig=fig)

        if self_col.count() > 0:
            # Extract information
            id_ = self_col.get_field_arr('id')
            id_title = self_col.get_field_title('id')

            start_idx = self_col.get_map_field_to_index('start_idx')
            start_idx_title = self_col.get_field_title('start_idx')
            if self_col.ts is not None:
                start_val = self_col.ts.loc[start_idx]
            else:
                start_val = np.full(len(start_idx), 0)

            end_idx = self_col.get_map_field_to_index('end_idx')
            end_idx_title = self_col.get_field_title('end_idx')
            if self_col.ts is not None:
                end_val = self_col.ts.loc[end_idx]
            else:
                end_val = np.full(len(end_idx), 0)

            duration = np.vectorize(str)(self_col.wrapper.to_timedelta(
                self_col.duration.values, to_pd=True, silence_warnings=True))

            status = self_col.get_field_arr('status')

            # Plot start markers
            start_customdata = id_[:, None]
            start_scatter = go.Scatter(
                x=start_idx,
                y=start_val,
                mode='markers',
                marker=dict(
                    symbol='diamond',
                    color=plotting_cfg['contrast_color_schema']['blue'],
                    size=7,
                    line=dict(
                        width=1,
                        color=adjust_lightness(
                            plotting_cfg['contrast_color_schema']['blue']))),
                name='Start',
                customdata=start_customdata,
                hovertemplate=f"{id_title}: %{{customdata[0]}}"
                f"<br>{start_idx_title}: %{{x}}")
            start_scatter.update(**start_trace_kwargs)
            fig.add_trace(start_scatter, **add_trace_kwargs)

            closed_mask = status == RangeStatus.Closed
            if closed_mask.any():
                # Plot end markers
                closed_end_customdata = np.stack(
                    (id_[closed_mask], duration[closed_mask]), axis=1)
                closed_end_scatter = go.Scatter(
                    x=end_idx[closed_mask],
                    y=end_val[closed_mask],
                    mode='markers',
                    marker=dict(
                        symbol='diamond',
                        color=plotting_cfg['contrast_color_schema']['green'],
                        size=7,
                        line=dict(width=1,
                                  color=adjust_lightness(
                                      plotting_cfg['contrast_color_schema']
                                      ['green']))),
                    name='Closed',
                    customdata=closed_end_customdata,
                    hovertemplate=f"{id_title}: %{{customdata[0]}}"
                    f"<br>{end_idx_title}: %{{x}}"
                    f"<br>Duration: %{{customdata[1]}}")
                closed_end_scatter.update(**end_trace_kwargs)
                fig.add_trace(closed_end_scatter, **add_trace_kwargs)

                if plot_zones:
                    # Plot closed range zones
                    for i in range(len(id_[closed_mask])):
                        fig.add_shape(**merge_dicts(
                            dict(
                                type="rect",
                                xref=xref,
                                yref="paper",
                                x0=start_idx[closed_mask][i],
                                y0=y_domain[0],
                                x1=end_idx[closed_mask][i],
                                y1=y_domain[1],
                                fillcolor='teal',
                                opacity=0.2,
                                layer="below",
                                line_width=0,
                            ), closed_shape_kwargs))

            open_mask = status == RangeStatus.Open
            if open_mask.any():
                # Plot end markers
                open_end_customdata = np.stack(
                    (id_[open_mask], duration[open_mask]), axis=1)
                open_end_scatter = go.Scatter(
                    x=end_idx[open_mask],
                    y=end_val[open_mask],
                    mode='markers',
                    marker=dict(
                        symbol='diamond',
                        color=plotting_cfg['contrast_color_schema']['orange'],
                        size=7,
                        line=dict(width=1,
                                  color=adjust_lightness(
                                      plotting_cfg['contrast_color_schema']
                                      ['orange']))),
                    name='Open',
                    customdata=open_end_customdata,
                    hovertemplate=f"{id_title}: %{{customdata[0]}}"
                    f"<br>{end_idx_title}: %{{x}}"
                    f"<br>Duration: %{{customdata[1]}}")
                open_end_scatter.update(**end_trace_kwargs)
                fig.add_trace(open_end_scatter, **add_trace_kwargs)

                if plot_zones:
                    # Plot open range zones
                    for i in range(len(id_[open_mask])):
                        fig.add_shape(**merge_dicts(
                            dict(
                                type="rect",
                                xref=xref,
                                yref="paper",
                                x0=start_idx[open_mask][i],
                                y0=y_domain[0],
                                x1=end_idx[open_mask][i],
                                y1=y_domain[1],
                                fillcolor='orange',
                                opacity=0.2,
                                layer="below",
                                line_width=0,
                            ), open_shape_kwargs))

        return fig
Ejemplo n.º 14
0
    def plot(self,
             column: tp.Optional[tp.Label] = None,
             close_trace_kwargs: tp.KwargsLike = None,
             buy_trace_kwargs: tp.KwargsLike = None,
             sell_trace_kwargs: tp.KwargsLike = None,
             add_trace_kwargs: tp.KwargsLike = None,
             fig: tp.Optional[tp.BaseFigure] = None,
             **layout_kwargs) -> tp.BaseFigure:  # pragma: no cover
        """Plot orders.

        Args:
            column (str): Name of the column to plot.
            close_trace_kwargs (dict): Keyword arguments passed to `plotly.graph_objects.Scatter` for `Orders.close`.
            buy_trace_kwargs (dict): Keyword arguments passed to `plotly.graph_objects.Scatter` for "Buy" markers.
            sell_trace_kwargs (dict): Keyword arguments passed to `plotly.graph_objects.Scatter` for "Sell" markers.
            add_trace_kwargs (dict): Keyword arguments passed to `add_trace`.
            fig (Figure or FigureWidget): Figure to add traces to.
            **layout_kwargs: Keyword arguments for layout.

        ## Example

        ```python-repl
        >>> import pandas as pd
        >>> from datetime import datetime, timedelta
        >>> import vectorbt as vbt

        >>> price = pd.Series([1., 2., 3., 2., 1.], name='Price')
        >>> price.index = [datetime(2020, 1, 1) + timedelta(days=i) for i in range(len(price))]
        >>> size = pd.Series([1., 1., 1., 1., -1.])
        >>> orders = vbt.Portfolio.from_orders(price, size).orders

        >>> orders.plot()
        ```

        ![](/docs/img/orders_plot.svg)"""
        from vectorbt._settings import settings
        plotting_cfg = settings['plotting']

        self_col = self.select_one(column=column, group_by=False)

        if close_trace_kwargs is None:
            close_trace_kwargs = {}
        close_trace_kwargs = merge_dicts(dict(
            line=dict(
                color=plotting_cfg['color_schema']['blue']
            ),
            name='Close'
        ), close_trace_kwargs)
        if buy_trace_kwargs is None:
            buy_trace_kwargs = {}
        if sell_trace_kwargs is None:
            sell_trace_kwargs = {}
        if add_trace_kwargs is None:
            add_trace_kwargs = {}

        if fig is None:
            fig = make_figure()
        fig.update_layout(**layout_kwargs)

        # Plot price
        if self_col.close is not None:
            fig = self_col.close.vbt.plot(trace_kwargs=close_trace_kwargs, add_trace_kwargs=add_trace_kwargs, fig=fig)

        if self_col.count() > 0:
            # Extract information
            id_ = self_col.get_field_arr('id')
            id_title = self_col.get_field_title('id')

            idx = self_col.get_map_field_to_index('idx')
            idx_title = self_col.get_field_title('idx')

            size = self_col.get_field_arr('size')
            size_title = self_col.get_field_title('size')

            fees = self_col.get_field_arr('fees')
            fees_title = self_col.get_field_title('fees')

            price = self_col.get_field_arr('price')
            price_title = self_col.get_field_title('price')

            side = self_col.get_field_arr('side')

            buy_mask = side == OrderSide.Buy
            if buy_mask.any():
                # Plot buy markers
                buy_customdata = np.stack((
                    id_[buy_mask],
                    size[buy_mask],
                    fees[buy_mask]
                ), axis=1)
                buy_scatter = go.Scatter(
                    x=idx[buy_mask],
                    y=price[buy_mask],
                    mode='markers',
                    marker=dict(
                        symbol='triangle-up',
                        color=plotting_cfg['contrast_color_schema']['green'],
                        size=8,
                        line=dict(
                            width=1,
                            color=adjust_lightness(plotting_cfg['contrast_color_schema']['green'])
                        )
                    ),
                    name='Buy',
                    customdata=buy_customdata,
                    hovertemplate=f"{id_title}: %{{customdata[0]}}"
                                  f"<br>{idx_title}: %{{x}}"
                                  f"<br>{price_title}: %{{y}}"
                                  f"<br>{size_title}: %{{customdata[1]:.6f}}"
                                  f"<br>{fees_title}: %{{customdata[2]:.6f}}"
                )
                buy_scatter.update(**buy_trace_kwargs)
                fig.add_trace(buy_scatter, **add_trace_kwargs)

            sell_mask = side == OrderSide.Sell
            if sell_mask.any():
                # Plot sell markers
                sell_customdata = np.stack((
                    id_[sell_mask],
                    size[sell_mask],
                    fees[sell_mask]
                ), axis=1)
                sell_scatter = go.Scatter(
                    x=idx[sell_mask],
                    y=price[sell_mask],
                    mode='markers',
                    marker=dict(
                        symbol='triangle-down',
                        color=plotting_cfg['contrast_color_schema']['red'],
                        size=8,
                        line=dict(
                            width=1,
                            color=adjust_lightness(plotting_cfg['contrast_color_schema']['red'])
                        )
                    ),
                    name='Sell',
                    customdata=sell_customdata,
                    hovertemplate=f"{id_title}: %{{customdata[0]}}"
                                  f"<br>{idx_title}: %{{x}}"
                                  f"<br>{price_title}: %{{y}}"
                                  f"<br>{size_title}: %{{customdata[1]:.6f}}"
                                  f"<br>{fees_title}: %{{customdata[2]:.6f}}"
                )
                sell_scatter.update(**sell_trace_kwargs)
                fig.add_trace(sell_scatter, **add_trace_kwargs)

        return fig
Ejemplo n.º 15
0
    def plot(self,
             column: tp.Optional[tp.Label] = None,
             top_n: int = 5,
             plot_ts: bool = True,
             plot_zones: bool = True,
             ts_trace_kwargs: tp.KwargsLike = None,
             peak_trace_kwargs: tp.KwargsLike = None,
             valley_trace_kwargs: tp.KwargsLike = None,
             recovery_trace_kwargs: tp.KwargsLike = None,
             active_trace_kwargs: tp.KwargsLike = None,
             ptv_shape_kwargs: tp.KwargsLike = None,
             vtr_shape_kwargs: tp.KwargsLike = None,
             active_shape_kwargs: tp.KwargsLike = None,
             add_trace_kwargs: tp.KwargsLike = None,
             xref: str = 'x',
             yref: str = 'y',
             fig: tp.Optional[tp.BaseFigure] = None,
             **layout_kwargs) -> tp.BaseFigure:  # 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 (Figure or FigureWidget): 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.svg)
        """
        from vectorbt._settings import settings
        plotting_cfg = settings['plotting']

        self_col = self.select_one(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=dict(color=plotting_cfg['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 = make_figure()
        fig.update_layout(**layout_kwargs)
        y_domain = get_domain(yref, fig)

        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, DatetimeIndexes):
                    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=plotting_cfg['contrast_color_schema']['blue'],
                        size=7,
                        line=dict(width=1,
                                  color=adjust_lightness(
                                      plotting_cfg['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=plotting_cfg['contrast_color_schema']['red'],
                        size=7,
                        line=dict(width=1,
                                  color=adjust_lightness(
                                      plotting_cfg['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=plotting_cfg['contrast_color_schema']['green'],
                        size=7,
                        line=dict(width=1,
                                  color=adjust_lightness(
                                      plotting_cfg['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=plotting_cfg['contrast_color_schema']['orange'],
                        size=7,
                        line=dict(width=1,
                                  color=adjust_lightness(
                                      plotting_cfg['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
Ejemplo n.º 16
0
    def plot(self,
             column: tp.Optional[tp.Label] = None,
             plot_close: bool = True,
             close_trace_kwargs: tp.KwargsLike = None,
             buy_trace_kwargs: tp.KwargsLike = None,
             sell_trace_kwargs: tp.KwargsLike = None,
             add_trace_kwargs: tp.KwargsLike = None,
             fig: tp.Optional[tp.BaseFigure] = None,
             **layout_kwargs) -> tp.BaseFigure:  # pragma: no cover
        """Plot orders.

        Args:
            column (str): Name of the column to plot.
            plot_close (bool): Whether to plot `Orders.close`.
            close_trace_kwargs (dict): Keyword arguments passed to `plotly.graph_objects.Scatter` for `Orders.close`.
            buy_trace_kwargs (dict): Keyword arguments passed to `plotly.graph_objects.Scatter` for "Buy" markers.
            sell_trace_kwargs (dict): Keyword arguments passed to `plotly.graph_objects.Scatter` for "Sell" markers.
            add_trace_kwargs (dict): Keyword arguments passed to `add_trace`.
            fig (Figure or FigureWidget): Figure to add traces to.
            **layout_kwargs: Keyword arguments for layout.

        ## Example

        ```python-repl
        >>> orders.plot()
        ```

        ![](/vectorbt/docs/img/orders_plot.svg)"""
        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 buy_trace_kwargs is None:
            buy_trace_kwargs = {}
        if sell_trace_kwargs is None:
            sell_trace_kwargs = {}
        if add_trace_kwargs is None:
            add_trace_kwargs = {}

        if fig is None:
            fig = make_figure()
        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']
            idx = self_col.values['idx']
            size = self_col.values['size']
            price = self_col.values['price']
            fees = self_col.values['fees']
            side = self_col.values['side']

            # Plot Buy markers
            buy_mask = side == OrderSide.Buy
            buy_customdata = np.stack(
                (_id[buy_mask], size[buy_mask], fees[buy_mask]), axis=1)
            buy_scatter = go.Scatter(
                x=self_col.wrapper.index[idx[buy_mask]],
                y=price[buy_mask],
                mode='markers',
                marker=dict(symbol='triangle-up',
                            color=contrast_color_schema['green'],
                            size=8,
                            line=dict(width=1,
                                      color=adjust_lightness(
                                          contrast_color_schema['green']))),
                name='Buy',
                customdata=buy_customdata,
                hovertemplate="Order Id: %{customdata[0]}"
                "<br>Date: %{x}"
                "<br>Price: %{y}"
                "<br>Size: %{customdata[1]:.6f}"
                "<br>Fees: %{customdata[2]:.6f}")
            buy_scatter.update(**buy_trace_kwargs)
            fig.add_trace(buy_scatter, **add_trace_kwargs)

            # Plot Sell markers
            sell_mask = side == OrderSide.Sell
            sell_customdata = np.stack(
                (_id[sell_mask], size[sell_mask], fees[sell_mask]), axis=1)
            sell_scatter = go.Scatter(
                x=self_col.wrapper.index[idx[sell_mask]],
                y=price[sell_mask],
                mode='markers',
                marker=dict(symbol='triangle-down',
                            color=contrast_color_schema['red'],
                            size=8,
                            line=dict(width=1,
                                      color=adjust_lightness(
                                          contrast_color_schema['red']))),
                name='Sell',
                customdata=sell_customdata,
                hovertemplate="Order Id: %{customdata[0]}"
                "<br>Date: %{x}"
                "<br>Price: %{y}"
                "<br>Size: %{customdata[1]:.6f}"
                "<br>Fees: %{customdata[2]:.6f}")
            sell_scatter.update(**sell_trace_kwargs)
            fig.add_trace(sell_scatter, **add_trace_kwargs)

        return fig
Ejemplo n.º 17
0
    def plot(self,
             column: tp.Optional[tp.Label] = None,
             plot_close: bool = True,
             close_trace_kwargs: tp.KwargsLike = None,
             middle_trace_kwargs: tp.KwargsLike = None,
             upper_trace_kwargs: tp.KwargsLike = None,
             lower_trace_kwargs: tp.KwargsLike = None,
             add_trace_kwargs: tp.KwargsLike = None,
             fig: tp.Optional[tp.BaseFigure] = None,
             **layout_kwargs) -> tp.BaseFigure:  # pragma: no cover
        """Plot `BBANDS.middle`, `BBANDS.upper` and `BBANDS.lower` against
        `BBANDS.close`.

        Args:
            column (str): Name of the column to plot.
            plot_close (bool): Whether to plot `MA.close`.
            close_trace_kwargs (dict): Keyword arguments passed to `plotly.graph_objects.Scatter` for `BBANDS.close`.
            middle_trace_kwargs (dict): Keyword arguments passed to `plotly.graph_objects.Scatter` for `BBANDS.middle`.
            upper_trace_kwargs (dict): Keyword arguments passed to `plotly.graph_objects.Scatter` for `BBANDS.upper`.
            lower_trace_kwargs (dict): Keyword arguments passed to `plotly.graph_objects.Scatter` for `BBANDS.lower`.
            add_trace_kwargs (dict): Keyword arguments passed to `add_trace`.
            fig (Figure or FigureWidget): Figure to add traces to.
            **layout_kwargs: Keyword arguments for layout.

        ## Example

        ```python-repl
        >>> vbt.BBANDS.run(ohlcv['Close']).plot()
        ```

        ![](/docs/img/BBANDS.svg)
        """
        from vectorbt._settings import settings
        plotting_cfg = settings['plotting']

        self_col = self.select_one(column=column)

        if fig is None:
            fig = make_figure()
        fig.update_layout(**layout_kwargs)

        if close_trace_kwargs is None:
            close_trace_kwargs = {}
        if middle_trace_kwargs is None:
            middle_trace_kwargs = {}
        if upper_trace_kwargs is None:
            upper_trace_kwargs = {}
        if lower_trace_kwargs is None:
            lower_trace_kwargs = {}
        lower_trace_kwargs = merge_dicts(dict(
            name='Lower Band',
            line=dict(
                color=adjust_opacity(plotting_cfg['color_schema']['gray'], 0.75)
            ),
        ), lower_trace_kwargs)
        upper_trace_kwargs = merge_dicts(dict(
            name='Upper Band',
            line=dict(
                color=adjust_opacity(plotting_cfg['color_schema']['gray'], 0.75)
            ),
            fill='tonexty',
            fillcolor='rgba(128, 128, 128, 0.2)'
        ), upper_trace_kwargs)  # default kwargs
        middle_trace_kwargs = merge_dicts(dict(
            name='Middle Band'
        ), middle_trace_kwargs)
        close_trace_kwargs = merge_dicts(dict(
            name='Close',
            line=dict(color=plotting_cfg['color_schema']['blue'])
        ), close_trace_kwargs)

        fig = self_col.lower.vbt.plot(
            trace_kwargs=lower_trace_kwargs,
            add_trace_kwargs=add_trace_kwargs, fig=fig)
        fig = self_col.upper.vbt.plot(
            trace_kwargs=upper_trace_kwargs,
            add_trace_kwargs=add_trace_kwargs, fig=fig)
        fig = self_col.middle.vbt.plot(
            trace_kwargs=middle_trace_kwargs,
            add_trace_kwargs=add_trace_kwargs, fig=fig)
        if plot_close:
            fig = self_col.close.vbt.plot(
                trace_kwargs=close_trace_kwargs,
                add_trace_kwargs=add_trace_kwargs, fig=fig)

        return fig
Ejemplo n.º 18
0
    def plot(self,
             column: tp.Optional[tp.Label] = None,
             levels: tp.Tuple[float, float] = (30, 70),
             percent_k_trace_kwargs: tp.KwargsLike = None,
             percent_d_trace_kwargs: tp.KwargsLike = None,
             shape_kwargs: tp.KwargsLike = None,
             add_trace_kwargs: tp.KwargsLike = None,
             xref: str = 'x',
             yref: str = 'y',
             fig: tp.Optional[tp.BaseFigure] = None,
             **layout_kwargs) -> tp.BaseFigure:  # 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 `Figure or FigureWidget.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 (Figure or FigureWidget): Figure to add traces to.
            **layout_kwargs: Keyword arguments for layout.

        ## Example

        ```python-repl
        >>> vbt.STOCH.run(ohlcv['High'], ohlcv['Low'], ohlcv['Close']).plot()
        ```

        ![](/docs/img/STOCH.svg)
        """
        self_col = self.select_one(column=column)

        if fig is None:
            fig = make_figure()
        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
Ejemplo n.º 19
0
    def __init__(self,
                 data: tp.Optional[tp.ArrayLike] = None,
                 trace_names: tp.TraceNames = None,
                 horizontal: bool = False,
                 remove_nan: bool = True,
                 from_quantile: tp.Optional[float] = None,
                 to_quantile: tp.Optional[float] = None,
                 trace_kwargs: tp.KwargsLikeSequence = None,
                 add_trace_kwargs: tp.KwargsLike = None,
                 fig: tp.Optional[tp.BaseFigure] = None,
                 **layout_kwargs) -> None:
        """Create a box plot.

        For keyword arguments, see `Histogram`.

        ## Example

        ```python-repl
        >>> import vectorbt as vbt

        >>> box = vbt.plotting.Box(
        ...     data=[[1, 2], [3, 4], [2, 1]],
        ...     trace_names=['a', 'b']
        ... )
        >>> box.fig
        ```
        ![](/vectorbt/docs/img/Box.svg)
        """
        Configured.__init__(self,
                            data=data,
                            trace_names=trace_names,
                            horizontal=horizontal,
                            remove_nan=remove_nan,
                            from_quantile=from_quantile,
                            to_quantile=to_quantile,
                            trace_kwargs=trace_kwargs,
                            add_trace_kwargs=add_trace_kwargs,
                            fig=fig,
                            **layout_kwargs)

        if trace_kwargs is None:
            trace_kwargs = {}
        if add_trace_kwargs is None:
            add_trace_kwargs = {}
        if data is None:
            if trace_names is None:
                raise ValueError("At least trace_names must be passed")
        if trace_names is None:
            data = reshape_fns.to_2d(data)
            trace_names = [None] * data.shape[1]
        if isinstance(trace_names, str):
            trace_names = [trace_names]

        if fig is None:
            fig = make_figure()
        fig.update_layout(**layout_kwargs)

        for i, trace_name in enumerate(trace_names):
            _trace_kwargs = resolve_dict(trace_kwargs, i=i)
            trace_name = _trace_kwargs.pop('name', trace_name)
            if trace_name is not None:
                trace_name = str(trace_name)
            box = go.Box(name=trace_name, showlegend=trace_name is not None)
            box.update(**_trace_kwargs)
            fig.add_trace(box, **add_trace_kwargs)

        TraceUpdater.__init__(self, fig, fig.data[-len(trace_names):])
        self.horizontal = horizontal
        self.remove_nan = remove_nan
        self.from_quantile = from_quantile
        self.to_quantile = to_quantile

        if data is not None:
            self.update(data)
Ejemplo n.º 20
0
    def plot(self,
             plot_type: tp.Union[None, str, tp.BaseTraceType] = None,
             ohlc_kwargs: tp.KwargsLike = None,
             entry_trace_kwargs: tp.KwargsLike = None,
             exit_trace_kwargs: tp.KwargsLike = None,
             add_trace_kwargs: tp.KwargsLike = None,
             fig: tp.Optional[tp.BaseFigure] = None,
             _base_cls_plot: tp.Callable = base_cls_plot,
             **layout_kwargs) -> tp.BaseFigure:  # pragma: no cover
        from vectorbt._settings import settings
        ohlcv_cfg = settings['ohlcv']
        plotting_cfg = settings['plotting']

        if self.wrapper.ndim > 1:
            raise TypeError("Select a column first. Use indexing.")

        if ohlc_kwargs is None:
            ohlc_kwargs = {}
        if add_trace_kwargs is None:
            add_trace_kwargs = {}

        if fig is None:
            fig = make_figure()
            fig.update_layout(showlegend=True,
                              xaxis_rangeslider_visible=False,
                              xaxis_showgrid=True,
                              yaxis_showgrid=True)
        fig.update_layout(**layout_kwargs)

        if plot_type is None:
            plot_type = ohlcv_cfg['plot_type']
        if isinstance(plot_type, str):
            if plot_type.lower() == 'ohlc':
                plot_type = 'OHLC'
                plot_obj = go.Ohlc
            elif plot_type.lower() == 'candlestick':
                plot_type = 'Candlestick'
                plot_obj = go.Candlestick
            else:
                raise ValueError(
                    "Plot type can be either 'OHLC' or 'Candlestick'")
        else:
            plot_obj = plot_type
        ohlc = plot_obj(
            x=self.wrapper.index,
            open=self.open,
            high=self.high,
            low=self.low,
            close=self.close,
            name=plot_type,
            increasing_line_color=plotting_cfg['color_schema']['increasing'],
            decreasing_line_color=plotting_cfg['color_schema']['decreasing'])
        ohlc.update(**ohlc_kwargs)
        fig.add_trace(ohlc, **add_trace_kwargs)

        # Plot entry and exit markers
        _base_cls_plot(self,
                       entry_y=self.open,
                       exit_y=self.hit_price,
                       exit_types=self.stop_type_readable,
                       entry_trace_kwargs=entry_trace_kwargs,
                       exit_trace_kwargs=exit_trace_kwargs,
                       add_trace_kwargs=add_trace_kwargs,
                       fig=fig)
        return fig
Ejemplo n.º 21
0
    def plot_pnl_returns(self,
                         column: tp.Optional[tp.Label] = None,
                         as_pct: bool = True,
                         marker_size_range: tp.Tuple[float, float] = (7, 14),
                         opacity_range: tp.Tuple[float, float] = (0.75, 0.9),
                         closed_profit_trace_kwargs: tp.KwargsLike = None,
                         closed_loss_trace_kwargs: tp.KwargsLike = None,
                         open_trace_kwargs: tp.KwargsLike = None,
                         hline_shape_kwargs: tp.KwargsLike = None,
                         add_trace_kwargs: tp.KwargsLike = None,
                         xref: str = 'x',
                         yref: str = 'y',
                         fig: tp.Optional[tp.BaseFigure] = None,
                         **layout_kwargs) -> tp.BaseFigure:  # pragma: no cover
        """Plot trade PnL.

        Args:
            column (str): Name of the column to plot.
            as_pct (bool): Whether to set y-axis to `Trades.returns`, otherwise to `Trades.pnl`.
            marker_size_range (tuple): Range of marker size.
            opacity_range (tuple): Range of marker opacity.
            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 (Figure or FigureWidget): Figure to add traces to.
            **layout_kwargs: Keyword arguments for layout.
        """
        from vectorbt._settings import settings
        plotting_cfg = settings['plotting']

        self_col = self.select_one(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)
        xaxis = 'xaxis' + xref[1:]
        yaxis = 'yaxis' + yref[1:]

        if fig is None:
            fig = make_figure()
        if as_pct:
            _layout_kwargs = dict()
            _layout_kwargs[yaxis] = dict(tickformat='.2%')
            fig.update_layout(**_layout_kwargs)
        fig.update_layout(**layout_kwargs)
        x_domain = get_domain(xref, fig)

        if len(self_col.values) > 0:
            # Extract information
            _pnl_str = '%{customdata[1]:.6f}' if as_pct else '%{y}'
            _return_str = '%{y}' if as_pct else '%{customdata[1]:.2%}'
            exit_idx = self_col.values['exit_idx']
            pnl = self_col.values['pnl']
            returns = self_col.values['return']
            status = self_col.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

            def _plot_scatter(mask: tp.Array1d, name: tp.TraceName,
                              color: tp.Any, kwargs: tp.Kwargs) -> None:
                if np.any(mask):
                    if self_col.trade_type == TradeType.Trade:
                        customdata = np.stack(
                            (self_col.values['id'][mask],
                             self_col.values['position_id'][mask],
                             pnl[mask] if as_pct else returns[mask]),
                            axis=1)
                        hovertemplate = "Trade Id: %{customdata[0]}" \
                                        "<br>Position Id: %{customdata[1]}" \
                                        "<br>Date: %{x}" \
                                        f"<br>PnL: {_pnl_str}" \
                                        f"<br>Return: {_return_str}"
                    else:
                        customdata = np.stack(
                            (self_col.values['id'][mask],
                             pnl[mask] if as_pct else returns[mask]),
                            axis=1)
                        hovertemplate = "Position Id: %{customdata[0]}" \
                                        "<br>Date: %{x}" \
                                        f"<br>PnL: {_pnl_str}" \
                                        f"<br>Return: {_return_str}"
                    scatter = go.Scatter(
                        x=self_col.wrapper.index[exit_idx[mask]],
                        y=returns[mask] if as_pct else pnl[mask],
                        mode='markers',
                        marker=dict(
                            symbol='circle',
                            color=color,
                            size=marker_size[mask],
                            opacity=opacity[mask],
                            line=dict(width=1, color=adjust_lightness(color)),
                        ),
                        name=name,
                        customdata=customdata,
                        hovertemplate=hovertemplate)
                    scatter.update(**kwargs)
                    fig.add_trace(scatter, **add_trace_kwargs)

            # Plot Closed - Profit scatter
            _plot_scatter(closed_profit_mask, 'Closed - Profit',
                          plotting_cfg['contrast_color_schema']['green'],
                          closed_profit_trace_kwargs)

            # Plot Closed - Profit scatter
            _plot_scatter(closed_loss_mask, 'Closed - Loss',
                          plotting_cfg['contrast_color_schema']['red'],
                          closed_loss_trace_kwargs)

            # Plot Open scatter
            _plot_scatter(open_mask, 'Open',
                          plotting_cfg['contrast_color_schema']['orange'],
                          open_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
Ejemplo n.º 22
0
    def __init__(self,
                 data: tp.Optional[tp.ArrayLike] = None,
                 x_labels: tp.Optional[tp.Labels] = None,
                 y_labels: tp.Optional[tp.Labels] = None,
                 is_x_category: bool = False,
                 is_y_category: bool = False,
                 trace_kwargs: tp.KwargsLike = None,
                 add_trace_kwargs: tp.KwargsLike = None,
                 fig: tp.Optional[tp.BaseFigure] = None,
                 **layout_kwargs) -> None:
        """Create a heatmap plot.

        Args:
            data (array_like): Data in any format that can be converted to NumPy.

                Must be of shape (`y_labels`, `x_labels`).
            x_labels (array_like): X-axis labels, corresponding to columns in pandas.
            y_labels (array_like): Y-axis labels, corresponding to index in pandas.
            is_x_category (bool): Whether X-axis is a categorical axis.
            is_y_category (bool): Whether Y-axis is a categorical axis.
            trace_kwargs (dict): Keyword arguments passed to `plotly.graph_objects.Heatmap`.
            add_trace_kwargs (dict): Keyword arguments passed to `add_trace`.
            fig (Figure or FigureWidget): Figure to add traces to.
            **layout_kwargs: Keyword arguments for layout.

        ## Example

        ```python-repl
        >>> import vectorbt as vbt

        >>> heatmap = vbt.plotting.Heatmap(
        ...     data=[[1, 2], [3, 4]],
        ...     x_labels=['a', 'b'],
        ...     y_labels=['x', 'y']
        ... )
        >>> heatmap.fig
        ```
        ![](/vectorbt/docs/img/Heatmap.svg)
        """
        Configured.__init__(self,
                            data=data,
                            x_labels=x_labels,
                            y_labels=y_labels,
                            trace_kwargs=trace_kwargs,
                            add_trace_kwargs=add_trace_kwargs,
                            fig=fig,
                            **layout_kwargs)

        from vectorbt._settings import settings
        layout_cfg = settings['plotting']['layout']

        if trace_kwargs is None:
            trace_kwargs = {}
        if add_trace_kwargs is None:
            add_trace_kwargs = {}
        if data is None:
            if x_labels is None or y_labels is None:
                raise ValueError(
                    "At least x_labels and y_labels must be passed")
        else:
            data = reshape_fns.to_2d(np.asarray(data))
        if x_labels is not None:
            x_labels = clean_labels(x_labels)
        if y_labels is not None:
            y_labels = clean_labels(y_labels)

        if fig is None:
            fig = make_figure()
            if 'width' in layout_cfg:
                # Calculate nice width and height
                max_width = layout_cfg['width']
                if data is not None:
                    x_len = data.shape[1]
                    y_len = data.shape[0]
                else:
                    x_len = len(x_labels)
                    y_len = len(y_labels)
                width = math.ceil(
                    renormalize(x_len / (x_len + y_len), (0, 1),
                                (0.3 * max_width, max_width)))
                width = min(width + 150, max_width)  # account for colorbar
                height = math.ceil(
                    renormalize(y_len / (x_len + y_len), (0, 1),
                                (0.3 * max_width, max_width)))
                height = min(height, max_width * 0.7)  # limit height
                fig.update_layout(width=width, height=height)

        heatmap = go.Heatmap(hoverongaps=False,
                             colorscale='Plasma',
                             x=x_labels,
                             y=y_labels)
        heatmap.update(**trace_kwargs)
        fig.add_trace(heatmap, **add_trace_kwargs)

        axis_kwargs = dict()
        if is_x_category:
            if fig.data[-1]['xaxis'] is not None:
                axis_kwargs['xaxis' +
                            fig.data[-1]['xaxis'][1:]] = dict(type='category')
            else:
                axis_kwargs['xaxis'] = dict(type='category')
        if is_y_category:
            if fig.data[-1]['yaxis'] is not None:
                axis_kwargs['yaxis' +
                            fig.data[-1]['yaxis'][1:]] = dict(type='category')
            else:
                axis_kwargs['yaxis'] = dict(type='category')
        fig.update_layout(**axis_kwargs)
        fig.update_layout(**layout_kwargs)

        TraceUpdater.__init__(self, fig, (fig.data[-1], ))

        if data is not None:
            self.update(data)
Ejemplo n.º 23
0
    def plot(self,
             column: tp.Optional[tp.Label] = None,
             plot_close: bool = True,
             plot_zones: bool = True,
             close_trace_kwargs: tp.KwargsLike = None,
             entry_trace_kwargs: tp.KwargsLike = None,
             exit_trace_kwargs: tp.KwargsLike = None,
             exit_profit_trace_kwargs: tp.KwargsLike = None,
             exit_loss_trace_kwargs: tp.KwargsLike = None,
             active_trace_kwargs: tp.KwargsLike = None,
             profit_shape_kwargs: tp.KwargsLike = None,
             loss_shape_kwargs: tp.KwargsLike = None,
             add_trace_kwargs: tp.KwargsLike = None,
             xref: str = 'x',
             yref: str = 'y',
             fig: tp.Optional[tp.BaseFigure] = None,
             **layout_kwargs) -> tp.BaseFigure:  # 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 (Figure or FigureWidget): Figure to add traces to.
            **layout_kwargs: Keyword arguments for layout.

        ## Example

        ```python-repl
        >>> trades.plot()
        ```

        ![](/vectorbt/docs/img/trades_plot.svg)"""
        from vectorbt._settings import settings
        plotting_cfg = settings['plotting']

        self_col = self.select_one(column=column, group_by=False)

        if close_trace_kwargs is None:
            close_trace_kwargs = {}
        close_trace_kwargs = merge_dicts(
            dict(line=dict(color=plotting_cfg['color_schema']['blue']),
                 name='Close'), 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 = make_figure()
        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 = enum_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: int, to_idx: int) -> tp.Array1d:
                if isinstance(self_col.wrapper.index, DatetimeIndexes):
                    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=plotting_cfg['contrast_color_schema']['blue'],
                        size=7,
                        line=dict(width=1,
                                  color=adjust_lightness(
                                      plotting_cfg['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: tp.Array1d, name: tp.TraceName,
                                  color: tp.Any, kwargs: tp.Kwargs) -> None:
                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',
                              plotting_cfg['contrast_color_schema']['gray'],
                              exit_trace_kwargs)

            # Plot Exit - Profit markers
            _plot_end_markers((status == TradeStatus.Closed) & (pnl > 0.),
                              'Exit - Profit',
                              plotting_cfg['contrast_color_schema']['green'],
                              exit_profit_trace_kwargs)

            # Plot Exit - Loss markers
            _plot_end_markers((status == TradeStatus.Closed) & (pnl < 0.),
                              'Exit - Loss',
                              plotting_cfg['contrast_color_schema']['red'],
                              exit_loss_trace_kwargs)

            # Plot Active markers
            _plot_end_markers(status == TradeStatus.Open, 'Active',
                              plotting_cfg['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
Ejemplo n.º 24
0
    def plot(self,
             column: tp.Optional[tp.Label] = None,
             top_n: int = 5,
             plot_zones: bool = True,
             ts_trace_kwargs: tp.KwargsLike = None,
             peak_trace_kwargs: tp.KwargsLike = None,
             valley_trace_kwargs: tp.KwargsLike = None,
             recovery_trace_kwargs: tp.KwargsLike = None,
             active_trace_kwargs: tp.KwargsLike = None,
             decline_shape_kwargs: tp.KwargsLike = None,
             recovery_shape_kwargs: tp.KwargsLike = None,
             active_shape_kwargs: tp.KwargsLike = None,
             add_trace_kwargs: tp.KwargsLike = None,
             xref: str = 'x',
             yref: str = 'y',
             fig: tp.Optional[tp.BaseFigure] = None,
             **layout_kwargs) -> tp.BaseFigure:  # pragma: no cover
        """Plot drawdowns.

        Args:
            column (str): Name of the column to plot.
            top_n (int): Filter top N drawdown records by maximum drawdown.
            plot_zones (bool): Whether to plot zones.
            ts_trace_kwargs (dict): Keyword arguments passed to `plotly.graph_objects.Scatter` for `Drawdowns.ts`.
            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.
            decline_shape_kwargs (dict): Keyword arguments passed to `plotly.graph_objects.Figure.add_shape` for decline zones.
            recovery_shape_kwargs (dict): Keyword arguments passed to `plotly.graph_objects.Figure.add_shape` for recovery zones.
            active_shape_kwargs (dict): Keyword arguments passed to `plotly.graph_objects.Figure.add_shape` for active recovery zones.
            add_trace_kwargs (dict): Keyword arguments passed to `add_trace`.
            xref (str): X coordinate axis.
            yref (str): Y coordinate axis.
            fig (Figure or FigureWidget): Figure to add traces to.
            **layout_kwargs: Keyword arguments for layout.

        ## Example

        ```python-repl
        >>> import vectorbt as vbt
        >>> from datetime import datetime, timedelta
        >>> import pandas as pd

        >>> price = pd.Series([1, 2, 1, 2, 3, 2, 1, 2], name='Price')
        >>> price.index = [datetime(2020, 1, 1) + timedelta(days=i) for i in range(len(price))]
        >>> vbt.Drawdowns.from_ts(price, wrapper_kwargs=dict(freq='1 day')).plot()
        ```

        ![](/docs/img/drawdowns_plot.svg)
        """
        from vectorbt._settings import settings
        plotting_cfg = settings['plotting']

        self_col = self.select_one(column=column, group_by=False)
        if top_n is not None:
            # Drawdowns is negative, thus top_n becomes bottom_n
            self_col = self_col.apply_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=dict(color=plotting_cfg['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 decline_shape_kwargs is None:
            decline_shape_kwargs = {}
        if recovery_shape_kwargs is None:
            recovery_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 = make_figure()
        fig.update_layout(**layout_kwargs)
        y_domain = get_domain(yref, fig)

        if self_col.ts is not None:
            fig = self_col.ts.vbt.plot(trace_kwargs=ts_trace_kwargs,
                                       add_trace_kwargs=add_trace_kwargs,
                                       fig=fig)

        if self_col.count() > 0:
            # Extract information
            id_ = self_col.get_field_arr('id')
            id_title = self_col.get_field_title('id')

            peak_idx = self_col.get_map_field_to_index('peak_idx')
            peak_idx_title = self_col.get_field_title('peak_idx')

            if self_col.ts is not None:
                peak_val = self_col.ts.loc[peak_idx]
            else:
                peak_val = self_col.get_field_arr('peak_val')
            peak_val_title = self_col.get_field_title('peak_val')

            valley_idx = self_col.get_map_field_to_index('valley_idx')
            valley_idx_title = self_col.get_field_title('valley_idx')

            if self_col.ts is not None:
                valley_val = self_col.ts.loc[valley_idx]
            else:
                valley_val = self_col.get_field_arr('valley_val')
            valley_val_title = self_col.get_field_title('valley_val')

            end_idx = self_col.get_map_field_to_index('end_idx')
            end_idx_title = self_col.get_field_title('end_idx')

            if self_col.ts is not None:
                end_val = self_col.ts.loc[end_idx]
            else:
                end_val = self_col.get_field_arr('end_val')
            end_val_title = self_col.get_field_title('end_val')

            drawdown = self_col.drawdown.values
            recovery_return = self_col.recovery_return.values
            decline_duration = np.vectorize(str)(self_col.wrapper.to_timedelta(
                self_col.decline_duration.values,
                to_pd=True,
                silence_warnings=True))
            recovery_duration = np.vectorize(str)(
                self_col.wrapper.to_timedelta(
                    self_col.recovery_duration.values,
                    to_pd=True,
                    silence_warnings=True))
            duration = np.vectorize(str)(self_col.wrapper.to_timedelta(
                self_col.duration.values, to_pd=True, silence_warnings=True))

            status = self_col.get_field_arr('status')

            peak_mask = peak_idx != np.roll(
                end_idx, 1)  # peak and recovery at same time -> recovery wins
            if peak_mask.any():
                # Plot peak markers
                peak_customdata = id_[peak_mask][:, None]
                peak_scatter = go.Scatter(
                    x=peak_idx[peak_mask],
                    y=peak_val[peak_mask],
                    mode='markers',
                    marker=dict(
                        symbol='diamond',
                        color=plotting_cfg['contrast_color_schema']['blue'],
                        size=7,
                        line=dict(width=1,
                                  color=adjust_lightness(
                                      plotting_cfg['contrast_color_schema']
                                      ['blue']))),
                    name='Peak',
                    customdata=peak_customdata,
                    hovertemplate=f"{id_title}: %{{customdata[0]}}"
                    f"<br>{peak_idx_title}: %{{x}}"
                    f"<br>{peak_val_title}: %{{y}}")
                peak_scatter.update(**peak_trace_kwargs)
                fig.add_trace(peak_scatter, **add_trace_kwargs)

            recovered_mask = status == DrawdownStatus.Recovered
            if recovered_mask.any():
                # Plot valley markers
                valley_customdata = np.stack(
                    (id_[recovered_mask], drawdown[recovered_mask],
                     decline_duration[recovered_mask]),
                    axis=1)
                valley_scatter = go.Scatter(
                    x=valley_idx[recovered_mask],
                    y=valley_val[recovered_mask],
                    mode='markers',
                    marker=dict(
                        symbol='diamond',
                        color=plotting_cfg['contrast_color_schema']['red'],
                        size=7,
                        line=dict(width=1,
                                  color=adjust_lightness(
                                      plotting_cfg['contrast_color_schema']
                                      ['red']))),
                    name='Valley',
                    customdata=valley_customdata,
                    hovertemplate=f"{id_title}: %{{customdata[0]}}"
                    f"<br>{valley_idx_title}: %{{x}}"
                    f"<br>{valley_val_title}: %{{y}}"
                    f"<br>Drawdown: %{{customdata[1]:.2%}}"
                    f"<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 range(len(id_[recovered_mask])):
                        fig.add_shape(**merge_dicts(
                            dict(
                                type="rect",
                                xref=xref,
                                yref="paper",
                                x0=peak_idx[recovered_mask][i],
                                y0=y_domain[0],
                                x1=valley_idx[recovered_mask][i],
                                y1=y_domain[1],
                                fillcolor='red',
                                opacity=0.2,
                                layer="below",
                                line_width=0,
                            ), decline_shape_kwargs))

                # Plot recovery markers
                recovery_customdata = np.stack(
                    (id_[recovered_mask], recovery_return[recovered_mask],
                     recovery_duration[recovered_mask]),
                    axis=1)
                recovery_scatter = go.Scatter(
                    x=end_idx[recovered_mask],
                    y=end_val[recovered_mask],
                    mode='markers',
                    marker=dict(
                        symbol='diamond',
                        color=plotting_cfg['contrast_color_schema']['green'],
                        size=7,
                        line=dict(width=1,
                                  color=adjust_lightness(
                                      plotting_cfg['contrast_color_schema']
                                      ['green']))),
                    name='Recovery/Peak',
                    customdata=recovery_customdata,
                    hovertemplate=f"{id_title}: %{{customdata[0]}}"
                    f"<br>{end_idx_title}: %{{x}}"
                    f"<br>{end_val_title}: %{{y}}"
                    f"<br>Return: %{{customdata[1]:.2%}}"
                    f"<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 range(len(id_[recovered_mask])):
                        fig.add_shape(**merge_dicts(
                            dict(
                                type="rect",
                                xref=xref,
                                yref="paper",
                                x0=valley_idx[recovered_mask][i],
                                y0=y_domain[0],
                                x1=end_idx[recovered_mask][i],
                                y1=y_domain[1],
                                fillcolor='green',
                                opacity=0.2,
                                layer="below",
                                line_width=0,
                            ), recovery_shape_kwargs))

            # Plot active markers
            active_mask = status == DrawdownStatus.Active
            if active_mask.any():
                active_customdata = np.stack(
                    (id_[active_mask], drawdown[active_mask],
                     duration[active_mask]),
                    axis=1)
                active_scatter = go.Scatter(
                    x=end_idx[active_mask],
                    y=end_val[active_mask],
                    mode='markers',
                    marker=dict(
                        symbol='diamond',
                        color=plotting_cfg['contrast_color_schema']['orange'],
                        size=7,
                        line=dict(width=1,
                                  color=adjust_lightness(
                                      plotting_cfg['contrast_color_schema']
                                      ['orange']))),
                    name='Active',
                    customdata=active_customdata,
                    hovertemplate=f"{id_title}: %{{customdata[0]}}"
                    f"<br>{end_idx_title}: %{{x}}"
                    f"<br>{end_val_title}: %{{y}}"
                    f"<br>Return: %{{customdata[1]:.2%}}"
                    f"<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 range(len(id_[active_mask])):
                        fig.add_shape(**merge_dicts(
                            dict(
                                type="rect",
                                xref=xref,
                                yref="paper",
                                x0=peak_idx[active_mask][i],
                                y0=y_domain[0],
                                x1=end_idx[active_mask][i],
                                y1=y_domain[1],
                                fillcolor='orange',
                                opacity=0.2,
                                layer="below",
                                line_width=0,
                            ), active_shape_kwargs))

        return fig
Ejemplo n.º 25
0
    def __init__(self,
                 value: tp.Optional[float] = None,
                 label: tp.Optional[str] = None,
                 value_range: tp.Optional[tp.Tuple[float, float]] = None,
                 cmap_name: str = 'Spectral',
                 trace_kwargs: tp.KwargsLike = None,
                 add_trace_kwargs: tp.KwargsLike = None,
                 fig: tp.Optional[tp.BaseFigure] = None,
                 **layout_kwargs) -> None:
        """Create a gauge plot.

        Args:
            value (float): The value to be displayed.
            label (str): The label to be displayed.
            value_range (tuple of float): The value range of the gauge.
            cmap_name (str): A matplotlib-compatible colormap name.

                See the [list of available colormaps](https://matplotlib.org/tutorials/colors/colormaps.html).
            trace_kwargs (dict): Keyword arguments passed to the `plotly.graph_objects.Indicator`.
            add_trace_kwargs (dict): Keyword arguments passed to `add_trace`.
            fig (Figure or FigureWidget): Figure to add traces to.
            **layout_kwargs: Keyword arguments for layout.

        ## Example

        ```python-repl
        >>> import vectorbt as vbt

        >>> gauge = vbt.plotting.Gauge(
        ...     value=2,
        ...     value_range=(1, 3),
        ...     label='My Gauge'
        ... )
        >>> gauge.fig
        ```
        ![](/vectorbt/docs/img/Gauge.svg)
        """
        Configured.__init__(self,
                            value=value,
                            label=label,
                            value_range=value_range,
                            cmap_name=cmap_name,
                            trace_kwargs=trace_kwargs,
                            add_trace_kwargs=add_trace_kwargs,
                            fig=fig,
                            **layout_kwargs)

        from vectorbt._settings import settings
        layout_cfg = settings['plotting']['layout']

        if trace_kwargs is None:
            trace_kwargs = {}
        if add_trace_kwargs is None:
            add_trace_kwargs = {}

        if fig is None:
            fig = make_figure()
            if 'width' in layout_cfg:
                # Calculate nice width and height
                fig.update_layout(width=layout_cfg['width'] * 0.7,
                                  height=layout_cfg['width'] * 0.5,
                                  margin=dict(t=80))
        fig.update_layout(**layout_kwargs)

        indicator = go.Indicator(domain=dict(x=[0, 1], y=[0, 1]),
                                 mode="gauge+number+delta",
                                 title=dict(text=label))
        indicator.update(**trace_kwargs)
        fig.add_trace(indicator, **add_trace_kwargs)

        TraceUpdater.__init__(self, fig, (fig.data[-1], ))
        self.value_range = value_range
        self.cmap_name = cmap_name

        if value is not None:
            self.update(value)