QUANTIDEDownload

Moving Average Convergence Divergence(MACD)

The distance between a fast and a slow EMA, plus an EMA of that distance.

Signature · quantide SDK
macd(prices, fast: int = 12, slow: int = 26, signal: int = 9)

MACD is the difference between two exponential moving averages, and a third EMA of that difference called the signal line. The gap between the two — the histogram — is what most people actually trade. Despite the name it is not measuring convergence or divergence of anything except its own two components.

Reading it as momentum is fair: the MACD line rises when the fast average is pulling away from the slow one, which is what accelerating price does. Reading it as a leading indicator is not. Every component is a moving average, so every value is a function of prices that have already happened.

The 12/26/9 defaults come from a period when charts were drawn on paper and the trading week was six days. They persist because everyone uses them, which is a genuine reason — a self-fulfilling level is still a level — but not a statistical one.

There are two distinct signals here and they are routinely conflated. The MACD line crossing its signal line is one event, and the histogram crossing zero is the same event by construction, since the histogram is their difference. The MACD line crossing zero is something else entirely: it means the fast and slow averages have crossed each other. A backtest that reports a MACD strategy without saying which crossing it traded is not reproducible.

Where it misleads

  • Three parameters is three dimensions to overfit in. A grid search across all of them on one instrument will always find something that looks excellent and generalises to nothing.
  • Every component lags. MACD confirms a move that has already begun; treating a crossover as a forecast rather than a confirmation is the standard misuse.
  • The histogram amplifies noise on volatile instruments, because it is a difference of differences.

In code

python
from quantide import fetch_data, macd

data = fetch_data("QQQ", "2015-01-01", "2025-01-01")
line, signal_line, hist = macd(data["Close"], fast=12, slow=26, signal=9)

entries = (hist > 0) & (hist.shift(1) <= 0)