Trend:ADX(平均趋向指数)技术指标详解
一、ADX的定义
ADX(Average Directional Index,平均趋向指数) 是由技术分析大师威尔斯·威尔德(Welles Wilder)于1978年在《技术交易系统新概念》一书中提出的经典趋势强度指标。ADX不直接指示趋势的方向(上涨或下跌),而是专门用于衡量趋势的强度。
ADX的核心价值在于帮助交易者回答一个关键问题:当前市场到底有没有趋势? 这个判断是决定使用趋势跟踪策略还是震荡策略的前提条件。
ADX的组成部分
完整的ADX指标实际上包含三条曲线:
| ADX线 | 趋势强度值 | 衡量趋势强弱,值越高趋势越强 |
| +DI | 正向方向指标 | 衡量上升动能 |
| -DI | 负向方向指标 | 衡量下降动能 |
注意:三条线的名称在不同平台略有差异——+DI有时写作DMP,-DI有时写作DMN。pandas_ta库返回的列名即为ADX_14、DMP_14、DMN_14。
二、ADX的计算方法
1. 第一步:计算真实波幅(True Range, TR)
TR衡量一根K线的有效波动范围,取以下三个值的最大值:
TRt=max(Ht−Lt,∣Ht−Ct−1∣,∣Lt−Ct−1∣)
\\mathrm{TR}_t = \\max \\left( \\mathrm{H}_t – \\mathrm{L}_t, \\left| \\mathrm{H}_t – \\mathrm{C}_{t-1} \\right|, \\left| \\mathrm{L}_t – \\mathrm{C}_{t-1} \\right| \\right)
TRt=max(Ht−Lt,∣Ht−Ct−1∣,∣Lt−Ct−1∣)
2. 第二步:计算方向移动(Directional Movement, DM)
- +DM:当前最高价比前日最高价向上突破的幅度,即 Ht−Ht−1\\mathrm{H}_t – \\mathrm{H}_{t-1}Ht−Ht−1,仅当该值为正且大于-DM时采用,否则为0
- -DM:前日最低价比当前最低价向下突破的幅度,即 Lt−1−Lt\\mathrm{L}_{t-1} – \\mathrm{L}_tLt−1−Lt,仅当该值为正且大于+DM时采用,否则为0
核心逻辑:DM衡量的是多头或空头将价格推出前一日交易区间的能力。如果今天的最高价高于前一天的最高价,说明多头力量在增强。
3. 第三步:平滑处理
对TR、+DM、-DM进行Wilder平滑(周期通常为14):
$$
\\begin{aligned}
平滑\\mathrm{TR}_t &= \\mathrm{WilderSMA}(\\mathrm{TR}, 14)_t \\
\\text{平滑+}\\mathrm{DM}_t &= \\mathrm{WilderSMA}(+\\mathrm{DM}, 14)_t \\
\\text{平滑-}\\mathrm{DM}_t &= \\mathrm{WilderSMA}(-\\mathrm{DM}, 14)_t
\\end{aligned}
$$
4. 第四步:计算方向指标(+DI和-DI)
$$
- \\mathrm{DI}_t = 100 \\times \\frac{平滑+\\mathrm{DM}_t}{平滑\\mathrm{TR}_t} \\
- \\mathrm{DI}_t = 100 \\times \\frac{平滑-\\mathrm{DM}_t}{平滑\\mathrm{TR}_t}
$$
5. 第五步:计算DX(Directional Movement Index)
DX衡量+DI与-DI之间的差距,反映趋势强度:
DXt=100×∣+DIt−-DIt∣+DIt+-DIt
\\mathrm{DX}_t = 100 \\times \\frac{|\\text{+}\\mathrm{DI}_t – \\text{-}\\mathrm{DI}_t|}{\\text{+}\\mathrm{DI}_t + \\text{-}\\mathrm{DI}_t}
DXt=100×+DIt+–DIt∣+DIt−–DIt∣
6. 第六步:计算ADX
ADX是对DX进行平滑得到的最终指标:
ADXt=WilderSMA(DX,14)t
\\mathrm{ADX}_t = \\mathrm{WilderSMA}(\\mathrm{DX}, 14)_t
ADXt=WilderSMA(DX,14)t
pandas_ta的平滑方式说明:pandas_ta的ADX默认使用Wilder平滑(mamode='rma'),与威尔德原始设计一致。用户可通过mamode='sma'参数改用简单移动平均。
三、ADX的使用方法
1. 数值区间判断——趋势强度分级
| < 20 | 无趋势/盘整 | 不宜使用趋势跟踪策略,考虑震荡策略 |
| 20 – 25 | 趋势开始萌芽 | 关注趋势发展,做好入场准备 |
| 25 – 40 | 趋势形成中 | 可考虑顺势交易,信号逐步可靠 |
| 40 – 50 | 强趋势 | 顺势持仓,趋势较为可靠 |
| > 50 | 极强趋势 | 趋势非常强劲,但需警惕过度延伸 |
2. 方向判断——+DI与-DI的交叉信号
威尔德建议:当**+DI高于-DI时买入**,当**+DI低于-DI时卖出**。
极值点规则(Wilder的改进):在+DI与-DI交叉时,记录交叉日的最高价或最低价作为入场过滤条件:
- 买入信号(+DI上穿-DI):必须等到价格突破交叉日的最高价后才能入场
- 卖出信号(+DI下穿-DI):必须等到价格跌破交叉日的最低价后才能入场
3. ADX与DI的配合策略
| ADX上升 | ADX在两条DI线之间上升 | 趋势正在加强,顺势持仓,这是最适合趋势跟踪的阶段 |
| ADX下降 | ADX从高位回落 | 趋势在减弱,考虑获利了结,不宜加仓 |
| ADX低于两线 | ADX在两条DI线下方 | 市场横盘,等待ADX向上突破发出新趋势信号 |
4. ADX极值反转信号
当ADX从底部(两条DI线下方)上升4个点以上时(如从9升到13),威尔德认为这是最强烈的信号——宣告新趋势可能刚刚启动。这种信号每年通常只出现一两次,往往对应重要的趋势转折点。
5. 注意事项与局限性
-
滞后性是ADX的主要缺点:由于经过多次平滑处理,ADX对价格变化的反应较慢,更适合确认趋势而非捕捉拐点。
-
在震荡市中ADX会反复穿越20线,产生噪音信号。只有在ADX明确高于25且持续上升时才信任趋势信号。
-
ADX本身不指示方向,必须结合+DI/-DI或价格结构判断多空。
四、使用pandas_ta计算ADX
1. 函数完整参数
pandas_ta.trend.adx(high, low, close, length=None, lensig=None, scalar=None, mamode=None, drift=None, offset=None, **kwargs)
| high | 必需 | 最高价序列 |
| low | 必需 | 最低价序列 |
| close | 必需 | 收盘价序列 |
| length | 14 | ADX计算周期 |
| mamode | ‘rma’ | 平滑方式,'rma’为Wilder平滑,'sma’为简单移动平均 |
注意:ta.adx()函数返回的是包含三列的DataFrame:ADX_14(趋势强度)、DMP_14(即+DI)、DMN_14(即-DI)。直接赋值给单列会报错,需要正确解包。
2. 示例代码
import pandas as pd
import pandas_ta as ta
import numpy as np
import matplotlib.pyplot as plt
# ========== 准备数据 ==========
np.random.seed(42)
dates = pd.date_range(start='2023-01-01', end='2024-12-31', freq='D')
n = len(dates)
# 生成带趋势和周期波动的价格序列
trend = np.linspace(0, 45, n)
cycle = np.sin(np.linspace(0, 6 * np.pi, n)) * 15
noise = np.random.randn(n) * 3
price_series = 100 + trend + cycle + noise
# 添加趋势变化区间
price_series = price_series.astype(float)
for i in range(300, 450):
price_series[i] = price_series[300] + (i–300) * 0.2
for i in range(450, 600):
price_series[i] = price_series[450] – (i–450) * 0.15
df = pd.DataFrame(index=dates[:n])
df['close'] = price_series[:n]
df['high'] = df['close'] + np.abs(np.random.randn(n)) * 2 + 1
df['low'] = df['close'] – np.abs(np.random.randn(n)) * 2 – 1
print("=" * 60)
print("数据预览:")
print(df.head())
print("\\n" + "=" * 60 + "\\n")
# ========== 计算ADX ==========
# 重要:ta.adx()返回三列DataFrame,不能直接赋值给单列
adx_df = ta.adx(df['high'], df['low'], df['close'], length=14)
# 查看返回的DataFrame结构
print("ADX返回列名称:")
print(adx_df.columns.tolist())
print("\\n" + "=" * 60 + "\\n")
# 将三列正确添加到原DataFrame
df['ADX'] = adx_df['ADX_14'] # ADX线:趋势强度
df['PLUS_DI'] = adx_df['DMP_14'] # +DI线:上升动能
df['MINUS_DI'] = adx_df['DMN_14'] # -DI线:下降动能
print("ADX计算结果(最近10行):")
print(df[['close', 'ADX', 'PLUS_DI', 'MINUS_DI']].tail(10))
print("\\n" + "=" * 60 + "\\n")
# ========== 生成趋势强度信号 ==========
df['trend_signal'] = ''
df.loc[df['ADX'] > 40, 'trend_signal'] = '强趋势'
df.loc[(df['ADX'] > 25) & (df['ADX'] <= 40), 'trend_signal'] = '趋势形成'
df.loc[(df['ADX'] > 20) & (df['ADX'] <= 25), 'trend_signal'] = '趋势萌芽'
df.loc[df['ADX'] <= 20, 'trend_signal'] = '无趋势/盘整'
print("趋势强度分布统计:")
print(df['trend_signal'].value_counts())
print("\\n" + "=" * 60 + "\\n")
# ========== 生成方向信号 ==========
# +DI > -DI 且 ADX > 25 时,上升趋势确认
df['long_signal'] = (df['PLUS_DI'] > df['MINUS_DI']) & (df['ADX'] > 25)
# -DI > +DI 且 ADX > 25 时,下降趋势确认
df['short_signal'] = (df['MINUS_DI'] > df['PLUS_DI']) & (df['ADX'] > 25)
print("方向信号统计:")
print(f"上升趋势信号(+DI > -DI 且 ADX > 25)数量:{df['long_signal'].sum()}")
print(f"下降趋势信号(-DI > +DI 且 ADX > 25)数量:{df['short_signal'].sum()}")
print("\\n" + "=" * 60 + "\\n")
# ========== 策略回测 ==========
# 策略:仅在ADX > 25且+DI > -DI时持仓多头
df['position'] = 0
df.loc[df['long_signal'], 'position'] = 1
df.loc[df['short_signal'], 'position'] = –1
df['returns'] = df['close'].pct_change()
df['strategy_returns'] = df['position'].shift(1) * df['returns']
total_return_buyhold = (1 + df['returns']).prod() – 1
total_return_strategy = (1 + df['strategy_returns']).prod() – 1
print("=" * 60)
print("策略绩效统计(ADX趋势过滤策略回测):")
print(f"买入持有策略总收益率:{total_return_buyhold:.2%}")
print(f"ADX趋势过滤策略总收益率:{total_return_strategy:.2%}")
print("\\n注意:此为简化回测,仅供参考")
print("=" * 60 + "\\n")
# ========== 可视化 ==========
plt.figure(figsize=(14, 10))
plt.subplot(2, 1, 1)
plt.plot(df.index[–200:], df['close'][–200:],
label='Close Price', linewidth=1.5, color='black')
plt.title('Price Chart (Last 200 days)', fontsize=14)
plt.ylabel('Price')
plt.legend()
plt.grid(True, alpha=0.3)
plt.subplot(2, 1, 2)
plt.plot(df.index[–200:], df['ADX'][–200:],
label='ADX (14)', linewidth=1.5, color='blue')
plt.plot(df.index[–200:], df['PLUS_DI'][–200:],
label='+DI', linewidth=1, color='green')
plt.plot(df.index[–200:], df['MINUS_DI'][–200:],
label='-DI', linewidth=1, color='red')
plt.axhline(y=25, color='gray', linestyle='–', linewidth=1, label='趋势阈值 (25)')
plt.axhline(y=40, color='orange', linestyle=':', linewidth=1, label='强趋势阈值 (40)')
plt.axhline(y=20, color='purple', linestyle=':', linewidth=1, label='盘整阈值 (20)')
plt.title('ADX(平均趋向指数)- 趋势强度判断', fontsize=14)
plt.xlabel('Date')
plt.ylabel('Value')
plt.legend(loc='upper left')
plt.grid(True, alpha=0.3)
plt.tight_layout()
plt.show()
3. 关键注意事项
正确解包返回值:
# 错误写法(会报错:Wrong number of items passed)
df['ADX'] = ta.adx(df['High'], df['Low'], df['Close'], length=14)
# 正确写法
adx_data = ta.adx(df['high'], df['low'], df['close'], length=14)
df['ADX'] = adx_data['ADX_14']
df['PLUS_DI'] = adx_data['DMP_14']
df['MINUS_DI'] = adx_data['DMN_14']
平滑方式调整:如果需要使用简单移动平均(SMA)而非Wilder平滑,可设置mamode='sma':
adx_sma = ta.adx(df['high'], df['low'], df['close'], length=14, mamode='sma')
五、总结
| 核心功能 | 衡量趋势强度,不指示方向 |
| 组成部分 | ADX线 + +DI线 + -DI线 |
| 关键阈值 | 20(盘整)、25(趋势形成)、40(强趋势) |
| 核心用法 | 趋势过滤:ADX>25时才使用趋势跟踪策略 |
| 最佳时机 | ADX上升且高于两条DI线时为趋势最强劲的阶段 |
| 主要局限 | 滞后性、不适用震荡市、需配合方向判断 |
实战使用三原则:

