def plot(self, tr_trace_kwargs={}, atr_trace_kwargs={}, fig=None, **layout_kwargs): # pragma: no cover """Plot `ATR.tr` and `ATR.atr`. Args: 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`. fig (plotly.graph_objects.Figure): Figure to add traces to. **layout_kwargs: Keyword arguments for layout. Example: ```py atr[(10, False)].plot() ``` ![](/vectorbt/docs/img/ATR.png)""" if self.wrapper.ndim > 1: raise TypeError("Select a column first. Use indexing.") tr_trace_kwargs = merge_kwargs(dict(name=f'TR ({self.short_name})'), tr_trace_kwargs) atr_trace_kwargs = merge_kwargs(dict(name=f'ATR ({self.short_name})'), atr_trace_kwargs) fig = self.tr.vbt.plot(trace_kwargs=tr_trace_kwargs, fig=fig, **layout_kwargs) fig = self.atr.vbt.plot(trace_kwargs=atr_trace_kwargs, fig=fig, **layout_kwargs) return fig
def plot(self, close_trace_kwargs={}, ma_trace_kwargs={}, fig=None, **layout_kwargs): # pragma: no cover """Plot `MA.ma` against `MA.close`. Args: 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`. fig (plotly.graph_objects.Figure): Figure to add traces to. **layout_kwargs: Keyword arguments for layout. Example: ```py ma[(10, False)].plot() ``` ![](/vectorbt/docs/img/MA.png)""" if self.wrapper.ndim > 1: raise TypeError("Select a column first. Use indexing.") close_trace_kwargs = merge_kwargs( dict(name=f'Close ({self.short_name})'), close_trace_kwargs) ma_trace_kwargs = merge_kwargs(dict(name=f'MA ({self.short_name})'), ma_trace_kwargs) fig = self.close.vbt.plot(trace_kwargs=close_trace_kwargs, fig=fig, **layout_kwargs) fig = self.ma.vbt.plot(trace_kwargs=ma_trace_kwargs, fig=fig, **layout_kwargs) return fig
def plot(self, macd_trace_kwargs=None, signal_trace_kwargs=None, hist_trace_kwargs=None, fig=None, **layout_kwargs): # pragma: no cover """Plot `MACD.macd`, `MACD.signal` and `MACD.hist`. Args: 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`. fig (plotly.graph_objects.Figure): Figure to add traces to. **layout_kwargs: Keyword arguments for layout. Example: ```python-repl >>> vbt.MACD.run(price['Close']).plot() ``` ![](/vectorbt/docs/img/MACD.png)""" if self.wrapper.ndim > 1: raise TypeError("Select a column first. Use indexing.") 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_kwargs(dict(name='MACD'), macd_trace_kwargs) signal_trace_kwargs = merge_kwargs(dict(name='Signal'), signal_trace_kwargs) hist_trace_kwargs = merge_kwargs(dict(name='Histogram'), hist_trace_kwargs) layout_kwargs = merge_kwargs(dict(bargap=0), layout_kwargs) fig = self.macd.vbt.plot(trace_kwargs=macd_trace_kwargs, fig=fig, **layout_kwargs) fig = self.signal.vbt.plot(trace_kwargs=signal_trace_kwargs, fig=fig, **layout_kwargs) # Plot hist hist = self.hist.values hist_diff = generic_nb.diff_1d_nb(hist) marker_colors = np.full(hist.shape, 'silver', dtype=np.object) marker_colors[(hist > 0) & (hist_diff > 0)] = 'green' marker_colors[(hist > 0) & (hist_diff <= 0)] = 'lightgreen' marker_colors[(hist < 0) & (hist_diff < 0)] = 'red' marker_colors[(hist < 0) & (hist_diff >= 0)] = 'lightcoral' hist_bar = go.Bar(x=self.hist.index, y=self.hist.values, marker_color=marker_colors, marker_line_width=0) hist_bar.update(**hist_trace_kwargs) fig.add_trace(hist_bar) return fig
def plot(self, close_trace_kwargs={}, middle_trace_kwargs={}, upper_trace_kwargs={}, lower_trace_kwargs={}, fig=None, **layout_kwargs): # pragma: no cover """Plot `BollingerBands.middle`, `BollingerBands.upper` and `BollingerBands.lower` against `BollingerBands.close`. Args: close_trace_kwargs (dict): Keyword arguments passed to `plotly.graph_objects.Scatter` for `BollingerBands.close`. middle_trace_kwargs (dict): Keyword arguments passed to `plotly.graph_objects.Scatter` for `BollingerBands.middle`. upper_trace_kwargs (dict): Keyword arguments passed to `plotly.graph_objects.Scatter` for `BollingerBands.upper`. lower_trace_kwargs (dict): Keyword arguments passed to `plotly.graph_objects.Scatter` for `BollingerBands.lower`. fig (plotly.graph_objects.Figure): Figure to add traces to. **layout_kwargs: Keyword arguments for layout. Example: ```py bb[(10, False, 2)].plot() ``` ![](/vectorbt/docs/img/BollingerBands.png)""" if self.wrapper.ndim > 1: raise TypeError("Select a column first. Use indexing.") lower_trace_kwargs = merge_kwargs( dict(name=f'Lower Band ({self.short_name})', line=dict(color='silver')), lower_trace_kwargs) upper_trace_kwargs = merge_kwargs( dict(name=f'Upper Band ({self.short_name})', line=dict(color='silver'), fill='tonexty', fillcolor='rgba(128, 128, 128, 0.25)'), upper_trace_kwargs) # default kwargs middle_trace_kwargs = merge_kwargs( dict(name=f'Middle Band ({self.short_name})', line=dict(color=defaults.layout['colorway'][1])), middle_trace_kwargs) close_trace_kwargs = merge_kwargs( dict(name=f'Close ({self.short_name})', line=dict(color=defaults.layout['colorway'][0])), close_trace_kwargs) fig = self.lower.vbt.plot(trace_kwargs=lower_trace_kwargs, fig=fig, **layout_kwargs) fig = self.upper.vbt.plot(trace_kwargs=upper_trace_kwargs, fig=fig, **layout_kwargs) fig = self.middle.vbt.plot(trace_kwargs=middle_trace_kwargs, fig=fig, **layout_kwargs) fig = self.close.vbt.plot(trace_kwargs=close_trace_kwargs, fig=fig, **layout_kwargs) return fig
def plot(self, levels=(30, 70), percent_k_trace_kwargs={}, percent_d_trace_kwargs={}, shape_kwargs={}, fig=None, **layout_kwargs): # pragma: no cover """Plot `Stochastic.percent_k` and `Stochastic.percent_d`. Args: percent_k_trace_kwargs (dict): Keyword arguments passed to `plotly.graph_objects.Scatter` for `Stochastic.percent_k`. percent_d_trace_kwargs (dict): Keyword arguments passed to `plotly.graph_objects.Scatter` for `Stochastic.percent_d`. shape_kwargs (dict): Keyword arguments passed to `plotly.graph_objects.Figure.add_shape` for zone between levels. fig (plotly.graph_objects.Figure): Figure to add traces to. **layout_kwargs: Keyword arguments for layout. Example: ```py stoch[(10, 2, False)].plot(levels=(20, 80)) ``` ![](/vectorbt/docs/img/Stochastic.png)""" if self.wrapper.ndim > 1: raise TypeError("Select a column first. Use indexing.") percent_k_trace_kwargs = merge_kwargs( dict(name=f'%K ({self.short_name})'), percent_k_trace_kwargs) percent_d_trace_kwargs = merge_kwargs( dict(name=f'%D ({self.short_name})'), percent_d_trace_kwargs) layout_kwargs = merge_kwargs(dict(yaxis=dict(range=[-5, 105])), layout_kwargs) fig = self.percent_k.vbt.plot(trace_kwargs=percent_k_trace_kwargs, fig=fig, **layout_kwargs) fig = self.percent_d.vbt.plot(trace_kwargs=percent_d_trace_kwargs, fig=fig, **layout_kwargs) # Plot levels # Fill void between levels shape_kwargs = merge_kwargs( dict( type="rect", xref="x", yref="y", x0=self.percent_k.index[0], y0=levels[0], x1=self.percent_k.index[-1], y1=levels[1], fillcolor="purple", opacity=0.15, layer="below", line_width=0, ), shape_kwargs) fig.add_shape(**shape_kwargs) return fig
def plot(self, macd_trace_kwargs={}, signal_trace_kwargs={}, histogram_trace_kwargs={}, fig=None, **layout_kwargs): # pragma: no cover """Plot `MACD.macd`, `MACD.signal` and `MACD.histogram`. Args: 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`. histogram_trace_kwargs (dict): Keyword arguments passed to `plotly.graph_objects.Bar` for `MACD.histogram`. fig (plotly.graph_objects.Figure): Figure to add traces to. **layout_kwargs: Keyword arguments for layout. Example: ```py macd[(10, 20, 30, False, True)].plot() ``` ![](/vectorbt/docs/img/MACD.png)""" if self.wrapper.ndim > 1: raise TypeError("You must select a column first") macd_trace_kwargs = merge_kwargs(dict( name=f'MACD ({self.name})' ), macd_trace_kwargs) signal_trace_kwargs = merge_kwargs(dict( name=f'Signal ({self.name})' ), signal_trace_kwargs) histogram_trace_kwargs = merge_kwargs(dict( name=f'Histogram ({self.name})', showlegend=False ), histogram_trace_kwargs) layout_kwargs = merge_kwargs(dict(bargap=0), layout_kwargs) fig = self.macd.vbt.plot(trace_kwargs=macd_trace_kwargs, fig=fig, **layout_kwargs) fig = self.signal.vbt.plot(trace_kwargs=signal_trace_kwargs, fig=fig, **layout_kwargs) # Plot histogram hist = self.histogram.values hist_diff = generic_nb.diff_1d_nb(hist) marker_colors = np.full(hist.shape, 'silver', dtype=np.object) marker_colors[(hist > 0) & (hist_diff > 0)] = 'green' marker_colors[(hist > 0) & (hist_diff <= 0)] = 'lightgreen' marker_colors[(hist < 0) & (hist_diff < 0)] = 'red' marker_colors[(hist < 0) & (hist_diff >= 0)] = 'lightcoral' histogram_bar = go.Bar( x=self.histogram.index, y=self.histogram.values, marker_color=marker_colors, marker_line_width=0 ) histogram_bar.update(**histogram_trace_kwargs) fig.add_trace(histogram_bar) return fig
def plot(self, mstd_trace_kwargs={}, fig=None, **layout_kwargs): # pragma: no cover """Plot `MSTD.mstd`. Args: mstd_trace_kwargs (dict): Keyword arguments passed to `plotly.graph_objects.Scatter` for `MSTD.mstd`. fig (plotly.graph_objects.Figure): Figure to add traces to. **layout_kwargs: Keyword arguments for layout. Example: ```py mstd[(10, False)].plot() ``` ![](/vectorbt/docs/img/MSTD.png)""" if self.wrapper.ndim > 1: raise TypeError("You must select a column first") mstd_trace_kwargs = merge_kwargs(dict( name=f'MSTD ({self.name})' ), mstd_trace_kwargs) fig = self.mstd.vbt.plot(trace_kwargs=mstd_trace_kwargs, fig=fig, **layout_kwargs) return fig
def plot(self, obv_trace_kwargs={}, fig=None, **layout_kwargs): # pragma: no cover """Plot `OBV.obv`. Args: obv_trace_kwargs (dict): Keyword arguments passed to `plotly.graph_objects.Scatter` for `OBV.obv`. fig (plotly.graph_objects.Figure): Figure to add traces to. **layout_kwargs: Keyword arguments for layout. Example: ```py obv.plot() ``` ![](/vectorbt/docs/img/OBV.png)""" if self.wrapper.ndim > 1: raise TypeError("You must select a column first") obv_trace_kwargs = merge_kwargs(dict( name=f'OBV ({self.name})' ), obv_trace_kwargs) fig = self.obv.vbt.plot(trace_kwargs=obv_trace_kwargs, fig=fig, **layout_kwargs) return fig
def plot(self, obv_trace_kwargs=None, fig=None, **layout_kwargs): # pragma: no cover """Plot `OBV.obv`. Args: obv_trace_kwargs (dict): Keyword arguments passed to `plotly.graph_objects.Scatter` for `OBV.obv`. fig (plotly.graph_objects.Figure): Figure to add traces to. **layout_kwargs: Keyword arguments for layout. Example: ```py >>> vbt.OBV.run(price['Close'], price['Volume']).plot() ``` ![](/vectorbt/docs/img/OBV.png)""" if self.wrapper.ndim > 1: raise TypeError("Select a column first. Use indexing.") if obv_trace_kwargs is None: obv_trace_kwargs = {} obv_trace_kwargs = merge_kwargs(dict(name='OBV'), obv_trace_kwargs) fig = self.obv.vbt.plot(trace_kwargs=obv_trace_kwargs, fig=fig, **layout_kwargs) return fig
def plot(self, mstd_trace_kwargs=None, fig=None, **layout_kwargs): # pragma: no cover """Plot `MSTD.mstd`. Args: mstd_trace_kwargs (dict): Keyword arguments passed to `plotly.graph_objects.Scatter` for `MSTD.mstd`. fig (plotly.graph_objects.Figure): Figure to add traces to. **layout_kwargs: Keyword arguments for layout. Example: ```python-repl >>> vbt.MSTD.run(price['Close'], 10).plot() ``` ![](/vectorbt/docs/img/MSTD.png)""" if self.wrapper.ndim > 1: raise TypeError("Select a column first. Use indexing.") if mstd_trace_kwargs is None: mstd_trace_kwargs = {} mstd_trace_kwargs = merge_kwargs(dict(name='MSTD'), mstd_trace_kwargs) fig = self.mstd.vbt.plot(trace_kwargs=mstd_trace_kwargs, fig=fig, **layout_kwargs) return fig
def plot(self, levels=(30, 70), rsi_trace_kwargs=None, fig=None, **layout_kwargs): # pragma: no cover """Plot `RSI.rsi`. Args: levels (tuple): Two extremes: bottom and top. rsi_trace_kwargs (dict): Keyword arguments passed to `plotly.graph_objects.Scatter` for `RSI.rsi`. fig (plotly.graph_objects.Figure): Figure to add traces to. **layout_kwargs: Keyword arguments for layout. Example: ```python-repl >>> vbt.RSI.run(price['Close']).plot() ``` ![](/vectorbt/docs/img/RSI.png)""" if self.wrapper.ndim > 1: raise TypeError("Select a column first. Use indexing.") if rsi_trace_kwargs is None: rsi_trace_kwargs = {} rsi_trace_kwargs = merge_kwargs(dict(name='RSI'), rsi_trace_kwargs) layout_kwargs = merge_kwargs(dict(yaxis=dict(range=[-5, 105])), layout_kwargs) fig = self.rsi.vbt.plot(trace_kwargs=rsi_trace_kwargs, fig=fig, **layout_kwargs) # Fill void between levels fig.add_shape( type="rect", xref="x", yref="y", x0=self.rsi.index[0], y0=levels[0], x1=self.rsi.index[-1], y1=levels[1], fillcolor="purple", opacity=0.15, layer="below", line_width=0, ) return fig
def plot_trades(self, buy_trace_kwargs={}, sell_trace_kwargs={}, fig=None, **layout_kwargs): """Plot trades as markers. Args: 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. fig (plotly.graph_objects.Figure): Figure to add traces to. **layout_kwargs: Keyword arguments for layout. Example: ```py vbt.Portfolio.from_orders(price, price.diff(), init_capital=100).plot_trades() ``` ![](/vectorbt/docs/img/portfolio_plot_trades.png)""" checks.assert_type(self.price, pd.Series) checks.assert_type(self.trades, pd.Series) sell_mask = self.trades < 0 buy_mask = self.trades > 0 # Plot time series fig = self.price.vbt.timeseries.plot(fig=fig, **layout_kwargs) # Plot markers buy_trace_kwargs = merge_kwargs( dict(customdata=self.trades[buy_mask], hovertemplate='(%{x}, %{y})<br>%{customdata:.6g}', marker=dict(symbol='triangle-up', color='limegreen')), buy_trace_kwargs) buy_mask.vbt.signals.plot_markers(self.price, name='Buy', trace_kwargs=buy_trace_kwargs, fig=fig, **layout_kwargs) sell_trace_kwargs = merge_kwargs( dict(customdata=self.trades[sell_mask], hovertemplate='(%{x}, %{y})<br>%{customdata:.6g}', marker=dict(symbol='triangle-down', color='orangered')), sell_trace_kwargs) sell_mask.vbt.signals.plot_markers(self.price, name='Sell', trace_kwargs=sell_trace_kwargs, fig=fig, **layout_kwargs) return fig
def plot(self, levels=(30, 70), rsi_trace_kwargs={}, fig=None, **layout_kwargs): # pragma: no cover """Plot `RSI.rsi`. Args: trace_kwargs (dict): Keyword arguments passed to `plotly.graph_objects.Scatter` for `RSI.rsi`. fig (plotly.graph_objects.Figure): Figure to add traces to. **layout_kwargs: Keyword arguments for layout. Example: ```py rsi[(10, False)].plot() ``` ![](/vectorbt/docs/img/RSI.png)""" if self.wrapper.ndim > 1: raise TypeError("You must select a column first") rsi_trace_kwargs = merge_kwargs(dict(name=f'RSI ({self.short_name})'), rsi_trace_kwargs) layout_kwargs = merge_kwargs(dict(yaxis=dict(range=[-5, 105])), layout_kwargs) fig = self.rsi.vbt.plot(trace_kwargs=rsi_trace_kwargs, fig=fig, **layout_kwargs) # Fill void between levels fig.add_shape( type="rect", xref="x", yref="y", x0=self.rsi.index[0], y0=levels[0], x1=self.rsi.index[-1], y1=levels[1], fillcolor="purple", opacity=0.15, layer="below", line_width=0, ) return fig
def plot(_self, entry_y=None, exit_y=None, entry_types=None, exit_types=None, entry_trace_kwargs=None, exit_trace_kwargs=None, fig=None, **layout_kwargs): # pragma: no cover if _self.wrapper.ndim > 1: raise TypeError("Select a column first. Use indexing.") if entry_trace_kwargs is None: entry_trace_kwargs = {} if exit_trace_kwargs is None: exit_trace_kwargs = {} if entry_types is not None: entry_trace_kwargs = merge_kwargs( dict(customdata=entry_types, hovertemplate="(%{x}, %{y})<br>Type: %{customdata}"), entry_trace_kwargs) if exit_types is not None: exit_trace_kwargs = merge_kwargs( dict(customdata=exit_types, hovertemplate="(%{x}, %{y})<br>Type: %{customdata}"), exit_trace_kwargs) if exit_only and iteratively: entries = _self.new_entries else: entries = _self.entries exits = _self.exits fig = entries.vbt.signals.plot_as_entry_markers( y=entry_y, trace_kwargs=entry_trace_kwargs, fig=fig, **layout_kwargs) fig = exits.vbt.signals.plot_as_exit_markers( y=exit_y, trace_kwargs=exit_trace_kwargs, fig=fig, **layout_kwargs) return fig
def plot_exit_markers(self, *args, name='Exit', trace_kwargs={}, **kwargs): """Plot signals as exit markers. See `Signals_SRAccessor.plot_markers`.""" trace_kwargs = merge_kwargs( dict(marker=dict(symbol='triangle-down', color='orangered')), trace_kwargs) return self.plot_markers(*args, name=name, trace_kwargs=trace_kwargs, **kwargs)
def generate_take_profit_exits(self, ts, stops, first=True, iteratively=False, keys=None, broadcast_kwargs={}): """Generate take profit exits. See `vectorbt.signals.nb.generate_tp_ex_iter_nb` if `iteratively` is `True`, otherwise see `vectorbt.signals.nb.generate_tp_ex_nb`. Arguments will be broadcasted using `vectorbt.base.reshape_fns.broadcast` with `broadcast_kwargs`. Argument `stops` can be either a single number, an array of numbers, or a 3D array, where each matrix corresponds to a single configuration. Use `keys` as the outermost level. Example: For each entry in `sig`, set take profit for 10% and 20% above the entry price: ```python-repl >>> ts = pd.Series([1, 2, 3, 4, 5]) >>> sig.vbt.signals.generate_take_profit_exits(ts, [0.1, 0.5]) take_profit 0.1 0.5 a b c a b c 2020-01-01 False False False False False False 2020-01-02 True True False True True False 2020-01-03 False False False False False False 2020-01-04 False True True False False False 2020-01-05 False False False False False True ```""" entries = self._obj checks.assert_type(ts, (pd.Series, pd.DataFrame)) broadcast_kwargs = merge_kwargs(dict(require_kwargs=dict(requirements='W')), broadcast_kwargs) entries, ts = reshape_fns.broadcast(entries, ts, **broadcast_kwargs) stops = reshape_fns.broadcast_to_array_of(stops, entries.vbt.to_2d_array()) # Build column hierarchy if keys is not None: param_columns = keys else: param_columns = index_fns.index_from_values(stops, name='take_profit') columns = index_fns.combine_indexes(param_columns, entries.vbt.columns) # Perform generation if iteratively: new_entries, exits = nb.generate_tp_ex_iter_nb( entries.vbt.to_2d_array(), ts.vbt.to_2d_array(), stops) return entries.vbt.wrap(new_entries, columns=columns), entries.vbt.wrap(exits, columns=columns) else: exits = nb.generate_tp_ex_nb( entries.vbt.to_2d_array(), ts.vbt.to_2d_array(), stops, first=first) return entries.vbt.wrap(exits, columns=columns)
def plot_entry_markers(self, *args, name='Entry', trace_kwargs={}, **kwargs): """Plot signals as entry markers. See `Signals_SRAccessor.plot_markers`.""" trace_kwargs = merge_kwargs( dict(marker=dict(symbol='triangle-up', color='limegreen')), trace_kwargs) return self.plot_markers(*args, name=name, trace_kwargs=trace_kwargs, **kwargs)
def plot(self, tr_trace_kwargs=None, atr_trace_kwargs=None, fig=None, **layout_kwargs): # pragma: no cover """Plot `ATR.tr` and `ATR.atr`. Args: 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`. fig (plotly.graph_objects.Figure): Figure to add traces to. **layout_kwargs: Keyword arguments for layout. Example: ```python-repl >>> vbt.ATR.run(price['High'], price['Low'], price['Close'], 10).plot() ``` ![](/vectorbt/docs/img/ATR.png)""" if self.wrapper.ndim > 1: raise TypeError("Select a column first. Use indexing.") if tr_trace_kwargs is None: tr_trace_kwargs = {} if atr_trace_kwargs is None: atr_trace_kwargs = {} tr_trace_kwargs = merge_kwargs(dict(name='TR'), tr_trace_kwargs) atr_trace_kwargs = merge_kwargs(dict(name='ATR'), atr_trace_kwargs) fig = self.tr.vbt.plot(trace_kwargs=tr_trace_kwargs, fig=fig, **layout_kwargs) fig = self.atr.vbt.plot(trace_kwargs=atr_trace_kwargs, fig=fig, **layout_kwargs) return fig
def plot_as_exit_markers(self, *args, name='Exit', trace_kwargs={}, **kwargs): # pragma: no cover """Plot signals as exit markers. See `Signals_SRAccessor.plot_as_markers`.""" trace_kwargs = merge_kwargs(dict( marker=dict( symbol='circle', color=contrast_color_schema['orange'], size=7, line=dict( width=1, color=adjust_lightness(contrast_color_schema['orange']) ) ) ), trace_kwargs) return self.plot_as_markers(*args, name=name, trace_kwargs=trace_kwargs, **kwargs)
def combine_with(self, other, *args, combine_func=None, pass_2d=False, broadcast_kwargs={}, **kwargs): """Combine both using `combine_func` into a Series/DataFrame of the same shape. All arguments will be broadcasted using `vectorbt.base.reshape_fns.broadcast` with `broadcast_kwargs`. Arguments `*args` and `**kwargs` will be directly passed to `combine_func`. If `pass_2d` is `True`, 2-dimensional NumPy arrays will be passed, otherwise as is. !!! note The resulted array must have the same shape as broadcasted input arrays. Example: ```python-repl >>> import vectorbt as vbt >>> import pandas as pd >>> sr = pd.Series([1, 2], index=['x', 'y']) >>> df = pd.DataFrame([[3, 4], [5, 6]], index=['x', 'y'], columns=['a', 'b']) >>> sr.vbt.combine_with(df, combine_func=lambda x, y: x + y) a b x 4 5 y 7 8 ```""" if isinstance(other, Base_Accessor): other = other._obj checks.assert_not_none(combine_func) if checks.is_numba_func(combine_func): # Numba requires writable arrays broadcast_kwargs = merge_kwargs(dict(require_kwargs=dict(requirements='W')), broadcast_kwargs) new_obj, new_other = reshape_fns.broadcast(self._obj, other, **broadcast_kwargs) # Optionally cast to 2d array if pass_2d: new_obj_arr = reshape_fns.to_2d(new_obj, raw=True) new_other_arr = reshape_fns.to_2d(new_other, raw=True) else: new_obj_arr = np.asarray(new_obj) new_other_arr = np.asarray(new_other) result = combine_func(new_obj_arr, new_other_arr, *args, **kwargs) return new_obj.vbt.wrap(result)
def volume(self, x_level=None, y_level=None, z_level=None, x_labels=None, y_labels=None, z_labels=None, slider_level=None, slider_labels=None, **kwargs): # pragma: no cover """Create a 3D volume figure based on object's multi-index and values. If multi-index contains more than three levels or you want them in specific order, pass `x_level`, `y_level`, and `z_level`, each (`int` if index or `str` if name) corresponding to an axis of the volume. Optionally, pass `slider_level` to use a level as a slider. See `vectorbt.generic.plotting.create_volume` for other keyword arguments.""" (x_level, y_level, z_level), (slider_level, ) = index_fns.pick_levels( self.index, required_levels=(x_level, y_level, z_level), optional_levels=(slider_level, )) x_level_vals = self.index.get_level_values(x_level) y_level_vals = self.index.get_level_values(y_level) z_level_vals = self.index.get_level_values(z_level) # Labels are just unique level values if x_labels is None: x_labels = np.unique(x_level_vals) if y_labels is None: y_labels = np.unique(y_level_vals) if z_labels is None: z_labels = np.unique(z_level_vals) x_name = x_level_vals.name if x_level_vals.name is not None else 'x' y_name = y_level_vals.name if y_level_vals.name is not None else 'y' z_name = z_level_vals.name if z_level_vals.name is not None else 'z' kwargs = merge_kwargs( dict(trace_kwargs=dict(hovertemplate=f"{x_name}: %{{x}}<br>" + f"{y_name}: %{{y}}<br>" + f"{z_name}: %{{z}}<br>" + "value: %{value}<extra></extra>"), scene=dict(xaxis_title=x_level_vals.name, yaxis_title=y_level_vals.name, zaxis_title=z_level_vals.name)), kwargs) contains_nans = False if slider_level is None: # No grouping v = self.unstack_to_array(levels=(x_level, y_level, z_level)) if np.isnan(v).any(): contains_nans = True fig = plotting.create_volume(data=v, x_labels=x_labels, y_labels=y_labels, z_labels=z_labels, **kwargs) else: # Requires grouping # See https://plotly.com/python/sliders/ fig = None _slider_labels = [] for i, (name, group) in enumerate(self._obj.groupby(level=slider_level)): if slider_labels is not None: name = slider_labels[i] _slider_labels.append(name) v = group.vbt.unstack_to_array(levels=(x_level, y_level, z_level)) if np.isnan(v).any(): contains_nans = True _kwargs = merge_kwargs( dict( trace_kwargs=dict( name=str(name) if name is not None else None, visible=False), width=700, height=520, ), kwargs) fig = plotting.create_volume(data=v, x_labels=x_labels, y_labels=y_labels, z_labels=z_labels, fig=fig, **_kwargs) fig.data[0].visible = True steps = [] for i in range(len(fig.data)): step = dict(method="update", args=[{ "visible": [False] * len(fig.data) }, {}], label=str(_slider_labels[i]) if _slider_labels[i] is not None else None) step["args"][0]["visible"][i] = True steps.append(step) prefix = f'{self.index.names[slider_level]}: ' if self.index.names[ slider_level] is not None else None sliders = [ dict(active=0, currentvalue={"prefix": prefix}, pad={"t": 50}, steps=steps) ] fig.update_layout(sliders=sliders) if contains_nans: warnings.warn( "Data contains NaNs. In case of visualization issues, use .show() method on the widget." ) return fig
def heatmap(self, x_level=None, y_level=None, symmetric=False, x_labels=None, y_labels=None, slider_level=None, slider_labels=None, **kwargs): # pragma: no cover """Create a heatmap figure based on object's multi-index and values. If multi-index contains more than two levels or you want them in specific order, pass `x_level` and `y_level`, each (`int` if index or `str` if name) corresponding to an axis of the heatmap. Optionally, pass `slider_level` to use a level as a slider. See `vectorbt.generic.plotting.create_heatmap` for other keyword arguments.""" (x_level, y_level), (slider_level, ) = index_fns.pick_levels( self.index, required_levels=(x_level, y_level), optional_levels=(slider_level, )) x_level_vals = self.index.get_level_values(x_level) y_level_vals = self.index.get_level_values(y_level) x_name = x_level_vals.name if x_level_vals.name is not None else 'x' y_name = y_level_vals.name if y_level_vals.name is not None else 'y' kwargs = merge_kwargs( dict(trace_kwargs=dict(hovertemplate=f"{x_name}: %{{x}}<br>" + f"{y_name}: %{{y}}<br>" + "value: %{z}<extra></extra>"), xaxis_title=x_level_vals.name, yaxis_title=y_level_vals.name), kwargs) if slider_level is None: # No grouping df = self.unstack_to_df(index_levels=x_level, column_levels=y_level, symmetric=symmetric) fig = df.vbt.heatmap(x_labels=x_labels, y_labels=y_labels, **kwargs) else: # Requires grouping # See https://plotly.com/python/sliders/ fig = None _slider_labels = [] for i, (name, group) in enumerate(self._obj.groupby(level=slider_level)): if slider_labels is not None: name = slider_labels[i] _slider_labels.append(name) df = group.vbt.unstack_to_df(index_levels=x_level, column_levels=y_level, symmetric=symmetric) if x_labels is None: x_labels = df.columns if y_labels is None: y_labels = df.index _kwargs = merge_kwargs( dict( trace_kwargs=dict( name=str(name) if name is not None else None, visible=False), width=600, height=520, ), kwargs) fig = plotting.create_heatmap(data=df.vbt.to_2d_array(), x_labels=x_labels, y_labels=y_labels, fig=fig, **_kwargs) fig.data[0].visible = True steps = [] for i in range(len(fig.data)): step = dict(method="update", args=[{ "visible": [False] * len(fig.data) }, {}], label=str(_slider_labels[i]) if _slider_labels[i] is not None else None) step["args"][0]["visible"][i] = True steps.append(step) prefix = f'{self.index.names[slider_level]}: ' if self.index.names[ slider_level] is not None else None sliders = [ dict(active=0, currentvalue={"prefix": prefix}, pad={"t": 50}, steps=steps) ] fig.update_layout(sliders=sliders) return fig
def plot(self, ts_trace_kwargs={}, peak_trace_kwargs={}, valley_trace_kwargs={}, recovery_trace_kwargs={}, active_trace_kwargs={}, ptv_shape_kwargs={}, vtr_shape_kwargs={}, active_shape_kwargs={}, fig=None, **layout_kwargs): # pragma: no cover """Plot drawdowns over `Drawdowns.ts`. Args: 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. fig (plotly.graph_objects.Figure): Figure to add traces to. **layout_kwargs: Keyword arguments for layout. Example: ```py import vectorbt as vbt import pandas as pd ts = pd.Series([1, 2, 1, 2, 3, 2, 1, 2]) vbt.records.Drawdowns.from_ts(ts, freq='1 days').plot() ``` ![](/vectorbt/docs/img/drawdowns.png)""" if self.wrapper.ndim > 1: raise TypeError("You must select a column first") fig = self.ts.vbt.plot(trace_kwargs=ts_trace_kwargs, fig=fig, **layout_kwargs) if self.records_arr.shape[0] == 0: return fig # Extract information start_idx = self.records_arr['start_idx'] valley_idx = self.records_arr['valley_idx'] end_idx = self.records_arr['end_idx'] status = self.records_arr['status'] start_val = self.ts.values[start_idx] valley_val = self.ts.values[valley_idx] end_val = self.ts.values[end_idx] def get_duration_str(from_idx, to_idx): if isinstance(self.wrapper.index, DatetimeTypes): duration = self.wrapper.index[to_idx] - self.wrapper.index[from_idx] elif self.wrapper.freq is not None: duration = self.wrapper.to_time_units(to_idx - from_idx) else: duration = to_idx - from_idx return np.vectorize(str)(duration) # Plot peak markers and zones peak_mask = start_idx != np.roll(end_idx, 1) # peak and recovery at same time -> recovery wins peak_scatter = go.Scatter( x=self.ts.index[start_idx[peak_mask]], y=start_val[peak_mask], mode='markers', marker=dict( symbol='circle', color=contrast_color_schema['blue'], size=7, line=dict( width=1, color=adjust_lightness(contrast_color_schema['blue']) ) ), name='Peak' ) peak_scatter.update(**peak_trace_kwargs) fig.add_trace(peak_scatter) recovery_mask = status == DrawdownStatus.Recovered if np.any(recovery_mask): # Plot valley markers and zones 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((valley_drawdown, valley_duration), axis=1) valley_scatter = go.Scatter( x=self.ts.index[valley_idx[recovery_mask]], y=valley_val[recovery_mask], mode='markers', marker=dict( symbol='circle', color=contrast_color_schema['red'], size=7, line=dict( width=1, color=adjust_lightness(contrast_color_schema['red']) ) ), name='Valley', customdata=valley_customdata, hovertemplate="(%{x}, %{y})<br>Drawdown: %{customdata[0]:.2%}<br>Duration: %{customdata[1]}" ) valley_scatter.update(**valley_trace_kwargs) fig.add_trace(valley_scatter) for i in np.flatnonzero(recovery_mask): fig.add_shape(**merge_kwargs(dict( type="rect", xref="x", yref="paper", x0=self.ts.index[start_idx[i]], y0=0, x1=self.ts.index[valley_idx[i]], y1=1, fillcolor='red', opacity=0.15, layer="below", line_width=0, ), ptv_shape_kwargs)) # Plot recovery markers and zones 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((recovery_return, recovery_duration), axis=1) recovery_scatter = go.Scatter( x=self.ts.index[end_idx[recovery_mask]], y=end_val[recovery_mask], mode='markers', marker=dict( symbol='circle', color=contrast_color_schema['green'], size=7, line=dict( width=1, color=adjust_lightness(contrast_color_schema['green']) ) ), name='Recovery/Peak', customdata=recovery_customdata, hovertemplate="(%{x}, %{y})<br>Return: %{customdata[0]:.2%}<br>Duration: %{customdata[1]}" ) recovery_scatter.update(**recovery_trace_kwargs) fig.add_trace(recovery_scatter) for i in np.flatnonzero(recovery_mask): fig.add_shape(**merge_kwargs(dict( type="rect", xref="x", yref="paper", x0=self.ts.index[valley_idx[i]], y0=0, x1=self.ts.index[end_idx[i]], y1=1, fillcolor='green', opacity=0.15, layer="below", line_width=0, ), vtr_shape_kwargs)) # Plot active markers and zones 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((active_drawdown, active_duration), axis=1) active_scatter = go.Scatter( x=self.ts.index[end_idx[active_mask]], y=end_val[active_mask], mode='markers', marker=dict( symbol='circle', color=contrast_color_schema['orange'], size=7, line=dict( width=1, color=adjust_lightness(contrast_color_schema['orange']) ) ), name='Active', customdata=active_customdata, hovertemplate="(%{x}, %{y})<br>Drawdown: %{customdata[0]:.2%}<br>Duration: %{customdata[1]}" ) active_scatter.update(**active_trace_kwargs) fig.add_trace(active_scatter) for i in np.flatnonzero(active_mask): fig.add_shape(**merge_kwargs(dict( type="rect", xref="x", yref="paper", x0=self.ts.index[start_idx[i]], y0=0, x1=self.ts.index[end_idx[i]], y1=1, fillcolor='orange', opacity=0.15, layer="below", line_width=0, ), active_shape_kwargs)) return fig
def plot(self, levels=(30, 70), percent_k_trace_kwargs=None, percent_d_trace_kwargs=None, shape_kwargs=None, fig=None, **layout_kwargs): # pragma: no cover """Plot `STOCH.percent_k` and `STOCH.percent_d`. Args: levels (tuple): Two extremes: bottom and top. percent_k_trace_kwargs (dict): Keyword arguments passed to `plotly.graph_objects.Scatter` for `STOCH.percent_k`. percent_d_trace_kwargs (dict): Keyword arguments passed to `plotly.graph_objects.Scatter` for `STOCH.percent_d`. shape_kwargs (dict): Keyword arguments passed to `plotly.graph_objects.Figure.add_shape` for zone between levels. fig (plotly.graph_objects.Figure): Figure to add traces to. **layout_kwargs: Keyword arguments for layout. Example: ```python-repl >>> vbt.STOCH.run(price['High'], price['Low'], price['Close']).plot() ``` ![](/vectorbt/docs/img/STOCH.png)""" if self.wrapper.ndim > 1: raise TypeError("Select a column first. Use indexing.") 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_kwargs(dict(name='%K'), percent_k_trace_kwargs) percent_d_trace_kwargs = merge_kwargs(dict(name='%D'), percent_d_trace_kwargs) layout_kwargs = merge_kwargs(dict(yaxis=dict(range=[-5, 105])), layout_kwargs) fig = self.percent_k.vbt.plot(trace_kwargs=percent_k_trace_kwargs, fig=fig, **layout_kwargs) fig = self.percent_d.vbt.plot(trace_kwargs=percent_d_trace_kwargs, fig=fig, **layout_kwargs) # Plot levels # Fill void between levels shape_kwargs = merge_kwargs( dict( type="rect", xref="x", yref="y", x0=self.percent_k.index[0], y0=levels[0], x1=self.percent_k.index[-1], y1=levels[1], fillcolor="purple", opacity=0.15, layer="below", line_width=0, ), shape_kwargs) fig.add_shape(**shape_kwargs) return fig
def plot(self, close_trace_kwargs=None, middle_trace_kwargs=None, upper_trace_kwargs=None, lower_trace_kwargs=None, fig=None, **layout_kwargs): # pragma: no cover """Plot `BBANDS.middle`, `BBANDS.upper` and `BBANDS.lower` against `BBANDS.close`. Args: 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`. fig (plotly.graph_objects.Figure): Figure to add traces to. **layout_kwargs: Keyword arguments for layout. Example: ```python-repl >>> vbt.BBANDS.run(price['Close']).plot() ``` ![](/vectorbt/docs/img/BBANDS.png)""" if self.wrapper.ndim > 1: raise TypeError("Select a column first. Use indexing.") 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_kwargs( dict(name='Lower Band', line=dict(color='silver')), lower_trace_kwargs) upper_trace_kwargs = merge_kwargs( dict(name='Upper Band', line=dict(color='silver'), fill='tonexty', fillcolor='rgba(128, 128, 128, 0.25)'), upper_trace_kwargs) # default kwargs middle_trace_kwargs = merge_kwargs( dict(name='Middle Band', line=dict(color=defaults.layout['colorway'][1])), middle_trace_kwargs) close_trace_kwargs = merge_kwargs( dict(name='Close', line=dict(color=defaults.layout['colorway'][0])), close_trace_kwargs) fig = self.lower.vbt.plot(trace_kwargs=lower_trace_kwargs, fig=fig, **layout_kwargs) fig = self.upper.vbt.plot(trace_kwargs=upper_trace_kwargs, fig=fig, **layout_kwargs) fig = self.middle.vbt.plot(trace_kwargs=middle_trace_kwargs, fig=fig, **layout_kwargs) fig = self.close.vbt.plot(trace_kwargs=close_trace_kwargs, fig=fig, **layout_kwargs) return fig
def from_choice_func(self, entry_choice_func=None, exit_choice_func=None, cache_func=None, entry_settings=None, exit_settings=None, cache_settings=None, **kwargs): """Build signal generator class around entry and exit choice functions. A choice function is simply a function that returns indices of signals. There are two types of it: entry choice function and exit choice function. Each choice function takes broadcast time series, broadcast in-place output time series, broadcast parameter arrays, and other arguments, and returns an array of indices corresponding to chosen signals. See `vectorbt.signals.nb.generate_nb`. If `exit_only` is True, calls `vectorbt.signals.nb.generate_ex_nb`. If `exit_only` is False or `iteratively` is True, calls `vectorbt.signals.nb.generate_enex_nb`. Args: entry_choice_func (callable): `choice_func_nb` that returns indices of entries. If `exit_only` is True, automatically set to `vectorbt.signals.nb.first_choice_nb`. exit_choice_func (callable): `choice_func_nb` that returns indices of exits. cache_func (callable): A caching function to preprocess data beforehand. All returned objects will be passed as last arguments to choice functions. entry_settings (dict): Settings dict for `entry_choice_func`. exit_settings (dict): Settings dict for `exit_choice_func`. cache_settings (dict): Settings dict for `cache_func`. **kwargs: Keyword arguments passed to `IndicatorFactory.from_custom_func`. !!! note Choice functions should be Numba-compiled. Which inputs, parameters and arguments to pass to each function should be explicitly indicated in the function's settings dict. By default, nothing is passed. Settings dict of each function can have the following keys: Attributes: pass_inputs (list of str): Input names to pass to the choice function. Defaults to []. Order matters. Each name must be in `input_names`. pass_in_outputs (list of str): In-place output names to pass to the choice function. Defaults to []. Order matters. Each name must be in `in_output_names`. pass_params (list of str): Parameter names to pass to the choice function. Defaults to []. Order matters. Each name must be in `param_names`. pass_kwargs (list of str or list of tuple): Keyword arguments from `kwargs` dict to pass as positional arguments to the choice function. Defaults to []. Order matters. If any element is a tuple, should contain the name and the default value. If any element is a string, the default value is None. Built-in keys include: * `input_shape`: Input shape if no input time series passed. Default is provided by the pipeline if `forward_input_shape` is True. * `wait`: Number of ticks to wait before placing signals. Default is 1. * `first`: Whether to stop as soon as the first exit signal is found. Default is True. * `temp_idx_arr`: Empty integer array used to temporarily store indices. Default is an automatically generated array of shape `input_shape[0]`. You can also pass `temp_idx_arr1`, `temp_idx_arr2`, etc. to generate multiple. * `flex_2d`: See `vectorbt.base.reshape_fns.flex_choose_i_and_col_nb`. Default is provided by the pipeline if `forward_flex_2d` is True. pass_cache (bool): Whether to pass cache from `cache_func` to the choice function. Defaults to False. Cache is passed unpacked. The following arguments can be passed to `run` and `run_combs` methods: Args: *args: Should be used instead of `exit_args` when `exit_only` is True. entry_args (tuple): Arguments passed to the entry choice function. exit_args (tuple): Arguments passed to the exit choice function. cache_args (tuple): Arguments passed to the cache function. entry_kwargs (tuple): Settings for the entry choice function. Also contains arguments passed as positional if in `pass_kwargs`. exit_kwargs (tuple): Settings for the exit choice function. Also contains arguments passed as positional if in `pass_kwargs`. cache_kwargs (tuple): Settings for the cache function. Also contains arguments passed as positional if in `pass_kwargs`. return_cache (bool): Whether to return only cache. use_cache (any): Cache to use. **kwargs: Should be used instead of `exit_kwargs` when `exit_only` is True. For more arguments, see `vectorbt.indicators.factory.run_pipeline`. Example: Take the first entry and place an exit after waiting `n` ticks. Find the next entry and repeat. Test three different `n` values. ```python-repl >>> from numba import njit >>> from vectorbt.signals.factory import SignalFactory >>> @njit ... def wait_choice_nb(col, from_i, to_i, n, temp_idx_arr): ... temp_idx_arr[0] = from_i + n # index of next exit ... if temp_idx_arr[0] < to_i: ... return temp_idx_arr[:1] ... return temp_idx_arr[:0] # must return array anyway >>> # Build signal generator >>> MySignals = SignalFactory( ... param_names=['n'], ... iteratively=True ... ).from_choice_func( ... exit_choice_func=wait_choice_nb, ... exit_settings=dict( ... pass_params=['n'], ... pass_kwargs=['temp_idx_arr'] # built-in kwarg ... ) ... ) >>> # Run signal generator >>> entries = [True, True, True, True, True] >>> my_sig = MySignals.run(entries, [0, 1, 2]) >>> my_sig.entries # input entries custom_n 0 1 2 0 True True True 1 True True True 2 True True True 3 True True True 4 True True True >>> my_sig.new_entries # output entries custom_n 0 1 2 0 True True True 1 False False False 2 True False False 3 False True False 4 True False True >>> my_sig.exits # output exits custom_n 0 1 2 0 False False False 1 True False False 2 False True False 3 True False True 4 False False False ``` To combine multiple iterative signals, you would need to create a choice function that does that. Here is an example of combining two random generators using "OR" rule: ```python-repl >>> from numba import njit >>> from collections import namedtuple >>> from vectorbt.signals.factory import SignalFactory >>> from vectorbt.signals.nb import rand_by_prob_choice_nb >>> from vectorbt.signals.basic import flex_elem_param_config >>> # Enum to distinguish random generators >>> RandType = namedtuple('RandType', ['R1', 'R2'])(0, 1) >>> # Define exit choice function >>> @njit ... def rand_exit_choice_nb(col, from_i, to_i, rand_type_out, prob1, ... prob2, temp_idx_arr1, temp_idx_arr2, flex_2d): ... idxs1 = rand_by_prob_choice_nb( ... col, from_i, to_i, prob1, True, temp_idx_arr1, flex_2d) ... if len(idxs1) > 0: ... to_i = idxs1[0] # no need to go beyond first signal ... idxs2 = rand_by_prob_choice_nb( ... col, from_i, to_i, prob2, True, temp_idx_arr2, flex_2d) ... if len(idxs2) > 0: ... rand_type_out[idxs2[0], col] = RandType.R2 ... return idxs2 ... if len(idxs1) > 0: ... rand_type_out[idxs1[0], col] = RandType.R1 ... return idxs1 ... return temp_idx_arr1[:0] >>> # Build signal generator >>> MySignals = SignalFactory( ... in_output_names=['rand_type'], ... in_output_settings=dict( ... rand_type=dict( ... dtype=int, ... default=-1 ... ) ... ), ... param_names=['prob1', 'prob2'], ... param_settings=dict( ... prob1=flex_elem_param_config, # param per frame/row/col/element ... prob2=flex_elem_param_config ... ), ... attr_settings=dict( ... rand_type=dict(dtype=RandType) # creates rand_type_readable ... ), ... iteratively=True ... ).from_choice_func( ... exit_choice_func=rand_exit_choice_nb, ... exit_settings=dict( ... pass_in_outputs=['rand_type'], ... pass_params=['prob1', 'prob2'], ... pass_kwargs=['temp_idx_arr1', 'temp_idx_arr2', 'flex_2d'] ... ), ... forward_flex_2d=True ... ) >>> # Run signal generator >>> entries = [True, True, True, True, True] >>> my_sig = MySignals.run(entries, [0., 1.], [0., 1.], param_product=True) >>> my_sig.new_entries custom_prob1 0.0 1.0 custom_prob2 0.0 1.0 0.0 1.0 0 True True True True 1 False False False False 2 False True True True 3 False False False False 4 False True True True >>> my_sig.exits custom_prob1 0.0 1.0 custom_prob2 0.0 1.0 0.0 1.0 0 False False False False 1 False True True True 2 False False False False 3 False True True True 4 False False False False >>> my_sig.rand_type_readable custom_prob1 0.0 1.0 custom_prob2 0.0 1.0 0.0 1.0 0 1 R2 R1 R1 2 3 R2 R1 R1 4 ``` """ exit_only = self.exit_only iteratively = self.iteratively input_names = self.input_names param_names = self.param_names in_output_names = self.in_output_names checks.assert_not_none(exit_choice_func) checks.assert_numba_func(exit_choice_func) if exit_only: if iteratively: if entry_choice_func is None: entry_choice_func = first_choice_nb if entry_settings is None: entry_settings = {} entry_settings = merge_kwargs(dict(pass_inputs=['entries']), entry_settings) else: checks.assert_not_none(entry_choice_func) checks.assert_numba_func(entry_choice_func) if entry_settings is None: entry_settings = {} if exit_settings is None: exit_settings = {} if cache_settings is None: cache_settings = {} def _check_settings(func_settings): for k in func_settings: if k not in ('pass_inputs', 'pass_in_outputs', 'pass_params', 'pass_kwargs', 'pass_cache'): raise ValueError( f"Can't find key {k} in function settings") _check_settings(entry_settings) _check_settings(exit_settings) _check_settings(cache_settings) # Get input names for each function def _get_func_names(func_settings, setting, all_names): func_input_names = func_settings.get(setting, None) if func_input_names is None: return [] else: for name in func_input_names: checks.assert_in(name, all_names) return func_input_names entry_input_names = _get_func_names(entry_settings, 'pass_inputs', input_names) exit_input_names = _get_func_names(exit_settings, 'pass_inputs', input_names) cache_input_names = _get_func_names(cache_settings, 'pass_inputs', input_names) entry_in_output_names = _get_func_names(entry_settings, 'pass_in_outputs', in_output_names) exit_in_output_names = _get_func_names(exit_settings, 'pass_in_outputs', in_output_names) cache_in_output_names = _get_func_names(cache_settings, 'pass_in_outputs', in_output_names) entry_param_names = _get_func_names(entry_settings, 'pass_params', param_names) exit_param_names = _get_func_names(exit_settings, 'pass_params', param_names) cache_param_names = _get_func_names(cache_settings, 'pass_params', param_names) if exit_only and not iteratively: @njit def apply_nb(i, entries, exit_wait, exit_input_list, exit_in_output_tuples, exit_param_tuples, exit_args): return generate_ex_nb(entries, exit_wait, exit_choice_func, *exit_input_list, *exit_in_output_tuples[i], *exit_param_tuples[i], *exit_args) else: @njit def apply_nb(i, shape, entry_wait, exit_wait, entry_input_list, exit_input_list, entry_in_output_tuples, exit_in_output_tuples, entry_param_tuples, exit_param_tuples, entry_args, exit_args): return generate_enex_nb( shape, entry_wait, exit_wait, entry_choice_func, (*entry_input_list, *entry_in_output_tuples[i], *entry_param_tuples[i], *entry_args), exit_choice_func, (*exit_input_list, *exit_in_output_tuples[i], *exit_param_tuples[i], *exit_args)) def custom_func(input_list, in_output_list, param_list, *args, input_shape=None, flex_2d=None, entry_args=None, exit_args=None, cache_args=None, entry_kwargs=None, exit_kwargs=None, cache_kwargs=None, return_cache=False, use_cache=None, **_kwargs): # Get arguments if len(input_list) == 0: if input_shape is None: raise ValueError( "Pass input_shape if no input time series passed") else: input_shape = input_list[0].shape if entry_args is None: entry_args = () if exit_args is None: exit_args = () if cache_args is None: cache_args = () if exit_only: if len(exit_args) > 0: raise ValueError( "Use *args instead of exit_args when exit_only=True") exit_args = args else: if len(args) > 0: raise ValueError( "*args can be only used when exit_only=True") if entry_kwargs is None: entry_kwargs = {} if exit_kwargs is None: exit_kwargs = {} if cache_kwargs is None: cache_kwargs = {} if exit_only: if len(exit_kwargs) > 0: raise ValueError( "Use **kwargs instead of exit_kwargs when exit_only=True" ) exit_kwargs = _kwargs else: if len(_kwargs) > 0: raise ValueError( "**kwargs can be only used when exit_only=True") kwargs_defaults = dict( input_shape=input_shape, wait=1, first=True, flex_2d=flex_2d, ) entry_kwargs = merge_kwargs(kwargs_defaults, entry_kwargs) exit_kwargs = merge_kwargs(kwargs_defaults, exit_kwargs) cache_kwargs = merge_kwargs(kwargs_defaults, cache_kwargs) entry_wait = entry_kwargs['wait'] exit_wait = exit_kwargs['wait'] # Distribute arguments across functions entry_input_list = () exit_input_list = () cache_input_list = () for input_name in entry_input_names: entry_input_list += ( input_list[input_names.index(input_name)], ) for input_name in exit_input_names: exit_input_list += ( input_list[input_names.index(input_name)], ) for input_name in cache_input_names: cache_input_list += ( input_list[input_names.index(input_name)], ) entry_in_output_list = () exit_in_output_list = () cache_in_output_list = () for in_output_name in entry_in_output_names: entry_in_output_list += ( in_output_list[in_output_names.index(in_output_name)], ) for in_output_name in exit_in_output_names: exit_in_output_list += ( in_output_list[in_output_names.index(in_output_name)], ) for in_output_name in cache_in_output_names: cache_in_output_list += ( in_output_list[in_output_names.index(in_output_name)], ) entry_param_list = () exit_param_list = () cache_param_list = () for param_name in entry_param_names: entry_param_list += ( param_list[param_names.index(param_name)], ) for param_name in exit_param_names: exit_param_list += ( param_list[param_names.index(param_name)], ) for param_name in cache_param_names: cache_param_list += ( param_list[param_names.index(param_name)], ) n_params = len(param_list[0]) if len(param_list) > 0 else 1 entry_in_output_tuples = tuple(zip(*entry_in_output_list)) if len(entry_in_output_tuples) == 0: entry_in_output_tuples = ((), ) * n_params exit_in_output_tuples = tuple(zip(*exit_in_output_list)) if len(exit_in_output_tuples) == 0: exit_in_output_tuples = ((), ) * n_params entry_param_tuples = tuple(zip(*entry_param_list)) if len(entry_param_tuples) == 0: entry_param_tuples = ((), ) * n_params exit_param_tuples = tuple(zip(*exit_param_list)) if len(exit_param_tuples) == 0: exit_param_tuples = ((), ) * n_params def _build_more_args(func_settings, func_kwargs): pass_kwargs = func_settings.get('pass_kwargs', []) more_args = () for key in pass_kwargs: value = None if isinstance(key, tuple): key, value = key else: if key.startswith('temp_idx_arr'): value = np.empty((input_shape[0], ), dtype=np.int_) value = func_kwargs.get(key, value) more_args += (value, ) return more_args entry_more_args = _build_more_args(entry_settings, entry_kwargs) exit_more_args = _build_more_args(exit_settings, exit_kwargs) cache_more_args = _build_more_args(cache_settings, cache_kwargs) # Caching cache = use_cache if cache is None and cache_func is not None: cache = cache_func(*cache_input_list, *cache_in_output_list, *cache_param_list, *cache_args, *cache_more_args) if return_cache: return cache if cache is None: cache = () if not isinstance(cache, (tuple, list, List)): cache = (cache, ) entry_cache = () exit_cache = () if entry_settings.get('pass_cache', False): entry_cache = cache if exit_settings.get('pass_cache', False): exit_cache = cache # Apply and concatenate if exit_only and not iteratively: return combine_fns.apply_and_concat_one_nb( n_params, apply_nb, input_list[0], exit_wait, exit_input_list, exit_in_output_tuples, exit_param_tuples, exit_args + exit_more_args + exit_cache) else: return combine_fns.apply_and_concat_multiple_nb( n_params, apply_nb, input_shape, entry_wait, exit_wait, entry_input_list, exit_input_list, entry_in_output_tuples, exit_in_output_tuples, entry_param_tuples, exit_param_tuples, entry_args + entry_more_args + entry_cache, exit_args + exit_more_args + exit_cache) return self.from_custom_func(custom_func, pass_lists=True, **kwargs)
def test_merge_kwargs(self): assert config.merge_kwargs({'a': 1}, {'b': 2}) == {'a': 1, 'b': 2} assert config.merge_kwargs({'a': 1}, {'a': 2}) == {'a': 2} assert config.merge_kwargs({'a': {'b': 2}}, {'a': {'c': 3}}) == {'a': {'b': 2, 'c': 3}} assert config.merge_kwargs({'a': {'b': 2}}, {'a': {'b': 3}}) == {'a': {'b': 3}}
def combine_with_multiple(self, others, *args, combine_func=None, pass_2d=False, concat=False, broadcast_kwargs={}, keys=None, **kwargs): """Combine with `others` using `combine_func`. All arguments will be broadcasted using `vectorbt.base.reshape_fns.broadcast` with `broadcast_kwargs`. If `concat` is `True`, concatenate the results along columns, see `vectorbt.base.combine_fns.combine_and_concat`. Otherwise, pairwise combine into a Series/DataFrame of the same shape, see `vectorbt.base.combine_fns.combine_multiple`. Arguments `*args` and `**kwargs` will be directly passed to `combine_func`. If `pass_2d` is `True`, 2-dimensional NumPy arrays will be passed, otherwise as is. Use `keys` as the outermost level. !!! note If `combine_func` is Numba-compiled, will broadcast using `WRITEABLE` and `C_CONTIGUOUS` flags, which can lead to an expensive computation overhead if passed objects are large and have different shape/memory order. You also must ensure that all objects have the same data type. Also remember to bring each in `*args` to a Numba-compatible format. Example: ```python-repl >>> import vectorbt as vbt >>> import pandas as pd >>> sr = pd.Series([1, 2], index=['x', 'y']) >>> df = pd.DataFrame([[3, 4], [5, 6]], index=['x', 'y'], columns=['a', 'b']) >>> sr.vbt.combine_with_multiple([df, df*2], ... combine_func=lambda x, y: x + y) a b x 10 13 y 17 20 >>> sr.vbt.combine_with_multiple([df, df*2], ... combine_func=lambda x, y: x + y, concat=True, keys=['c', 'd']) c d a b a b x 4 5 7 9 y 7 8 12 14 ```""" others = tuple(map(lambda x: x._obj if isinstance(x, Base_Accessor) else x, others)) checks.assert_not_none(combine_func) checks.assert_type(others, Iterable) # Broadcast arguments if checks.is_numba_func(combine_func): # Numba requires writeable arrays # Plus all of our arrays must be in the same order broadcast_kwargs = merge_kwargs(dict(require_kwargs=dict(requirements=['W', 'C'])), broadcast_kwargs) new_obj, *new_others = reshape_fns.broadcast(self._obj, *others, **broadcast_kwargs) # Optionally cast to 2d array if pass_2d: bc_arrays = tuple(map(lambda x: reshape_fns.to_2d(x, raw=True), (new_obj, *new_others))) else: bc_arrays = tuple(map(lambda x: np.asarray(x), (new_obj, *new_others))) if concat: # Concat the results horizontally if checks.is_numba_func(combine_func): for i in range(1, len(bc_arrays)): checks.assert_meta_equal(bc_arrays[i - 1], bc_arrays[i]) result = combine_fns.combine_and_concat_nb(bc_arrays[0], bc_arrays[1:], combine_func, *args, **kwargs) else: result = combine_fns.combine_and_concat(bc_arrays[0], bc_arrays[1:], combine_func, *args, **kwargs) columns = new_obj.vbt.columns if keys is not None: new_columns = index_fns.combine_indexes(keys, columns) else: top_columns = pd.Index(np.arange(len(new_others)), name='combine_idx') new_columns = index_fns.combine_indexes(top_columns, columns) return new_obj.vbt.wrap(result, columns=new_columns) else: # Combine arguments pairwise into one object if checks.is_numba_func(combine_func): for i in range(1, len(bc_arrays)): checks.assert_dtype_equal(bc_arrays[i - 1], bc_arrays[i]) result = combine_fns.combine_multiple_nb(bc_arrays, combine_func, *args, **kwargs) else: result = combine_fns.combine_multiple(bc_arrays, combine_func, *args, **kwargs) return new_obj.vbt.wrap(result)
def plot(self, main_price_trace_kwargs={}, entry_trace_kwargs={}, exit_trace_kwargs={}, exit_profit_trace_kwargs={}, exit_loss_trace_kwargs={}, active_trace_kwargs={}, profit_shape_kwargs={}, loss_shape_kwargs={}, fig=None, **layout_kwargs): # pragma: no cover """Plot orders. Args: main_price_trace_kwargs (dict): Keyword arguments passed to `plotly.graph_objects.Scatter` for main price. 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. fig (plotly.graph_objects.Figure): Figure to add traces to. **layout_kwargs: Keyword arguments for layout. Example: ```py import vectorbt as vbt import pandas as pd price = pd.Series([1, 2, 3, 2, 1]) orders = pd.Series([1, -1, 1, -1, 0]) portfolio = vbt.Portfolio.from_orders(price, orders, freq='1D') portfolio.trades.plot() ``` ![](/vectorbt/docs/img/events.png)""" if self.wrapper.ndim > 1: raise TypeError("You must select a column first") # Plot main price fig = self.main_price.vbt.plot(trace_kwargs=main_price_trace_kwargs, fig=fig, **layout_kwargs) # Extract information size = self.records_arr['size'] entry_idx = self.records_arr['entry_idx'] entry_price = self.records_arr['entry_price'] entry_fees = self.records_arr['entry_fees'] exit_idx = self.records_arr['exit_idx'] exit_price = self.records_arr['exit_price'] exit_fees = self.records_arr['exit_fees'] pnl = self.records_arr['pnl'] ret = self.records_arr['return'] status = self.records_arr['status'] def get_duration_str(from_idx, to_idx): if isinstance(self.wrapper.index, DatetimeTypes): duration = self.wrapper.index[to_idx] - self.wrapper.index[ from_idx] elif self.wrapper.freq is not None: duration = self.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) # Plot Entry markers entry_customdata = np.stack((size, entry_fees), axis=1) entry_scatter = go.Scatter( x=self.wrapper.index[entry_idx], y=entry_price, mode='markers', marker=dict(symbol='circle', color=contrast_color_schema['blue'], size=7, line=dict(width=1, color=adjust_lightness( contrast_color_schema['blue']))), name='Entry', customdata=entry_customdata, hovertemplate= "%{x}<br>Price: %{y}<br>Size: %{customdata[0]:.4f}<br>Fees: %{customdata[1]:.2f}" ) entry_scatter.update(**entry_trace_kwargs) fig.add_trace(entry_scatter) # Plot end markers def plot_end_markers(mask, name, color, kwargs): customdata = np.stack((size[mask], exit_fees[mask], pnl[mask], ret[mask], duration[mask]), axis=1) scatter = go.Scatter(x=self.wrapper.index[exit_idx[mask]], y=exit_price[mask], mode='markers', marker=dict( symbol='circle', color=color, size=7, line=dict(width=1, color=adjust_lightness(color))), name=name, customdata=customdata, hovertemplate="%{x}<br>Price: %{y}" + "<br>Size: %{customdata[0]:.4f}" + "<br>Fees: %{customdata[1]:.2f}" + "<br>PnL: %{customdata[2]:.2f}" + "<br>Return: %{customdata[3]:.2%}" + "<br>Duration: %{customdata[4]}") scatter.update(**kwargs) fig.add_trace(scatter) # Plot Exit markers plot_end_markers((status == EventStatus.Closed) & (pnl == 0.), 'Exit', contrast_color_schema['gray'], exit_trace_kwargs) # Plot Exit - Profit markers plot_end_markers((status == EventStatus.Closed) & (pnl > 0.), 'Exit - Profit', contrast_color_schema['green'], exit_profit_trace_kwargs) # Plot Exit - Loss markers plot_end_markers((status == EventStatus.Closed) & (pnl < 0.), 'Exit - Loss', contrast_color_schema['red'], exit_loss_trace_kwargs) # Plot Active markers plot_end_markers(status == EventStatus.Open, 'Active', contrast_color_schema['orange'], active_trace_kwargs) # Plot profit zones profit_mask = pnl > 0. for i in np.flatnonzero(profit_mask): fig.add_shape(**merge_kwargs( dict( type="rect", xref="x", yref="y", x0=self.wrapper.index[entry_idx[i]], y0=entry_price[i], x1=self.wrapper.index[exit_idx[i]], y1=exit_price[i], fillcolor='green', opacity=0.15, layer="below", line_width=0, ), profit_shape_kwargs)) # Plot loss zones loss_mask = pnl < 0. for i in np.flatnonzero(loss_mask): fig.add_shape(**merge_kwargs( dict( type="rect", xref="x", yref="y", x0=self.wrapper.index[entry_idx[i]], y0=entry_price[i], x1=self.wrapper.index[exit_idx[i]], y1=exit_price[i], fillcolor='red', opacity=0.15, layer="below", line_width=0, ), loss_shape_kwargs)) return fig
def custom_func(input_list, in_output_list, param_list, *args, input_shape=None, flex_2d=None, entry_args=None, exit_args=None, cache_args=None, entry_kwargs=None, exit_kwargs=None, cache_kwargs=None, return_cache=False, use_cache=None, **_kwargs): # Get arguments if len(input_list) == 0: if input_shape is None: raise ValueError( "Pass input_shape if no input time series passed") else: input_shape = input_list[0].shape if entry_args is None: entry_args = () if exit_args is None: exit_args = () if cache_args is None: cache_args = () if exit_only: if len(exit_args) > 0: raise ValueError( "Use *args instead of exit_args when exit_only=True") exit_args = args else: if len(args) > 0: raise ValueError( "*args can be only used when exit_only=True") if entry_kwargs is None: entry_kwargs = {} if exit_kwargs is None: exit_kwargs = {} if cache_kwargs is None: cache_kwargs = {} if exit_only: if len(exit_kwargs) > 0: raise ValueError( "Use **kwargs instead of exit_kwargs when exit_only=True" ) exit_kwargs = _kwargs else: if len(_kwargs) > 0: raise ValueError( "**kwargs can be only used when exit_only=True") kwargs_defaults = dict( input_shape=input_shape, wait=1, first=True, flex_2d=flex_2d, ) entry_kwargs = merge_kwargs(kwargs_defaults, entry_kwargs) exit_kwargs = merge_kwargs(kwargs_defaults, exit_kwargs) cache_kwargs = merge_kwargs(kwargs_defaults, cache_kwargs) entry_wait = entry_kwargs['wait'] exit_wait = exit_kwargs['wait'] # Distribute arguments across functions entry_input_list = () exit_input_list = () cache_input_list = () for input_name in entry_input_names: entry_input_list += ( input_list[input_names.index(input_name)], ) for input_name in exit_input_names: exit_input_list += ( input_list[input_names.index(input_name)], ) for input_name in cache_input_names: cache_input_list += ( input_list[input_names.index(input_name)], ) entry_in_output_list = () exit_in_output_list = () cache_in_output_list = () for in_output_name in entry_in_output_names: entry_in_output_list += ( in_output_list[in_output_names.index(in_output_name)], ) for in_output_name in exit_in_output_names: exit_in_output_list += ( in_output_list[in_output_names.index(in_output_name)], ) for in_output_name in cache_in_output_names: cache_in_output_list += ( in_output_list[in_output_names.index(in_output_name)], ) entry_param_list = () exit_param_list = () cache_param_list = () for param_name in entry_param_names: entry_param_list += ( param_list[param_names.index(param_name)], ) for param_name in exit_param_names: exit_param_list += ( param_list[param_names.index(param_name)], ) for param_name in cache_param_names: cache_param_list += ( param_list[param_names.index(param_name)], ) n_params = len(param_list[0]) if len(param_list) > 0 else 1 entry_in_output_tuples = tuple(zip(*entry_in_output_list)) if len(entry_in_output_tuples) == 0: entry_in_output_tuples = ((), ) * n_params exit_in_output_tuples = tuple(zip(*exit_in_output_list)) if len(exit_in_output_tuples) == 0: exit_in_output_tuples = ((), ) * n_params entry_param_tuples = tuple(zip(*entry_param_list)) if len(entry_param_tuples) == 0: entry_param_tuples = ((), ) * n_params exit_param_tuples = tuple(zip(*exit_param_list)) if len(exit_param_tuples) == 0: exit_param_tuples = ((), ) * n_params def _build_more_args(func_settings, func_kwargs): pass_kwargs = func_settings.get('pass_kwargs', []) more_args = () for key in pass_kwargs: value = None if isinstance(key, tuple): key, value = key else: if key.startswith('temp_idx_arr'): value = np.empty((input_shape[0], ), dtype=np.int_) value = func_kwargs.get(key, value) more_args += (value, ) return more_args entry_more_args = _build_more_args(entry_settings, entry_kwargs) exit_more_args = _build_more_args(exit_settings, exit_kwargs) cache_more_args = _build_more_args(cache_settings, cache_kwargs) # Caching cache = use_cache if cache is None and cache_func is not None: cache = cache_func(*cache_input_list, *cache_in_output_list, *cache_param_list, *cache_args, *cache_more_args) if return_cache: return cache if cache is None: cache = () if not isinstance(cache, (tuple, list, List)): cache = (cache, ) entry_cache = () exit_cache = () if entry_settings.get('pass_cache', False): entry_cache = cache if exit_settings.get('pass_cache', False): exit_cache = cache # Apply and concatenate if exit_only and not iteratively: return combine_fns.apply_and_concat_one_nb( n_params, apply_nb, input_list[0], exit_wait, exit_input_list, exit_in_output_tuples, exit_param_tuples, exit_args + exit_more_args + exit_cache) else: return combine_fns.apply_and_concat_multiple_nb( n_params, apply_nb, input_shape, entry_wait, exit_wait, entry_input_list, exit_input_list, entry_in_output_tuples, exit_in_output_tuples, entry_param_tuples, exit_param_tuples, entry_args + entry_more_args + entry_cache, exit_args + exit_more_args + exit_cache)