Skip to content
Open RaamTrade

ADX — Average Directional Index

Wilder’s trend-strength gauge: smoothed directional movement measured against true range. .adx reads 0–100 and is direction-blind — it says how trending, never which way; .di_plus and .di_minus carry the direction. The classic regime filter.

Signature

ADX(period, on=<source>)
Argument Type Required Notes
period int Param or literal Yes Wilder smoothing length. Default 14, bounds 1–100.
on= symbol No Source symbol. Defaults to the trade symbol. Reads high, low, close.

Formula

$$ +DM = \max(H_t - H_{t-1}, 0) \text{ when it exceeds } -DM, \quad -DM = \max(L_{t-1} - L_t, 0) \text{ when it exceeds } +DM $$ $$ \pm DI = 100,\frac{\text{smoothed}(\pm DM)}{\text{smoothed}(TR)}, \quad DX = 100,\frac{|+DI - -DI|}{+DI + -DI}, \quad ADX = \text{smoothed}(DX) $$

Wilder’s smoothing throughout, seeded with sums/averages of the first n values.

Warmup

About 2n + 1 bars (n to seed the DI smoothing, n more to seed ADX). All outputs are 0 until seeded; DX is 0 on a bar with no range and no gap.

Reading the value

adx.adx
adx.di_plus
adx.di_minus
adx.adx > 25 and adx.di_plus > adx.di_minus

Multi-value: cannot be chained onto or crossed (use thresholds).

Example

class AdxFilteredBreakout(Strategy):
"""Only take breakouts when ADX says the market is trending."""
period = Param(14, min=1, max=100)
channel = Param(20, min=1, max=400)
trade = Symbol()
sizing = FixedCapital()
def execute(self):
adx = ADX(self.period, on=self.trade)
dh = DonchianHighTrue(self.channel, on=self.trade)
if adx.adx > 25 and adx.di_plus > adx.di_minus and self.trade.close >= dh.value_at(1) and self.trade.is_flat:
self.buy(self.trade, reason=f"Breakout, ADX {adx.adx:.0f}")
if adx.adx < 20 and self.trade.is_long:
self.sell(self.trade, self.trade.position, reason="Trend faded")

Notes

  • Wilder, New Concepts in Technical Trading Systems (1978), including his seeding convention.
  • Proven by an assert-via-trade run on SPY daily (all three outputs converged to an independent strategy-side Wilder replication within 0.5 once warm; zero alarm trades).
  • Readings above ~25 are conventionally “trending”, below ~20 “ranging”; those thresholds are folklore, not part of the formula.
  • The recursion seeds from the first bar rather than from a warm-up window, so early values are not comparable with TA-Lib’s ADX/+DI/−DI (Wilder smoothing, period 14), which seeds from an average of the first n; measured on a 300-bar tape the two agree to 1e-6 from bar 197 onward.