技术分析 · 趋势过滤

ADX/DMI 怎么判断「现在有没有趋势」?
当成其他策略的开关使用

2026-08-18·11 分钟阅读

你的突破策略在爆走行情里三天赚了 6%,接着市场走平两周,同一组进场条件把那 6% 又还回去,代码没改过一行。你已经在怀疑,是不是有办法让策略在盘整期间直接不进场。

ADX(Average Directional Index)本来就是为了这件事被造出来的——它是 Wilder 1978 年在《New Concepts in Technical Trading Systems》里跟 RSI 一起提出的一组指标。跟 RSI、MACD 不一样,它不是拿来当进场信号,它是拿来回答一个更前面的问题:现在这套策略该不该跑。

先讲结论:ADX 单独拿来当进场信号,几乎每个人都试过、也每个人都被骗线甩过。它的实际用途只有一种——你已经有一套会在盘整期被锯的突破策略,或者一套会在爆走时被辗的均值回归策略,用 ADX 把它们该关的时候关掉。这篇不教你怎么用 ADX 进场,只讲怎么把它接上你已经有的策略。文末给一份可以直接复制的 Pine Script 骨架,也就是 TradingView 图表上写策略逻辑用的那个脚本语言。
14
Wilder 的默认周期,1978 沿用至今
20 → 25
Wilder 原文的「盘整→有趋势」阈值
P70
加密永续建议改用的动态阈值:过去 200 根 K 线 ADX 分布的 70% 分位数

ADX 在算什么——先看机制

ADX 这条线本身没什么直观意义。要看懂它,得先看它下面那两条——`+DI` 跟 `−DI`。这三条合起来叫 DMI(Directional Movement Index),Wilder 的原设计是三条一起看,只看 ADX 反而漏掉了方向信息。

机制大略是这样。每根 K 线跟上一根比,如果今天高点比昨天高、而且高过的幅度比低点低过的幅度多,就算一个 `+DM`(正向动能);反过来就算 `−DM`。把过去 14 根 K 线的 `+DM` 加起来、除以同期的 True Range 做 smoothing,得到 `+DI`;`−DM` 同样处理得到 `−DI`。ADX 是 `+DI` 跟 `−DI` 差距的绝对值再 smoothing 一次。

结论是:ADX 只告诉你「趋势的强度」,不告诉你方向。方向要看 `+DI` 跟 `−DI` 谁在上面。三条线各自代表的东西看下面这张。

线回答的问题数值范围
+DI多方力道有多强0–100,通常 10–40
−DI空方力道有多强0–100,通常 10–40
ADX多空之中优势方的强度(不含方向)0–100,通常 10–60

来源:Pine Script v6 语言参考手册的 ta.dmi 说明;Wilder 1978《New Concepts in Technical Trading Systems》原始定义(查证日期 2026 年 8 月)。

默认周期 14 是 Wilder 自己的原始参数,之后被大部分平台沿用。TradingView 的 ta.dmi() 就是这样:ta.dmi(14, 14),第一个 14 是 DI 的长度、第二个 14 是 ADX 的 smoothing 长度。这两个参数可以分开调,但没有先实测就不要动——大多数讨论 ADX 阈值的资料都是默认 14 为前提写的,动了之后那些阈值就不再对得起来。

为什么 ADX 不能单独拿来进场

只看 ADX 这条线很容易得出「ADX > 25 就是有趋势,我进场」这种规则,然后在实盘上被打回来。原因就在上面那张表的第三栏——ADX 没有方向。ADX 从 15 爬到 30,可能是价格在往上走的趋势在强化,也可能是在往下崩的趋势在强化,只看那条线分不出来。

第二个问题是延迟。ADX 是两层 smoothing 叠出来的(先 smooth DM/TR、再 smooth 差距的绝对值),等它爬到 25 以上、你判断「有趋势」的时候,价格已经走了一段。用它当进场信号,你永远是在趋势的中后段才上车。

这里是认识论免责
「ADX 中后段才亮」这件事是从两层 smoothing 的定义推导出来的必然结果,不是 Wilder 原文有一句话直接这样讲。你自己在图表上把 ta.dmi(14, 14) 叠到任何一段明显的趋势上都会看到这个延迟,但这是「机制推论 + 图表观察」,不是官方白纸黑字的一句话。

