There are many factors that quants and algorithmic traders use when they develop trading strategies. Some of them are value, quality, size and, the one we will be backtesting today, momentum.
Momentum is an investing factor that aims to benefit from the ongoing trend of a stock or asset. A stock that has been rising is said to have positive momentum while a stock that has been crashing is said to have negative momentum. We show you how to code such a strategy using Python.
In this article, we are going to show you how to backtest a momentum trading strategy in Python: from downloading the data and calculating momentum to backtesting the strategy and plotting the results.
The first step to backtest any trading strategy is to gather the necessary data. In this case we are going to download the historical data using the library yfinance. If you want to know more about how to download not only historical stock prices but also fundamental data from the Yahoo Finance website check out these two posts:
- How To Download Data For Your Trading Strategy From Yahoo!Finance With Python
- How To Build A Trading Strategy From FRED Data In Python (Strategy, Backtest, Rules)
For the purpose of this backtest, we will also use the libraries pandas, numpy and matplotlib.pyplot.
Downloading historical data
Regarding the strategy we are going to backtest today, we are going to be using the select sectors SPDR ETFs. The ticker symbols are:
- XLK: Technology
- XLY: Consumer discretionary
- XLP: Consumer staples
- XLU: Utilities
- XLI: Industrials
- XLE: Energy
- XLF: Financials
- XLV: Healthcare
- XLC: Communications
With this in mind, all we need to do is create a list with all the tickers symbols, define a variable(data), and use the yf.download() function to download the data. Take into account we will only use the adjusted close prices. You should always use adjusted data to make sure you include dividends. Dividends make up a large part of total returns for a lot of assets.
The strategy will rebalance the portfolio monthly so we will use the .resample(‘M’).last() function to keep just the last monthly closing price of the ETF. Lastly, we use the pct_change() function and add 1 to get the percentage change from the previous month
And here is the output if we print data:
We now have the monthly change of every ETF going back to 2005!
Calculating the momentum factor
There are many ways to measure the momentum of a stock or ETF. In this backtest, we will define it as the rolling past 9 month performance of the ETF.
To calculate it we will create a function called get_rolling_ret() which receives two inputs: the data and the number of months to calculate the past performance.
Then we will define a new variable(ret9) and run this function using as input the variable data and 9 (because we want the performance of the last 9 months).
The results will be a dataframe of the past 9 month performance of each ETF in every month:
The trading rules – momentum strategy in Python
The trading rules of the strategy are the following: Every month we measure the momentum of each ETF and buy and hold the top 3 with the highest momentum the prior month. The strategy is rebalanced monthly.
So for example, if XLE, XLK, and XLI had the highest 9 month performance of all the ETFs in March, we will buy and hold them in April. At the end of April we will re-evaluate momentum again, and determine which 3 ETFs should we hold in May.
Calculating the returns of the strategy
In order to code the above trading rules, we are going to create two functions. The first one is get_top(). The input of the function is a date. What it does is return the 3 ETFs that have the best 9 month performance in a specific date/month.
The other one is portfolio_ret(). This function also takes as an input a date. What it does is take the returns of the top 3 ETFs selected previously, create an equally-weighted portfolio, and return the performance of the portfolio. Inside this function we use the previous function.
Then, we will create a new column called returns and use the for function to do a loop and calculate the performance of the portfolio in every row of the data frame since 2005 using the portfolio_ret() function.
Plotting the returns of the momentum trading strategy
Now, the only thing left is to plot the returns using matplotlib. Moreover, we will download the historical data from the SPY as well so we can compare it to the strategy.
To avoid getting into details about how to use matplotlib, here is what every line of code does:
- Creates a blank chart of 11 inches in width and 8 inches in length
- Plot the cumulative returns for the SPY and add a label called S&P500
- Same but with the cumulative returns of the strategy and the label called TIMING
- Makes the y-scale logarithmic
- Creates a title in the top of the chart
- Shows the labels in the chart
- Displays the chart
And here is the chart:
As you can see, the strategy performance was nearly identical to the SPY. It compounded at a 9.89% CAGR while the SPY did it at a 9.46% CAGR. Thus, not much alpha with these trading rules.
Implementing a momentum trading strategy in Python – conclusion
To sum up, today you learned how to backtest a momentum trading strategy using Python. Using the sector ETFs, we ranked them by momentum and bought the top performers every month. The strategy ended up performing more or less like the SPY, but there may be other combinations that work better.