ADX/DMI 怎麼判斷「現在有沒有趨勢」?
當成其他策略的開關使用
你的突破策略在爆走行情裡三天賺了 6%,接著市場走平兩週,同一組進場條件把那 6% 又還回去,程式碼沒改過一行。你已經在懷疑,是不是有辦法讓策略在盤整期間直接不進場。
ADX(Average Directional Index)本來就是為了這件事被造出來的——它是 Wilder 1978 年在《New Concepts in Technical Trading Systems》裡跟 RSI 一起提出的一組指標。跟 RSI、MACD 不一樣,它不是拿來當進場訊號,它是拿來回答一個更前面的問題:現在這套策略該不該跑。
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 以上、你判斷「有趨勢」的時候,價格已經走了一段。用它當進場訊號,你永遠是在趨勢的中後段才上車。
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 的閾值等於整天都在「有趨勢」,開關等於沒開。
ta.dmi(14, 14) 疊到不同時間框架、看過去 90 天 ADX 分布拉出來的參考數字(查證日期 2026 年 8 月)。實際做開關前建議你自己去圖表上疊一次——不同標的、不同期間會漂移,尤其是牛熊轉換時整體數值會整段上移或下移。| 時間框架 | ADX 大致中位數 | 當開關的參考閾值 |
|---|---|---|
| 5m | 22–28 | 看 90 天分布的 70% 分位數 |
| 1H | 18–24 | 20–22 |
| 4H | 16–22 | 20–25(比較接近 Wilder 原文) |
| 1D | 15–20 | 25(接近 Wilder 原文) |
以 BTCUSDT.P 過去 90 天為例的觀察數值(查證日期 2026 年 8 月)。這是快照不是保證,實際套用前自己在圖表上驗一次。
比起硬記數字,更耐用的做法是用分位數而不是絕對值——ADX 大於「過去 200 根 K 棒 ADX 分布的 70% 分位數」時算有趨勢。這樣一來,同一份 Pine Script 可以跨時間框架跟不同標的用,不用每換一個對就手動改閾值。
把它接上策略:兩種疊法
ADX 當開關的核心邏輯只有一種,方向兩種:趨勢類策略在 ADX 高的時候開、在 ADX 低的時候關;均值回歸類策略反過來。這件事的直覺很簡單,但寫進策略時要對的欄位不少。
| 趨勢類(突破、動能) | 均值回歸類(布林通道回歸) | ||
|---|---|---|---|
| ADX 開關條件 | ADX > 閾值時進場 | ADX < 閾值時進場 | |
| 在盤整期表現 | 被鋸來鋸去、勝率掉 | 本來就是它的主場 | |
| 在爆走期表現 | 本來就是它的主場 | 會被輾——回歸假設失效 | |
| 加上 ADX 開關後的效果 | 盤整期少進場、少虧 | 爆走期少進場、少虧 | |
| 開關關掉時要不要平倉現有部位 | 看 stop,不強制平 | 強烈建議平——回歸假設沒了 |
最後那一列是最容易漏的:「開關關掉」跟「平倉」不是同一件事。開關關掉只擋住新單,已經在場內的部位怎麼處理是另一個決策。趨勢策略靠自己的 stop 出場沒問題;均值回歸策略如果進場後 ADX 從 15 飆到 35,代表你當初賭的「會回來」的前提已經被打破,這時候比較安全的做法是主動平掉,不要等 stop。
用 Pine Script 把這個開關寫出來
下面是最小可用的骨架。這段本身不能直接拿去回測,因為進場邏輯留白了——重點是那個 regimeOn 變數怎麼算、怎麼跟你的進場條件相接。你把 longSig 跟 shortSig 換成自己的邏輯就是一個完整策略。
//@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 換成自己的就是一個完整策略。
// 承上,接續同一份 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 當開關最常見的兩個錯誤
這兩個錯誤幾乎是新做濾網的人一定踩的,都不會報錯,也都不會在回測數字上明顯露餡。
request.security——它的 lookahead 從 v3 起預設就是 barmerge.lookahead_off(安全值),只要你沒有顯式傳 barmerge.lookahead_on,回測就不會偷用未來資料。percentile_linear_interpolation)而不是固定閾值可以緩解,但不能根治——最好的做法是先做 walk-forward,不要一次跑全部歷史。第一個錯誤——時間框架錯配——寫錯不會編譯不過,只會回測跟實盤對不上。第二個錯誤更陰險,它會在回測期間「安靜地變好」:你在 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 可以改嗎?
ADX 跟 RSI、MACD 可以疊在一起用嗎?
TradingView Alert 要不要把 ADX 的當前值傳出去?
regimeOn 為 false 時 Pine Script 根本不會觸發 strategy.entry,也就不會發出 alert。傳出去反而增加 payload 大小跟解析工作量。真的要 debug 有沒有觸發,用 plotshape 在圖表上標開關轉態的時刻比較直接。回測績效加了 ADX 開關明顯變好,可以直接上實盤嗎?
把 ADX 開關關掉的那些期間,我的資金應該做什麼?
Get started
把加了 ADX 開關的 Pine 策略接上 webhook,交給 TVSBot——用你自己的交易所 API key、先 dry-run 驗開關真的有關掉、風控自己設。
免費開始使用