def __init__(self, obj: tp.SeriesFrame, **kwargs) -> None: if not checks.is_pandas(obj): # parent accessor obj = obj._obj checks.assert_dtype(obj, np.bool_) GenericAccessor.__init__(self, obj, **kwargs)
def __init__(self, obj, **kwargs): if not checks.is_pandas(obj): # parent accessor obj = obj._obj checks.assert_dtype(obj, np.bool) GenericAccessor.__init__(self, obj, **kwargs)
def __init__(self, obj, freq=None): if not checks.is_pandas(obj): # parent accessor obj = obj._obj checks.assert_dtype(obj, np.bool) Generic_Accessor.__init__(self, obj, freq=freq)
def map_reduce_between(self, other=None, map_func_nb=None, map_args=None, reduce_func_nb=None, reduce_args=None, broadcast_kwargs=None): """See `vectorbt.signals.nb.map_reduce_between_nb`. If `other` specified, see `vectorbt.signals.nb.map_reduce_between_two_nb`. Both will be broadcast using `vectorbt.base.reshape_fns.broadcast` with `broadcast_kwargs`. Note that `map_args` and `reduce_args` won't be broadcast. ## Example Get average distance between signals in `sig`: ```python-repl >>> distance_map_nb = njit(lambda from_i, to_i, col: to_i - from_i) >>> mean_reduce_nb = njit(lambda col, a: np.nanmean(a)) >>> sig.vbt.signals.map_reduce_between( ... map_func_nb=distance_map_nb, ... reduce_func_nb=mean_reduce_nb) a NaN b 2.0 c 1.0 dtype: float64 ``` """ if broadcast_kwargs is None: broadcast_kwargs = {} checks.assert_not_none(map_func_nb) checks.assert_not_none(reduce_func_nb) checks.assert_numba_func(map_func_nb) checks.assert_numba_func(reduce_func_nb) if map_args is None: map_args = () if reduce_args is None: reduce_args = () if other is None: # One input array result = nb.map_reduce_between_nb(self.to_2d_array(), map_func_nb, map_args, reduce_func_nb, reduce_args) if isinstance(self._obj, pd.Series): return result[0] return pd.Series(result, index=self.wrapper.columns) else: # Two input arrays obj, other = reshape_fns.broadcast(self._obj, other, **broadcast_kwargs) checks.assert_dtype(other, np.bool) result = nb.map_reduce_between_two_nb(obj.vbt.to_2d_array(), other.vbt.to_2d_array(), map_func_nb, map_args, reduce_func_nb, reduce_args) return obj.vbt.wrapper.wrap_reduced(result)
def map_reduce_between(self, *args, other=None, map_func_nb=None, reduce_func_nb=None, broadcast_kwargs={}): """See `vectorbt.signals.nb.map_reduce_between_nb`. If `other` specified, see `vectorbt.signals.nb.map_reduce_between_two_nb`. Arguments will be broadcasted using `vectorbt.utils.reshape_fns.broadcast` with `broadcast_kwargs`. Example: Get maximum distance between signals in `signals`: ```python-repl >>> distance_map_nb = njit(lambda col, prev_i, next_i: next_i - prev_i) >>> max_reduce_nb = njit(lambda col, a: np.nanmax(a)) >>> print(signals.vbt.signals.map_reduce_between( ... map_func_nb=distance_map_nb, reduce_func_nb=max_reduce_nb)) a 3.0 b 3.0 c NaN dtype: float64 ```""" checks.assert_not_none(map_func_nb) checks.assert_not_none(reduce_func_nb) checks.assert_numba_func(map_func_nb) checks.assert_numba_func(reduce_func_nb) if other is None: # One input array result = nb.map_reduce_between_nb(self.to_2d_array(), map_func_nb, reduce_func_nb, *args) if isinstance(self._obj, pd.Series): return result[0] return pd.Series(result, index=self.columns) else: # Two input arrays obj, other = reshape_fns.broadcast(self._obj, other, **broadcast_kwargs) checks.assert_dtype(other, np.bool_) result = nb.map_reduce_between_two_nb(self.to_2d_array(), other.vbt.to_2d_array(), map_func_nb, reduce_func_nb, *args) if isinstance(obj, pd.Series): return result[0] return pd.Series(result, index=obj.vbt.columns)
def test_assert_dtype(self): checks.assert_dtype(np.zeros(1), np.float) checks.assert_dtype(pd.Series([1, 2, 3]), np.int) checks.assert_dtype(pd.DataFrame({'a': [1, 2], 'b': [3, 4]}), np.int) try: checks.assert_dtype(pd.DataFrame({'a': [1, 2], 'b': [3., 4.]}), np.int) raise Exception except: pass
def _validate(cls, obj): checks.assert_dtype(obj, np.bool)
def map_reduce_between( self, other: tp.Optional[tp.ArrayLike] = None, map_func_nb: tp.Optional[tp.SignalMapFunc] = None, map_args: tp.Optional[tp.Args] = None, reduce_func_nb: tp.Optional[tp.SignalReduceFunc] = None, reduce_args: tp.Optional[tp.Args] = None, broadcast_kwargs: tp.KwargsLike = None, wrap_kwargs: tp.KwargsLike = None) -> tp.MaybeSeries: """See `vectorbt.signals.nb.map_reduce_between_nb`. If `other` specified, see `vectorbt.signals.nb.map_reduce_between_two_nb`. Both will broadcast using `vectorbt.base.reshape_fns.broadcast` with `broadcast_kwargs`. Note that `map_args` and `reduce_args` won't be broadcast. ## Example Get average distance between signals in `sig`: ```python-repl >>> distance_map_nb = njit(lambda from_i, to_i, col: to_i - from_i) >>> mean_reduce_nb = njit(lambda col, a: np.nanmean(a)) >>> sig.vbt.signals.map_reduce_between( ... map_func_nb=distance_map_nb, ... reduce_func_nb=mean_reduce_nb) a NaN b 2.0 c 1.0 dtype: float64 ``` """ if broadcast_kwargs is None: broadcast_kwargs = {} checks.assert_not_none(map_func_nb) checks.assert_not_none(reduce_func_nb) checks.assert_numba_func(map_func_nb) checks.assert_numba_func(reduce_func_nb) if map_args is None: map_args = () if reduce_args is None: reduce_args = () wrap_kwargs = merge_dicts(dict(name_or_index='map_reduce_between'), wrap_kwargs) if other is None: # One input array result = nb.map_reduce_between_nb(self.to_2d_array(), map_func_nb, map_args, reduce_func_nb, reduce_args) return self.wrapper.wrap_reduced(result, **wrap_kwargs) else: # Two input arrays obj, other = reshape_fns.broadcast(self._obj, other, **broadcast_kwargs) checks.assert_dtype(other, np.bool_) result = nb.map_reduce_between_two_nb(obj.vbt.to_2d_array(), other.vbt.to_2d_array(), map_func_nb, map_args, reduce_func_nb, reduce_args) return obj.vbt.wrapper.wrap_reduced(result, **wrap_kwargs)
def test_assert_dtype(self): checks.assert_dtype(np.zeros(1), np.float) checks.assert_dtype(pd.Series([1, 2, 3]), np.int) checks.assert_dtype(pd.DataFrame({'a': [1, 2], 'b': [3, 4]}), np.int) with pytest.raises(Exception) as e_info: checks.assert_dtype(pd.DataFrame({'a': [1, 2], 'b': [3., 4.]}), np.int)
def from_signals(cls, price, entries, exits, size=np.inf, entry_price=None, exit_price=None, init_capital=None, fees=None, fixed_fees=None, slippage=None, accumulate=False, broadcast_kwargs={}, **kwargs): """Build portfolio from entry and exit signals. At each entry signal in `entries`, buys `size` of shares for `entry_price` to enter a position. At each exit signal in `exits`, sells everything for `exit_price` to exit the position. Accumulation of orders is disabled by default. Args: price (pandas_like): Main price of the asset, such as close. entries (array_like): Boolean array of entry signals. exits (array_like): Boolean array of exit signals. size (int, float or array_like): The amount of shares to order. To buy/sell everything, set the size to `numpy.inf`. entry_price (array_like): Entry price. Defaults to `price`. exit_price (array_like): Exit price. Defaults to `price`. init_capital (int or float): The initial capital. fees (float or array_like): Fees in percentage of the order value. fixed_fees (float or array_like): Fixed amount of fees to pay per order. slippage (float or array_like): Slippage in percentage of price. accumulate (bool): If `accumulate` is `True`, entering the market when already in the market will be allowed to increase a position. **kwargs: Keyword arguments passed to the `__init__` method. For defaults, see `vectorbt.defaults.portfolio`. All array-like arguments will be broadcasted together using `vectorbt.utils.reshape_fns.broadcast` with `broadcast_kwargs`. At the end, all array objects will have the same metadata. Example: Portfolio from various signal sequences: ```python-repl >>> entries = pd.DataFrame({ ... 'a': [True, False, False, False, False], ... 'b': [True, False, True, False, True], ... 'c': [True, True, True, True, True] ... }, index=index) >>> exits = pd.DataFrame({ ... 'a': [False, False, False, False, False], ... 'b': [False, True, False, True, False], ... 'c': [True, True, True, True, True] ... }, index=index) >>> portfolio = vbt.Portfolio.from_signals( ... price, entries, exits, size=10, ... init_capital=100, fees=0.0025, fixed_fees=1., slippage=0.001) >>> print(portfolio.order_records) Column Index Size Price Fees Side 0 0.0 0.0 10.0 1.001 1.025025 0.0 1 1.0 0.0 10.0 1.001 1.025025 0.0 2 1.0 1.0 10.0 1.998 1.049950 1.0 3 1.0 2.0 10.0 3.003 1.075075 0.0 4 1.0 3.0 10.0 1.998 1.049950 1.0 5 1.0 4.0 10.0 1.001 1.025025 0.0 6 2.0 0.0 10.0 1.001 1.025025 0.0 >>> print(portfolio.shares) a b c 2018-01-01 10.0 10.0 10.0 2018-01-02 10.0 0.0 10.0 2018-01-03 10.0 10.0 10.0 2018-01-04 10.0 0.0 10.0 2018-01-05 10.0 10.0 10.0 >>> print(portfolio.cash) a b c 2018-01-01 88.964975 88.964975 88.964975 2018-01-02 88.964975 107.895025 88.964975 2018-01-03 88.964975 76.789950 88.964975 2018-01-04 88.964975 95.720000 88.964975 2018-01-05 88.964975 84.684975 88.964975 ``` """ # Get defaults if entry_price is None: entry_price = price if exit_price is None: exit_price = price if init_capital is None: init_capital = defaults.portfolio['init_capital'] if fees is None: fees = defaults.portfolio['fees'] if fixed_fees is None: fixed_fees = defaults.portfolio['fixed_fees'] if slippage is None: slippage = defaults.portfolio['slippage'] # Perform checks checks.assert_type(price, (pd.Series, pd.DataFrame)) checks.assert_dtype(entries, np.bool_) checks.assert_dtype(exits, np.bool_) # Broadcast inputs price, entries, exits, size, entry_price, exit_price, fees, fixed_fees, slippage = \ reshape_fns.broadcast(price, entries, exits, size, entry_price, exit_price, fees, fixed_fees, slippage, **broadcast_kwargs, writeable=True) # Perform calculation order_records, cash, shares = nb.simulate_from_signals_nb( reshape_fns.to_2d(price, raw=True).shape, init_capital, reshape_fns.to_2d(entries, raw=True), reshape_fns.to_2d(exits, raw=True), reshape_fns.to_2d(size, raw=True), reshape_fns.to_2d(entry_price, raw=True), reshape_fns.to_2d(exit_price, raw=True), reshape_fns.to_2d(fees, raw=True), reshape_fns.to_2d(fixed_fees, raw=True), reshape_fns.to_2d(slippage, raw=True), accumulate) # Bring to the same meta cash = price.vbt.wrap(cash) shares = price.vbt.wrap(shares) return cls(price, init_capital, order_records, cash, shares, **kwargs)
def __init__(self, parent): checks.assert_dtype(parent._obj, np.bool) Base_DFAccessor.__init__(self, parent) Signals_Accessor.__init__(self, parent)
def from_signals(cls, main_price, entries, exits, size=np.inf, size_type=SizeType.Shares, entry_price=None, exit_price=None, init_capital=None, fees=None, fixed_fees=None, slippage=None, accumulate=False, broadcast_kwargs={}, freq=None, **kwargs): """Build portfolio from entry and exit signals. For each signal in `entries`, buys `size` of shares for `entry_price` to enter a position. For each signal in `exits`, sells everything for `exit_price` to exit the position. Accumulation of orders is disabled by default. For more details, see `vectorbt.portfolio.nb.simulate_from_signals_nb`. Args: main_price (pandas_like): Main price of the asset, such as close. Will broadcast. entries (array_like): Boolean array of entry signals. Will broadcast. exits (array_like): Boolean array of exit signals. Will broadcast. size (float or array_like): The amount of shares to order. Will broadcast. To buy/sell everything, set the size to `np.inf`. size_type (int or array_like): See `vectorbt.portfolio.enums.SizeType`. Only `SizeType.Shares` and `SizeType.Cash` are supported. entry_price (array_like): Entry price. Defaults to `main_price`. Will broadcast. exit_price (array_like): Exit price. Defaults to `main_price`. Will broadcast. init_capital (float or array_like): The initial capital. Will broadcast. Allowed is either a single value or value per column. fees (float or array_like): Fees in percentage of the order value. Will broadcast. fixed_fees (float or array_like): Fixed amount of fees to pay per order. Will broadcast. slippage (float or array_like): Slippage in percentage of price. Will broadcast. accumulate (bool): If `accumulate` is `True`, entering the market when already in the market will be allowed to increase a position. broadcast_kwargs: Keyword arguments passed to `vectorbt.base.reshape_fns.broadcast`. freq (any): Index frequency in case `main_price.index` is not datetime-like. **kwargs: Keyword arguments passed to the `__init__` method. For defaults, see `vectorbt.defaults.portfolio`. All time series will be broadcasted together using `vectorbt.base.reshape_fns.broadcast`. At the end, they will have the same metadata. Example: Portfolio from various signal sequences: ```python-repl >>> entries = pd.DataFrame({ ... 'a': [True, False, False, False, False], ... 'b': [True, False, True, False, True], ... 'c': [True, True, True, True, True] ... }, index=index) >>> exits = pd.DataFrame({ ... 'a': [False, False, False, False, False], ... 'b': [False, True, False, True, False], ... 'c': [True, True, True, True, True] ... }, index=index) >>> portfolio = vbt.Portfolio.from_signals( ... price, entries, exits, size=10, ... init_capital=100, fees=0.0025, fixed_fees=1., slippage=0.001) >>> portfolio.orders.records col idx size price fees side 0 0 0 10.0 1.001 1.025025 0 1 1 0 10.0 1.001 1.025025 0 2 1 1 10.0 1.998 1.049950 1 3 1 2 10.0 3.003 1.075075 0 4 1 3 10.0 1.998 1.049950 1 5 1 4 10.0 1.001 1.025025 0 6 2 0 10.0 1.001 1.025025 0 >>> portfolio.equity a b c 2020-01-01 98.964975 98.964975 98.964975 2020-01-02 108.964975 107.895025 108.964975 2020-01-03 118.964975 106.789950 118.964975 2020-01-04 108.964975 95.720000 108.964975 2020-01-05 98.964975 94.684975 98.964975 ``` """ # Get defaults if entry_price is None: entry_price = main_price if exit_price is None: exit_price = main_price if init_capital is None: init_capital = defaults.portfolio['init_capital'] if size is None: size = defaults.portfolio['size'] if size_type is None: size_type = defaults.portfolio['size_type'] if fees is None: fees = defaults.portfolio['fees'] if fixed_fees is None: fixed_fees = defaults.portfolio['fixed_fees'] if slippage is None: slippage = defaults.portfolio['slippage'] # Perform checks checks.assert_type(main_price, (pd.Series, pd.DataFrame)) checks.assert_dtype(entries, np.bool_) checks.assert_dtype(exits, np.bool_) # Broadcast inputs # Only main_price is broadcasted, others can remain unchanged thanks to flexible indexing keep_raw = (False, True, True, True, True, True, True, True, True, True, True) main_price, entries, exits, size, size_type, entry_price, \ exit_price, fees, fixed_fees, slippage, init_capital = \ reshape_fns.broadcast( main_price, entries, exits, size, size_type, entry_price, exit_price, fees, fixed_fees, slippage, init_capital, **broadcast_kwargs, writeable=True, keep_raw=keep_raw) target_shape = (main_price.shape[0], main_price.shape[1] if main_price.ndim > 1 else 1) # Perform calculation order_records, cash, shares = nb.simulate_from_signals_nb( target_shape, init_capital, entries, exits, size, size_type, entry_price, exit_price, fees, fixed_fees, slippage, accumulate, is_2d=main_price.ndim == 2) # Bring to the same meta cash = main_price.vbt.wrap(cash) shares = main_price.vbt.wrap(shares) orders = Orders(order_records, main_price, freq=freq) if checks.is_series(main_price): init_capital = init_capital.item(0) else: init_capital = np.broadcast_to(init_capital, (target_shape[1], )) init_capital = main_price.vbt.wrap_reduced(init_capital) return cls(main_price, init_capital, orders, cash, shares, freq=freq, **kwargs)
def _validate(cls, obj): if cls.dtype is not None: checks.assert_dtype(obj, cls.dtype)