How I Made A Profitable TEMA Trading Strategy In Python (Code, Rules, Backtest)
Triple Exponential Moving Average (TEMA)
This indicator is used to identify the trend, retracement, and potential support and resistance. After the development of the Double Exponential Moving Average (DEMA), Patrik Mulloy enhanced the indicator and developed the TEMA. The interpretation of the indicator is similar to a moving average; when the indicator goes up, it is a bullish signal, and when the indicator goes down, it is a bearish signal. Let’s make an effort to make a profitable TEMA trading strategy. That said, the main purpose of the article is to show how it can be done codingwise.
The calculation of TEMA is as follows:
First, calculate an EMA using the price level:
1) EMA1 = EMA of Price Level
Second, calculate the EMA of EMA1:
2) EMA2 = EMA of EMA1
Third, calculate the EMA of EMA2
3) EMA3 = EMA of EMA2
Fourth, calculate the TEMA:
TEMA = (3 x EMA1) – (3 x EMA2) + (EMA3)
Python Implementation
Python Library Backtesting.py
This library is another backtesting Python framework that creates trading strategies, tests the strategy performance with multiple risk metrics, plots the result, and so on. Its main characteristic is the simplicity of constructing the algorithms.
We queried Google Bard with the question, “Top 10 Python libraries for backtesting,” and the third position was occupied by backtesting.py. This indicates the significance of the library in the realm of financial trading.
Data Visualization
This tutorial will use iShares Core MSCI Emerging Markets ETF (IEMG). Like in the previous tutorial, the first step is to download the data:
The next step is the calculation of the TEMA indicator. The key function is ta.trend.ema_indicator, which calculates the EMA.
It is always convenient to plot and see the indicator evolution; the following image shows the time series from the TEMA indicator. The closing price and the indicator have similar values.
To be sure both time series have different values, the following code and plot show the last year of data:
Let’s transform the TEMA indicator into a function. The backtesting.py library requires writing the indicators as functions.
This output is equal to the previous TEMA calculation.
Also, let’s make an RSI function:
TEMA Trading Strategy
This article will apply the following trading strategy:
- Go long when TEMA(5) > Closing Price and RSI(21) < 35%, hold the position until TEMA(5) < Closing Price and RSI(21) > 65%,
- Go short when TEMA(5) < Closing Price and RSI(21) > 65%, and hold the position until TEMA(5) > Closing Price and RSI(21) < 35%.
The inclusion of the RSI is for improving the signal. The financial market is complex, and most of the time, it is convenient to include different indicators.
The following image shows the trading strategy implementation using the backtesting.py framework.
Before delving into the next section of the code, it is important to be familiar with the Object Oriented Programming approach (OOP). The article How I Made a Profitable Stochastic Oscillator Strategy with Python implements a trading strategy following the OOP.
In addition, the article How to Build a Profitable Money Flow Index Strategy Using Python explains the inheritance mechanism in the OOP. Both articles will shed light on the backtesting.py code structure.
Let’s break down the code.
The backtesting.py Python library requires encapsulating the strategy in a class TEMAStrategy(Strategy). Inside are the initial lock-back periods n1=5 (TEMA) and n2=21 (RSI).
The init function is where you write the indicators. The strategy to implement requires a TEMA(5) and a RSI(21). The self.I function add the indicators, the first parameter is for the indicator function (TEMAIndicator), the second parameter is for the data (self.data.close), and the other components are for the parameters (self.n1 and self.n2).
The following image shows the next function; this is the most important function because it is where goes the trading logic. The trading rule is: buy when closing price > TEMA(5) and RSI(21) <35%; the term self.buy() invests 100% of the portfolio in a long.
For a short position, the decision rule is to sell when the closing price < TEMA(5) and RSI(21) > 65%; self.sell() invests 100% of the portfolio in a short position.
The other part of the function checks the moment to close the positions. After you have opened a position, the algorithm will check when is the moment to exit the trade self.position.close().
TEMA trading strategy backtest
The following image depicts how to run the strategy bt = Backtest(df, TEMAStrategy, cash=10000, commission=.002). Backtest needs the class strategy TEMAStrategy, the initial cash of 10000, and the commission of 0.002 per trade (0.2%). The commission will depend on your broker; for example, the commission for TradeStation is zero (for certain customers), so the strategy should have commission=0.
The previous image shows more than 20 trading metric print(statats). Let’s look at some interesting metrics:
- CAGR (annual return)=5.91%
- N Trades: 14
- Win Rate = 78.57%
- Max Dowdown = 41.47%
- Average Trade: 4.65%
- Best Trade = 34.01%
- Worst Trade = -21.17%
In your browser it should look like this:
Before explaining the following section, let’s see what range does:
The heatmap function plots the total return (Equity Final) of every possible connection between TEMA and RSI.
For instance, when the TEMA’s look-back n1=5, and the RSI’s look-back n2=21 (see the arrow); the Final Equity is around 18,879.
The previous heatmap shows a range of strategies between n1 = 2 to 24 and n2 = 19 to 24 with total returns over 20,000; the recommendation is to run the code with some of the strategies in that range and see how well they perform. You should check the numbers of trade. More trades means the strategy is robust.
Improving the Strategy
The bt.optimize function can find the strategy with the maximum return, among other options. This functionality allows us to identify potentially profitable strategies by visualizing the heatmap and other outputs.
Rather than changing manually one parameter at a time, and then waiting to see the result, to repeat the process over and over, bt.optimize can make that activity for you and track hundreds or thousands of combinations for you. The previous heatmap shows the result of 380 strategies.
The following images show the strategy TEMA(18) and RSI(21). In the heatmap n1=18 and n2=21. This strategy has the maximum return among the 576 strategies.
This strategy outperforms the previous strategy:
- CAGR (annual return)=16.97%
- N Trades: 11
- Win Rate = 90.91%
- Max Dowdown = 47.22%
- Average Trade: 17.10%
- Best Trade = 52.9%
- Worst Trade = -13.32%
The only problem is that executed eleven trades in ten years, on average around one trade per year, which is not much.
Is there something more we can do? yes, to run bt.optimize with maximize=”# Trades”.
The previous image shows the heatmap with the numbers of trades. We highlight the area with the highest returns and it is the area with 20 or fewer trades.
Is there something more you can do?
The answer is yes. You can optimize the trading strategy, but that is outside the scope of this article.
You can pass to bt.optimize the following arguments: