Ejemplo n.º 1
0
    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)
Ejemplo n.º 2
0
    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)
Ejemplo n.º 3
0
    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)
Ejemplo n.º 4
0
    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)
Ejemplo n.º 5
0
    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)
Ejemplo n.º 6
0
 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
Ejemplo n.º 7
0
 def _validate(cls, obj):
     checks.assert_dtype(obj, np.bool)
Ejemplo n.º 8
0
    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)
Ejemplo n.º 9
0
 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)
Ejemplo n.º 10
0
    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)
Ejemplo n.º 11
0
    def __init__(self, parent):
        checks.assert_dtype(parent._obj, np.bool)

        Base_DFAccessor.__init__(self, parent)
        Signals_Accessor.__init__(self, parent)
Ejemplo n.º 12
0
    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)
Ejemplo n.º 13
0
 def _validate(cls, obj):
     if cls.dtype is not None:
         checks.assert_dtype(obj, cls.dtype)