Skip to content

accessors module

Custom Pandas accessors for signals.

Methods can be accessed as follows:

>>> from vectorbtpro import *

>>> # vectorbtpro.signals.accessors.SignalsAccessor.pos_rank
>>> pd.Series([False, True, True, True, False]).vbt.signals.pos_rank()
0   -1
1    0
2    1
3    2
4   -1
dtype: int64

The accessors extend vectorbtpro.generic.accessors.

Note

The underlying Series/DataFrame must already be a signal series and have boolean data type.

Grouping is only supported by the methods that accept the group_by argument.

Accessors do not utilize caching.

Run for the examples below

>>> mask = pd.DataFrame({
...     'a': [True, False, False, False, False],
...     'b': [True, False, True, False, True],
...     'c': [True, True, True, False, False]
... }, index=pd.date_range("2020", periods=5))
>>> mask
                a      b      c
2020-01-01   True   True   True
2020-01-02  False  False   True
2020-01-03  False   True   True
2020-01-04  False  False  False
2020-01-05  False   True  False

Stats

>>> mask.vbt.signals.stats(column='a')
Start                         2020-01-01 00:00:00
End                           2020-01-05 00:00:00
Period                            5 days 00:00:00
Total                                           1
Rate [%]                                     20.0
First Index                   2020-01-01 00:00:00
Last Index                    2020-01-01 00:00:00
Norm Avg Index [-1, 1]                       -1.0
Distance: Min                                 NaT
Distance: Median                              NaT
Distance: Max                                 NaT
Total Partitions                                1
Partition Rate [%]                          100.0
Partition Length: Min             1 days 00:00:00
Partition Length: Median          1 days 00:00:00
Partition Length: Max             1 days 00:00:00
Partition Distance: Min                       NaT
Partition Distance: Median                    NaT
Partition Distance: Max                       NaT
Name: a, dtype: object

We can pass another signal array to compare this array with:

>>> mask.vbt.signals.stats(column='a', settings=dict(target=mask['b']))

Start                         2020-01-01 00:00:00
End                           2020-01-05 00:00:00
Period                            5 days 00:00:00
Total                                           1
Rate [%]                                     20.0
Total Overlapping                               1
Overlapping Rate [%]                    33.333333
First Index                   2020-01-01 00:00:00
Last Index                    2020-01-01 00:00:00
Norm Avg Index [-1, 1]                       -1.0
Distance -> Target: Min           0 days 00:00:00
Distance -> Target: Median        2 days 00:00:00
Distance -> Target: Max           4 days 00:00:00
Total Partitions                                1
Partition Rate [%]                          100.0
Partition Length: Min             1 days 00:00:00
Partition Length: Median          1 days 00:00:00
Partition Length: Max             1 days 00:00:00
Partition Distance: Min                       NaT
Partition Distance: Median                    NaT
Partition Distance: Max                       NaT
Name: a, dtype: object

We can also return duration as a floating number rather than a timedelta:

>>> mask.vbt.signals.stats(column='a', settings=dict(to_timedelta=False))
Start                         2020-01-01 00:00:00
End                           2020-01-05 00:00:00
Period                                          5
Total                                           1
Rate [%]                                     20.0
First Index                   2020-01-01 00:00:00
Last Index                    2020-01-01 00:00:00
Norm Avg Index [-1, 1]                       -1.0
Distance: Min                                 NaN
Distance: Median                              NaN
Distance: Max                                 NaN
Total Partitions                                1
Partition Rate [%]                          100.0
Partition Length: Min                         1.0
Partition Length: Median                      1.0
Partition Length: Max                         1.0
Partition Distance: Min                       NaN
Partition Distance: Median                    NaN
Partition Distance: Max                       NaN
Name: a, dtype: object

StatsBuilderMixin.stats() also supports (re-)grouping:

>>> mask.vbt.signals.stats(column=0, group_by=[0, 0, 1])
Start                         2020-01-01 00:00:00
End                           2020-01-05 00:00:00
Period                            5 days 00:00:00
Total                                           4
Rate [%]                                     40.0
First Index                   2020-01-01 00:00:00
Last Index                    2020-01-05 00:00:00
Norm Avg Index [-1, 1]                      -0.25
Distance: Min                     2 days 00:00:00
Distance: Median                  2 days 00:00:00
Distance: Max                     2 days 00:00:00
Total Partitions                                4
Partition Rate [%]                          100.0
Partition Length: Min             1 days 00:00:00
Partition Length: Median          1 days 00:00:00
Partition Length: Max             1 days 00:00:00
Partition Distance: Min           2 days 00:00:00
Partition Distance: Median        2 days 00:00:00
Partition Distance: Max           2 days 00:00:00
Name: 0, dtype: object

Plots

This class inherits subplots from GenericAccessor.


SignalsAccessor class

SignalsAccessor(
    wrapper,
    obj=None,
    **kwargs
)

Accessor on top of signal series. For both, Series and DataFrames.

Accessible via pd.Series.vbt.signals and pd.DataFrame.vbt.signals.

Superclasses

Inherited members

Subclasses


between_partition_ranges method

SignalsAccessor.between_partition_ranges(
    group_by=None,
    jitted=None,
    chunked=None,
    **kwargs
)

Wrap the result of between_partition_ranges_nb() with Ranges.

Usage

>>> mask_sr = pd.Series([True, False, False, True, False, True, True])
>>> mask_sr.vbt.signals.between_partition_ranges().readable
   Range Id  Column  Start Timestamp  End Timestamp  Status
0         0       0                0              3  Closed
1         1       0                3              5  Closed

between_ranges method

SignalsAccessor.between_ranges(
    target=None,
    relation='onemany',
    incl_open=False,
    broadcast_kwargs=None,
    group_by=None,
    attach_target=False,
    jitted=None,
    chunked=None,
    **kwargs
)

Wrap the result of between_ranges_nb() with Ranges.

If target specified, see between_two_ranges_nb(). Both will broadcast using broadcast() and broadcast_kwargs.

Usage

  • One array:
>>> mask_sr = pd.Series([True, False, False, True, False, True, True])
>>> ranges = mask_sr.vbt.signals.between_ranges()
>>> ranges
<vectorbtpro.generic.ranges.Ranges at 0x7ff29ea7c7b8>

>>> ranges.readable
   Range Id  Column  Start Index  End Index  Status
0         0       0            0          3  Closed
1         1       0            3          5  Closed
2         2       0            5          6  Closed

>>> ranges.duration.values
array([3, 2, 1])
  • Two arrays, traversing the signals of the first array:
>>> mask_sr1 = pd.Series([True, True, True, False, False])
>>> mask_sr2 = pd.Series([False, False, True, False, True])
>>> ranges = mask_sr1.vbt.signals.between_ranges(target=mask_sr2)
>>> ranges
<vectorbtpro.generic.ranges.Ranges at 0x7ff29e3b80f0>

>>> ranges.readable
   Range Id  Column  Start Index  End Index  Status
0         0       0            2          2  Closed
1         1       0            2          4  Closed

>>> ranges.duration.values
array([0, 2])
  • Two arrays, traversing the signals of the second array:
>>> ranges = mask_sr1.vbt.signals.between_ranges(target=mask_sr2, relation="manyone")
>>> ranges
<vectorbtpro.generic.ranges.Ranges at 0x7ff29eccbd68>

>>> ranges.readable
   Range Id  Column  Start Index  End Index  Status
0         0       0            0          2  Closed
1         1       0            1          2  Closed
2         2       0            2          2  Closed

>>> ranges.duration.values
array([0, 2])

clean class method

SignalsAccessor.clean(
    *objs,
    force_first=True,
    keep_conflicts=False,
    reverse_order=False,
    broadcast_kwargs=None,
    jitted=None,
    chunked=None,
    wrap_kwargs=None
)

Clean signals.

If one array is passed, see SignalsAccessor.first(). If two arrays passed, entries and exits, see clean_enex_nb().


delta_ranges method

SignalsAccessor.delta_ranges(
    delta,
    group_by=None,
    **kwargs
)

Build a record array of the type Ranges from a delta applied after each signal (or before if delta is negative).


distance_from_last method

SignalsAccessor.distance_from_last(
    nth=1,
    jitted=None,
    chunked=None,
    wrap_kwargs=None
)

See distance_from_last_nb().

Usage

  • Get the distance to the last signal:
>>> mask.vbt.signals.distance_from_last()
            a  b  c
2020-01-01 -1 -1 -1
2020-01-02  1  1  1
2020-01-03  2  2  1
2020-01-04  3  1  1
2020-01-05  4  2  2
  • Get the distance to the second last signal:
>>> mask.vbt.signals.distance_from_last(nth=2)
            a  b  c
2020-01-01 -1 -1 -1
2020-01-02 -1 -1  1
2020-01-03 -1  2  1
2020-01-04 -1  3  2
2020-01-05 -1  2  3

empty class method

SignalsAccessor.empty(
    *args,
    fill_value=False,
    **kwargs
)

BaseAccessor.empty() with fill_value=False.


empty_like class method

SignalsAccessor.empty_like(
    *args,
    fill_value=False,
    **kwargs
)

BaseAccessor.empty_like() with fill_value=False.


first method

SignalsAccessor.first(
    wrap_kwargs=None,
    **kwargs
)

Select signals that satisfy the condition pos_rank == 0.

Uses SignalsAccessor.pos_rank().


first_after method

SignalsAccessor.first_after(
    reset_by,
    wrap_kwargs=None,
    **kwargs
)

Select signals that satisfy the condition pos_rank == 0.

Uses SignalsAccessor.pos_rank_after().


from_nth method

SignalsAccessor.from_nth(
    n,
    wrap_kwargs=None,
    **kwargs
)

Select signals that satisfy the condition pos_rank >= n.

Uses SignalsAccessor.pos_rank().


from_nth_after method

SignalsAccessor.from_nth_after(
    n,
    reset_by,
    wrap_kwargs=None,
    **kwargs
)

Select signals that satisfy the condition pos_rank >= n.

Uses SignalsAccessor.pos_rank_after().


generate class method

SignalsAccessor.generate(
    shape,
    place_func_nb,
    *args,
    place_args=None,
    only_once=True,
    wait=1,
    broadcast_named_args=None,
    broadcast_kwargs=None,
    template_context=None,
    jitted=None,
    chunked=None,
    wrapper=None,
    wrap_kwargs=None
)

See generate_nb().

shape can be a shape-like tuple or an instance of ArrayWrapper (will be used as wrapper).

Arguments to place_func_nb can be passed either as *args or place_args (but not both!).

Usage

  • Generate random signals manually:
>>> @njit
... def place_func_nb(c):
...     i = np.random.choice(len(c.out))
...     c.out[i] = True
...     return i

>>> vbt.pd_acc.signals.generate(
...     (5, 3),
...     place_func_nb,
...     wrap_kwargs=dict(
...         index=mask.index,
...         columns=mask.columns
...     )
... )
                a      b      c
2020-01-01   True  False  False
2020-01-02  False   True  False
2020-01-03  False  False   True
2020-01-04  False  False  False
2020-01-05  False  False  False

generate_both class method

SignalsAccessor.generate_both(
    shape,
    entry_place_func_nb,
    exit_place_func_nb,
    *args,
    entry_place_args=None,
    exit_place_args=None,
    entry_wait=1,
    exit_wait=1,
    broadcast_named_args=None,
    broadcast_kwargs=None,
    template_context=None,
    jitted=None,
    chunked=None,
    wrapper=None,
    wrap_kwargs=None
)

See generate_enex_nb().

shape can be a shape-like tuple or an instance of ArrayWrapper (will be used as wrapper).

Arguments to entry_place_func_nb can be passed either as *args or entry_place_args while arguments to exit_place_func_nb can be passed either as *args or exit_place_args (but not both!).

Usage

  • Generate entry and exit signals one after another:
