Average Directional Index (ADX)
I. What is ADX
The Average Directional Index (ADX) is a trend strength indicator introduced by American technical analysis pioneer J. Welles Wilder Jr. in 1978 in his book New Concepts in Technical Trading Systems.
ADX belongs to the Trend Strength class of indicators and is used to measure how strong or weak the current market trend is. ADX is typically used together with two auxiliary lines, +DI (Positive Directional Indicator) and -DI (Negative Directional Indicator). Together, these three form the DMI system (Directional Movement Index).
The core design philosophy of ADX is: first determine the directional bias of price through Directional Movement, then apply successive layers of smoothing to produce a stable trend strength reading. The default period is .
ADX itself does not indicate trend direction — it only measures trend strength. An ADX of 40 could represent a strong uptrend or a strong downtrend. You must combine it with the +DI and -DI relationship to determine direction.
II. Mathematical Principles and Calculation
2.1 Directional Movement (DM)
+DM (Positive Directional Movement):
-DM (Negative Directional Movement):
On any given bar, at most one of +DM and -DM is positive; the other is 0. If the two are equal, both are 0. This ensures each bar is classified into only one direction.
2.2 Wilder’s Smoothing
Apply Wilder’s Smoothing (period ) separately to +DM, -DM, and TR:
Initial value (sum of the first periods):
Subsequent recursive calculation:
2.3 Directional Indicators (+DI / -DI)
2.4 Directional Index (DX) and ADX
DX (Directional Index):
ADX (Average Directional Index):
- The first ADX = simple average of the first DX values
- Subsequent values:
2.5 Calculation Steps Summary
- Calculate +DM, -DM, and TR for each bar
- Apply Wilder’s Smoothing to +DM, -DM, and TR separately (period 14)
- Calculate +DI and -DI
- Calculate DX
- Apply Wilder’s Smoothing to DX to obtain ADX
Because ADX requires an additional round of smoothing on DX, it needs at least bars to produce the first valid value.
III. Python Implementation
import numpy as np
import pandas as pd
def adx(high: pd.Series, low: pd.Series, close: pd.Series,
period: int = 14) -> pd.DataFrame:
"""
Calculate the ADX / +DI / -DI indicators.
Parameters:
high : Series of high prices
low : Series of low prices
close : Series of closing prices
period : Smoothing period, default 14
Returns:
DataFrame containing plus_di, minus_di, adx columns
"""
n = len(close)
# Calculate True Range
prev_close = close.shift(1)
tr1 = high - low
tr2 = (high - prev_close).abs()
tr3 = (low - prev_close).abs()
tr = pd.concat([tr1, tr2, tr3], axis=1).max(axis=1)
# Calculate +DM and -DM
up_move = high - high.shift(1)
down_move = low.shift(1) - low
plus_dm = pd.Series(0.0, index=close.index)
minus_dm = pd.Series(0.0, index=close.index)
# +DM: take value when upward movement is positive and greater than downward movement
plus_dm[(up_move > down_move) & (up_move > 0)] = up_move
# -DM: take value when downward movement is positive and greater than upward movement
minus_dm[(down_move > up_move) & (down_move > 0)] = down_move
# Wilder's Smoothing
smoothed_tr = pd.Series(np.nan, index=close.index)
smoothed_plus_dm = pd.Series(np.nan, index=close.index)
smoothed_minus_dm = pd.Series(np.nan, index=close.index)
# Initial values: sum of the first 'period' valid values
smoothed_tr.iloc[period] = tr.iloc[1:period + 1].sum()
smoothed_plus_dm.iloc[period] = plus_dm.iloc[1:period + 1].sum()
smoothed_minus_dm.iloc[period] = minus_dm.iloc[1:period + 1].sum()
for i in range(period + 1, n):
smoothed_tr.iloc[i] = smoothed_tr.iloc[i-1] - smoothed_tr.iloc[i-1] / period + tr.iloc[i]
smoothed_plus_dm.iloc[i] = smoothed_plus_dm.iloc[i-1] - smoothed_plus_dm.iloc[i-1] / period + plus_dm.iloc[i]
smoothed_minus_dm.iloc[i] = smoothed_minus_dm.iloc[i-1] - smoothed_minus_dm.iloc[i-1] / period + minus_dm.iloc[i]
# +DI and -DI
plus_di = 100 * smoothed_plus_dm / smoothed_tr
minus_di = 100 * smoothed_minus_dm / smoothed_tr
# DX
dx = 100 * (plus_di - minus_di).abs() / (plus_di + minus_di)
# ADX: apply Wilder's Smoothing to DX
adx_values = pd.Series(np.nan, index=close.index)
# First ADX = mean of 'period' DX values starting from index 'period'
first_adx_start = period + period # need 'period' DX values
if first_adx_start < n:
adx_values.iloc[first_adx_start] = dx.iloc[period + 1:first_adx_start + 1].mean()
for i in range(first_adx_start + 1, n):
adx_values.iloc[i] = (adx_values.iloc[i-1] * (period - 1) + dx.iloc[i]) / period
return pd.DataFrame({
'plus_di': plus_di,
'minus_di': minus_di,
'dx': dx,
'adx': adx_values
})
# ============ Usage Example ============
if __name__ == '__main__':
np.random.seed(42)
n_days = 120
# Generate simulated OHLCV data with trend phases
trend = np.concatenate([
np.linspace(0, 5, 40), # Uptrend
np.linspace(5, 5, 40), # Sideways
np.linspace(5, -2, 40) # Downtrend
])
noise = np.cumsum(np.random.randn(n_days) * 0.3)
base_price = 100 + trend + noise
df = pd.DataFrame({
'open': base_price + np.random.randn(n_days) * 0.2,
'high': base_price + np.abs(np.random.randn(n_days) * 0.6),
'low': base_price - np.abs(np.random.randn(n_days) * 0.6),
'close': base_price,
'volume': np.random.randint(1000, 10000, n_days)
})
# Calculate ADX
result = adx(df['high'], df['low'], df['close'], period=14)
df = pd.concat([df, result], axis=1)
print("=== ADX Results (Last 10 Rows) ===")
print(df[['close', 'plus_di', 'minus_di', 'adx']].tail(10).round(2).to_string())
# Trend strength assessment
latest = df.dropna(subset=['adx']).iloc[-1]
adx_val = latest['adx']
if adx_val > 25:
direction = "uptrend" if latest['plus_di'] > latest['minus_di'] else "downtrend"
print(f"\nCurrent ADX = {adx_val:.2f}, market is in a {direction}")
else:
print(f"\nCurrent ADX = {adx_val:.2f}, market is in a range-bound / trendless state")
IV. Problems the Indicator Solves
4.1 Trend Strength Assessment
The most essential function of ADX is to answer: “Is there a trend in the market right now? How strong is it?”
| ADX Value | Trend Strength | Suggested Strategy |
|---|---|---|
| 0 — 20 | No trend or very weak trend | Use oscillator strategies (mean reversion) |
| 20 — 25 | Trend may be forming | Watch closely, prepare to enter |
| 25 — 50 | Confirmed trend | Use trend-following strategies |
| 50 — 75 | Strong trend | Hold trend positions |
| 75 — 100 | Extreme trend (rare) | Watch for potential trend reversal |
4.2 Trend Direction
Crossovers of +DI and -DI provide directional signals:
- +DI crosses above -DI: bullish signal, upward directional movement dominates
- -DI crosses above +DI: bearish signal, downward directional movement dominates
+DI/-DI crossover signals generate frequent false signals in range-bound markets. It is recommended to execute crossover signals only when ADX > 20 or ADX > 25, filtering out low-quality trades.
4.3 Strategy Selection Filter
ADX helps traders choose between trend-following and mean-reversion strategies:
- ADX > 25: activate trend-following strategies (e.g., moving average crossovers, breakouts)
- ADX < 20: activate oscillator strategies (e.g., RSI overbought/oversold, Bollinger Band reversion)
4.4 Typical Trading Strategies
- DI Crossover Strategy: go long when +DI crosses above -DI and ADX > 25; go short when -DI crosses above +DI and ADX > 25
- ADX Breakout Strategy: when ADX rises from below 20 to above 25, enter in the direction of the DI lines
- ADX Pullback Strategy: when ADX declines from elevated levels, the trend may be ending — prepare to reverse or exit
- ADXR Crossover Strategy: ADXR = (current ADX + ADX from periods ago) / 2; when ADX crosses above ADXR, the trend is strengthening
V. Advantages, Disadvantages, and Use Cases
Advantages
| Advantage | Description |
|---|---|
| Quantifies trend strength | Provides a clear 0—100 reading that is programmable and backtestable |
| Separates direction from strength | ADX measures strength, +DI/-DI provide direction — clear separation of concerns |
| Double smoothing | Two layers of Wilder’s Smoothing produce very stable output |
| Strategy selector | Helps decide whether to use trend-following or mean-reversion strategies |
Disadvantages
| Disadvantage | Description |
|---|---|
| Significant lag | Double smoothing causes ADX to react slowly to trend changes |
| Long warm-up period | Requires at least 27 bars to produce the first ADX value |
| Not suitable as a standalone system | ADX works best as a filter; needs a separate entry signal |
| ADX decline does not equal trend reversal | A falling ADX only means the trend is weakening, not reversing |
Use Cases
- Best suited for: determining whether a trend exists, filtering false signals in range-bound markets
- Well suited for: combining with other trend indicators (moving averages, MACD)
- Not suited for: use as a standalone entry/exit signal for short-term trading
Comparison with Similar Indicators
| Indicator | What It Measures | Characteristics |
|---|---|---|
| ADX | Trend strength (not direction) | The classic trend strength indicator; lagging but stable |
| Aroon | Trend direction and strength | Faster response; based on position of highs/lows |
| Vortex | Trend direction | Based on vortex movement concept; simpler calculation |
| Parabolic SAR | Trend direction and stop-loss | Provides direct stop-loss levels; does not quantify strength |
Parameter Tuning Recommendations: Wilder’s original 14-period setting performs well across most time frames. Shortening the period (e.g., 7) makes ADX more responsive but increases noise; lengthening the period (e.g., 21) provides smoother output but more lag. In practice, using ADX as a “switch” or “filter” rather than as a standalone trading signal is the most effective approach.