
炒股的朋友们都知道,趋势是咱们交易里的"风向标"。可光知道方向还不够呀,你得知道这股风到底是微风徐徐,还是飓风呼啸!😂 这就是 ADX(Average Directional Index,平均趋向指标) 登场的地方。
为什么要测趋势的"强度"呢?很简单:
- 趋势弱 → 随时可能反转,交易胜率打折。
 - 趋势强 → 上车后跟着走,成功率和人均盈利都更香。
 
不过,这里有个小秘密------别被名字骗了,ADX 并不告诉你趋势的方向!它只是告诉你"风有多大"。那方向咋办?别急,ADX 有两位好搭档:
- +DI(正向指标) → 检测上涨趋势
 - -DI(负向指标) → 检测下跌趋势
 
所以,ADX 就是趋势强度的量尺,+DI 和 -DI 则是方向指南针,三剑合璧,才算完整。
接下来,花姐会依次聊聊:
- ADX 指标到底是啥?
 - 它的计算逻辑(数学党最爱)
 - 用 Python 写个小策略,把理论搬到实盘里
 
ADX 指标到底是啥?
提到 ADX 指标 ,就不得不说它的"发明人"------技术分析大佬 Welles Wilder。这位老哥可是技术指标界的"顶流",不光搞出了 RSI,还整出了 ADX 和方向性指标(Directional Movement Indicator)。
那 ADX 是怎么来的呢?它的家族成员有三位:
- True Range(真实波动范围)
 - +DI(正向指标)
 - -DI(负向指标)
 
Wilder 的思路是:先用 +DI 和 -DI 分别捕捉上升和下降的趋势,然后再算它们的"差异强度",最后用平滑处理得到一个指标------这就是 ADX。简单说,ADX 就是告诉你趋势"强不强",而不是"涨还是跌"。
👉 指标范围在 0 到 100 之间:
- 小于 20:市场基本在划水,趋势弱。
 - 大于 25:趋势算是成型,可以关注。
 - 50 以上:趋势非常强,但你可能也得小心见顶或见底。
 
ADX 的计算逻辑
讲到指标,免不了得撸一遍"数学流程图"。别怕,这里我帮你分了几步拆开讲,简单清晰:
- 
True Range(真实波动范围) 衡量一天价格的真实波动。比单纯的 High - Low 更全面。
 - 
+DM(正向动向) 今天的高点比昨天高多少,如果比低点变化更大,那算上涨动向。
 - 
-DM(负向动向) 今天的低点比昨天低多少,如果比高点变化更大,那算下跌动向。
 - 
平滑处理 Wilder 很喜欢平滑,直接用指数平滑(类似移动平均)来消除噪音。
 - 
+DI 和 -DI 把 +DM 和 -DM 分别除以 True Range,然后算百分比,得到 +DI 和 -DI。
 - 
ADX 最终计算 用 |(+DI -- -DI)| - (+DI + -DI) 算出一个"方向差异比率",再平滑一下,就是 ADX。
 
现在我们来举个小栗子。假设某只股票的数据如下:
| Date | High | Low | Close | 
|---|---|---|---|
| 2019-11-29 | 90 | 82 | 87 | 
| 2019-12-2 | 95 | 85 | 87 | 
| 2019-12-3 | 105 | 93 | 97 | 
| 2019-12-4 | 120 | 106 | 114 | 
| 2019-12-5 | 140 | 124 | 133 | 
| 2019-12-6 | 165 | 147 | 157 | 
| 2019-12-9 | 195 | 175 | 186 | 
| 2019-12-10 | 230 | 208 | 223 | 
| 2019-12-11 | 270 | 246 | 264 | 
| 2019-12-12 | 315 | 289 | 311 | 
| 2019-12-13 | 365 | 337 | 350 | 
别看表格有点长,其实就是我们接下来要用来一步步算 ADX 的"样本数据"。
第一步:True Range(真实波动范围)
很多小伙伴看到 High - Low 就以为波动范围很简单嘛,今天最高价减最低价不就完了?😂 但是!如果只这么算,就会漏掉一个重要细节:价格可能在收盘和次日开盘之间跳空 。这就是为什么 Wilder 引入了 True Range(真实波动范围)。
👉 公式其实也不难,就是三者取最大:
- 当前最高价 - 当前最低价
 - |当前最高价 - 昨日收盘价|
 - |当前最低价 - 昨日收盘价|
 
