Make A Hull Moving Average In Python

How To Make A Hull Moving Average In Python

We show you how I build a profitable Hull-moving average strategy in Python. However, the main purpose of the article to show how it can be done using Python. The strategy itself is of minor importance in this tutorial.

Hull Moving Average Indicator

The HMA is a technical indicator that measures the price trend, just like the SMA, EMA, etc. The main difference is that the Hull Moving Average fits better the trend; its formula is:

\text{RAW HMA} &= (2 \cdot \text{WMA}(n/2)) - \text{WMA}(n) \\\
\text{HMA} &= \text{WMA}(\sqrt{n}) \text{ of RAW HMA} \\\
\text{HMA} &=\text{WMA}(\text{RAW HMA}, \sqrt{n})


  1. WMA = Weighted Moving Average
  2. n = look-back period
  3. RAW HMA = Raw Hull Moving Average
  4. sqrt = squared root
  5. HMA = Hull Moving Average

Let’s use the matplotlib Python package to plot the closing price, SMA, and HMA. This article will use historical data from the Vanguard Emerging Markets Stock Index Fund (VWO). With the help of the pandas_ta Python library, we calculate the HMA. The following plot shows only one year of data.

How I Build A Profitable Hull Moving Average Trading Strategy In Python
How I Build A Profitable Hull Moving Average Trading Strategy In Python

The previous chart shows how the Hull Moving Average fits the trend better.

One question arises: is it better to use the HMA rather than SMA to find the short trend?, the answer partially is yes; if the goal is to find short-term trends, the HMA will be superior.

On the other hand, the answer can be NO because your selection depends on the analysis of your data (to see the chart, analyze fundamental data, etc).

Trading Strategy – trading rules

We use the following trading rules:

  1. Go long when the 390-day Hull Moving Average is higher than the closing price.

This choice was made after examining different look-back periods using trading strategy optimization.

The following image shows the HullMovingAverageStrategy class, which contains the strategy to backtest.

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 code structure.

Let’s break down the previous code:

The term n1=390 is the look-back period for the HMA indicator.

The function init is the place to initialize the indicator. The trading strategy has one indicator HMA(390). To subscribe the Hull Moving Average to is necessary self.I function. self.I require a function to calculate the indicator hull_moving_average, the data, and the parameter self.n1.

The next function is the area where the trading logic goes. The long position that depict this section is: buy if there is no position in the portfolio and the closing price is higher than HMA. The remaining part of this function implements the logic to close the open positions.

The Backtest function wraps data data, the trading strategy class HullMovingAverageStrategy, the initial cash 10000, commission 0.0, and other parameters to run the backtest.

Keep in mind that the art of backtesting implies having reasonable results to what the actual trades would be.

For instance, trade_on_close=True indicates closing the position at market close, and trade_on_close=False states closing the position at the next open bar.

Is it well grounded to place a position at the very last second before the market closes, or is it realistic to open a position at the market opening the next day?

The answer will depend on your broker and your critical thinking; that is one consideration, but multiple factors can influence your backtesting result.

After running, these are some interesting risk metrics:

Total Return: 148.97%
Total buy-and-hold return: 61.88%
CARG (Return (Ann.): 5.00%
Max. Drawdown: -38.14%
Trades: 90
Win Rate: 25.56%
Best Trade: 80.51%
Worst Trade: -4.69%
Avg. Trade: 1.02%

To see the plot:

The previous chart shows the evolution over time from the strategy. Let’s see in detail its main chart:

The previous chart has green arrows indicating when the closing price has an upward trend. When the stock price goes up (see the green arrow), the HMA(390) tends to stay below the historical price; remember, the strategy places long positions when the closing price > HMA(390).

Therefore, this strategy worked well in those segments marked by the green arrows. A post-mortem analysis is convenient for analyzing the trades and getting insights to improve the trading strategy.

Let’s use bt.optimize to find the strategy with the best performance maximize=’Equity Final [$]’

What does range do?

The use of a sequence increasing by five is because two strategies with similar look-back should have similar results.

If you implement the strategy with the following look-backs 99, 101, 101, and 102, they should have similar values; therefore, the crossing points between the indicator and the closing price should be similar.

Additionally, because in this way, it will take less time to obtain the results. This is not true for look-backs between 1 to 10.

After waiting some seconds or some minutes, you will get:

The previous image shows the total equity of 90 strategies (1, 6, 11, .., 441, 446); the x-axis represents the look-back. When the look-back period is between 350 and 450 days, the total equity exceeds 20,000.

Maximum Drawdown

A good question is: what is the strategy with the lowest maximum drawdown?

The following image shows the maximum drawdown in the set of strategies with look-back periods between 350 and 450 days, increasing by 3 (34 strategies).

The strategies with the minimum maximum drawdown are 443,446 and 449.

If you run the strategy with HMA(446), you will get:

  • Total Return: 147.51%
  • Total buy-and-hold return: 61.88%
  • CARG (Return (Ann.): 4.97%
  • Max. Drawdown: -27.40%
  • Trades: 73
  • Win Rate: 24.66%
  • Best Trade: 88.37%
  • Worst Trade: -7.64%
  • Avg. Trade: 1.25%

To recap, we first implemented a 390-day Hull Moving Average trading strategy. Then, we ran 90 strategies with lengths ranging from 1, 6, 11, …, 441, and 446 days.

Subsequently, we plotted the performance of each strategy; after visualizing the chart, we selected the strategies with lengths of 350 to 450 days (HMA(350) to HMA(450)). Then, we calculated the maximum drawdown of each strategy in that range.

Finally, we chose the strategy HMA(446) because it has the lowest drawdown in the range of 350 to 450.

Similar Posts