欢迎光临
我们一直在努力

Momentum:DM(动向指标)技术指标详解

Momentum:DM(动向指标)技术指标详解

一、DM的定义

DM(Directional Movement,动向指标) 是J. Welles Wilder于1978年在《技术交易系统新概念》一书中提出的趋向分析系统(Directional Movement System) 的核心基础概念。

核心思想

DM的本质是量化价格向某一方向运动的“有效力度”。它不是简单地看涨跌了多少,而是通过比较每一根K线突破前一个K线价格范围的能力,来判断当前是买方还是卖方主导了市场的新动向。

为了排除日内无意义的随机波动,DM只提取能够“创新高”或“创新低”的部分作为有效动向。

DM的核心特征

特征说明
组成部分 +DM(上升动向)和 -DM(下降动向),两者通常成对出现
数值范围 非负值,通常取平滑后的均值进行计算
数学含义 正DM代表买方将价格推至新高的“力量”;负DM代表卖方将价格压至新低的“力量”
主要用途 作为DMI指标(ADX、+DI、-DI) 的基础数据

DM与DMI/ADX的关系

DM是DMI指标体系的原始数据层:

层级指标计算基础
第一层(原始数据) +DM / -DM 价格突破的原始力度
第二层(方向指标) +DI / -DI DM除以ATR(真实波幅),反映相对趋势强度
第三层(趋势强度) ADX / DX 基于DI差值计算,衡量趋势有无

二、DM的计算方法

1. 核心公式

首先计算两个中间变量——上升动向值

U

p

M

o

v

e

\\mathrm{UpMove}

UpMove 和下降动向值

D

o

w

n

M

o

v

e

\\mathrm{DownMove}

DownMove

U

p

M

o

v

e

t

=

H

i

g

h

t

H

i

g

h

t

1

D

o

w

n

M

o

v

e

t

=

L

o

w

t

1

H

i

g

h

t

\\begin{aligned} \\mathrm{UpMove}_t &= \\mathrm{High}_t – \\mathrm{High}_{t-1} \\\\ \\mathrm{DownMove}_t &= \\mathrm{Low}_{t-1} – \\mathrm{High}_{t} \\end{aligned}

UpMovetDownMovet=HightHight1=Lowt1Hight 其中

H

i

g

h

t

\\mathrm{High}_t

Hight

L

o

w

t

\\mathrm{Low}_t

Lowt 分别为

t

t

t 期的最高价和最低价。

2. 原始+DM与-DM的判定规则

根据

U

p

M

o

v

e

\\mathrm{UpMove}

UpMove

D

o

w

n

M

o

v

e

\\mathrm{DownMove}

DownMove 的比较结果,判定原始

+

D

M

r

a

w

+\\mathrm{DM}_\\mathrm{raw}

+DMraw

D

M

r

a

w

-\\mathrm{DM}_\\mathrm{raw}

DMraw

+

D

M

r

a

w

,

t

=