选最大值,就是当天的 True Range。
举个例子: 日期:2019-12-2
- (High - Low) = 95 - 85 = 10
 - |High - Prev Close| = |95 - 87| = 8
 - |Low - Prev Close| = |85 - 87| = 2
 
所以 True Range = max(10, 8, 2) = 10
来看一下完整的 True Range 表格:
| Date | High | Low | Close | High-Low | High-PrevClose | Low-PrevClose | True Range | 
|---|---|---|---|---|---|---|---|
| 2019-11-29 | 90 | 82 | 87 | - | - | - | - | 
| 2019-12-2 | 95 | 85 | 87 | 10 | 8 | 2 | 10 | 
| 2019-12-3 | 105 | 93 | 97 | 12 | 18 | 6 | 18 | 
| 2019-12-4 | 120 | 106 | 114 | 14 | 23 | 9 | 23 | 
| 2019-12-5 | 140 | 124 | 133 | 16 | 26 | 10 | 26 | 
| 2019-12-6 | 165 | 147 | 157 | 18 | 32 | 14 | 32 | 
| 2019-12-9 | 195 | 175 | 186 | 20 | 38 | 18 | 38 | 
| 2019-12-10 | 230 | 208 | 223 | 22 | 44 | 22 | 44 | 
| 2019-12-11 | 270 | 246 | 264 | 24 | 47 | 23 | 47 | 
| 2019-12-12 | 315 | 289 | 311 | 26 | 51 | 25 | 51 | 
| 2019-12-13 | 365 | 337 | 350 | 28 | 54 | 26 | 54 | 
是不是比单纯的 High - Low 更"真实"了?
好了,True Range 我们拿下 。
第二步:正向动向(+DM)
前面咱们搞定了 True Range,现在该看 市场往上冲的力量 了。顾名思义,+DM(Positive Directional Movement) 就是用来捕捉上涨动能的。
问题来了:怎么判断"市场是真的在往上走"?🤔
直觉上,如果今天的 最高价 比昨天更高,那就说明有人愿意出更高的价买入,市场在往上推。 但是 Wilder 可没这么随便,他给了个小公式:
判断逻辑(if-else 版本):
- 如果 (今天高点 - 昨天高点) > (昨天低点 - 今天低点) → +DM = 今天高点 - 昨天高点
 - 否则 → +DM = 0
 
这么做的意义在哪? 其实就是在比较:
- 上涨的"幅度" vs 下跌的"幅度"。
如果上涨的那一截更大,说明买盘更强,+DM 就记下来;
反之,就算了(记 0)。 
这样,我们就能比较客观地量化"市场是否真的在往上走"。
第三步:负向动向(-DM)
股市就像人生,有上就有下。前面我们聊了 +DM(正向动向) 用来捕捉上涨力量,那这一步,自然要看看 市场往下砸的力度 ------也就是 -DM(Negative Directional Movement)。
判断逻辑也不难,和 +DM 是镜像关系:
👉 公式:
- 如果 (昨天低点 - 今天低点) > (今天高点 - 昨天高点) → -DM = 昨天低点 - 今天低点
 - 否则 → -DM = 0
 
