Last Updated on 23 July, 2024 by Abrahamtolle
How to Build a Profitable ATR-Based Trading Strategy with Python
Introduction
In this tutorial, we will implement a trading strategy using the ATR indicator. The first part will explain this indicator. Then, we will show a practical example in Python using the SPY data. Throughout this tutorial, we will implement various Python functions.
Average True Range
ATR is a volatility indicator that captures how much fluctuates the price in a day. It aims to identify periods with high uncertainty.
To calculate the ATR, the maximum, minimum, and closing prices are necessary. We calculate the True Range (TR) as follows:
Where:
H = Maximum price of the day
L = Minimum price of the day
= Previous day’s closing price
With the previous result, the ATR is:
The first ATR value is:
Practical Example in Python
Data and Financial instruments
In this tutorial, we will use the following financial instruments:
The start and end period of our analysis are “2021-01-01” and “2023-10-23″, respectively.
Data Download and Analysis:
As in our previous tutorials, we first import the Python libraries:
Next, we download the data using the following function:
The function download_data takes three arguments: stock_name, start, and end. In the function body, we download the data, and finally, the variable data is returned.
The implementation and results of the previous function are:
:
ATR Implementation
We will use the ta library to calculate the ATR. The following image shows the get_atr function, which takes two parameters: data and window. Inside the function, the AverageTrueRange method from the ta package estimates the Average True Range. Finally, the variable atr containing the data of this indicator is returned.
In the previous image, you can also see the implementation of get_atr with its first five results; note that the ATR has a 14-day window length.
In the following image, you can see the function that calculates the 14-day SMA using values from result[“ATR”]:
Price and ATR Chart
This section involves plotting both the closing price and ATR to draw insight from our data. We will plot the closing price at the top and ATR below it.
The following image shows the plot_data function, which has two parameters: data and ticker. Inside the function, you can see plt.subplots(2, 1, gridspec_kw={‘height_ratios’: [3, 1]}), the first two arguments indicate we want the two graphs in a vertical orientation. The height_ratios indicates that the first graph is three times larger than the second one. The NullFormatter() expression is used to hide the horizontal axis in the first graph. We have explained the other commands in other tutorials:
https://www.quantifiedstrategies.com/how-i-made-a-weighted-moving-average-strategy-using-python/
https://www.quantifiedstrategies.com/how-to-build-a-simple-sma-trading-strategy-using-python/
Implementing plot_data with parameters result and stock_name yields the following:
The image above shows high volatility throughout 2022, with periods of sharp price increases and decreases. When we observe the ATR series, we can see the highest values in the same period.
In addition, in the previous image, we computed a 14-day SMA with the ATR values, an essential component to find periods of high price variation. It is possible to observe that every time the ATR surpasses SMA, it is likely a sharp increase or decrease in the price.
Backtesting the Strategy
One of the key components of backtesting our strategy is the signals. The signals show the time for having a long or short position. To obtain these signals, we express the following trading rules.
Buy:
-
When the closing price is higher than the closing price of the previous three days, and ATR is higher than its 14-day historical average.
Sell:
-
When the closing price is lower than the closing price of the previous three days, and ATR is higher than its 14-day historical average.
The idea is simple: when ATR is higher than its SMA, it shows a sudden price movement. Meanwhile, the price change of the last 5 days identifies the trend, whether the price is decreasing or increasing.
In the following image, to estimate its function, a loop is necessary to get, for each day, the closing price, the closing price of the previous 5 days, and the ATR value.
The following image shows the get_signals function, which takes two arguments: data and size. Inside the function, the first step is to get the positions of the columns Close, ATR, and ATR_SMA with .get_loc. Then, the variable signal_ is created, showing a long position with 1 and a short position with -1. Subsequently, we create the values_list to record buy or sell operations. Inside the loop, you can see how the first size values are skipped. Then, the difference between closing prices, price_diff (in the example size=3, meaning three days) is calculated. Next, the ATR values and their averages in atr_value and atr_sma_value are obtained. It’s important to remember that .iloc[i, patr] means that we will find the value located in row i with column patr. Then, we implement the conditions price_diff < 0 and atr_value > atr_sma_value and price_diff > 0 and atr_value > atr_sma_value, indicating a buy (signal_=1) and a short position (signal_=-1), respectively.
Getting Returns and Results
The following image shows the return calculations, and plots them:
We explained the previous code in another article. You can find examples with similar codes in the following articles:
https://www.quantifiedstrategies.com/how-to-build-a-simple-sma-trading-strategy-using-python/
https://www.quantifiedstrategies.com/how-i-made-a-weighted-moving-average-strategy-using-python/
Our strategy generates a cumulative return of approximately 50%, which is higher than the return obtained from SPY. Similarly, our strategy’s returns grow steadily over time with less variation.
The same Strategy for Four ETFs
In this section, we will explore the utility of the functions created in Python. First, we create another function:
If we compare the equity_curve_2 function with equity_curve, we see they are similar but not identical. The new function uses plt.plot which plots multiple graphs on one chart.
The following image implements the previously created functions. First, we make a list with four ETFs. Inside the loop, we add the functions. Notice how we need only a few lines of code to obtain results for four ETFs.
Using the functions created, we can automatize the task of plotting the equity curve in less time and with fewer lines of code. You can add 10 or more financial tickers and analyze them with the previous function.
The following image shows the equity curve for the four ETFs: