How to use VectorBT PRO to algorithmically find chart patterns
How to use VectorBT PRO to algorithmically find chart patterns
VectorBT PRO (VBT) is a proprietary Python package designed for backtesting and analyzing quantitative trading strategies.
In today’s guest post, you’ll use VectorBT PRO to algorithmically detect chart patterns from 230 million unique pattern and window combinations.
All in about 2 minutes.
Ready?
Using VectorBT PRO to algorithmically find chart patterns
VectorBT PRO provides a comprehensive suite of tools for every stage of an algorithmic trading workflow, including data acquisition, signal generation and analysis, portfolio optimization, strategy simulation, hyperparameter tuning, and cross-validation. These modular components empower users to flexibly customize their analysis, setting it apart from monolithic backtesting frameworks.
One of these components is a data pattern detector that efficiently scans data using variable-length windows, assessing their similarity to a specified pattern. This process operates on any hardware without the need for machine learning.
In today’s newsletter, we will conduct backtesting on a range of patterns and their combinations on a single dataset.
Imports and set up
Due to VectorBT PRO's self-contained design, only minimal imports are necessary.
1import vectorbtpro as vbt
2import pandas as pd
3import numpy as np
4
5vbt.settings.set_theme("dark")
VectorBT PRO features built-in data downloading from sources such as Yahoo Finance, Alpaca, Polygon, TradingView, and many more. We will perform pattern detection on hourly price data pulled from TradingView.
1symbols = [
2 "NASDAQ:META",
3 "NASDAQ:AMZN",
4 "NASDAQ:AAPL",
5 "NASDAQ:NFLX",
6 "NASDAQ:GOOG",
7]
8
9data = vbt.TVData.pull(symbols, timeframe="hourly")
10
11data = data.xloc["2020":None]
12
13price = data.hlc3
After slicing the data for the dates we want, we choose the suitable feature. We'll utilize HLC/3, which effectively captures price fluctuations.
Define 5 bullish and bearish patterns
Chart patterns can be translated into numerical sequences, like the "Double Top" represented as [1, 5, 3, 5, 1], below. It's important to note that while the numbers themselves can be arbitrary, their relative spacing should mirror the relative distance between the pattern's chart points. For instance, in this sequence, 2 aligns with the midpoint between valley point 1 and peak point 3. The same principle applies to temporal distribution: points should be equidistant from one another.
1bullish_patterns = {
2 "double_bottom": [5, 1, 3, 1, 5],
3 "exp_triangle": [3, 4, 2, 5, 1, 6],
4 "asc_triangle": [1, 5, 2, 5, 3, 6],
5 "symm_triangle": [1, 6, 2, 5, 3, 6],
6 "pennant": [6, 1, 5, 2, 4, 3, 6]
7}
8bearish_patterns = {
9 "head_and_shoulders": [1, 4, 2, 6, 2, 4, 1],
10 "double_top": [1, 5, 3, 5, 1],
11 "desc_triangle": [6, 2, 5, 2, 4, 1],
12 "symm_triangle": [6, 1, 5, 2, 4, 1],
13 "pennant": [1, 6, 2, 5, 3, 4, 1]
14}
15
16pd.Series(bullish_patterns["double_bottom"]).vbt.plot().show_png()
Each generated sequence serves as a rough approximation of the desired chart pattern, and there's no need for precise adjustments: VBT similarity-based algorithm is flexible and can identify patterns, even if they are not perfectly consistent in their design.
Detect the appearance of the patterns in the data
Iterate through each pattern, dataset, and timestamp within the dataset. Search for matches within windows spanning from 1 to 30 days, and create a record for each match that exceeds a pre-defined minimum similarity score, which is set by default to 85%.
1min_window = 24
2max_window = 24 * 30
3
4def detect_patterns(patterns):
5 return vbt.PatternRanges.from_pattern_search(
6 price,
7 open=data.open, # OHLC for plotting
8 high=data.high,
9 low=data.low,
10 close=data.close,
11 pattern=patterns,
12 window=min_window,
13 max_window=max_window,
14 execute_kwargs=dict( # multithreading
15 engine="threadpool",
16 chunk_len="auto",
17 show_progress=True
18 )
19 )
20
21bullish_matches = detect_patterns(
22 vbt.Param(
23 bullish_patterns,
24 name="bullish_pattern"
25 )
26)
27bearish_matches = detect_patterns(
28 vbt.Param(
29 bearish_patterns,
30 name="bearish_pattern"
31 )
32)
In about two minutes, VBT detects matches among all patterns. This process, involving around 230 million unique pattern and window combinations, was executed in parallel.
Plot the pattern and dataset with the most matches.
1vbt.settings.plotting.auto_rangebreaks = True # for stocks
2
3display_column = bullish_matches.count().idxmax()
4
5bullish_matches.plot(column=display_column, fit_ranges=True).show_png()
Or zoom in on a specific match.
1display_match = 3
2
3bullish_matches.plot(
4 column=display_column,
5 fit_ranges=display_match
6).show_png()
The window data closely aligns with the pattern. This functionality is highly comprehensive, offering the flexibility to adjust fitness levels, modify rescaling and interpolation algorithms, and more to suit specific requirements.
Transform the matches and backtest the signals
To conduct backtesting on the identified patterns, we will convert them into signals, triggering a signal once a pattern has fully developed.
1entries = bullish_matches.last_pd_mask
2exits = bearish_matches.last_pd_mask
3
4entries, exits = entries.vbt.x(exits)
This code generate a Cartesian product of bullish and bearish patterns to systematically test each bullish pattern against each bearish pattern. Both arrays have been converted into equally-shaped DataFrames with each column representing an individual backtest, encompassing three parameters: bullish pattern, bearish pattern, and symbol.
Next, establish a portfolio by simulating signals and get the mean total return for every combination of bullish and bearish patterns.
1pf = vbt.Portfolio.from_signals(data, entries, exits)
2
3mean_total_return.vbt.heatmap(
4 x_level="bearish_pattern",
5 y_level="bullish_pattern"
6).show_png()
From there, generate a heat map to visualize where the best combination might lie.
Next steps
Although the displayed performance of each pattern combination does not guarantee future results, it provides insight into how the market responded to pattern events in the past. For instance, it's noteworthy that the "Bearish Symmetrical Triangle" exhibited a notably bullish trend. Cross-validation and robustness testing are next essential steps for a comprehensive assessment.