Tutorials

Stop signals

Test stop-loss, trailing-stop, and take-profit configurations

Our goal is to use large-scale backtesting to compare the performance of trading with and without stop loss (SL), trailing stop (TS), and take profit (TP) signals. To ensure this analysis is comprehensive, we will conduct a large number of experiments across three dimensions: instruments, time, and parameters.

First, we will select 10 cryptocurrencies by market capitalization (excluding stablecoins such as USDT) and gather 3 years of their daily pricing data. Specifically, we will backtest the period from 2018 to 2021, as this range includes periods of sharp price declines (such as corrections after the all-time high in December 2017 and during the coronavirus crisis in March 2020) as well as surges (the all-time high in December 2020). This provides a balanced perspective. For each instrument, we will split this period into 400 smaller, overlapping time windows, each lasting 6 months. We will run our tests on each of these windows to account for different market conditions. For each instrument and time window, we will generate an entry signal at the very first bar and determine an exit signal according to the stop configuration. We will test 100 stop values, increasing by 1% increments, and compare the performance of each one to trading randomly and holding during that specific time window. In total, we will conduct 2,000,000 tests.

Make sure you have at least 16 GB of free RAM available, or memory swapping enabled.

Parameters

The first step is to define the parameters of the analysis pipeline. As discussed above, we will backtest 3 years of pricing data, use 400 time windows, 10 cryptocurrencies, and 100 stop values. We will also set fees and slippage to 0.25% each, and the initial capital to $100. The absolute amount does not matter, but it must be consistent across all assets to allow for direct comparison. Feel free to change any parameter of interest.

from vectorbtpro import *
import ipywidgets

seed = 42
symbols = [
    "BTC-USD", "ETH-USD", "XRP-USD", "BCH-USD", "LTC-USD",
    "BNB-USD", "EOS-USD", "XLM-USD", "XMR-USD", "ADA-USD"
]
start_date = vbt.utc_timestamp("2018-01-01")
end_date = vbt.utc_timestamp("2021-01-01")
time_delta = end_date - start_date
window_len = vbt.timedelta("180d")
window_cnt = 400
exit_types = ["SL", "TS", "TP", "Random", "Holding"]
step = 0.01  
stops = np.arange(step, 1 + step, step)

vbt.settings.wrapping["freq"] = "d"
vbt.settings.plotting["layout"]["template"] = "vbt_dark"
vbt.settings.portfolio["init_cash"] = 100.  
vbt.settings.portfolio["fees"] = 0.0025  
vbt.settings.portfolio["slippage"] = 0.0025  

pd.Series({
    "Start date": start_date,
    "End date": end_date,
    "Time period (days)": time_delta.days,
    "Assets": len(symbols),
    "Window length": window_len,
    "Windows": window_cnt,
    "Exit types": len(exit_types),
    "Stop values": len(stops),
    "Tests per asset": window_cnt * len(stops) * len(exit_types),
    "Tests per window": len(symbols) * len(stops) * len(exit_types),
    "Tests per exit type": len(symbols) * window_cnt * len(stops),
    "Tests per stop type and value": len(symbols) * window_cnt,
    "Tests total": len(symbols) * window_cnt * len(stops) * len(exit_types)
})
Start date                       2018-01-01 00:00:00+00:00
End date                         2021-01-01 00:00:00+00:00
Time period (days)                                    1096
Assets                                                  10
Window length                            180 days 00:00:00
Windows                                                400
Exit types                                               5
Stop values                                            100
Tests per asset                                     200000
Tests per window                                      5000
Tests per exit type                                 400000
Tests per stop type and value                         4000
Tests total                                        2000000
dtype: object

Our configuration produces sample sizes with enough statistical power to analyze four variables: assets (200k tests per asset), time (5k tests per time window), exit types (400k tests per exit type), and stop values (4k tests per stop type and value). Similar to Tableau's approach to dimensions and measures, we can group our performance metrics by each of these variables. However, we will mainly focus on 5 exit types: SL exits, TS exits, TP exits, random exits, and holding exits (executed at the last bar).

cols = ["Open", "Low", "High", "Close", "Volume"]
yfdata = vbt.YFData.pull(symbols, start=start_date, end=end_date)
Symbol 10/10
yfdata.data.keys()
dict_keys(['BTC-USD', 'ETH-USD', 'XRP-USD', 'BCH-USD', 'LTC-USD',
           'BNB-USD', 'EOS-USD', 'XLM-USD', 'XMR-USD', 'ADA-USD'])
yfdata.data["BTC-USD"].shape
(1096, 7)

The data instance yfdata contains a dictionary of OHLCV data, keyed by cryptocurrency name. Each DataFrame contains 1096 rows (days) and 5 columns (O, H, L, C, and V). You can plot each DataFrame as follows:

yfdata.plot(symbol="BTC-USD").show()  

Since assets are one of the dimensions we want to analyze, VBT expects us to combine them as columns into a single DataFrame and label them clearly. To do this, we swap assets and features to create a dictionary of DataFrames, now with assets as columns and keyed by feature name, such as "Open".

ohlcv = yfdata.concat()

ohlcv.keys()
dict_keys(['Open', 'High', 'Low', 'Close',
           'Volume', 'Dividends', 'Stock Splits'])
ohlcv['Open'].shape
(1096, 10)

Time windows

Stop orders are a vital part of an investor's toolkit. They help limit losses on a security position and secure profits. But what would be the optimal combination of stop types and values for a specific market regime?

✅ Learn how to run 2 million tests in just a few minutes to answer this question for top cryptocurrency markets. Unlike the open-source article, we will also include the stop price in our analysis to improve accuracy!

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.