def generate_random_after(self, n_range, n_prob=None, min_space=None, seed=None): """See `vectorbt.signals.nb.generate_random_after_nb`. Example: Generate exactly one random signal after each signal in `signals`: ```python-repl >>> print(signals.vbt.signals.generate_random_after(1, seed=42)) a b c 2018-01-01 False False False 2018-01-02 False False False 2018-01-03 True True False 2018-01-04 False False False 2018-01-05 True False True ```""" n_range = reshape_fns.to_1d(n_range) if n_prob is not None: n_prob = reshape_fns.to_1d(n_prob) checks.assert_same_shape(n_range, n_prob) return self.wrap_array( nb.generate_random_after_nb(self.to_2d_array(), n_range, n_prob=n_prob, min_space=min_space, seed=seed))
def closed_rate(self): """How many positions are closed in each column.""" closed_count = reshape_fns.to_1d(self.closed.count, raw=True) count = reshape_fns.to_1d(self.count, raw=True) closed_rate = closed_count / count return self.wrap_reduced_array(closed_rate)
def win_rate(self): """How many positions won in each column.""" winning_count = reshape_fns.to_1d(self.winning.count, raw=True) count = reshape_fns.to_1d(self.count, raw=True) win_rate = winning_count / count return self.wrap_reduced_array(win_rate)
def total_costs(self): """Total costs of each column.""" total_paid_fees = reshape_fns.to_1d(self.total_paid_fees, raw=True) total_paid_slippage = reshape_fns.to_1d(self.total_paid_slippage, raw=True) total_costs = total_paid_fees + total_paid_slippage return self.wrap_reduced_array(total_costs)
def loss_rate(self): """How many positions lost in each column.""" losing_count = reshape_fns.to_1d(self.losing.count, raw=True) count = reshape_fns.to_1d(self.count, raw=True) loss_rate = losing_count / count return self.wrap_reduced_array(loss_rate)
def total_costs(self): """Total costs.""" total_fees_paid = reshape_fns.to_1d(self.total_fees_paid, raw=True) total_slippage_paid = reshape_fns.to_1d(self.total_slippage_paid, raw=True) total_costs = total_fees_paid + total_slippage_paid return self.wrap_reduced_array(total_costs)
def closed_rate(self): """Rate of closed positions.""" closed_count = reshape_fns.to_1d(self.closed.count, raw=True) count = reshape_fns.to_1d(self.count, raw=True) closed_rate = closed_count / count return self.wrapper.wrap_reduced(closed_rate)
def win_rate(self): """Rate of profitable events.""" winning_count = reshape_fns.to_1d(self.winning.count, raw=True) count = reshape_fns.to_1d(self.count, raw=True) win_rate = winning_count / count return self.wrapper.wrap_reduced(win_rate)
def generate_random(cls, shape, n_range, n_prob=None, min_space=None, seed=None, **kwargs): """See `vectorbt.signals.nb.generate_random_nb`. `**kwargs` will be passed to pandas constructor. Example: For each column, generate either 1 (with 30% probability) or 2 (with 70% probability) signals randomly. Leave one position free between signals: ```python-repl >>> print(pd.DataFrame.vbt.signals.generate_random((5, 3), [1, 2], ... n_prob=[0.3, 0.7], min_space=1, seed=42, index=index, columns=columns)) a b c 2018-01-01 True False False 2018-01-02 False True False 2018-01-03 True False False 2018-01-04 False True True 2018-01-05 False False False ```""" if not isinstance(shape, tuple): shape = (shape, 1) elif isinstance(shape, tuple) and len(shape) == 1: shape = (shape[0], 1) n_range = reshape_fns.to_1d(n_range) if n_prob is not None: n_prob = reshape_fns.to_1d(n_prob) checks.assert_same_shape(n_range, n_prob) result = nb.generate_random_nb(shape, n_range, n_prob=n_prob, min_space=min_space, seed=seed) if cls.is_series(): return pd.Series(result[:, 0], **kwargs) return pd.DataFrame(result, **kwargs)
def calmar_ratio(self): """Calmar ratio, or drawdown ratio, of a strategy.""" return self.wrapper.wrap_reduced( nb.calmar_ratio_nb( self.returns.vbt.to_2d_array(), reshape_fns.to_1d(self.annualized_return, raw=True), reshape_fns.to_1d(self.max_drawdown, raw=True), self.ann_factor))
def appt(self): """Average profitability per trade (APPT) For every trade you place, you are likely to win/lose this amount. What matters is that your APPT comes up positive.""" appt = reshape_fns.to_1d(self.win_rate, raw=True) * reshape_fns.to_1d(self.avg_win, raw=True) - \ reshape_fns.to_1d(self.loss_rate, raw=True) * reshape_fns.to_1d(self.avg_loss, raw=True) return self.wrap_metric(appt)
def profit_factor(self): """Profit factor.""" total_win = reshape_fns.to_1d(self.winning.total_pnl, raw=True) total_loss = reshape_fns.to_1d(self.losing.total_pnl, raw=True) # Otherwise columns with only wins or losses will become NaNs has_values = reshape_fns.to_1d(self.count, raw=True) > 0 total_win[np.isnan(total_win) & has_values] = 0. total_loss[np.isnan(total_loss) & has_values] = 0. profit_factor = total_win / np.abs(total_loss) return self.wrapper.wrap_reduced(profit_factor)
def profit_factor(self): """Profit factor of each column.""" total_win = reshape_fns.to_1d(self.winning.total_pnl, raw=True) total_loss = reshape_fns.to_1d(self.losing.total_pnl, raw=True) # Otherwise columns with only wins or losses will become NaNs has_trades = reshape_fns.to_1d(self.portfolio.has_trades, raw=True) total_win[np.isnan(total_win) & has_trades] = 0. total_loss[np.isnan(total_loss) & has_trades] = 0. profit_factor = total_win / np.abs(total_loss) return self.wrap_reduced_array(profit_factor)
def expectancy(self): """Average profitability.""" win_rate = reshape_fns.to_1d(self.win_rate, raw=True) avg_win = reshape_fns.to_1d(self.winning.avg_pnl, raw=True) avg_loss = reshape_fns.to_1d(self.losing.avg_pnl, raw=True) # Otherwise columns with only wins or losses will become NaNs has_values = reshape_fns.to_1d(self.count, raw=True) > 0 avg_win[np.isnan(avg_win) & has_values] = 0. avg_loss[np.isnan(avg_loss) & has_values] = 0. expectancy = win_rate * avg_win - (1 - win_rate) * np.abs(avg_loss) return self.wrapper.wrap_reduced(expectancy)
def expectancy(self): """Average profitability per trade (APPT) of each column.""" win_rate = reshape_fns.to_1d(self.win_rate, raw=True) loss_rate = reshape_fns.to_1d(self.loss_rate, raw=True) avg_win = reshape_fns.to_1d(self.winning.avg_pnl, raw=True) avg_loss = reshape_fns.to_1d(self.losing.avg_pnl, raw=True) # Otherwise columns with only wins or losses will become NaNs has_trades = reshape_fns.to_1d(self.portfolio.has_trades, raw=True) avg_win[np.isnan(avg_win) & has_trades] = 0. avg_loss[np.isnan(avg_loss) & has_trades] = 0. expectancy = win_rate * avg_win - loss_rate * np.abs(avg_loss) return self.wrap_reduced_array(expectancy)
def describe(self, percentiles=[0.25, 0.5, 0.75], **kwargs): """See `vectorbt.timeseries.nb.describe_reduce_func_nb`. `**kwargs` will be passed to `TimeSeries_Accessor.wrap_reduced`. For `percentiles`, see `pandas.DataFrame.describe`. Example: ```python-repl >>> print(df.vbt.timeseries.describe()) a b c count 5.000000 5.000000 5.00000 mean 3.000000 3.000000 1.80000 std 1.581139 1.581139 0.83666 min 1.000000 1.000000 1.00000 25.00% 2.000000 2.000000 1.00000 50.00% 3.000000 3.000000 2.00000 75.00% 4.000000 4.000000 2.00000 max 5.000000 5.000000 3.00000 ```""" if percentiles is not None: percentiles = reshape_fns.to_1d(percentiles) else: percentiles = np.empty(0) index = pd.Index([ 'count', 'mean', 'std', 'min', *map(lambda x: '%.2f%%' % (x * 100), percentiles), 'max' ]) return self.reduce_to_array(nb.describe_reduce_func_nb, percentiles, index=index, **kwargs)
def sortino_ratio(self): """Sortino ratio of a strategy.""" return self.wrapper.wrap_reduced( nb.sortino_ratio_nb(self.returns.vbt.to_2d_array(), reshape_fns.to_1d(self.downside_risk, raw=True), self.ann_factor, required_return=self.required_return))
def alpha(self): """Annualized alpha. !!! note `factor_returns` must be set.""" checks.assert_not_none(self.factor_returns) return self.wrapper.wrap_reduced( nb.alpha_nb(self.returns.vbt.to_2d_array(), self.factor_returns.vbt.to_2d_array(), reshape_fns.to_1d(self.beta, raw=True), self.ann_factor, risk_free=self.risk_free))
def total_return(self): """Total return of each column.""" total_return = reshape_fns.to_1d(self.total_profit, raw=True) / self.init_capital return self.wrap_reduced_array(total_return)
def profit_factor(self): profit_factor = reshape_fns.to_1d( self.sum_win, raw=True) / reshape_fns.to_1d(self.sum_loss, raw=True) return self.wrap_metric(profit_factor)
def total_return(self): total_return = reshape_fns.to_1d(self.total_profit, raw=True) / self.investment return self.wrap_metric(total_return)
def total_return(self): """Total return.""" total_return = reshape_fns.to_1d(self.total_profit, raw=True) / self.init_capital return self.wrapper.wrap_reduced(total_return)
def plot_against(self, other, name=None, other_name=None, above_trace_kwargs={}, below_trace_kwargs={}, other_trace_kwargs={}, equal_trace_kwargs={}, fig=None, **layout_kwargs): """Plot Series against `other` as markers. Args: other (float, int, or array_like): The other time series/value. name (str): Name of the time series. other_name (str): Name of the other time series/value. other_trace_kwargs (dict): Keyword arguments passed to `plotly.graph_objects.Scatter` for `other`. above_trace_kwargs (dict): Keyword arguments passed to `plotly.graph_objects.Scatter` for values above `other`. below_trace_kwargs (dict): Keyword arguments passed to `plotly.graph_objects.Scatter` for values below `other`. equal_trace_kwargs (dict): Keyword arguments passed to `plotly.graph_objects.Scatter` for values equal `other`. fig (plotly.graph_objects.Figure): Figure to add traces to. **layout_kwargs: Keyword arguments for layout. Example: Can plot against single values such as benchmarks. ```py df['a'].vbt.timeseries.plot_against(3) ``` ![](/vectorbt/docs/img/timeseries_plot_against_line.png) But also against other time series. ```py df['a'].vbt.timeseries.plot_against(df['b']) ``` ![](/vectorbt/docs/img/timeseries_plot_against_series.png)""" if name is None: name = self._obj.name if other_name is None: other_name = getattr(other, 'name', None) # Prepare data other = reshape_fns.to_1d(other) other = reshape_fns.broadcast_to(other, self._obj) above_obj = self._obj[self._obj > other] below_obj = self._obj[self._obj < other] equal_obj = self._obj[self._obj == other] # Set up figure if fig is None: fig = DefaultFigureWidget() fig.update_layout(**layout_kwargs) # Plot other other_scatter = go.Scatter(x=other.index, y=other, line=dict( color="grey", width=2, dash="dot", ), name=other_name, showlegend=other_name is not None) other_scatter.update(**other_trace_kwargs) fig.add_trace(other_scatter) # Plot markets above_scatter = go.Scatter(x=above_obj.index, y=above_obj, mode='markers', marker=dict(symbol='circle', color='green', size=10), name=f'{name} (above)', showlegend=name is not None) above_scatter.update(**above_trace_kwargs) fig.add_trace(above_scatter) below_scatter = go.Scatter(x=below_obj.index, y=below_obj, mode='markers', marker=dict(symbol='circle', color='red', size=10), name=f'{name} (below)', showlegend=name is not None) below_scatter.update(**below_trace_kwargs) fig.add_trace(below_scatter) equal_scatter = go.Scatter(x=equal_obj.index, y=equal_obj, mode='markers', marker=dict(symbol='circle', color='grey', size=10), name=f'{name} (equal)', showlegend=name is not None) equal_scatter.update(**equal_trace_kwargs) fig.add_trace(equal_scatter) # If other is a straight line, make y-axis symmetric if np.all(other.values == other.values.item(0)): maxval = np.nanmax(np.abs(self.to_array())) space = 0.1 * 2 * maxval y = other.values.item(0) fig.update_layout( yaxis=dict(range=[y - (maxval + space), y + maxval + space]), shapes=[ dict(type="line", xref="paper", yref='y', x0=0, x1=1, y0=y, y1=y, line=dict( color="grey", width=2, dash="dot", )) ]) return fig
def to_1d_array(self): """Convert to 1-dim NumPy array See `vectorbt.utils.reshape_fns.to_1d`.""" return reshape_fns.to_1d(self._obj, raw=True)
def to_1d_array(self): return reshape_fns.to_1d(self._obj, raw=True)