Skip to content

Technical indicators

Average Directional Index

How strong the trend is, whichever way it runs, with two lines for its direction.

+DI and −DI compare how far each bar pushed above the previous high with how far it pushed below the previous low, as a share of the Average True Range. ADX smooths the gap between them, so it rises when one side dominates and falls when they balance. It says nothing about direction on its own: a strong fall lifts it as much as a strong rise.

Traders read ADX above about 25 as a trending market and below about 20 as a ranging one, and use it to choose between trend-following and range rules. In Wilder’s own system, +DI crossing above −DI is a buy signal and crossing below a sell, taken only when ADX is high enough.

+DM = HIGH − previous HIGH, if that is positive and larger than previous LOW − LOW; else 0
−DM = previous LOW − LOW, if that is positive and larger than HIGH − previous HIGH; else 0
+DI = 100 × Wilder(+DM, periods) / Wilder(true range, periods)
−DI = 100 × Wilder(−DM, periods) / Wilder(true range, periods)
DX = 100 × |+DI − −DI| / (+DI + −DI)
ADX = Wilder(DX, periods)

Wilder’s average starts from the mean of the first periods values, then takes (previous × (periods − 1) + value) / periods. With no movement at all, the DIs and ADX are 0.

Call: adx(periods)

Setting Chart default
periods: Periods 14

What you get: a list of 3, in this order: 1. a number (“ADX”); 2. a number (“+DI”); 3. a number (“−DI”).

Warm-up: with adx(14), it asks for 0 bars of history and a run-up of 56, and its first value came on bar 28 of the example data.

Average Directional Index drawn by its own code on example prices

The prices are made up, hourly, and the same on every page, so the chart shows how the indicator behaves, not how a market behaved.

function onIntervalClose() {
const [strength, plusDI, minusDI] = adx(14);
if (strength !== null && strength > 25 && plusDI > minusDI && OPEN_POSITIONS_COUNT === 0) {
openPosition("BUY", { quantity: 10000, stopLossPips: 30 });
}
}