{

U

p

M

o

v

e

t

若 

U

p

M

o

v

e

t

>

D

o

w

n

M

o

v

e

t

 且 

U

p

M

o

v

e

t

>

0

0

其他情况

+\\mathrm{DM}_{\\mathrm{raw},t} = \\begin{cases} \\mathrm{UpMove}_t & \\text{若 } \\mathrm{UpMove}_t > \\mathrm{DownMove}_t \\text{ 且 } \\mathrm{UpMove}_t > 0 \\\\ 0 & \\text{其他情况} \\end{cases}

+DMraw,t={UpMovet0 UpMovet>DownMovet  UpMovet>0其他情况

D

M

r

a

w

,

t

=

{

D

o

w

n

M

o

v

e

t

若 

D

o

w

n

M

o

v

e

t

>

U

p

M

o

v

e

t

 且 

D

o

w

n

M

o

v

e

t

>

0

0

其他情况

-\\mathrm{DM}_{\\mathrm{raw},t} = \\begin{cases} \\mathrm{DownMove}_t & \\text{若 } \\mathrm{DownMove}_t > \\mathrm{UpMove}_t \\text{ 且 } \\mathrm{DownMove}_t > 0 \\\\ 0 & \\text{其他情况} \\end{cases}

DMraw,t={DownMovet0 DownMovet>UpMovet  DownMovet>0其他情况 判定规则的通俗解释:

市场状态UpMoveDownMove+DM_raw-DM_raw
上升动向 >0 且 > DownMove = UpMove 0
下降动向 >0 且 > UpMove 0 = DownMove
内移日(无动向) ≤0 ≤0 0 0
两力均衡 = DownMove >0 = UpMove >0 0 0

3. 平滑处理(pandas_ta的实现)

原始

+

D

M

r

a

w

+\\mathrm{DM}_\\mathrm{raw}

+DMraw

D

M

r

a

w

-\\mathrm{DM}_\\mathrm{raw}

DMraw 波动较大,通常需要进行平滑。pandas_ta默认使用RMA(Wilder平滑移动平均) 对原始值进行平滑:

+

D

M

t

=

R

M

A

(

+

D

M

r

a

w

,

l

e

n

g

t

h

)

t

D

M

t

=

R

M

A

(

D

M

r

a

w

,

l

e

n

g

t

h

)

t

\\begin{aligned} +\\mathrm{DM}_t &= \\mathrm{RMA}(+\\mathrm{DM}_\\mathrm{raw}, \\mathrm{length})_t \\\\ -\\mathrm{DM}_t &= \\mathrm{RMA}(-\\mathrm{DM}_\\mathrm{raw}, \\mathrm{length})_t \\\\ \\end{aligned}

+DMtDMt=RMA(+DMraw,length)t=RMA(DMraw,length)t 其中length为计算周期(默认14),RMA的递推公式为:

R

M

A

t

=

R

M

A

t

1

×

(

l

e

n

g

t

h

1

)

+

V

a

l

u

e

t

l

e

n

g

t

h

\\mathrm{RMA}_t = \\frac{\\mathrm{RMA}_{t-1} \\times(\\mathrm{length} – 1) + \\mathrm{Value}_t}{\\mathrm{length}}

RMAt=lengthRMAt1×(length1)+Valuet

4. 参数说明

参数默认值说明
length 14 平滑周期(Wilder推荐值)
mamode “rma” 移动平均类型(默认使用Wilder平滑)
drift 1 计算差值的周期步长,通常为1

5. 计算示例

假设某股票连续两日的数据如下:

项目昨日今日
最高价 100 105
最低价 98 102

计算过程:

  • 计算UpMove和DownMove:
    • UpMove = 105 – 100 = 5
    • DownMove = 98 – 102 = -4(≤0,取0)
  • 判定原始DM:
    • UpMove > DownMove 且 UpMove > 0 → +DM_raw = 5,-DM_raw = 0
  • 注意:当UpMove和DownMove均为正值且不相等时,取较大者作为DM,较小者归零。


    三、DM的使用方法

    DM通常不单独使用,而是作为DMI指标的基础数据。以下是最常见的应用方法:

    1. 计算方向线DI(核心应用)

    DM平滑后,除以ATR(真实波幅)得到方向线DI:

    +

    D

    I

    =

    100

    ×

    +

    D

    M

    (

    平滑

    )

    A

    T

    R

    D

    I

    =

    100

    ×

    D

    M

    (

    平滑

    )

    A

    T

    R

    \\begin{aligned} +\\mathrm{DI} = 100 \\times \\frac{+\\mathrm{DM}(平滑)}{\\mathrm{ATR}} \\\\[1.5ex] -\\mathrm{DI} = 100 \\times \\frac{-\\mathrm{DM}(平滑)}{\\mathrm{ATR}} \\\\ \\end{aligned}

    +DI=100×ATR+DM(平滑)DI=100×ATRDM(平滑)

    • +DI 数值越高,表示在给定的平滑周期内,上升力量越强
    • -DI 数值越高,表示在给定的平滑周期内,下降力量越强

    2. DI交叉信号(买入/卖出信号)

    信号触发条件含义
    买入(金叉) +DI从下方上穿-DI 上升力量开始主导市场
    卖出(死叉) +DI从上方下穿-DI 下降力量开始主导市场

    3. 计算ADX(趋势强度)

    基于+DI和-DI计算DX,再平滑得到ADX: $$ \\begin{aligned} \\mathrm{DX}_t &= \\left| \\frac{(+\\mathrm{DI}_t) – (-\\mathrm{DI}_t)}{(+\\mathrm{DI}_t) + (-\\mathrm{DI}_t)} \\right| \\times 100 \\[1.5ex]

    \\mathrm{ADX}_t &= \\mathrm{RMA}(\\mathrm{DX}, \\mathrm{length})_t \\end{aligned} $$ ADX的数值范围0-100,用于判断趋势强度:

    ADX区间趋势状态操作策略
    < 20 无趋势 / 盘整 观望,不宜使用趋势策略
    20 – 25 趋势形成中 可关注DI交叉信号
    25 – 50 强趋势 顺势操作,DI交叉信号可信度高
    > 50 极端趋势 注意趋势可能过度延伸

    4. 与ATR的关系

    DM必须与ATR(真实波幅均值) 结合才能转化为+DI和-DI。ATR的计算公式为:

    T

    R

    t

    =

    max

    (

    H

    i

    g

    h

    t

    L

    o

    w

    t

    ,

    H

    i

    g

    h

    t

    C

    l

    o

    s

    e

    t

    1

    ,

    L

    o

    w

    t

    C

    l

    o

    s

    e

    t

    1

    )

    A

    T

    R

    t

    =

    A

    T

    R

    t

    1

    ×

    (

    l

    e

    n

    g

    t

    h

    1

    )

    +

    T

    R

    t

    l

    e

    n

    g

    t

    h

    \\begin{aligned} \\mathrm{TR}_t &= \\max(\\mathrm{High}_t – \\mathrm{Low}_t, |\\mathrm{High}_t – \\mathrm{Close}_{t-1}|,|\\mathrm{Low}_t – \\mathrm{Close}_{t-1}|) \\\\ \\mathrm{ATR}_t &= \\frac{\\mathrm{ATR}_{t-1} \\times (\\mathrm{length} – 1) + \\mathrm{TR}_t}{\\mathrm{length}} \\end{aligned}

    TRtATRt=max(HightLowt,HightCloset1,LowtCloset1)=lengthATRt1×(length1)+TRt

    • 当ATR很小、DM很大时,DI值会很大,代表趋势非常强劲
    • 当ATR很大、DM很小时,DI值会很小,代表当前方向虽在延续但力度不足

    5. 注意事项与局限性

    使用DM/DMI前需了解以下要点:

    局限性说明
    本质是滞后指标 DM经过两次平滑处理,对价格变化反应较慢,属于中长线指标,不适合短线交易
    震荡市表现差 盘整行情中DI线反复交叉,信号频繁失效
    不单独使用 DM原始值必须与ATR结合转化为DI才能用于交易决策
    参数固定 Wilder原始设定的14周期通常不建议随意改动

    四、使用pandas_ta计算DM(附示例代码)

    1. pandas_ta中的DM函数

    pandas_ta库内置了DM指标的完整实现,函数位于momentum模块中。

    2. 函数完整参数

    pandas_ta.momentum.dm(high, low, length=None, mamode=None, talib=None, drift=None, offset=None, **kwargs)

    参数详解:

    参数类型默认值说明
    high pd.Series 必需 最高价序列
    low pd.Series 必需 最低价序列
    length int 14 平滑周期(Wilder推荐)
    mamode str “rma” 移动平均类型(默认使用Wilder平滑)
    talib bool True 若安装TA-Lib则使用其版本
    drift int 1 价格变化的步长
    offset int 0 结果偏移周期数

    返回值:pd.DataFrame——包含两列:DMP_{length}(+DM)和DMN_{length}(-DM)。

    3. 示例代码

    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='2024-01-01', end='2024-12-31', freq='D')
    n = len(dates)

    # 生成带趋势和周期波动的价格序列
    trend = np.linspace(0, 25, n)
    cycle = np.sin(np.linspace(0, 6 * np.pi, n)) * 10
    noise = np.random.randn(n) * 2
    price_series = 100 + trend + cycle + noise

    df = pd.DataFrame(index=dates)
    df['close'] = price_series
    df['high'] = df['close'] + np.abs(np.random.randn(n)) * 2 + 1
    df['low'] = df['close'] np.abs(np.random.randn(n)) * 2 1
    df['volume'] = np.random.randint(1000000, 10000000, n)

    print("=" * 60)
    print("数据预览:")
    print(df.head())
    print("\\n" + "=" * 60 + "\\n")

    # ========== 第二步:计算DM(基础用法) ==========
    # 使用默认参数 length=14, mamode='rma'
    dm_df = ta.dm(df['high'], df['low'])

    # 将计算结果添加到原DataFrame
    df['DMP'] = dm_df['DMP_14']
    df['DMN'] = dm_df['DMN_14']

    print("DM计算结果(最近10行):")
    print(df[['high', 'low', 'DMP', 'DMN']].tail(10))
    print("\\n" + "=" * 60 + "\\n")

    # ========== 第三步:手动验证DM计算 ==========
    def manual_dm(high, low, length=14):
    """手动计算DM验证pandas_ta结果"""
    # 计算UpMove和DownMove
    up_move = high high.shift(1)
    down_move = low.shift(1) low

    # 判定原始DM
    pos_raw = pd.Series(0, index=high.index)
    neg_raw = pd.Series(0, index=high.index)

    # 上升动向条件:up_move > down_move and up_move > 0
    condition_pos = (up_move > down_move) & (up_move > 0)
    pos_raw[condition_pos] = up_move[condition_pos]

    # 下降动向条件:down_move > up_move and down_move > 0
    condition_neg = (down_move > up_move) & (down_move > 0)
    neg_raw[condition_neg] = down_move[condition_neg]

    # 使用RMA平滑
    def rma(series, length):
    alpha = 1 / length
    rma_values = series.ewm(alpha=alpha, adjust=False).mean()
    return rma_values

    pos_smoothed = rma(pos_raw, length)
    neg_smoothed = rma(neg_raw, length)

    return pos_smoothed, neg_smoothed

    df['DMP_manual'], df['DMN_manual'] = manual_dm(df['high'], df['low'])

    # 验证一致性
    diff_pos = (df['DMP'] df['DMP_manual']).abs().max()
    diff_neg = (df['DMN'] df['DMN_manual']).abs().max()
    print(f"pandas_ta与手动计算+DM的最大差异:{diff_pos:.10f}")
    print(f"pandas_ta与手动计算-DM的最大差异:{diff_neg:.10f}")
    print("\\n" + "=" * 60 + "\\n")

    # ========== 第四步:基于DM计算DI和ADX ==========
    # 计算ATR(真实波幅均值)
    df['ATR'] = ta.atr(df['high'], df['low'], df['close'], length=14)

    # 计算+DI和-DI
    df['PLUS_DI'] = 100 * df['DMP'] / df['ATR']
    df['MINUS_DI'] = 100 * df['DMN'] / df['ATR']

    # 计算DX
    df['DI_DIFF'] = abs(df['PLUS_DI'] df['MINUS_DI'])
    df['DI_SUM'] = df['PLUS_DI'] + df['MINUS_DI']
    df['DX'] = 100 * df['DI_DIFF'] / df['DI_SUM']

    # 使用RMA计算ADX
    def rma(series, length):
    alpha = 1 / length
    return series.ewm(alpha=alpha, adjust=False).mean()

    df['ADX'] = rma(df['DX'], length=14)

    print("DI和ADX计算结果(最近10行):")
    print(df[['PLUS_DI', 'MINUS_DI', 'ADX']].tail(10))
    print("\\n" + "=" * 60 + "\\n")

    # ========== 第五步:生成DI交叉信号 ==========
    # DI金叉:+DI上穿-DI
    df['DI_cross_buy'] = (df['PLUS_DI'] > df['MINUS_DI']) & (df['PLUS_DI'].shift(1) <= df['MINUS_DI'].shift(1))

    # DI死叉:+DI下穿-DI
    df['DI_cross_sell'] = (df['PLUS_DI'] < df['MINUS_DI']) & (df['PLUS_DI'].shift(1) >= df['MINUS_DI'].shift(1))

    # ADX趋势强度判断
    df['strong_trend'] = df['ADX'] > 25
    df['no_trend'] = df['ADX'] < 20

    # 综合信号
    df['signal'] = ''
    df.loc[df['DI_cross_buy'] & df['strong_trend'], 'signal'] = '买入(金叉+强趋势)'
    df.loc[df['DI_cross_buy'] & ~df['strong_trend'], 'signal'] = '买入信号弱(ADX不足)'
    df.loc[df['DI_cross_sell'] & df['strong_trend'], 'signal'] = '卖出(死叉+强趋势)'
    df.loc[df['DI_cross_sell'] & ~df['strong_trend'], 'signal'] = '卖出信号弱(ADX不足)'

    print("DI交叉信号统计:")
    print(f"金叉数量:{df['DI_cross_buy'].sum()}")
    print(f"死叉数量:{df['DI_cross_sell'].sum()}")
    print(f"强趋势交易日占比:{df['strong_trend'].mean():.2%}")

    print("\\n最近10个交易信号:")
    signals = df[df['signal'] != ''].tail(10)
    if not signals.empty:
    print(signals[['close', 'PLUS_DI', 'MINUS_DI', 'ADX', 'signal']])
    else:
    print("近期无信号")
    print("\\n" + "=" * 60 + "\\n")

    # ========== 第六步:策略回测(DI交叉+ADX过滤策略) ==========
    # 策略:+DI > -DI 且 ADX > 25 时持仓多头
    df['position'] = ((df['PLUS_DI'] > df['MINUS_DI']) & (df['ADX'] > 25)).astype(int)

    # 计算收益
    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
    win_rate = (df['strategy_returns'] > 0).sum() / (df['strategy_returns'] != 0).sum() if (df['strategy_returns'] != 0).sum() > 0 else 0

    print("=" * 60)
    print("策略绩效统计(DI交叉+ADX过滤策略回测):")
    print(f"买入持有策略总收益率:{total_return_buyhold:.2%}")
    print(f"趋势过滤策略总收益率:{total_return_strategy:.2%}")
    print(f"策略胜率:{win_rate:.2%}")
    print("\\n⚠️ 注意:此为简化回测,仅供参考")
    print("=" * 60 + "\\n")

    # ========== 第七步:可视化 ==========
    plt.figure(figsize=(14, 12))

    # 子图1:价格走势
    plt.subplot(3, 1, 1)
    plt.plot(df.index[150:], df['close'][150:], label='Close Price',
    linewidth=1.5, color='black')
    plt.title('Price Chart (Last 150 days)', fontsize=14)
    plt.ylabel('Price')
    plt.legend()
    plt.grid(True, alpha=0.3)

    # 子图2:+DI、-DI和DM值
    plt.subplot(3, 1, 2)
    plt.plot(df.index[150:], df['DMP'][150:], label='+DM (Bullish Power)',
    linewidth=1, alpha=0.7, color='green')
    plt.plot(df.index[150:], df['DMN'][150:], label='-DM (Bearish Power)',
    linewidth=1, alpha=0.7, color='red')
    plt.plot(df.index[150:], df['PLUS_DI'][150:], label='+DI',
    linewidth=1.5, color='darkgreen')
    plt.plot(df.index[150:], df['MINUS_DI'][150:], label='-DI',
    linewidth=1.5, color='darkred')
    plt.axhline(y=25, color='blue', linestyle='–', linewidth=0.8, alpha=0.5)
    plt.title('Directional Movement: +DM/-DM (raw) and +DI/-DI', fontsize=14)
    plt.ylabel('Value')
    plt.legend(loc='upper left')
    plt.grid(True, alpha=0.3)

    # 子图3:ADX趋势强度
    plt.subplot(3, 1, 3)
    plt.plot(df.index[150:], df['ADX'][150:], label='ADX (Trend Strength)',
    linewidth=1.5, color='purple')
    plt.axhline(y=25, color='blue', linestyle='–', linewidth=1, label='ADX > 25: 趋势强')
    plt.axhline(y=20, color='orange', linestyle='–', linewidth=1, label='ADX < 20: 无趋势')
    plt.fill_between(df.index[150:], 20, 25, alpha=0.2, color='yellow')
    plt.title('ADX – Average Directional Index', fontsize=14)
    plt.xlabel('Date')
    plt.ylabel('ADX Value')
    plt.ylim(0, 60)
    plt.legend(loc='upper left')
    plt.grid(True, alpha=0.3)

    plt.tight_layout()
    plt.show()

    # ========== 第八步:不同参数对比 ==========
    # 对比不同length参数对DM的影响
    dm_9 = ta.dm(df['high'], df['low'], length=9)
    dm_14 = ta.dm(df['high'], df['low'], length=14)
    dm_20 = ta.dm(df['high'], df['low'], length=20)

    df['DMP_9'] = dm_9['DMP_9']
    df['DMP_14'] = dm_14['DMP_14']
    df['DMP_20'] = dm_20['DMP_20']

    plt.figure(figsize=(14, 6))
    plt.plot(df.index[100:], df['DMP_9'][100:], label='DMP (length=9) – Fast',
    linewidth=1, alpha=0.7)
    plt.plot(df.index[100:], df['DMP_14'][100:], label='DMP (length=14) – Standard',
    linewidth=1.5, color='blue')
    plt.plot(df.index[100:], df['DMP_20'][100:], label='DMP (length=20) – Slow',
    linewidth=1.5, color='green')
    plt.title('+DM: Different Periods Comparison (9, 14, 20)', fontsize=14)
    plt.xlabel('Date')
    plt.ylabel('+DM Value')
    plt.legend()
    plt.grid(True, alpha=0.3)
    plt.show()

    # ========== 第九步:数据清洗提示 ==========
    nan_count = df['DMP'].isna().sum()
    print(f"\\nDM初始NaN数量:{nan_count}")
    print("原因:DM需要至少length个周期的数据才能计算平滑值")
    print("处理建议:df_clean = df.iloc[14:].copy()")

    5. 关键注意事项

    DM是DMI指标系统的基础: DM并不是最终用于交易的指标,而是计算+DI、-DI和ADX的中间变量。完整的DMI指标分析需要结合这三条线。

    参数名称注意: DM函数返回的DataFrame包含DMP_{length}(+DM)和DMN_{length}(-DM)两列,列名中的length会被替换为实际周期值。

    pandas_ta与TA-Lib的差异: pandas_ta的开发文档明确指出,其原生实现的DM与TA-Lib的PLUS_DM、MINUS_DM在数值上可能存在差异。用户可通过talib=True参数强制使用TA-Lib版本(需预先安装TA-Lib库)。


    五、总结

    DM是J. Welles Wilder趋向分析系统的基础数据层,其核心价值与定位如下:

    维度特点
    核心创新 只提取“突破前高/前低”的部分作为有效动向,过滤无关波动
    两个关键值 +DM(上升动向)和 -DM(下降动向),非负值
    主要用途 与ATR结合转化为+DI和-DI,进而计算ADX
    最佳应用场景 中长线趋势识别、趋势强度判断
    主要局限 滞后性(两次平滑)、震荡市假信号多

    实战使用三原则:

  • DM必须与ATR结合:原始的DM无法直接用于交易决策,需转化为+DI和-DI。
  • ADX > 25时使用DI交叉:只有当ADX确认市场处于趋势状态时,DI交叉信号才具有实际意义。
  • ADX < 20时停止使用:当市场处于盘整状态时,DI线会频繁交叉、信号无效,应退出观望或改用震荡指标。
  • 最后提醒:DM作为DMI指标系统的基石,其核心价值在于帮助交易者回答两个核心问题:当前市场是否有趋势?趋势的方向是什么? 然而,一个完整的交易决策不应仅依靠单一指标。将DMI系统与K线形态、移动平均线等其他工具结合使用,可以显著提高信号的可靠性。

    赞(0)
    未经允许不得转载:171主机测评 » Momentum:DM(动向指标)技术指标详解
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址