所以拿 ADX 进场的做法——不管是「ADX 突破 20」还是「+DI 上穿 −DI」——你都得先接受两件事:进的是趋势的中后段,而且会被骗线常态性地扫出去。这条路不是没有人走通,但走通的人几乎都在 ADX 之外还叠了别的过滤条件(时间框架、成交量、突破的 wick)。ADX 单独扛不起进场这件事。

那 ADX 到底该多少算「有趋势」

Wilder 原文其实只给两个阈值:ADX 低于 20 没有明确趋势、高于 25 有明确趋势——StockCharts 的 ChartSchool 就是这样引 Wilder。常见教材再往上补几段更细的分级(25–50 明确趋势、50 以上极强趋势)方便对应直觉,但那是后来的通用写法,Wilder 原书没有把 50 这个数字写进去。这两个原始阈值被沿用了将近 50 年,大部分教科书讲「有没有趋势」时用的就是这一组。

但这组数字是他在 1970 年代的商品与外汇市场上观察出来的。日内加密永续是另一个世界:波动大、假突破多、24 小时交易没有收盘缺口,同样的 14 周期在 5 分钟图上,ADX 几乎不会低于 20。硬套 25 的阈值等于整天都在「有趋势」,开关等于没开。

这份阈值是快照,不是永久保证
下面这张表是我在 BTCUSDT.P 上把 ta.dmi(14, 14) 叠到不同时间框架、看过去 90 天 ADX 分布拉出来的参考数字(查证日期 2026 年 8 月)。实际做开关前建议你自己去图表上叠一次——不同标的、不同期间会漂移,尤其是牛熊转换时整体数值会整段上移或下移。
时间框架ADX 大致中位数当开关的参考阈值
5m22–28看 90 天分布的 70% 分位数
1H18–2420–22
4H16–2220–25(比较接近 Wilder 原文)
1D15–2025(接近 Wilder 原文)

以 BTCUSDT.P 过去 90 天为例的观察数值(查证日期 2026 年 8 月)。这是快照不是保证,实际套用前自己在图表上验一次。

比起硬记数字,更耐用的做法是用分位数而不是绝对值——ADX 大于「过去 200 根 K 线 ADX 分布的 70% 分位数」时算有趋势。这样一来,同一份 Pine Script 可以跨时间框架跟不同标的用,不用每换一个对就手动改阈值。

把它接上策略:两种叠法

趋势类
ADX 高开、低关(突破、动能、CTA)
均值回归类
ADX 低开、高关(布林通道回归、支撑反弹)
同一条 ADX
两种方向相反,理论上轮班上场

ADX 当开关的核心逻辑只有一种,方向两种:趋势类策略在 ADX 高的时候开、在 ADX 低的时候关;均值回归类策略反过来。这件事的直觉很简单,但写进策略时要对的字段不少。

趋势类(突破、动能)均值回归类(布林通道回归)
ADX 开关条件ADX > 阈值时进场ADX < 阈值时进场
在盘整期表现被锯来锯去、胜率掉本来就是它的主场
在爆走期表现本来就是它的主场会被辗——回归假设失效
加上 ADX 开关后的效果盘整期少进场、少亏爆走期少进场、少亏
开关关掉时要不要平仓现有仓位看 stop,不强制平强烈建议平——回归假设没了

最后那一列是最容易漏的:「开关关掉」跟「平仓」不是同一件事。开关关掉只挡住新单,已经在场内的仓位怎么处理是另一个决策。趋势策略靠自己的 stop 出场没问题;均值回归策略如果进场后 ADX 从 15 飙到 35,代表你当初赌的「会回来」的前提已经被打破,这时候比较安全的做法是主动平掉,不要等 stop。

用 Pine Script 把这个开关写出来

下面是最小可用的骨架。这段本身不能直接拿去回测,因为进场逻辑留白了——重点是那个 regimeOn 变量怎么算、怎么跟你的进场条件相接。你把 longSig 跟 shortSig 换成自己的逻辑就是一个完整策略。