说白了,就是看 下跌的那一截是不是比上涨的更大。 如果跌幅更明显,就把这部分记为 -DM; 如果涨得多,说明空头不够强,那就记 0。
用我们之前的数据表来跑一下,结果如下:
| Date | High | Low | Close | True Range | +DM | -DM | 
|---|---|---|---|---|---|---|
| 2019-11-29 | 90 | 82 | 87 | - | - | - | 
| 2019-12-2 | 95 | 85 | 87 | 10 | 5 | 0 | 
| 2019-12-3 | 105 | 93 | 97 | 18 | 10 | 0 | 
| 2019-12-4 | 120 | 106 | 114 | 23 | 15 | 0 | 
| 2019-12-5 | 140 | 124 | 133 | 26 | 20 | 0 | 
| 2019-12-6 | 165 | 147 | 157 | 32 | 25 | 0 | 
| 2019-12-9 | 195 | 175 | 186 | 38 | 30 | 0 | 
| 2019-12-10 | 230 | 208 | 223 | 44 | 35 | 0 | 
| 2019-12-11 | 270 | 246 | 264 | 47 | 40 | 0 | 
| 2019-12-12 | 315 | 289 | 311 | 51 | 45 | 0 | 
| 2019-12-13 | 365 | 337 | 350 | 54 | 50 | 0 | 
可以看到,这段时间几乎全是上涨行情,所以 -DM 一直是 0,+DM 独自刷存在感。😂
第四步:平滑处理(Smoothed Values)
前面我们算了 True Range、+DM、-DM,但这些数据每天波动太大了,直接拿来用容易"眼花缭乱"。怎么办? 👉 Wilder 给出了答案:平滑处理,有点像移动平均,用来消掉短期噪音。
这里我们随便取个周期,假设 Period = 5 。 先看 +DM 的平滑值:
- 第一个平滑值 = 前 5 个 +DM 直接求和。 (5 + 10 + 15 + 20 + 25) = 75
 - 然后算平均 = 75 / 5 = 15
 - 接着,算下一个平滑值时,就用: 前一个平滑值 - (前一个平滑值/周期) + 当前值
 
举个例子:
- 当前是第 6 个 +DM(= 30)
 - 所以第二个平滑值 = 75 - 15 + 30 = 90
 
是不是看着有点像 指数平滑平均?其实就是一个"滚动更新"的过程。
算出来的平滑表格如下:
| Date | True Range | +DM | -DM | Smoothed +DM | 
|---|---|---|---|---|
| 2019-11-29 | - | - | - | - | 
| 2019-12-2 | 10 | 5 | 0 | - | 
| 2019-12-3 | 18 | 10 | 0 | - | 
| 2019-12-4 | 23 | 15 | 0 | - | 
| 2019-12-5 | 26 | 20 | 0 | - | 
| 2019-12-6 | 32 | 25 | 0 | 75.0 | 
| 2019-12-9 | 38 | 30 | 0 | 90.0 | 
| 2019-12-10 | 44 | 35 | 0 | 107.0 | 
| 2019-12-11 | 47 | 40 | 0 | 125.6 | 
| 2019-12-12 | 51 | 45 | 0 | 145.5 | 
| 2019-12-13 | 54 | 50 | 0 | 166.4 | 
因为之前 -DM 一直是 0,所以它的平滑值自然也全是 0。 同理,True Range 也要做同样的平滑处理,完整表格如下:
| Date | True Range | +DM | -DM | Smoothed +DM | Smoothed -DM | Smoothed TR | 
|---|---|---|---|---|---|---|
| 2019-11-29 | - | - | - | - | - | - | 
| 2019-12-2 | 10 | 5 | 0 | - | - | - | 
| 2019-12-3 | 18 | 10 | 0 | - | - | - | 
| 2019-12-4 | 23 | 15 | 0 | - | - | - | 
| 2019-12-5 | 26 | 20 | 0 | - | - | - | 
| 2019-12-6 | 32 | 25 | 0 | 75.0 | 0.0 | 109.0 | 
| 2019-12-9 | 38 | 30 | 0 | 90.0 | 0.0 | 125.2 | 
| 2019-12-10 | 44 | 35 | 0 | 107.0 | 0.0 | 144.2 | 
| 2019-12-11 | 47 | 40 | 0 | 125.6 | 0.0 | 162.3 | 
| 2019-12-12 | 51 | 45 | 0 | 145.5 | 0.0 | 180.9 | 
| 2019-12-13 | 54 | 50 | 0 | 166.4 | 0.0 | 198.7 | 
到这里,我们手里已经有了 平滑后的 TR、+DM、-DM,就差最后三剑客了:
- +DI(正向指标)
 - -DI(负向指标)
 - ADX(平均趋向指标,大Boss)
 
下一步,就要正式进入这三兄弟的计算了!
正向指标(+DI)和负向指标(-DI)
我们之前算出了 平滑后的 +DM 和 -DM ,但单独看这两个指标,其实作用有限。Wilder 的聪明之处在于:把两者结合起来看交叉信号,这才有意义。💡
不过两个指标大小可能差别很大,为了好比较,我们要 归一化:
- 方法就是用平滑后的 +DM 或 -DM 除以平滑后的 True Range,然后转成百分比。
 
举个例子:
- 
平滑后的 +DM = 75
 - 
平滑后的 True Range = 70
 - 
所以 +DI = 75 ÷ 70 ≈ 1.07 → 107%
 - 
而我们的 -DM 一直是 0,所以 -DI = 0%
 
ADX 指标:最终计算
记住一件事:ADX 告诉我们趋势的"强度",而不是方向。方向是 +DI 和 -DI 告诉我们的,但单看方向不足以判断市场有多"猛"。
想象一下,如果 +DI 和 -DI 很接近,说明买卖力量差不多,趋势可能很弱; 如果两者差距很大,说明趋势很强!Wilder 就用这个思想推出了 DX(Directional Index):
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"> D X = ∣ + D I − − D I ∣ + D I + − D I × 100 DX = \frac{|+DI - -DI|}{+DI + -DI} \times 100 </math>DX=+DI+−DI∣+DI−−DI∣×100
举例:2019-12-6
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"> D X = 75 − 0 75 + 0 × 100 = 100 DX = \frac{75 - 0}{75 + 0} \times 100 = 100 </math>DX=75+075−0×100=100
为了去掉波动,我们再对 DX 做 移动平均 ,得到最终的 ADX 指标。
- 时间周期取 5,那么 ADX = 最近 5 个 DX 的平均值。
 
计算出来后,我们可以直接看趋势强弱:
- ADX = 100 → 趋势非常强烈
 - ADX < 20 → 趋势很弱或盘整
 
| Date | True Range | +DM | -DM | Smoothed +DM | Smoothed -DM | Smoothed TR | +DI | -DI | DX | ADX | 
|---|---|---|---|---|---|---|---|---|---|---|
| 2019-11-29 | - | - | - | - | - | - | - | - | - | - | 
| 2019-12-2 | 10 | 5 | 0 | - | - | - | - | - | - | - | 
| 2019-12-3 | 18 | 10 | 0 | - | - | - | - | - | - | - | 
| 2019-12-4 | 23 | 15 | 0 | - | - | - | - | - | - | - | 
| 2019-12-5 | 26 | 20 | 0 | - | - | - | - | - | - | - | 
| 2019-12-6 | 32 | 25 | 0 | 75.0 | 0.0 | 109.0 | 68.81 | 0 | 100 | - | 
| 2019-12-9 | 38 | 30 | 0 | 90.0 | 0.0 | 125.2 | 71.88 | 0 | 100 | - | 
| 2019-12-10 | 44 | 35 | 0 | 107.0 | 0.0 | 144.2 | 74.22 | 0 | 100 | - | 
| 2019-12-11 | 47 | 40 | 0 | 125.6 | 0.0 | 162.3 | 77.37 | 0 | 100 | - | 
| 2019-12-12 | 51 | 45 | 0 | 145.5 | 0.0 | 180.9 | 80.44 | 0 | 100 | 100 | 
| 2019-12-13 | 54 | 50 | 0 | 166.4 | 0.0 | 198.7 | 83.74 | 0 | 100 | 100 | 
💡 从表格可以看出,从 2019-12-6 开始正向指标远高于负向指标,DX 值保持在 100,说明趋势非常强。12月12日、12月13日 的 ADX 达到 100,也明确表明趋势力度极强,这种情况下,即便价格波动,趋势方向的可靠性很高。
用Python玩转ADX指标,轻松搞定趋势交易策略
首先,我们从AKShare库拿数据,随便挑了比亚迪,从2024年1月1日到2025年8月1日:
            
            
              python
              
              
            
          
          import akshare as ak