>>> @njit
... def place_func_nb(c):
...     c.out[0] = True
...     return 0

>>> en, ex = vbt.pd_acc.signals.generate_both(
...     (5, 3),
...     entry_place_func_nb=place_func_nb,
...     exit_place_func_nb=place_func_nb,
...     wrap_kwargs=dict(
...         index=mask.index,
...         columns=mask.columns
...     )
... )
>>> en
                a      b      c
2020-01-01   True   True   True
2020-01-02  False  False  False
2020-01-03   True   True   True
2020-01-04  False  False  False
2020-01-05   True   True   True
>>> ex
                a      b      c
2020-01-01  False  False  False
2020-01-02   True   True   True
2020-01-03  False  False  False
2020-01-04   True   True   True
2020-01-05  False  False  False
  • Generate three entries and one exit one after another:
>>> @njit
... def entry_place_func_nb(c, n):
...     c.out[:n] = True
...     return n - 1

>>> @njit
... def exit_place_func_nb(c, n):
...     c.out[:n] = True
...     return n - 1

>>> en, ex = vbt.pd_acc.signals.generate_both(
...     (5, 3),
...     entry_place_func_nb=entry_place_func_nb,
...     entry_place_args=(3,),
...     exit_place_func_nb=exit_place_func_nb,
...     exit_place_args=(1,),
...     wrap_kwargs=dict(
...         index=mask.index,
...         columns=mask.columns
...     )
... )
>>> en
                a      b      c
2020-01-01   True   True   True
2020-01-02   True   True   True
2020-01-03   True   True   True
2020-01-04  False  False  False
2020-01-05   True   True   True
>>> ex
                a      b      c
2020-01-01  False  False  False
2020-01-02  False  False  False
2020-01-03  False  False  False
2020-01-04   True   True   True
2020-01-05  False  False  False

generate_exits method

SignalsAccessor.generate_exits(
    exit_place_func_nb,
    *args,
    exit_place_args=None,
    wait=1,
    until_next=True,
    skip_until_exit=False,
    broadcast_named_args=None,
    broadcast_kwargs=None,
    template_context=None,
    jitted=None,
    chunked=None,
    wrap_kwargs=None
)

See generate_ex_nb().

Usage

  • Generate an exit just before the next entry:
>>> @njit
... def exit_place_func_nb(c):
...     c.out[-1] = True
...     return len(c.out) - 1

>>> mask.vbt.signals.generate_exits(exit_place_func_nb)
                a      b      c
2020-01-01  False  False  False
2020-01-02  False   True  False
2020-01-03  False  False  False
2020-01-04  False   True  False
2020-01-05   True  False   True

generate_ohlc_stop_exits method

SignalsAccessor.generate_ohlc_stop_exits(
    entry_price,
    open=nan,
    high=nan,
    low=nan,
    close=nan,
    sl_stop=nan,
    tsl_th=nan,
    tsl_stop=nan,
    tp_stop=nan,
    reverse=False,
    is_entry_open=False,
    out_dict=None,
    entry_wait=1,
    exit_wait=1,
    until_next=True,
    skip_until_exit=False,
    chain=False,
    broadcast_kwargs=None,
    jitted=None,
    chunked=None,
    wrap_kwargs=None,
    **kwargs
)

Generate exits based on when the price hits (trailing) stop loss or take profit.

Use out_dict as a dict to pass stop_price and stop_type arrays. You can also set out_dict to {} to produce these arrays automatically and still have access to them.

For arguments, see ohlc_stop_place_nb(). If chain is True, uses SignalsAccessor.generate_both(). Otherwise, uses SignalsAccessor.generate_exits().

All array-like arguments including stops and out_dict will broadcast using broadcast() and broadcast_kwargs.

For arguments, see ohlc_stop_place_nb().

Hint

Default arguments will generate an exit signal strictly between two entry signals. If both entry signals are too close to each other, no exit will be generated.

To ignore all entries that come between an entry and its exit, set until_next to False and skip_until_exit to True.

To remove all entries that come between an entry and its exit, set chain to True. This will return two arrays: new entries and exits.

Usage

  • Generate exits for TSL and TP of 10%:
>>> price = pd.DataFrame({
...     'open': [10, 11, 12, 11, 10],
...     'high': [11, 12, 13, 12, 11],
...     'low': [9, 10, 11, 10, 9],
...     'close': [10, 11, 12, 11, 10]
... })
>>> out_dict = {}
>>> exits = mask.vbt.signals.generate_ohlc_stop_exits(
...     price["open"],
...     price['open'],
...     price['high'],
...     price['low'],
...     price['close'],
...     tsl_stop=0.1,
...     tp_stop=0.1,
...     is_entry_open=True,
...     out_dict=out_dict,
... )
>>> exits
                a      b      c
2020-01-01  False  False  False
2020-01-02   True   True  False
2020-01-03  False  False  False
2020-01-04  False   True   True
2020-01-05  False  False  False

>>> out_dict['stop_price']
               a     b     c
2020-01-01   NaN   NaN   NaN
2020-01-02  11.0  11.0   NaN
2020-01-03   NaN   NaN   NaN
2020-01-04   NaN  10.8  10.8
2020-01-05   NaN   NaN   NaN

>>> out_dict['stop_type'].vbt(mapping=vbt.sig_enums.StopType).apply_mapping()
               a     b     c
2020-01-01  None  None  None
2020-01-02    TP    TP  None
2020-01-03  None  None  None
2020-01-04  None   TSL   TSL
2020-01-05  None  None  None

Notice how the first two entry signals in the third column have no exit signal - there is no room between them for an exit signal.

  • To find an exit for the first entry and ignore all entries that are in-between them, we can pass until_next=False and skip_until_exit=True:
>>> out_dict = {}
>>> exits = mask.vbt.signals.generate_ohlc_stop_exits(
...     price['open'],
...     price['open'],
...     price['high'],
...     price['low'],
...     price['close'],
...     tsl_stop=0.1,
...     tp_stop=0.1,
...     is_entry_open=True,
...     out_dict=out_dict,
...     until_next=False,
...     skip_until_exit=True
... )
>>> exits
                a      b      c
