12 — Trend & Momentum

Average Directional Index#

Debated1 min read
◆ The PatternHow strongly price is trending, whichever way it goes: strength without direction

The Average Directional Index — Wilder's trend strength indicator (1978).

+DI = smoothed +DM / ATR
−DI = smoothed −DM / ATR
DX = |+DI − −DI| / (+DI + −DI) × 100
ADX = smoothed DX (default 14)
ADX values:
• 0–25 → absent or weak trend → avoid trend-following strategies
• 25–50 → strong trend
• 50–75 → very strong trend
• 75–100 → extremely strong (rare)
ADX doesn't tell direction — use +DI vs −DI for that.
Worked example

Five-day ADX with Wilder’s smoothing. Through the rally there are no down-moves, so −DI is 0 and ADX is pinned at 100 — a start-up effect of such a short series. By day 13 −DI (36.4) is far above +DI (8.8): a downtrend, with ADX at 56. On day 15 the two cross (+DI 23.1, −DI 22.7) while ADX is still 46: it reports how strong the move was, and is slow to notice that it has ended.

The example data: 15 days, used in every indicator here
DayHighLowCloseVolume
11019910050,000
210310010262,000
310310010148,000
410510110470,000
510810410785,000
610810510660,000
711010610990,000
811010710872,000
910810410580,000
1010510210366,000
1110510210455,000
1210410110275,000
13102989995,000
141029910164,000
1510510110471,000
Where it misleads
  • It says nothing about direction: a high ADX fits a crash as well as a rally.
  • It lags badly, rising after a trend has peaked and staying high into the reversal.
  • The 25 line for “trending” comes from Wilder’s commodity work; it is a convention, not a calibrated level.
In code
up, down = df['high'].diff(), -df['low'].diff()
plus_dm = np.where((up > down) & (up > 0), up, 0.0)
minus_dm = np.where((down > up) & (down > 0), down, 0.0)
pc = df['close'].shift()
tr = pd.concat([df['high'] - df['low'], (df['high'] - pc).abs(), (df['low'] - pc).abs()], axis=1).max(axis=1)
w = lambda s: pd.Series(s, index=df.index).ewm(alpha=1/5, adjust=False).mean()   # Wilder
atr = w(tr)
df['+di'] = 100 * w(plus_dm) / atr
df['-di'] = 100 * w(minus_dm) / atr
dx = 100 * (df['+di'] - df['-di']).abs() / (df['+di'] + df['-di'])
df['adx5'] = w(dx)
Python, in your browser — edit the code and run it again

Assumes import numpy as np, import pandas as pd, and a DataFrame df with columns high, low, close and volume — the 15 days above.

Sources
  • New Concepts in Technical Trading Systems, J. W. Wilder, Trend Research, 1978
  • Technical Analysis of the Financial Markets, J. J. Murphy, New York Institute of Finance, 1999
  • C.-H. Park & S. H. Irwin, “What Do We Know About the Profitability of Technical Analysis?”, Journal of Economic Surveys 21(4), 2007 — a review of the evidence across studies

Last reviewed October 2026 · How this site checks its content · Report a mistake

Pattern bridge: ADX measures trend strength without direction — like standard deviation measuring spread without sign. In ML, PCA eigenvalues measure how much variance each component explains.
Linked from 2
← Previous
MACD
Open in the full reader, with the topic sidebar