This article will explain how to backrest a Weighted Moving Average in Python. In the first part, there will be a brief introduction and explanation of this indicator. Subsequently, we will implement a Python example to calculate the result of the trading strategy. Finally, we will implement a for-loop with our strategy.
- Looking for a good, robust, and profitable trading strategy? (Hundreds in that link)
- ….and a few trading strategies for sale
- Python algorithmic trading code examples (plenty of backtested and coded examples)
What is a weighted moving average?
A Weighted Moving Average is a statistical estimator that calculates an average in a range of days. It puts more weight on recent data.
Imagine that two days ago, a company released its financial statements with positive results; its stock price will increase because of the new information. Under this new scenario, the closing price before the earnings release will not be as important as the new one. Therefore, trying to assign more weight to new information will be more relevant.
Illustration of a weighted moving average
Let’s calculate the following Weighted Moving Average (4-day):
On a chart the different averages look like this:
In column 3 from the previous table, we see how to estimate a WMA(4) by hand.
Let’s explain the last row. The multiplication of 0.6×22 is the first coefficient in the previous formula and the value from Price of day eleven.
The multiplication of 0.25*18 represents the second coefficient and the value from Price of day ten.
While 0.1*20 represents the third coefficient and the value from Price of day nine, 0.05*20 represents the last coefficient and the value from Price of day eight.
The previous plot shows the evolution of Price, WMA, and SMA(4). We see how WMA(4) is a better fit for the closing price.
A practical example of a weighted moving average
In this tutorial, we will use data from yfinance. The financial instrument to use is SPY, while the start and end periods are 01/01/20 and 31/12/2020:
|Name||SPDR S&P 500 ETF Trust (SPY)|
A weighted moving average trading strategy
The strategy to implement will be a crossover weighted moving average; there will be two Weighted Moving Averages, a short and a long WMA. The length period will be 5 and 10 days, respectively. The purpose here is to show how it’s done, not to make a tradable trading strategy.
The idea of this strategy is to have a short-term indicator that will show the trend and a long-term indicator that will show the long-term trend.
This strategy will generate long and short positions, following these trading rules:
- When the short-term WMA(4) is higher than the long-term WMA(10).
- When the long-term WMA(10) is higher than the short-term WMA(20).
Let’s import the libraries:
The Python libraries (packages) comprise functions, classes, and other collections of elements. For example, the library yfinance retrieves historical financial data from Yahoo Finance in Python. On the other hand, it is necessary to go to the Yahoo Finance website to manually download the data, and then import it in Python; therefore, the user will spend more time.
We employ yf.Ticker to retrieve information from the financial symbol. Then, we implement stock.history for downloading financial data. The upcoming image shows the code implementation and the first five rows from the dataset:
Now, let’s examine the last three rows:
Estimate returns of the trading strategy
The daily return shows how much a financial instrument changes from one day to the next. Its formula:
In Python, the estimation is:
Example of how to use rolling and apply in pandas
In pandas, there is no direct method for estimating the Weighted Moving Average. It is necessary to use .rolling and .apply; these components facilitate the implementation of custom functions.
To illustrate the following image, let’s create a dictionary (example) and pass its values to a pandas DataFrame (pexample). The .rolling(window=3).apply(lambda x: x.sum()) means that we will generate a new series. The elements of this series will be the sum of the last previous values of pexample[“Example”].
For instance, the third element in pexample[“rolling_sum“] is 1 + 2”3=6. While the fourth element is 2+3+4=9, etc.
Estimate WMA in Python
In this section, we will create two Weighted Moving Averages. The WMAs will have the following weights:
First weights: w1 = [0.6, 0.25, 0.15, 0.05]
Second weights: w2 = [0.18181818, 0.16363636, … , 0.03636364, 0.01818182]
In the following image, list(range(10, 0, -1)) is a vector with elements from 1 to 10.
To explain in more detail what does .rolling(len(w1)).apply(lambda x: (w1*x).sum()/np.sum(w1), raw=True), let’s calculate by hand the first two elements of WMA:
- From stock_data[”Close”], we get its first four elements 306.295227, 303.975861, 305.135620, and 304.277588 and multiply them by w1:
- 0.6*306.295227 +0.25*303.975861 +0.15*305.135620 +0.05*304.277588)/ (0.6 + 0.25 + 0.15 + 0.05)= 305.481261
- From stock_data[”Close”], we get the rows two, three, four and five (303.975861, 305.135620, 304.277588, and 305.899231) and multiply them by w1:
- 0.6*303.975861 + 0.25*305.135620 + 0.15*304.277588 + 0.05*305.899231/(0.6 + 0.25 + 0.15 + 0.05) = 304.386687
How to calculate trading signals
The next step is to apply the trading rules:
- When wma1 > wma2
Sell (short selling):
- When wma2 <= wma1
The np.where(wma1 > wma2, 1, -1) command enables us to implement both conditions. When wma1 > wma2, the corresponding value in the series will be one (1); otherwise, when wma1 <= wma2, the value becomes minus one (-1).
How to calculate strategy performance
In the forthcoming chart, we will calculate the returns from the strategy. We will achieve this by multiplying stock_data[”Close”].pct_change() * stock_data[”Signal”].shift(1). Remember that stock_data[”Signal”] is a series denoting (buy=1,sell=-1).
To illustrate, if the return of a particular day is 0.01 (1%) and the signal is 1, the calculation will result in 0.01 * 1= 0.01, representing a long position. Conversely, if the return of a particular day is 0.01 (1%) and the signal is -1, the calculation will result in 0.01*-1=-0.01, representing a long position.
How to plot strategy performance
The following image shows the strategy performance:
Part 3: for-loop
In this section, we will implement the same strategy using the following stocks:
BHP: BHP Group Limited
RIO: Rio Tinto Group
Examples 1 for-loop
The previous code prints the elements from 0 to 4. In total 5 elements.
Example 2 for-loop
Strategy and for-loop
The previous code will print “For Loop”; then, it will print the number of elements 0 and 1; finally, it will print the name of the stock (stock_list[i]).
The following image shows how to implement the strategy in a for-loop for BHP and RIO. In green is highlighted the code that we implemented in the previous section.
fig, axes = plt.subplots(nrows=1, ncols=2, figsize=(12, 4)) is creating the framework to make the charts; stock_list are the two financial instruments; finally, axes[i] help to plot both charts.