2020-01-01  False  False  False
2020-01-02   True   True   True
2020-01-03  False  False  False
2020-01-04  False   True   True
2020-01-05  False  False  False

>>> out_dict['stop_price']
               a     b     c
2020-01-01   NaN   NaN   NaN
2020-01-02  11.0  11.0  11.0
2020-01-03   NaN   NaN   NaN
2020-01-04   NaN  10.8  10.8
2020-01-05   NaN   NaN   NaN

>>> out_dict['stop_type'].vbt(mapping=vbt.sig_enums.StopType).apply_mapping()
               a     b     c
2020-01-01  None  None  None
2020-01-02    TP    TP    TP
2020-01-03  None  None  None
2020-01-04  None   TSL   TSL
2020-01-05  None  None  None

Now, the first signal in the third column gets executed regardless of the entries that come next, which is very similar to the logic that is implemented in Portfolio.from_signals().

  • To automatically remove all ignored entry signals, pass chain=True. This will return a new entries array:
>>> out_dict = {}
>>> new_entries, exits = mask.vbt.signals.generate_ohlc_stop_exits(
...     price['open'],
...     price['open'],
...     price['high'],
...     price['low'],
...     price['close'],
...     tsl_stop=0.1,
...     tp_stop=0.1,
...     is_entry_open=True,
...     out_dict=out_dict,
...     chain=True
... )
>>> new_entries
                a      b      c
2020-01-01   True   True   True
2020-01-02  False  False  False  << removed entry in the third column
2020-01-03  False   True   True
2020-01-04  False  False  False
2020-01-05  False   True  False
>>> exits
                a      b      c
2020-01-01  False  False  False
2020-01-02   True   True   True
2020-01-03  False  False  False
2020-01-04  False   True   True
2020-01-05  False  False  False

Warning

The last two examples above make entries dependent upon exits - this makes only sense if you have no other exit arrays to combine this stop exit array with.

  • Test multiple parameter combinations:
>>> exits = mask.vbt.signals.generate_ohlc_stop_exits(
...     price['open'],
...     price['open'],
...     price['high'],
...     price['low'],
...     price['close'],
...     sl_stop=vbt.Param([False, 0.1]),
...     tsl_stop=vbt.Param([False, 0.1]),
...     is_entry_open=True
... )
>>> exits
sl_stop     False                                       0.1                \
tsl_stop    False                  0.1                False
                a      b      c      a      b      c      a      b      c
2020-01-01  False  False  False  False  False  False  False  False  False
2020-01-02  False  False  False  False  False  False  False  False  False
2020-01-03  False  False  False  False  False  False  False  False  False
2020-01-04  False  False  False   True   True   True  False   True   True
2020-01-05  False  False  False  False  False  False   True  False  False

sl_stop
tsl_stop      0.1
                a      b      c
2020-01-01  False  False  False
2020-01-02  False  False  False
2020-01-03  False  False  False
2020-01-04   True   True   True
2020-01-05  False  False  False

generate_random class method

SignalsAccessor.generate_random(
    shape,
    n=None,
    prob=None,
    pick_first=False,
    seed=None,
    jitted=None,
    chunked=None,
    **kwargs
)

Generate signals randomly.

shape can be a shape-like tuple or an instance of ArrayWrapper (will be used as wrapper).

If n is set, uses rand_place_nb(). If prob is set, uses rand_by_prob_place_nb().

For arguments, see SignalsAccessor.generate().

n must be either a scalar or an array that will broadcast to the number of columns. prob must be either a single number or an array that will broadcast to match shape.

Specify seed to make output deterministic.

Usage

  • For each column, generate a variable number of signals:
>>> vbt.pd_acc.signals.generate_random(
...     (5, 3),
...     n=[0, 1, 2],
...     seed=42,
...     wrap_kwargs=dict(
...         index=mask.index,
...         columns=mask.columns
...     )
... )
                a      b      c
2020-01-01  False  False  False
2020-01-02  False  False  False
2020-01-03  False  False   True
2020-01-04  False   True  False
2020-01-05  False  False   True
  • For each column and time step, pick a signal with 50% probability:
>>> vbt.pd_acc.signals.generate_random(
...     (5, 3),
...     prob=0.5,
...     seed=42,
...     wrap_kwargs=dict(
...         index=mask.index,
...         columns=mask.columns
...     )
... )
                a      b      c
2020-01-01   True   True   True
2020-01-02  False   True  False
2020-01-03  False  False  False
2020-01-04  False  False   True
2020-01-05   True  False   True

generate_random_both class method

SignalsAccessor.generate_random_both(
    shape,
    n=None,
    entry_prob=None,
    exit_prob=None,
    seed=None,
    entry_wait=1,
    exit_wait=1,
    entry_pick_first=True,
    exit_pick_first=True,
    jitted=None,
    chunked=None,
    wrapper=None,
    wrap_kwargs=None
)

Generate chain of entry and exit signals randomly.

shape can be a shape-like tuple or an instance of ArrayWrapper (will be used as wrapper).

If n is set, uses generate_rand_enex_nb(). If entry_prob and exit_prob are set, uses SignalsAccessor.generate_both() with rand_by_prob_place_nb().

Usage

  • For each column, generate two entries and exits randomly:
>>> en, ex = vbt.pd_acc.signals.generate_random_both(
...     (5, 3),
...     n=2,
...     seed=42,
...     wrap_kwargs=dict(
...         index=mask.index,
...         columns=mask.columns
...     )
... )
>>> en
                a      b      c
2020-01-01  False  False   True
2020-01-02   True   True  False
2020-01-03  False  False  False
2020-01-04   True   True   True
2020-01-05  False  False  False
>>> ex
                a      b      c
2020-01-01  False  False  False
2020-01-02  False  False   True
2020-01-03   True   True  False
2020-01-04  False  False  False
2020-01-05   True   True   True
  • For each column and time step, pick entry with 50% probability and exit right after:
>>> en, ex = vbt.pd_acc.signals.generate_random_both(
...     (5, 3),
...     entry_prob=0.5,
...     exit_prob=1.,
...     seed=42,
...     wrap_kwargs=dict(
...         index=mask.index,
...         columns=mask.columns
...     )
... )
>>> en
                a      b      c
2020-01-01   True   True   True
2020-01-02  False  False  False
2020-01-03  False  False  False
2020-01-04  False  False   True
2020-01-05   True  False  False
>>> ex
                a      b      c
2020-01-01  False  False  False
2020-01-02   True   True   True
2020-01-03  False  False  False
2020-01-04  False  False  False
2020-01-05  False  False   True

generate_random_exits method

SignalsAccessor.generate_random_exits(
    prob=None,
    seed=None,
    wait=1,
    until_next=True,
    skip_until_exit=False,
    broadcast_kwargs=None,
    jitted=None,
    chunked=None,
    wrap_kwargs=None,
    **kwargs
)

Generate exit signals randomly.

If prob is None, uses rand_place_nb(). Otherwise, uses rand_by_prob_place_nb().

Uses SignalsAccessor.generate_exits().

Specify seed to make output deterministic.

Usage

  • After each entry in mask, generate exactly one exit:
>>> mask.vbt.signals.generate_random_exits(seed=42)
                a      b      c
2020-01-01  False  False  False
2020-01-02  False   True  False
2020-01-03  False  False  False
2020-01-04   True   True  False
2020-01-05  False  False   True
  • After each entry in mask and at each time step, generate exit with 50% probability:
>>> mask.vbt.signals.generate_random_exits(prob=0.5, seed=42)
                a      b      c
2020-01-01  False  False  False
2020-01-02   True  False  False
2020-01-03  False  False  False
2020-01-04  False  False  False
2020-01-05  False  False   True

generate_stop_exits method

SignalsAccessor.generate_stop_exits(
    entry_ts,
    ts=nan,
    follow_ts=nan,
    stop=nan,
    trailing=False,
    out_dict=None,
    entry_wait=1,
    exit_wait=1,
    until_next=True,
    skip_until_exit=False,
    chain=False,
    broadcast_kwargs=None,
    jitted=None,
    chunked=None,
    wrap_kwargs=None,
    **kwargs
)

Generate exits based on when ts hits the stop.

For arguments, see stop_place_nb(). If chain is True, uses SignalsAccessor.generate_both(). Otherwise, uses SignalsAccessor.generate_exits().

Use out_dict as a dict to pass stop_ts array. You can also set out_dict to {} to produce this array automatically and still have access to it.

All array-like arguments including stops and out_dict will broadcast using broadcast() and broadcast_kwargs.

Hint

Default arguments will generate an exit signal strictly between two entry signals. If both entry signals are too close to each other, no exit will be generated.

To ignore all entries that come between an entry and its exit, set until_next to False and skip_until_exit to True.

To remove all entries that come between an entry and its exit, set chain to True. This will return two arrays: new entries and exits.

Usage

  • Regular stop loss:
>>> ts = pd.Series([1, 2, 3, 2, 1])

>>> mask.vbt.signals.generate_stop_exits(ts, stop=-0.1)
                a      b      c
2020-01-01  False  False  False
2020-01-02  False  False  False
2020-01-03  False  False  False
2020-01-04  False   True   True
2020-01-05  False  False  False
  • Trailing stop loss:
>>> mask.vbt.signals.generate_stop_exits(ts, stop=-0.1, trailing=True)
                a      b      c
2020-01-01  False  False  False
2020-01-02  False  False  False
2020-01-03  False  False  False
2020-01-04   True   True   True
2020-01-05  False  False  False
  • Testing multiple take profit stops:
>>> mask.vbt.signals.generate_stop_exits(ts, stop=vbt.Param([1.0, 1.5]))
stop                        1.0                  1.5
                a      b      c      a      b      c
2020-01-01  False  False  False  False  False  False
2020-01-02   True   True  False  False  False  False
2020-01-03  False  False  False   True  False  False
2020-01-04  False  False  False  False  False  False
2020-01-05  False  False  False  False  False  False

get_relation_str method

SignalsAccessor.get_relation_str(
    relation
)

Get direction string for relation.


index_from_unravel class method

SignalsAccessor.index_from_unravel(
    range_,
    row_idxs,
    index,
    signal_index_type='range',
    signal_index_name='signal'
)

Get index from an unraveling operation.


index_mapped method

SignalsAccessor.index_mapped(
    group_by=None,
    **kwargs
)

Get a mapped array of indices.

See GenericAccessor.to_mapped().

Only True values will be considered.


metrics class variable

Metrics supported by SignalsAccessor.