df = ak.stock_zh_a_hist(
    symbol='002594',
    period='daily',
    start_date='20240101',
    end_date='20250801',
    adjust='qfq')
        返回结果如下:
            
            
              yaml
              
              
            
          
                日期    股票代码      开盘      收盘      最高      最低     成交量           成交额    振幅   涨跌幅   涨跌额   换手率
0    2024-01-02  002594   64.18   61.84   64.18   61.77  113928  2.212162e+09  3.79 -2.83 -1.80  0.98
1    2024-01-03  002594   61.81   62.03   62.27   61.21   84486  1.624093e+09  1.71  0.31  0.19  0.73
2    2024-01-04  002594   62.42   61.98   62.53   61.34   89870  1.731882e+09  1.92 -0.08 -0.05  0.77
3    2024-01-05  002594   61.80   61.87   63.48   61.35  112402  2.187836e+09  3.44 -0.18 -0.11  0.97
4    2024-01-08  002594   61.91   60.78   62.20   60.69   81078  1.543894e+09  2.44 -1.76 -1.09  0.70
..          ...     ...     ...     ...     ...     ...     ...           ...   ...   ...   ...   ...
378  2025-07-28  002594  111.32  111.01  111.71  110.52  117057  3.947001e+09  1.07 -0.28 -0.31  1.01
379  2025-07-29  002594  112.01  111.42  112.50  109.77  387360  4.322742e+09  2.46  0.37  0.41  1.11
380  2025-07-30  002594  108.05  108.70  111.16  107.12  524834  5.743879e+09  3.63 -2.44 -2.72  1.51
381  2025-07-31  002594  108.20  105.24  108.20  105.00  599499  6.344901e+09  2.94 -3.18 -3.46  1.72
382  2025-08-01  002594  104.95  105.80  106.24  104.36  362337  3.816660e+09  1.79  0.53  0.56  1.04
        接下来,直接调用Python里的神器库ta,计算ADX指标:
            
            
              python
              
              
            
          
          from ta.trend import ADXIndicator
adxi = ADXIndicator(
    high=df['最高'],
    low=df['最低'],
    close=df['收盘'],
    window=14,
    fillna=False)
df['adx_pos'] = adxi.adx_pos()
df['adx_neg'] = adxi.adx_neg()
df['adx'] = adxi.adx()
        得到的结果如下:
            
            
              yaml
              
              
            
          
               日期    股票代码      开盘      收盘      最高      最低     成交量           成交额    振幅   涨跌幅   涨跌额   换手率    adx_pos    adx_neg        adx
0    2024-01-02  002594   64.18   61.84   64.18   61.77  113928  2.212162e+09  3.79 -2.83 -1.80  0.98   0.000000   0.000000   0.000000
1    2024-01-03  002594   61.81   62.03   62.27   61.21   84486  1.624093e+09  1.71  0.31  0.19  0.73   0.000000   0.000000   0.000000
2    2024-01-04  002594   62.42   61.98   62.53   61.34   89870  1.731882e+09  1.92 -0.08 -0.05  0.77   0.000000   0.000000   0.000000
3    2024-01-05  002594   61.80   61.87   63.48   61.35  112402  2.187836e+09  3.44 -0.18 -0.11  0.97   0.000000   0.000000   0.000000
4    2024-01-08  002594   61.91   60.78   62.20   60.69   81078  1.543894e+09  2.44 -1.76 -1.09  0.70   0.000000   0.000000   0.000000
..          ...     ...     ...     ...     ...     ...     ...           ...   ...   ...   ...   ...        ...        ...        ...
378  2025-07-28  002594  111.32  111.01  111.71  110.52  117057  3.947001e+09  1.07 -0.28 -0.31  1.01  25.205620  19.053876  20.202389
379  2025-07-29  002594  112.01  111.42  112.50  109.77  387360  4.322742e+09  2.46  0.37  0.41  1.11  25.537332  17.360353  20.120903
380  2025-07-30  002594  108.05  108.70  111.16  107.12  524834  5.743879e+09  3.63 -2.44 -2.72  1.51  22.191630  23.159943  18.836205
381  2025-07-31  002594  108.20  105.24  108.20  105.00  599499  6.344901e+09  2.94 -3.18 -3.46  1.72  19.789164  26.855653  18.572874
382  2025-08-01  002594  104.95  105.80  106.24  104.36  362337  3.816660e+09  1.79  0.53  0.56  1.04  18.682433  27.257589  18.579524
        现在我们有了三个核心数据:正向指标、负向指标和ADX本体,直接看就知道趋势强弱了。
