If you’re using VectorBT to backtest trading strategies, you’ve probably seen the term “broadcasting” pop up. It’s not just a fancy technical term. Broadcasting is what allows you to run multiple versions of your strategy – like trying out two stop-loss values – without writing loops or duplicate code.

In this blog, you’ll learn:
- What broadcasting means in NumPy
- How VectorBT builds on that
- A real example using RELIANCE.NS and two stop-loss settings
Let’s start from the basics.
What Is Broadcasting in NumPy?
Broadcasting in NumPy is a smart way of doing operations between arrays of different shapes. It works by “stretching” smaller arrays to match the shape of the larger ones, without actually copying data.
For example:
import numpy as np
a = np.array([10, 20, 30])
b = 2
print(a + b)
Output:
[12 22 32]
Here, b is a scalar. NumPy acts as if it expanded b into [2, 2, 2], matching the shape of a. That’s broadcasting.
Another example:
a = np.array([[1], [2], [3]]) # shape (3,1)
b = np.array([10, 20, 30]) # shape (3,) → interpreted as (1,3)
print(a + b)
Output:
[[11 21 31]
[12 22 32]
[13 23 33]]
NumPy expands both arrays to a 3×3 shape to make the addition work.
Broadcasting in VectorBT
Now that you understand how NumPy stretches arrays, let’s talk about how VectorBT uses the same idea to stretch trading logic.
Suppose you want to test the same EMA crossover strategy on RELIANCE.NS with two stop-loss levels: 2 percent and 4 percent. Normally, you’d have to write two backtests, but VectorBT handles both in one shot using broadcasting.
Here’s how the full example looks:
Full Example: Compare Two Stop-Loss Values
import vectorbt as vbt
import numpy as np
import pandas as pd
# Download price data
close = vbt.YFData.download("RELIANCE.NS", start="2020-01-01", end="2025-04-22").get("Close")
# Create indicators
fast_ma = vbt.MA.run(close, window=2, ewm=True)
slow_ma = vbt.MA.run(close, window=27, ewm=True)
entries = fast_ma.ma_crossed_above(slow_ma)
exits = fast_ma.ma_crossed_below(slow_ma)
# Create a 2-column version of all inputs
n_cols = 2
close_2d = close.vbt.tile(n_cols)
entries_2d = entries.vbt.tile(n_cols)
exits_2d = exits.vbt.tile(n_cols)
# Add column labels for clarity
close_2d.columns = pd.Index(["SL_2pct", "SL_4pct"])
# Stop-loss values for each version
sl_vals = np.array([0.02, 0.04]) # 2% and 4% stop-loss
# Run the backtest
pf = vbt.Portfolio.from_signals(
close=close_2d,
entries=entries_2d,
exits=exits_2d,
direction="longonly",
size=100,
size_type="percent",
fees=0.0012,
init_cash=1_000_000,
sl_stop=sl_vals,
freq="1D"
)
# View stats for each stop-loss
print(pf.stats(column="SL_2pct"))
print(pf.stats(column="SL_4pct"))
Getting the Strategy Portfolio Columns
pf.wrapper.columns
Output
Index(['SL_2pct', 'SL_4pct'], dtype='object')
Strategy with 2% Stoploss (output)
Start 2019-12-31 18:30:00+00:00
End 2025-04-20 18:30:00+00:00
Period 1312 days 00:00:00
Start Value 1000000.0
End Value 916877.984016
Total Return [%] -8.312202
Benchmark Return [%] 89.805087
Max Gross Exposure [%] 100.0
Total Fees Paid 110188.612807
Max Drawdown [%] 32.921334
Max Drawdown Duration 421 days 00:00:00
Total Trades 47
Total Closed Trades 46
Total Open Trades 1
Open Trade PnL 38722.465161
Win Rate [%] 17.391304
Best Trade [%] 21.287751
Worst Trade [%] -7.716157
Avg Winning Trade [%] 9.418538
Avg Losing Trade [%] -2.166681
Avg Winning Trade Duration 41 days 00:00:00
Avg Losing Trade Duration 5 days 05:41:03.157894736
Profit Factor 0.853069
Expectancy -2648.793068
Sharpe Ratio -0.047403
Calmar Ratio -0.072456
Omega Ratio 0.989503
Sortino Ratio -0.068071
Name: SL_2pct, dtype: object
Strategy with 4% Stoploss (output)
Start 2019-12-31 18:30:00+00:00
End 2025-04-20 18:30:00+00:00
Period 1312 days 00:00:00
Start Value 1000000.0
End Value 891825.998083
Total Return [%] -10.8174
Benchmark Return [%] 89.805087
Max Gross Exposure [%] 100.0
Total Fees Paid 110296.708271
Max Drawdown [%] 29.487156
Max Drawdown Duration 381 days 00:00:00
Total Trades 47
Total Closed Trades 46
Total Open Trades 1
Open Trade PnL 37664.445807
Win Rate [%] 19.565217
Best Trade [%] 9.369394
Worst Trade [%] -7.716157
Avg Winning Trade [%] 7.592608
Avg Losing Trade [%] -2.168598
Avg Winning Trade Duration 17 days 18:40:00
Avg Losing Trade Duration 5 days 13:37:17.837837837
Profit Factor 0.818244
Expectancy -3170.401037
Sharpe Ratio -0.148478
Calmar Ratio -0.10631
Omega Ratio 0.960984
Sortino Ratio -0.207001
Name: SL_4pct, dtype: object