HybridConfig(
    start_index=dict(
        title='Start Index',
        calc_func=<function SignalsAccessor.<lambda> at 0x16727be20>,
        agg_func=None,
        tags='wrapper'
    ),
    end_index=dict(
        title='End Index',
        calc_func=<function SignalsAccessor.<lambda> at 0x16727bec0>,
        agg_func=None,
        tags='wrapper'
    ),
    total_duration=dict(
        title='Total Duration',
        calc_func=<function SignalsAccessor.<lambda> at 0x16727bf60>,
        apply_to_timedelta=True,
        agg_func=None,
        tags='wrapper'
    ),
    total=dict(
        title='Total',
        calc_func='total',
        tags='signals'
    ),
    rate=dict(
        title='Rate [%]',
        calc_func='rate',
        post_calc_func=<function SignalsAccessor.<lambda> at 0x1672a0040>,
        tags='signals'
    ),
    total_overlapping=dict(
        title='Total Overlapping',
        calc_func=<function SignalsAccessor.<lambda> at 0x1672a00e0>,
        check_silent_has_target=True,
        tags=[
            'signals',
            'target'
        ]
    ),
    overlapping_rate=dict(
        title='Overlapping Rate [%]',
        calc_func=<function SignalsAccessor.<lambda> at 0x1672a0180>,
        post_calc_func=<function SignalsAccessor.<lambda> at 0x1672a0220>,
        check_silent_has_target=True,
        tags=[
            'signals',
            'target'
        ]
    ),
    first_index=dict(
        title='First Index',
        calc_func='nth_index',
        n=0,
        wrap_kwargs=dict(
            to_index=True
        ),
        tags=[
            'signals',
            'index'
        ]
    ),
    last_index=dict(
        title='Last Index',
        calc_func='nth_index',
        n=-1,
        wrap_kwargs=dict(
            to_index=True
        ),
        tags=[
            'signals',
            'index'
        ]
    ),
    norm_avg_index=dict(
        title='Norm Avg Index [-1, 1]',
        calc_func='norm_avg_index',
        tags=[
            'signals',
            'index'
        ]
    ),
    distance=dict(
        title=RepEval(
            template="f'Distance {self.get_relation_str(relation)} {target_name}' if target is not None else 'Distance'",
            context=None,
            strict=None,
            context_merge_kwargs=None,
            eval_id=None
        ),
        calc_func='between_ranges.duration',
        post_calc_func=<function SignalsAccessor.<lambda> at 0x1672a02c0>,
        apply_to_timedelta=True,
        tags=RepEval(
            template="['signals', 'distance', 'target'] if target is not None else ['signals', 'distance']",
            context=None,
            strict=None,
            context_merge_kwargs=None,
            eval_id=None
        )
    ),
    total_partitions=dict(
        title='Total Partitions',
        calc_func='total_partitions',
        tags=[
            'signals',
            'partitions'
        ]
    ),
    partition_rate=dict(
        title='Partition Rate [%]',
        calc_func='partition_rate',
        post_calc_func=<function SignalsAccessor.<lambda> at 0x1672a0360>,
        tags=[
            'signals',
            'partitions'
        ]
    ),
    partition_len=dict(
        title='Partition Length',
        calc_func='partition_ranges.duration',
        post_calc_func=<function SignalsAccessor.<lambda> at 0x1672a0400>,
        apply_to_timedelta=True,
        tags=[
            'signals',
            'partitions',
            'distance'
        ]
    ),
    partition_distance=dict(
        title='Partition Distance',
        calc_func='between_partition_ranges.duration',
        post_calc_func=<function SignalsAccessor.<lambda> at 0x1672a04a0>,
        apply_to_timedelta=True,
        tags=[
            'signals',
            'partitions',
            'distance'
        ]
    )
)

Returns SignalsAccessor._metrics, which gets (hybrid-) copied upon creation of each instance. Thus, changing this config won't affect the class.

To change metrics, you can either change the config in-place, override this property, or overwrite the instance variable SignalsAccessor._metrics.


norm_avg_index method

SignalsAccessor.norm_avg_index(
    group_by=None,
    jitted=None,
    chunked=None,
    wrap_kwargs=None
)

See norm_avg_index_nb().

Normalized average index measures the average signal location relative to the middle of the column. This way, we can quickly see where the majority of signals are located.

Common values are:

  • -1.0: only the first signal is set
  • 1.0: only the last signal is set
  • 0.0: symmetric distribution around the middle
  • [-1.0, 0.0): average signal is on the left
  • (0.0, 1.0]: average signal is on the right

Usage

>>> pd.Series([True, False, False, False]).vbt.signals.norm_avg_index()
-1.0

>>> pd.Series([False, False, False, True]).vbt.signals.norm_avg_index()
1.0

>>> pd.Series([True, False, False, True]).vbt.signals.norm_avg_index()
0.0

nth method

SignalsAccessor.nth(
    n,
    wrap_kwargs=None,
    **kwargs
)

Select signals that satisfy the condition pos_rank == n.

Uses SignalsAccessor.pos_rank().


nth_after method

SignalsAccessor.nth_after(
    n,
    reset_by,
    wrap_kwargs=None,
    **kwargs
)

Select signals that satisfy the condition pos_rank == n.

Uses SignalsAccessor.pos_rank_after().


nth_index method

SignalsAccessor.nth_index(
    n,
    group_by=None,
    jitted=None,
    chunked=None,
    wrap_kwargs=None
)

See nth_index_nb().

Usage

>>> mask.vbt.signals.nth_index(0)
a   2020-01-01
b   2020-01-01
c   2020-01-01
Name: nth_index, dtype: datetime64[ns]

>>> mask.vbt.signals.nth_index(2)
a          NaT
b   2020-01-05
c   2020-01-03
Name: nth_index, dtype: datetime64[ns]

>>> mask.vbt.signals.nth_index(-1)
a   2020-01-01
b   2020-01-05
c   2020-01-03
Name: nth_index, dtype: datetime64[ns]

>>> mask.vbt.signals.nth_index(-1, group_by=True)
Timestamp('2020-01-05 00:00:00')

partition_pos_rank method

SignalsAccessor.partition_pos_rank(
    jitted=None,
    chunked=None,
    **kwargs
)

Get partition position ranks.

Uses SignalsAccessor.rank() with part_pos_rank_nb().

Usage

  • Rank each partition of True values in mask:
>>> mask.vbt.signals.partition_pos_rank()
            a  b  c
2020-01-01  0  0  0
2020-01-02 -1 -1  0
2020-01-03 -1  1  0
2020-01-04 -1 -1 -1
2020-01-05 -1  2 -1

>>> mask.vbt.signals.partition_pos_rank(after_false=True)
            a  b  c
2020-01-01 -1 -1 -1
2020-01-02 -1 -1 -1
2020-01-03 -1  0 -1
2020-01-04 -1 -1 -1
2020-01-05 -1  1 -1

>>> mask.vbt.signals.partition_pos_rank(reset_by=mask)
            a  b  c
2020-01-01  0  0  0
2020-01-02 -1 -1  0
2020-01-03 -1  0  0
2020-01-04 -1 -1 -1
2020-01-05 -1  0 -1

