Tutorials

Signal development

Develop, clean, rank, and validate trading signals

Signals add an extra layer of abstraction on top of orders. Instead of specifying every detail about what needs to be ordered at each timestamp, you can define what a typical order should look like and then decide when to issue such an order. In VBT, this timing decision is made using signals, which are represented as boolean masks: True stands for "order" and False means "no order". The meaning of each signal can be changed either statically or dynamically, depending on the current simulation state. For example, you can instruct the simulator to ignore an "order" signal if you are already in the market, which is not possible with the "from-orders" method alone. Because VBT emphasizes data science, it is much easier to compare multiple strategies with the same trading conditions but different signal permutations (for example, order timings and directions). This approach reduces errors and leads to more fair experiments.

Since buying and selling are constant activities, it is ideal to include the order direction within each signal as well. However, because booleans only have two states, they cannot represent the three possibilities: "order to buy", "order to sell", and "no order". As a result, signals are typically distributed across two or more boolean arrays, each representing a different decision dimension. The most common way to define signals is by using two direction-unaware arrays: 1️⃣ entries and 2️⃣ exits. The meaning of these two arrays changes based on the direction, which is specified using a separate variable. For instance, when only the long direction is enabled, an entry signal opens a new long position and an exit signal closes it. When both directions are enabled, an entry signal opens a new long position, and an exit signal reverses it to open a short one. For more precise control over whether to reverse a position or simply close it, you can define four direction-aware arrays: 1️⃣ long entries, 2️⃣ long exits, 3️⃣ short entries, and 4️⃣ short exits. This method provides the greatest flexibility.

For example, to open a long position, close it, open a short position, and then reverse it, the signals would look like this:

Long entryLong exitShort entryShort exit
TrueFalseFalseFalse
FalseTrueFalseFalse
FalseFalseTrueFalse
TrueFalseFalseFalse

The same strategy can also be defined using an entry signal, an exit signal, and a direction:

EntryExitDirection
TrueFalseLong only
FalseTrueLong only
True/FalseFalse/TrueShort only/Both
TrueFalseLong only/Both

Direction-unaware signals can be easily converted to direction-aware signals:

  • True, True, Long only → True, True, False, False.
  • True, True, Short only → False, False, True, True.
  • True, True, Both → True, False, True, False.

However, direction-aware signals cannot be converted to direction-unaware signals if both directions are enabled and there is an exit signal present:

  • False, True, False, True → ❓

Therefore, it is important to carefully evaluate which conditions you are interested in before generating signals.

You might wonder why not use an integer data type, where a positive number means "order to buy," a negative number means "order to sell," and zero means "no order," as is done in backtrader. Boolean arrays are much easier for users to generate and maintain. In addition, a boolean NumPy array uses eight times less memory than a 64-bit signed integer NumPy array. Boolean masks are also much more convenient to combine and analyze than integer arrays. For example, you can combine two masks with the logical OR (| in NumPy) operator, or sum the elements in a mask to get the number of signals. Since booleans are a subtype of integers, they behave just like regular integers in most math expressions.

Comparison

Properly generating signals can sometimes be much more challenging than simulating them. This is because you need to consider not only the distribution of signals but also how they interact across multiple boolean arrays. For example, setting both an entry and exit at the same timestamp will effectively cancel both. That's why VBT offers many functions and techniques to support you in this process.

Signal generation usually begins by comparing two or more numeric arrays. Remember, when comparing entire arrays, you iterate over each row and column (that is, each element) in a vectorized way and compare their scalar values at each element. Essentially, you run the same comparison operation on every single element across all the arrays being compared. Let's look at an example using Bollinger Bands for two different assets. At each timestamp, we will place a signal whenever the low price is below the lower band, expecting the price to reverse back to its rolling mean:

Signals are among the most powerful tools for simulating trading strategies with VBT.

✅ Learn how to convert any trading strategy into signals using both vectorized and iterative methods, and how to analyze their distribution to uncover logical flaws in the strategy 🌗

Topics

Copyright © 2021–2026 Oleg Polakow. All rights reserved.

Site content and documentation are provided for using and evaluating VectorBT PRO and for educational purposes. Any other use, including building or supporting competing products or services, requires prior written consent.