Arnaud Legoux Moving Average (ALMA)
I. What is ALMA
The Arnaud Legoux Moving Average (ALMA) is an advanced moving average that uses a Gaussian distribution (normal distribution) as its weight function. It was co-invented by French engineer Arnaud Legoux and Greek mathematician Dimitris Kouzis-Loukas, and was first published around 2009.
Historical Background
Legoux and Kouzis-Loukas drew inspiration from the signal processing domain, arguing that the ideal moving average weight function should be a bell curve (Gaussian function), rather than a linear decay (WMA) or exponential decay (EMA). Gaussian weights offer the following advantages:
- The center region carries the highest weight, with rapid decay on both sides
- The weight curve is extremely smooth, with no hard cutoff
- By adjusting the offset parameter, the weight center can be positioned anywhere within the window
ALMA allows users to fine-tune the weight distribution through offset and sigma parameters, achieving flexible control over the trade-off between lag and smoothness.
Indicator Classification
- Type: Overlay indicator, plotted on the price chart
- Category: Moving Averages
- Default Parameters: Period , offset , sigma , data source is closing price (Close)
II. Mathematical Principles and Calculation
Core Formula
ALMA weights are based on the Gaussian function:
Where:
- (index within the window, 0 being the oldest data)
- (center position of the Gaussian bell curve)
- (standard deviation of the Gaussian function, controlling the bell width)
The ALMA calculation formula is:
Parameter Details
offset (range 0 ~ 1, default 0.85)
Controls the position of the Gaussian bell center within the window:
- : Weight center at the window midpoint; maximum lag but smoothest
- : Weight center at the newest data point; minimum lag but less smooth
- (default): Biased toward recent data; a good balance between lag and smoothness
sigma (width parameter, default 6)
Controls the width of the Gaussian bell:
- Smaller : Narrower bell, weights more concentrated near the center, behaves like a short-period average
- Larger : Wider bell, more uniformly distributed weights, behaves like SMA
Imagine a bell curve placed over the last 20 days of prices. offset=0.85 means the bell’s peak (highest weight) is at approximately day 17 (i.e., the 3rd most recent day), very close to the current price but not exactly at the latest day. This allows ALMA to be both responsive and resistant to latest-day noise.
Numerical Example
For , , :
Weights for each index:
| 0 | 0.000 | |
| 1 | 0.016 | |
| 2 | 0.244 | |
| 3 | 0.891 | |
| 4 | 0.772 |
As shown, weight is concentrated at and (the two most recent days), consistent with the offset=0.85 bias.
III. Python Implementation
import numpy as np
import pandas as pd
def alma(close: pd.Series, period: int = 20,
offset: float = 0.85, sigma: float = 6.0) -> pd.Series:
"""
Calculate the Arnaud Legoux Moving Average (ALMA)
Parameters
----------
close : pd.Series
Closing price series
period : int
Calculation period, default is 20
offset : float
Gaussian weight center offset (0~1), default is 0.85
sigma : float
Gaussian width parameter, default is 6
Returns
-------
pd.Series
ALMA value series
"""
m = offset * (period - 1)
s = period / sigma
# Calculate Gaussian weights
weights = np.array([
np.exp(-((i - m) ** 2) / (2 * s * s))
for i in range(period)
])
weights /= weights.sum() # Normalize
def _alma(window):
return np.dot(window, weights)
return close.rolling(window=period, min_periods=period).apply(_alma, raw=True)
def alma_numpy(close: np.ndarray, period: int = 20,
offset: float = 0.85, sigma: float = 6.0) -> np.ndarray:
"""
Manual ALMA implementation using numpy
"""
n = len(close)
result = np.full(n, np.nan)
m = offset * (period - 1)
s = period / sigma
# Construct Gaussian weights
idx = np.arange(period)
weights = np.exp(-((idx - m) ** 2) / (2 * s * s))
weights /= weights.sum()
for i in range(period - 1, n):
window = close[i - period + 1 : i + 1]
result[i] = np.dot(window, weights)
return result
# ========== 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 ALMA with different offsets
df["EMA_20"] = df["close"].ewm(span=20, adjust=False).mean()
df["ALMA_default"] = alma(df["close"], 20, offset=0.85, sigma=6)
df["ALMA_fast"] = alma(df["close"], 20, offset=0.95, sigma=6)
df["ALMA_smooth"] = alma(df["close"], 20, offset=0.50, sigma=6)
print("=== ALMA Comparison with Different Offsets ===")
print(df[["close", "EMA_20", "ALMA_default", "ALMA_fast", "ALMA_smooth"]].tail(10))
# Compare different sigma values
df["ALMA_s3"] = alma(df["close"], 20, offset=0.85, sigma=3)
df["ALMA_s6"] = alma(df["close"], 20, offset=0.85, sigma=6)
df["ALMA_s12"] = alma(df["close"], 20, offset=0.85, sigma=12)
print("\n=== ALMA Comparison with Different Sigma Values ===")
print(df[["close", "ALMA_s3", "ALMA_s6", "ALMA_s12"]].tail(10))
# Crossover signal
df["signal"] = np.where(df["close"] > df["ALMA_default"], 1, -1)
df["cross"] = df["signal"].diff().abs() > 0
print("\n=== ALMA Crossover Signals ===")
print(df.loc[df["cross"], ["close", "ALMA_default", "signal"]])
IV. Problems the Indicator Solves
1. Flexible Lag-Smoothness Trade-off
ALMA’s most unique capability is the fine-grained control over lag and smoothness through the offset and sigma parameters. This is something other moving averages cannot achieve:
- Increase offset -> Reduce lag
- Increase sigma -> Increase smoothness
Traders can freely adjust these according to different market conditions and strategy requirements.
2. Eliminating Window Boundary Effects
Gaussian weights naturally decay to near zero at both ends of the window, avoiding the hard cutoff problem seen in SMA and WMA. When price data exits the window, ALMA’s value does not experience abrupt jumps.
3. Signal-Processing-Grade Smoothing
ALMA’s Gaussian kernel function is directly related to low-pass filters in signal processing. Its suppression of high-frequency noise in the frequency domain is superior to linear or exponential weighting schemes.
4. Reducing False Crossovers
Due to the smooth nature of Gaussian weights, ALMA produces fewer false crossover signals than same-period EMA, performing more stably especially in ranging markets.
- Clear trends: offset = 0.85~0.95, sigma = 6, biased toward fast response
- Ranging markets: offset = 0.5
0.7, sigma = 610, biased toward smoothness - Long-term investing: offset = 0.5, sigma = 10+, similar to a smoother SMA
V. Advantages, Disadvantages, and Use Cases
Advantages
| Advantage | Description |
|---|---|
| Flexible parameters | offset and sigma provide fine-grained lag/smoothness control |
| No hard cutoff | Gaussian weights decay naturally; no window boundary jumps |
| High smoothness | Gaussian kernel smoothing outperforms linear/exponential weights |
| Theoretically rigorous | Based on well-established Gaussian filtering theory from signal processing |
Disadvantages
| Disadvantage | Description |
|---|---|
| Too many parameters | Three parameters (period, offset, sigma) increase tuning complexity |
| Higher computation | Each calculation requires a weighted sum over the entire window |
| High learning barrier | The Gaussian distribution concept is not intuitive for non-mathematical traders |
| Non-mainstream | Uncommon in traditional technical analysis textbooks; may lack shared understanding |
Use Cases
- Quantitative strategy development: Research requiring fine control over moving average behavior
- Signal-processing-oriented analysis: Optimizing price filtering from a frequency-domain perspective
- Multi-market adaptation: Using different offset/sigma parameters for different markets
Comparison with Similar Indicators
| Feature | SMA | EMA | WMA | ALMA |
|---|---|---|---|---|
| Weight function | Constant | Exponential | Linear | Gaussian |
| Adjustable parameters | 1 | 1 | 1 | 3 |
| Boundary effects | Severe | None | Moderate | None |
| Smoothness | Medium | Medium | Medium | High |
| Flexibility | Low | Low | Low | High |
- Do not blindly increase offset (close to 1.0), as this makes ALMA focus almost exclusively on the last one or two days, losing its smoothing function.
- Setting sigma to very small values (e.g., 1-2) causes ALMA to degenerate to looking at only a few prices near the window center; this is not recommended.
- When backtesting ALMA strategies, all three parameters should be optimized simultaneously. Be aware of overfitting risk and use cross-validation or out-of-sample testing.