可视化更直观 用matplotlib画图,趋势一目了然:
            
            
              python
              
              
            
          
          import matplotlib.pyplot as plt
def plot_price_and_adx(price, adx, xlabel='日期'):
    # 解决中文乱码
    plt.rcParams['font.sans-serif'] = ['SimHei']
    plt.rcParams['axes.unicode_minus'] = False
    fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(12,10), sharex=True)  # 2行1列,x轴共享
    
    # 上图:收盘价
    ax1.plot(price, color='blue', label='收盘价')
    ax1.set_ylabel('价格')
    ax1.set_title('收盘价')
    ax1.grid(True)
    
    # 下图:ADX
    ax2.plot(adx, color='red', label='ADX')
    ax2.set_ylabel('ADX')
    ax2.set_xlabel(xlabel)
    ax2.set_title('ADX')
    ax2.grid(True)
    
    plt.tight_layout()  # 自动调整间距
    plt.show()
# 调用函数
plot_price_and_adx(df['收盘'], df['adx'])
        
不过光看ADX还是有点抽象,我们可以直接标记出ADX>25的强趋势区域:
            
            
              python
              
              
            
          
          import numpy as np
df['trend'] = np.where(df.adx>25, df['收盘'], np.nan)
df['trend_signal'] = np.where(df.adx>25, 1, 0)
plt.figure(figsize=(10,7))
plt.grid()
plt.plot(df['收盘'])
plt.plot(df['trend'])
plt.ylabel('Price')
plt.xlabel('Date')
plt.show()
        
 看到没有?一条线标出来,告诉你哪段时间趋势强!
怎么用在交易上? ADX只告诉你趋势强弱,不告诉你方向。我们还需要结合其它指标,比如+DI > -DI → 看多
策略代码可以这样写,算出每日收益:
            
            
              python
              
              
            
          
          df['direction'] = np.where(df.adx_pos > df.adx_neg, 1, -1) * df['trend_signal']
df['daily_returns'] = df['收盘'].pct_change()
df['strategy_returns'] = df.daily_returns.shift(-1) * df.direction
plt.figure(figsize=(10,7))
plt.grid()
plt.plot((df['strategy_returns'] + 1).cumprod())
plt.ylabel('Returns')
plt.xlabel('Date')
plt.show()
        
ADX是趋势强弱的利器,但没有万能指标,最好搭配其他指标一起用,确认信号再出手,收益更稳。
总结一下今天的ADX之旅
今天我们从手工计算ADX指标 →到Python一键实现 →再到用ADX指导交易策略,完整走了一遍流程。
💡 小结几点:
- ADX执行起来其实很简单,帮我们快速找到市场中的强趋势。
 - 虽然它有局限性(比如不告诉你趋势方向),但是配合其他指标使用,交易策略会更稳、更可靠。
 - 用Python操作,让数据处理和策略回测变得轻松又高效,再也不用靠Excel算到手抽筋😂。
 
免责声明 本文所有数据仅供参考,花姐不保证信息的完整性、准确性或时效性,也不对因使用本文信息而产生的任何损失负责。信息提供均为"原样"提供,投资有风险,入市需谨慎。