pine
//@version=6
        strategy("Trend Strategy with ADX Regime Filter",
             overlay          = true,
             default_qty_type = strategy.percent_of_equity,
             default_qty_value= 10,
             initial_capital  = 10000)

        // ─── ADX 参数 ───
        diLen       = input.int(14,   "DI Length")
        adxLen      = input.int(14,   "ADX Smoothing")
        useAdaptive = input.bool(true,"用分位数而不是固定阈值")
        fixedThr    = input.float(20, "固定阈值(useAdaptive=false 时用)")
        pctLookback = input.int(200,  "分位数回看根数")
        pctRank     = input.float(70, "分位数(%)")

        // ─── DMI + 开关 ───
        [diPlus, diMinus, adx] = ta.dmi(diLen, adxLen)
        adaptiveThr = ta.percentile_linear_interpolation(adx, pctLookback, pctRank)
        threshold   = useAdaptive ? adaptiveThr : fixedThr
        regimeOn    = adx > threshold

三个地方值得展开一下。ta.dmi() 一次回传三个值——`+DI`、`−DI`、ADX——用数组解构接。ta.percentile_linear_interpolation(adx, 200, 70) 是 Pine v5 之后才有的内建函数,直接算「过去 200 根 K 线 ADX 分布的 70% 分位数」,不用自己排序。useAdaptive 那个开关留着是为了让你能拿分位数版跟固定阈值版做同一段回测的 A/B——不然争论哪种好比较没有意义。

上面只算了开关本身。下面示范怎么把它接到你自己的进场条件——这里用最基础的 Donchian 突破当范例,你把 longSig 跟 shortSig 换成自己的就是一个完整策略。

pine
// 承上,接续同一份 strategy() 底下

        // ─── 进场条件(换成你自己的) ───
        lookback = 20
        hh       = ta.highest(high, lookback)[1]
        ll       = ta.lowest(low,  lookback)[1]
        longSig  = close > hh
        shortSig = close < ll

        // ─── 只有开关开启才进场 ───
        if regimeOn and longSig
            strategy.entry("Long",  strategy.long)
        if regimeOn and shortSig
            strategy.entry("Short", strategy.short)

        // 图表视觉化:开关开启时背景绿色
        bgcolor(regimeOn ? color.new(color.green, 90) : na, title = "Regime On")

`[1]` 那个位移是刻意的——`ta.highest(high, 20)[1]` 是「不包含这根 K 线」的过去 20 根最高点,避免用未来数据算进场条件(`repainting` 的其中一种)。这是Pine Script 自动化陷阱里最经典的一种。

这段代码没回测过
这段是骨架,我没有实际跑回测。加上 ADX 开关之后回测数字往上跳是常态,但实盘跟不上也是常态——原因是回测期间如果刚好牛熊分明,开关关掉的那些盘整期天生就是亏钱期,回测数字自然漂亮,但那不代表下一段实盘的盘整期会被同样的开关挡掉。实盘与回测落差请看 Webhook 仓位大小 那篇的第一节。

用 ADX 当开关最常见的两个错误

这两个错误几乎是新做滤网的人一定踩的,都不会报错,也都不会在回测数字上明显露馅。

1
你的策略跑在 5 分钟图,但 ADX 条件是判断 4 小时图的 ADX > 25?
是 →时间框架错配。4H ADX 一根往上动,可能对应 5m 图上的 48 根,但 4H ADX > 25 那个瞬间 5m 早就已经开始钝化了。要跨时间框架就用 request.security——它的 lookahead 从 v3 起默认就是 barmerge.lookahead_off(安全值),只要你没有显式传 barmerge.lookahead_on,回测就不会偷用未来数据。
否,我在同一个时间框架上算 ADX →下一题。
2
加上 ADX 开关后,你把回测期间从一年拉到三年,数字还是同样漂亮吗?
一年很漂亮、三年就崩 →过拟合的味道很重。ADX 开关的阈值如果是「一年前一年后最佳化」跑出来的,本质上就是曲线拟合。用分位数(percentile_linear_interpolation)而不是固定阈值可以缓解,但不能根治——最好的做法是先做 walk-forward,不要一次跑全部历史。
三年也还可以,但实盘跟不上 →看下一个 Callout,多半是牛熊期分布跟未来不一样导致的偏差。
一年、三年都很漂亮而且实盘也对得起来 →那就上小资金试三个月,实测比什么都准。

