def concat(self_or_cls, *others, as_columns=None, broadcast_kwargs={}): """Concatenate with `others` along columns. All arguments will be broadcasted using `vectorbt.utils.reshape_fns.broadcast` with `broadcast_kwargs`. Use `as_columns` as a top-level column level. Example: ```python-repl >>> 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']) >>> print(sr.vbt.concat(df, as_columns=['c', 'd'])) c d a b a b x 1 1 3 4 y 2 2 5 6 ```""" others = tuple( map(lambda x: x._obj if isinstance(x, Base_Accessor) else x, others)) if isinstance(self_or_cls, type): objs = others else: objs = (self_or_cls._obj, ) + others broadcasted = reshape_fns.broadcast(*objs, **broadcast_kwargs) broadcasted = tuple(map(reshape_fns.to_2d, broadcasted)) if checks.is_pandas(broadcasted[0]): concated = pd.concat(broadcasted, axis=1) if as_columns is not None: concated.columns = index_fns.combine_indexes( as_columns, broadcasted[0].columns) else: concated = np.hstack(broadcasted) return concated
def repeat(self, n, as_columns=None): """See `vectorbt.utils.reshape_fns.repeat`. Use `as_columns` as a top-level column level.""" repeated = reshape_fns.repeat(self._obj, n, axis=1) if as_columns is not None: new_columns = index_fns.combine_indexes(self.columns, as_columns) return self.wrap_array(repeated.values, columns=new_columns) return repeated
def tile(self, n, as_columns=None): """See `vectorbt.utils.reshape_fns.tile`. Use `as_columns` as a top-level column level.""" tiled = reshape_fns.tile(self._obj, n, axis=1) if as_columns is not None: new_columns = index_fns.combine_indexes(as_columns, self.columns) return self.wrap(tiled.values, columns=new_columns) return tiled
def generate_stop_loss(self, ts, stops, trailing=False, relative=True, as_columns=None, broadcast_kwargs={}): """See `vectorbt.signals.nb.generate_stop_loss_nb`. Arguments will be broadcasted using `vectorbt.utils.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 `as_columns` as a top-level column level. Example: For each entry in `signals`, set stop loss for 10% and 20% below the price `ts`: ```python-repl >>> print(signals.vbt.signals.generate_stop_loss(ts, [0.1, 0.2])) stop_loss 0.1 0.2 a b c a b c 2018-01-01 False False False False False False 2018-01-02 False True False False False False 2018-01-03 False False False False False False 2018-01-04 False True True False True True 2018-01-05 False False False False False False ```""" entries = self._obj checks.assert_type(ts, (pd.Series, pd.DataFrame)) entries, ts = reshape_fns.broadcast(entries, ts, **broadcast_kwargs, writeable=True) stops = reshape_fns.broadcast_to_array_of(stops, entries.vbt.to_2d_array()) exits = nb.generate_stop_loss_nb(entries.vbt.to_2d_array(), ts.vbt.to_2d_array(), stops, trailing, relative) # Build column hierarchy if as_columns is not None: param_columns = as_columns else: name = 'trail_stop' if trailing else 'stop_loss' param_columns = index_fns.index_from_values(stops, name=name) columns = index_fns.combine_indexes(param_columns, entries.vbt.columns) return entries.vbt.wrap_array(exits, columns=columns)
def build_column_hierarchy(param_list, level_names, ts_columns): """For each parameter in `param_list`, create a new column level with parameter values. Combine this level with columns `ts_columns` using Cartesian product.""" checks.assert_same_shape(param_list, level_names, axis=0) param_indexes = [ index_fns.index_from_values(param_list[i], name=level_names[i]) for i in range(len(param_list)) ] param_columns = None for param_index in param_indexes: if param_columns is None: param_columns = param_index else: param_columns = index_fns.stack_indexes(param_columns, param_index) if param_columns is not None: return index_fns.combine_indexes(param_columns, ts_columns) return ts_columns
def apply_and_concat(self, ntimes, *args, apply_func=None, pass_2d=False, as_columns=None, **kwargs): """Apply `apply_func` `ntimes` times and concatenate the results along columns. See `vectorbt.utils.combine_fns.apply_and_concat`. Arguments `*args` and `**kwargs` will be directly passed to `apply_func`. If `pass_2d` is `True`, 2-dimensional NumPy arrays will be passed, otherwise as is. Use `as_columns` as a top-level column level. Example: ```python-repl >>> import pandas as pd >>> df = pd.DataFrame([[3, 4], [5, 6]], index=['x', 'y'], columns=['a', 'b']) >>> print(df.vbt.apply_and_concat(3, [1, 2, 3], ... apply_func=lambda i, a, b: a * b[i], as_columns=['c', 'd', 'e'])) c d e a b a b a b x 3 4 6 8 9 12 y 5 6 10 12 15 18 ```""" checks.assert_not_none(apply_func) # Optionally cast to 2d array if pass_2d: obj_arr = reshape_fns.to_2d(np.asarray(self._obj)) else: obj_arr = np.asarray(self._obj) if checks.is_numba_func(apply_func): result = combine_fns.apply_and_concat_nb(obj_arr, ntimes, apply_func, *args, **kwargs) else: result = combine_fns.apply_and_concat(obj_arr, ntimes, apply_func, *args, **kwargs) # Build column hierarchy if as_columns is not None: new_columns = index_fns.combine_indexes(as_columns, self.columns) else: new_columns = index_fns.tile_index(self.columns, ntimes) return self.wrap_array(result, columns=new_columns)
def rolling_window(self, window, n=None): """Split time series into `n` time ranges each `window` long. The result will be a new DataFrame with index of length `window` and columns of length `len(columns) * n`. If `n` is `None`, will return the maximum number of time ranges. Example: ```python-repl >>> print(df.vbt.timeseries.rolling_window(2, n=2)) a b c start_date 2018-01-01 2018-01-04 2018-01-01 2018-01-04 2018-01-01 2018-01-04 0 1.0 4.0 5.0 2.0 1.0 2.0 1 2.0 5.0 4.0 1.0 2.0 1.0 ```""" cube = nb.rolling_window_nb(self.to_2d_array(), window) if n is not None: idxs = np.round(np.linspace(0, cube.shape[2] - 1, n)).astype(int) cube = cube[:, :, idxs] else: idxs = np.arange(cube.shape[2]) matrix = np.hstack(cube) range_columns = pd.Index(self.index[idxs], name='start_date') new_columns = index_fns.combine_indexes(self.columns, range_columns) return pd.DataFrame(matrix, columns=new_columns)
def combine_with_multiple(self, others, *args, combine_func=None, pass_2d=False, concat=False, broadcast_kwargs={}, as_columns=None, **kwargs): """Combine with `others` using `combine_func`. All arguments will be broadcasted using `vectorbt.utils.reshape_fns.broadcast` with `broadcast_kwargs`. If `concat` is `True`, concatenate the results along columns, see `vectorbt.utils.combine_fns.combine_and_concat`. Otherwise, pairwise combine into a Series/DataFrame of the same shape, see `vectorbt.utils.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 `as_columns` as a top-level column level. !!! note If `combine_func` is Numba-compiled, will broadcast using `writeable=True` and copy using `order='C'` 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 pandas as pd >>> sr = pd.Series([1, 2], index=['x', 'y']) >>> df = pd.DataFrame([[3, 4], [5, 6]], index=['x', 'y'], columns=['a', 'b']) >>> print(sr.vbt.combine_with_multiple([df, df*2], ... combine_func=lambda x, y: x + y)) a b x 10 13 y 17 20 >>> print(sr.vbt.combine_with_multiple([df, df*2], ... combine_func=lambda x, y: x + y, concat=True, as_columns=['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 writable arrays broadcast_kwargs = {**dict(writeable=True), **broadcast_kwargs} # Plus all of our arrays must be in the same order broadcast_kwargs['copy_kwargs'] = { **dict(order='C'), **broadcast_kwargs.get('copy_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(np.asarray(x)), (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_same_meta(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 as_columns is not None: new_columns = index_fns.combine_indexes(as_columns, columns) else: new_columns = index_fns.tile_index(columns, len(others)) return new_obj.vbt.wrap_array(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_same_dtype(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_array(result)