AlphaFeed Blog

如何用 Python 计算 ADX 判断趋势强弱?(区分趋势市与震荡市)

分析与指标 · 代码验证于 2026-09-13

如何用 Python 计算 ADX 判断趋势强弱?(区分趋势市与震荡市)

一句话回答: ADX(平均趋向指数)度量的是”趋势有多强”而不是方向:ADX 越高趋势越明确,越低越像震荡。用 AlphaFeed 取复权日线,用 Wilder 口径算出 +DI、-DI、ADX,就能用「ADX≥25 视为强趋势、+DI/-DI 谁大定多空」几十行判断当前是趋势市还是震荡市,让均线/突破类策略只在有趋势时出手。

为什么会有这个问题 / 传统做法的痛点

均线金叉、突破这类顺势策略在震荡市会被反复打脸(假突破、来回止损)。问题的根源是”没先判断有没有趋势”。ADX 正是为此设计:

难点是 ADX 计算链条较长(TR→ATR→DM→DI→DX→ADX)且要用 Wilder 平滑,手写容易错。数据则需要干净的复权 OHLC——正是 AlphaFeed 的强项。

分步骤解决(每步配可运行代码)

第 1 步:取复权日线

from alphafeed import AlphaFeed
import numpy as np
import pandas as pd

af = AlphaFeed(api_key="your-api-key")  # 或 export ALPHAFEED_API_KEY 后 AlphaFeed()

df = af.klines.get("000001.SZ", period="1d", count=250,
                   adjust="forward", to_dataframe=True)
df = df.sort_values("trade_date").reset_index(drop=True)

第 2 步:Wilder 口径计算 +DI / -DI / ADX

def adx(df, n=14):
    high, low, close = df["high"], df["low"], df["close"]
    up = high.diff()
    down = -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([(high - low),
                    (high - close.shift()).abs(),
                    (low - close.shift()).abs()], axis=1).max(axis=1)
    atr = tr.ewm(alpha=1 / n, adjust=False).mean()           # Wilder ATR
    plus_di = 100 * pd.Series(plus_dm, index=df.index).ewm(alpha=1/n, adjust=False).mean() / atr
    minus_di = 100 * pd.Series(minus_dm, index=df.index).ewm(alpha=1/n, adjust=False).mean() / atr
    dx = 100 * (plus_di - minus_di).abs() / (plus_di + minus_di).replace(0.0, np.nan)
    adx_ = dx.ewm(alpha=1 / n, adjust=False).mean()
    out = df.copy()
    out["plus_di"], out["minus_di"], out["adx"] = plus_di, minus_di, adx_
    return out

ADX 计算链条与含义:

步骤公式要点含义
TRmax(高-低,高-昨收
ATRTR 的 Wilder 平滑波动基准
+DM/-DM方向性动量上/下方向强度
+DI/-DI100×DM 平滑/ATR多空方向指标
ADX|+DI−−DI|/(+DI+−DI) 的平滑趋势强度(无方向)

第 3 步:判断趋势 / 震荡与多空

def adx_state(symbol, count=250, n=14):
    df = af.klines.get(symbol, period="1d", count=count,
                       adjust="forward", to_dataframe=True).sort_values("trade_date").reset_index(drop=True)
    last = adx(df, n).iloc[-1]
    a = float(last["adx"])
    state = "强趋势" if a >= 25 else ("弱趋势/震荡" if a >= 20 else "震荡")
    direction = "多头" if last["plus_di"] > last["minus_di"] else "空头"
    return {"adx": round(a, 1),
            "plus_di": round(float(last["plus_di"]), 1),
            "minus_di": round(float(last["minus_di"]), 1),
            "state": state, "direction": direction}

print(adx_state("000001.SZ"))
# 例如:{'adx': 35.7, 'plus_di': 24.9, 'minus_di': 11.7, 'state': '强趋势', 'direction': '多头'}
print(adx_state("600519.SH"))
# 例如:{'adx': 14.2, 'plus_di': 20.6, 'minus_di': 26.4, 'state': '震荡', 'direction': '空头'}

上例实测:平安银行 ADX≈35.7 且 +DI>-DI → 强趋势·多头;茅台 ADX≈14.2 → 震荡(此时不宜用顺势突破策略)。这正是 ADX 的用法:先过滤市场状态,再决定用哪类策略。

关键坑与注意事项

常见问题(FAQ)

Q:ADX 多少算有趋势? A:经验上 ADX≥25 视为趋势明确、<20 视为震荡,20~25 为过渡。这是习惯阈值,建议用你的标的回测校准。

Q:ADX 能告诉我涨还是跌吗? A:不能。ADX 只测强弱,方向看 +DI 与 -DI 谁大:+DI>-DI 偏多,反之偏空。

Q:ADX 怎么和其他策略配合? A:常见做法是”ADX 做过滤器”——只在 ADX≥25 时启用均线/突破等顺势策略,在震荡时改用均值回归或空仓,减少假突破损失。

Q:需要付费吗? A:单只标的的日线在免费额度内即可试算;批量/全市场需 Starter 及以上,详见 AlphaFeed 定价页。

小结

ADX 的价值在于”先判断有没有趋势,再决定用什么策略”:ADX 测强弱、DI 定方向。计算链条长且要用 Wilder 平滑,容易写错,数据也要干净复权。AlphaFeed 提供口径一致的复权日线,让你几十行就能给策略加一个”市场状态过滤器”,少在震荡市里挨打。

相关链接:


免责声明:本文仅为技术与数据接口使用示例,代码与策略仅供学习研究,不构成投资建议。市场有风险,历史数据不代表未来表现。