第一个错误——时间框架错配——写错不会编译不过,只会回测跟实盘对不上。第二个错误更阴险,它会在回测期间「安静地变好」:你在 2022 年到 2024 年的数据上把阈值调到 22,看起来很棒;但 2022 年整年是明显的下行趋势,那组阈值本质上是为那段行情调的,换到 2025 年横盘三个月的时候就完全失效。

诚实的一段:ADX 过滤不了的东西

ADX 这个工具再怎么用,也有几件事它结构上就做不到。这里先列出来,免得你花时间去试——我们自己也试过,答案是不行。

  • 短促的 news spike。Wilder 的 ADX 有两层 smoothing,反应速度天生就慢。CPI 公布后 3 根 K 线内的爆走,等 ADX 亮起来已经回吐一半了。
  • 低波动的缓慢趋势。价格慢慢涨、但每根 K 线振幅都很小,True Range 小、DM 也小,ADX 可以整段爬升期间都在 15 以下。这种行情 ADX 开关会全程关着,你会错过它。
  • 「假趋势」的头尾。ADX 从 15 爬到 22 的过程你分不出是真趋势在启动、还是假突破在制造动能。要靠 ADX 确认就得等 25 以上,那时候通常已经接近趋势中段。
  • 「这是涨的趋势还是跌的趋势」。ADX 没有方向。要方向就得看 `+DI` 跟 `−DI` 谁在上,或者搭 均线 之类的方向判断。

任何滤网——不只 ADX——都是在「少进亏单」跟「错过真信号」之间做选择,你把滤网收紧就一定会漏。ADX 滤网收得越紧,漏的越明显的是那种「安静的多头趋势」,价格默默上去但 ADX 从来没破 20 过。这种型态在牛市末段特别多,是这个工具结构上的盲点。

常见问题

ADX 的周期 14 可以改吗?
可以,但改之后你在教科书上、讨论区上看到的「25 是趋势阈值」这类数字就不再对得起来了。想拿短一点(例如 7)的通常是希望 ADX 反应快一点,代价是变得更嘈杂——会在盘整期频繁误触开关开启。想拿长一点(例如 21)的通常是希望滤掉短促的假突破,代价是进场更慢。没有实测就先留在 14。
ADX 跟 RSI、MACD 可以叠在一起用吗?
可以,而且很常见。但要分清楚它们各自在回答什么:RSI 讲超买超卖(动能相对强弱)、MACD 讲两条均线的关系(趋势方向 + 动能),ADX 只讲强度。三个叠在一起如果都要满足才进场,信号会变得非常稀有——这通常代表你多滤了几层之后回测数字漂亮,但实盘几乎不下单。加滤网之前先想一下:这个滤网要滤的是哪一种假信号?
TradingView Alert 要不要把 ADX 的当前值传出去?
没必要,除非你下游还要用到那个数字。ADX 开关是「进场条件」的一部分——当 regimeOn 为 false 时 Pine Script 根本不会触发 strategy.entry,也就不会发出 alert。传出去反而增加 payload 大小跟解析工作量。真的要 debug 有没有触发,用 plotshape 在图表上标开关转态的时刻比较直接。
回测绩效加了 ADX 开关明显变好,可以直接上实盘吗?
先问自己一个问题:你的阈值是不是就是在这段历史上调出来的?如果是,那个「变好」大概率是曲线拟合。至少要做两件事——第一,把数据切成 in-sample / out-of-sample,只在 in-sample 调参数、out-of-sample 验证;第二,用分位数而不是固定阈值,减少对特定期间 ADX 绝对值的依赖。做完这两件事之后如果实盘还是跟不上,那可能就是市场结构已经跟你调参那段不一样了。
把 ADX 开关关掉的那些期间,我的资金应该做什么?
这个问题没有一体适用的答案,取决于你有几套策略。只有一套趋势策略时,开关关掉就是空手等——资金放在稳定币或活存都行。有一套趋势 + 一套均值回归时,两套用同一条 ADX 但方向相反,理论上会轮班上场。单套策略想避开空手期,就只能接受它跑在盘整期会亏一些,或者接受它偶尔错过真趋势——这两者取一,没有第三个选项。

Get started

想把今天学到的东西自动化跑起来?

把加了 ADX 开关的 Pine 策略接上 webhook,交给 TVSBot——用你自己的交易所 API key、先 dry-run 验开关真的有关掉、风控自己设。

免费开始使用