Algorithmic Trading & Indicators Intermediate

Calculate Wilder's ADX (+DI / -DI) Trend Strength Filter in Python

Filter out sideways market whipsaws in Python by computing Wilder's Average Directional Index (ADX), +DI, and -DI using vectorized Pandas.

#pandas#numpy

Problem Statement

Moving average and Supertrend strategies bleed capital in choppy, range-bound markets. Adding an ADX > 25 regime filter ensures entries only trigger during strong directional trends.

Formula / Core Logic

+DM = High_t - High_{t-1}, -DM = Low_{t-1} - Low_t DX = 100 * |(+DI - -DI)| / (+DI + -DI) ADX = Wilder_RMA(DX, 14)

Installation

pip install pandas numpy
adx-average-directional-index-trend-filter-python.py
import pandas as pd
import numpy as np

def calculate_adx(df, period=14):
    df = df.copy()
    up = df['High'].diff()
    down = -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)
    
    tr = pd.concat([df['High'] - df['Low'], (df['High'] - df['Close'].shift(1)).abs(), (df['Low'] - df['Close'].shift(1)).abs()], axis=1).max(axis=1)
    atr = tr.ewm(alpha=1/period, adjust=False).mean()
    
    plus_di = 100 * (pd.Series(plus_dm, index=df.index).ewm(alpha=1/period, adjust=False).mean() / atr)
    minus_di = 100 * (pd.Series(minus_dm, index=df.index).ewm(alpha=1/period, adjust=False).mean() / atr)
    dx = 100 * ((plus_di - minus_di).abs() / (plus_di + minus_di))
    df['ADX'] = dx.ewm(alpha=1/period, adjust=False).mean()
    df['Trending_Regime'] = np.where(df['ADX'] > 25, 1, 0)
    return df

Get the Complete 100+ Python Quant & Automation Bundle

Instant lifetime access to all production-ready scripts, valuation models, and trading bots ($19).

Buy Full Bundle on Gumroad ($19) →

Frequently Asked Questions

Q: Does ADX indicate bullish or bearish direction?

No, ADX measures trend strength regardless of direction. Compare +DI vs -DI to determine bullish or bearish dominance.