Hull Moving Average (HMA)
I. What is HMA
The Hull Moving Average (HMA) is a moving average that achieves both extremely low lag and high smoothness simultaneously. It was invented in 2005 by Australian mathematician and trader Alan Hull, and is widely regarded as one of the best compromise solutions in the moving average family.
Historical Background
In his trading practice, Alan Hull identified a fundamental contradiction: traditional moving averages are either smooth but laggy (like SMA) or fast but noisy (like TEMA). His goal was to design a moving average that could simultaneously:
- Nearly eliminate lag
- Maintain curve smoothness
He ingeniously achieved this through a combination of WMA (Weighted Moving Average) and square-root period smoothing. Since its introduction, HMA has quickly gained recognition from traders worldwide, often called the “Holy Grail of moving averages.”
Indicator Classification
- Type: Overlay indicator, plotted on the price chart
- Category: Moving Averages
- Default Parameters: Period , data source is closing price (Close)
II. Mathematical Principles and Calculation
Core Formula
HMA is calculated in three steps:
Step 1: Calculate half-period WMA and full-period WMA
Step 2: Construct the difference series
Step 3: Apply -period WMA to the difference series
The combined formula is:
Design Rationale
Why use ?
- uses half the period, responding faster but with some lag
- uses the full period, responding slower with greater lag
- acts as an “extrapolation,” overcompensating for the slower average’s lag to approximate the true price. This approach is analogous to DEMA’s
Why apply a final -period WMA?
- The difference has extremely low lag but the curve is not smooth enough (noisy)
- Applying -period WMA to provides final smoothing. is a carefully chosen value — sufficient to eliminate noise without introducing too much additional lag
Alan Hull chose WMA over EMA because WMA’s linear weights perform better in combination operations. WMA’s finite window ensures that the intermediate difference is computed cleanly, while EMA’s infinite memory could introduce extra lag in multi-layer nesting.
Numerical Example
Taking as an example:
- Calculate and
- Difference
- Final result
III. Python Implementation
import numpy as np
import pandas as pd
def wma(close: pd.Series, period: int) -> pd.Series:
"""Calculate the Weighted Moving Average (WMA)"""
weights = np.arange(1, period + 1, dtype=float)
def _wma(window):
return np.dot(window, weights) / weights.sum()
return close.rolling(window=period, min_periods=period).apply(_wma, raw=True)
def hma(close: pd.Series, period: int = 20) -> pd.Series:
"""
Calculate the Hull Moving Average (HMA)
Parameters
----------
close : pd.Series
Closing price series
period : int
Calculation period, default is 20
Returns
-------
pd.Series
HMA value series
"""
half_period = int(period / 2)
sqrt_period = int(np.sqrt(period))
# Steps 1 & 2: Construct the difference series
wma_half = wma(close, half_period)
wma_full = wma(close, period)
delta = 2 * wma_half - wma_full
# Step 3: Final smoothing
return wma(delta, sqrt_period)
def hma_numpy(close: np.ndarray, period: int = 20) -> np.ndarray:
"""
Manual HMA implementation using numpy
"""
def _wma_np(src, p):
n = len(src)
result = np.full(n, np.nan)
w = np.arange(1, p + 1, dtype=float)
d = w.sum()
for i in range(p - 1, n):
window = src[i - p + 1 : i + 1]
if np.any(np.isnan(window)):
continue
result[i] = np.dot(window, w) / d
return result
half_p = int(period / 2)
sqrt_p = int(np.sqrt(period))
wma_half = _wma_np(close, half_p)
wma_full = _wma_np(close, period)
delta = 2 * wma_half - wma_full
return _wma_np(delta, sqrt_p)
# ========== Usage Example ==========
if __name__ == "__main__":
np.random.seed(42)
dates = pd.date_range("2024-01-01", periods=150, freq="D")
price = 100 + np.cumsum(np.random.randn(150) * 0.8)
df = pd.DataFrame({
"date": dates,
"open": price + np.random.randn(150) * 0.3,
"high": price + np.abs(np.random.randn(150) * 0.6),
"low": price - np.abs(np.random.randn(150) * 0.6),
"close": price,
"volume": np.random.randint(1000, 10000, size=150),
})
df.set_index("date", inplace=True)
# Compare multiple moving averages
df["SMA_20"] = df["close"].rolling(20).mean()
df["EMA_20"] = df["close"].ewm(span=20, adjust=False).mean()
df["HMA_20"] = hma(df["close"], 20)
print("=== SMA vs EMA vs HMA Comparison ===")
print(df[["close", "SMA_20", "EMA_20", "HMA_20"]].tail(10))
# HMA direction change signal
df["hma_dir"] = np.where(df["HMA_20"] > df["HMA_20"].shift(1), 1, -1)
df["dir_change"] = df["hma_dir"].diff().abs() > 0
print("\n=== HMA Direction Change Points ===")
print(df.loc[df["dir_change"], ["close", "HMA_20", "hma_dir"]])
IV. Problems the Indicator Solves
1. The Lag vs. Smoothness Dilemma
This is HMA’s most significant contribution. Traditionally, reducing lag means sacrificing smoothness (as with TEMA), but HMA’s unique multi-layer WMA combination achieves excellent smoothness alongside extremely low lag.
2. Trend Direction Detection
Changes in HMA’s direction (rising or falling) provide a highly reliable trend signal:
- HMA turns upward -> Uptrend begins
- HMA turns downward -> Downtrend begins
Due to HMA’s smoothness, direction change signals do not flip frequently, resulting in fewer false signals.
3. Precise Entry / Exit Timing
HMA’s low lag makes it especially suitable for:
- Precise entry after trend confirmation
- Timely exit when trends end
- Minimizing entry lag and exit delay
4. Visual Trend Analysis
Many traders use HMA color changes (green when rising / red when falling) for visual trend assessment. HMA’s smooth curve makes this visual analysis very intuitive and reliable.
In charting software, set HMA to display as green when rising and red when falling. Since HMA rarely produces false direction changes, this color coding provides very clear trend signals.
V. Advantages, Disadvantages, and Use Cases
Advantages
| Advantage | Description |
|---|---|
| Low lag, high smoothness | Achieves both simultaneously; one of the best compromises in moving average design |
| Reliable direction signals | Low false signal rate for direction changes |
| Strong adaptability | Performs consistently across different markets and timeframes |
| Elegant construction | Uses only simple WMA combinations; clear computational logic |
Disadvantages
| Disadvantage | Description |
|---|---|
| Slight overshoot | May briefly overshoot during sharp reversals |
| Higher computation | Requires three WMA operations, each |
| Not ideal for support/resistance | Due to overshoot, HMA is less suitable as a support/resistance level |
| Parameter sensitive | The floor operation on means different periods can produce notably different results |
Use Cases
- Trend-following strategies: HMA is one of the top choices for trend strategies
- Swing trading: Daily-level direction assessment
- Position trading: Capture swing start/end points using HMA direction changes
Comprehensive Moving Average Comparison
| Indicator | Lag | Smoothness | Overshoot | Best Use |
|---|---|---|---|---|
| SMA | High | Highest | None | Long-term trend / Support-resistance |
| EMA | Medium | High | None | General purpose / MACD component |
| DEMA | Low | Medium | Slight | Fast signals |
| TEMA | Very low | Lower | Notable | Ultra-fast signals |
| HMA | Very low | High | Slight | Trend following |
- HMA’s floor operation on means that periods 16 and 20 both use a 4-period WMA for the final smoothing step. Consider this discretization effect when selecting periods.
- HMA may still produce false direction changes in extreme ranging markets (e.g., narrow consolidation). Combining with ADX is recommended.
- Due to its overshoot characteristic, HMA is not recommended for setting stop-loss levels. Use ATR or smoother moving averages for stop-loss placement.