partition_pos_rank_after method

SignalsAccessor.partition_pos_rank_after(
    reset_by,
    **kwargs
)

Get partition position ranks after each signal in reset_by.


partition_pos_rank_mapped method

SignalsAccessor.partition_pos_rank_mapped(
    group_by=None,
    **kwargs
)

Get a mapped array of partition position ranks.

Uses SignalsAccessor.partition_pos_rank().


partition_ranges method

SignalsAccessor.partition_ranges(
    group_by=None,
    jitted=None,
    chunked=None,
    **kwargs
)

Wrap the result of partition_ranges_nb() with Ranges.

If use_end_idxs is True, uses the index of the last signal in each partition as idx_arr. Otherwise, uses the index of the first signal.

Usage

>>> mask_sr = pd.Series([True, True, True, False, True, True])
>>> mask_sr.vbt.signals.partition_ranges().readable
   Range Id  Column  Start Timestamp  End Timestamp  Status
0         0       0                0              3  Closed
1         1       0                4              5    Open

partition_rate method

SignalsAccessor.partition_rate(
    wrap_kwargs=None,
    group_by=None,
    **kwargs
)

SignalsAccessor.total_partitions() divided by SignalsAccessor.total() in each column/group.


plot method

SignalsAccessor.plot(
    yref='y',
    column=None,
    **kwargs
)

Plot signals.

Args

yref : str
Y coordinate axis.
column : hashable
Column to plot.
**kwargs
Keyword arguments passed to GenericAccessor.lineplot().

Usage

>>> mask[['a', 'c']].vbt.signals.plot().show()


plot_as_entries method

SignalsAccessor.plot_as_entries(
    y=None,
    column=None,
    **kwargs
)

Plot signals as entry markers.

See SignalsAccessor.plot_as_markers().


plot_as_entry_marks method

SignalsAccessor.plot_as_entry_marks(
    y=None,
    column=None,
    **kwargs
)

Plot signals as marked entry markers.

See SignalsAccessor.plot_as_markers().


plot_as_exit_marks method

SignalsAccessor.plot_as_exit_marks(
    y=None,
    column=None,
    **kwargs
)

Plot signals as marked exit markers.

See SignalsAccessor.plot_as_markers().


plot_as_exits method

SignalsAccessor.plot_as_exits(
    y=None,
    column=None,
    **kwargs
)

Plot signals as exit markers.

See SignalsAccessor.plot_as_markers().


plot_as_markers method

SignalsAccessor.plot_as_markers(
    y=None,
    column=None,
    **kwargs
)

Plot Series as markers.

Args

y : array_like
Y-axis values to plot markers on.
column : hashable
Column to plot.
**kwargs
Keyword arguments passed to GenericAccessor.scatterplot().

Usage

>>> ts = pd.Series([1, 2, 3, 2, 1], index=mask.index)
>>> fig = ts.vbt.lineplot()
>>> mask['b'].vbt.signals.plot_as_entries(y=ts, fig=fig)
>>> (~mask['b']).vbt.signals.plot_as_exits(y=ts, fig=fig).show()


plots_defaults property

Defaults for PlotsBuilderMixin.plots().

Merges GenericAccessor.plots_defaults and plots from signals.


pos_rank method

SignalsAccessor.pos_rank(
    jitted=None,
    chunked=None,
    allow_gaps=False,
    **kwargs
)

Get signal position ranks.

Uses SignalsAccessor.rank() with sig_pos_rank_nb().

Usage

  • Rank each True value in each partition in mask:
>>> mask.vbt.signals.pos_rank()
            a  b  c
2020-01-01  0  0  0
2020-01-02 -1 -1  1
2020-01-03 -1  0  2
2020-01-04 -1 -1 -1
2020-01-05 -1  0 -1

>>> mask.vbt.signals.pos_rank(after_false=True)
            a  b  c
2020-01-01 -1 -1 -1
2020-01-02 -1 -1 -1
2020-01-03 -1  0 -1
2020-01-04 -1 -1 -1
2020-01-05 -1  0 -1

>>> mask.vbt.signals.pos_rank(allow_gaps=True)
            a  b  c
2020-01-01  0  0  0
2020-01-02 -1 -1  1
2020-01-03 -1  1  2
2020-01-04 -1 -1 -1
2020-01-05 -1  2 -1

>>> mask.vbt.signals.pos_rank(reset_by=~mask, allow_gaps=True)
            a  b  c
2020-01-01  0  0  0
2020-01-02 -1 -1  1
2020-01-03 -1  0  2
2020-01-04 -1 -1 -1
2020-01-05 -1  0 -1

pos_rank_after method

SignalsAccessor.pos_rank_after(
    reset_by,
    after_reset=True,
    allow_gaps=True,
    **kwargs
)

Get signal position ranks after each signal in reset_by.

Note

allow_gaps is enabled by default.


pos_rank_mapped method

SignalsAccessor.pos_rank_mapped(
    group_by=None,
    **kwargs
)

Get a mapped array of signal position ranks.

Uses SignalsAccessor.pos_rank().


rank method

SignalsAccessor.rank(
    rank_func_nb,
    *args,
    rank_args=None,
    reset_by=None,
    after_false=False,
    after_reset=False,
    reset_wait=1,
    as_mapped=False,
    broadcast_named_args=None,
    broadcast_kwargs=None,
    template_context=None,
    jitted=None,
    chunked=None,
    wrap_kwargs=None,
    **kwargs
)

See rank_nb().

Arguments to rank_func_nb can be passed either as *args or rank_args (but not both!).

Will broadcast with reset_by using broadcast() and broadcast_kwargs.

Set as_mapped to True to return an instance of MappedArray.


rate method

SignalsAccessor.rate(
    wrap_kwargs=None,
    group_by=None,
    **kwargs
)

SignalsAccessor.total() divided by the total index length in each column/group.


ravel method

SignalsAccessor.ravel(
    group_by=None,
    jitted=None,
    wrap_kwargs=None
)

Ravel signals.

See ravel_nb().

Usage

>>> unravel_mask = mask.vbt.signals.unravel()
>>> original_mask = unravel_mask.vbt.signals.ravel(group_by=vbt.ExceptLevel("signal"))
>>> original_mask
                a      b      c
2020-01-01   True   True   True
2020-01-02  False  False   True
2020-01-03  False   True   True
2020-01-04  False  False  False
2020-01-05  False   True  False

stats_defaults property

Defaults for StatsBuilderMixin.stats().

Merges GenericAccessor.stats_defaults and stats from signals.


subplots class variable

Subplots supported by SignalsAccessor.

HybridConfig(
    plot=dict(
        check_is_not_grouped=True,
        plot_func='plot',
        pass_trace_names=False,
        tags='generic'
    )
)

Returns SignalsAccessor._subplots, which gets (hybrid-) copied upon creation of each instance. Thus, changing this config won't affect the class.

To change subplots, you can either change the config in-place, override this property, or overwrite the instance variable SignalsAccessor._subplots.


to_nth method

SignalsAccessor.to_nth(
    n,
    wrap_kwargs=None,
    **kwargs
)

Select signals that satisfy the condition pos_rank < n.

Uses SignalsAccessor.pos_rank().


to_nth_after method

SignalsAccessor.to_nth_after(
    n,
    reset_by,
    wrap_kwargs=None,
    **kwargs
)

Select signals that satisfy the condition pos_rank < n.

Uses SignalsAccessor.pos_rank_after().


total method

SignalsAccessor.total(
    wrap_kwargs=None,
    group_by=None
)

Total number of True values in each column/group.


total_partitions method

SignalsAccessor.total_partitions(
    wrap_kwargs=None,
    group_by=None,
    **kwargs
)

Total number of partitions of True values in each column/group.


unravel method

SignalsAccessor.unravel(
    incl_empty_cols=True,
    force_signal_index=False,
    signal_index_type='range',
    signal_index_name='signal',
    jitted=None,
    clean_index_kwargs=None,
    wrap_kwargs=None
)

Unravel signals.

See unravel_nb().

Argument signal_index_type takes the following values:

  • "range": Basic signal counter in a column
  • "position(s)": Integer position (row) of signal in a column
  • "label(s)": Label of signal in a column

Usage

>>> mask.vbt.signals.unravel()
signal          0      0      1      2      0      1      2
                a      b      b      b      c      c      c
2020-01-01   True   True  False  False   True  False  False
2020-01-02  False  False  False  False  False   True  False
2020-01-03  False  False   True  False  False  False   True
2020-01-04  False  False  False  False  False  False  False
2020-01-05  False  False  False   True  False  False  False

unravel_between class method

SignalsAccessor.unravel_between(
    *objs,
    relation='onemany',
    incl_open_source=False,
    incl_open_target=False,
    incl_empty_cols=True,
    broadcast_kwargs=None,
    force_signal_index=False,
    signal_index_type='pair_range',
    signal_index_name='signal',
    jitted=None,
    clean_index_kwargs=None,
    wrap_kwargs=None
)

Unravel signal pairs.

If one array is passed, see unravel_between_nb(). If two arrays are passed, see unravel_between_two_nb().

Argument signal_index_type takes the following values:

  • "pair_range": Basic pair counter in a column
  • "range": Basic signal counter in a column
  • "source_range": Basic signal counter in a source column
  • "target_range": Basic signal counter in a target column
  • "position(s)": Integer position (row) of signal in a column
  • "source_position(s)": Integer position (row) of signal in a source column
  • "target_position(s)": Integer position (row) of signal in a target column
  • "label(s)": Label of signal in a column
  • "source_label(s)": Label of signal in a source column
  • "target_label(s)": Label of signal in a target column

Usage

  • One mask:
>>> mask.vbt.signals.unravel_between()
signal         -1      0      1      0      1
                a      b      b      c      c
2020-01-01  False   True  False   True  False
2020-01-02  False  False  False   True   True
2020-01-03  False   True   True  False   True
2020-01-04  False  False  False  False  False
2020-01-05  False  False   True  False  False

>>> mask.vbt.signals.unravel_between(signal_index_type="position")
source_signal     -1      0      2      0      1
target_signal     -1      2      4      1      2
                   a      b      b      c      c
2020-01-01     False   True  False   True  False
2020-01-02     False  False  False   True   True
2020-01-03     False   True   True  False   True
2020-01-04     False  False  False  False  False
2020-01-05     False  False   True  False  False
  • Two masks:
>>> source_mask = pd.Series([True, True, False, False, True, True])
>>> target_mask = pd.Series([False, False, True, True, False, False])
>>> new_source_mask, new_target_mask = vbt.pd_acc.signals.unravel_between(
...     source_mask,
...     target_mask
... )
>>> new_source_mask
signal      0      1
0       False  False
1        True   True
2       False  False
3       False  False
4       False  False
5       False  False
>>> new_target_mask
signal      0      1
0       False  False
1       False  False
2        True  False
3       False   True
4       False  False
5       False  False

>>> new_source_mask, new_target_mask = vbt.pd_acc.signals.unravel_between(
...     source_mask,
...     target_mask,
...     relation="chain"
... )
>>> new_source_mask
signal      0      1
0        True  False
1       False  False
2       False  False
3       False  False
4       False   True
5       False  False
>>> new_target_mask
signal      0      1
0       False  False
1       False  False
2        True   True
3       False  False
4       False  False
5       False  False

SignalsDFAccessor class

SignalsDFAccessor(
    wrapper,
    obj=None,
    **kwargs
)

Accessor on top of signal series. For DataFrames only.

Accessible via pd.DataFrame.vbt.signals.

Superclasses

Inherited members


SignalsSRAccessor class

SignalsSRAccessor(
    wrapper,
    obj=None,
    **kwargs
)

Accessor on top of signal series. For Series only.

Accessible via pd.Series.vbt.signals.

Superclasses

Inherited members