欢迎光临
我们一直在努力

Volume:EOM(简易波动指标)技术指标详解

Volume:EOM(简易波动指标)技术指标详解

一、EOM的定义

EOM(Ease of Movement,简易波动指标) 是由著名技术分析师Richard W. Arms, Jr.(阿姆斯)于20世纪70年代开发的独特技术指标。该指标通过将成交量与价格变动幅度相结合,来衡量价格变化的“轻松程度”。

核心思想

EOM的设计理念是:在成交量较小的情况下实现较大幅度的价格上涨,代表趋势的“轻松”程度较高,上涨动能强劲;反之,如果价格需要巨大的成交量才能小幅上涨,则表示趋势“费力”,动能可能正在衰竭。

阿姆斯希望通过这一指标帮助交易者判断:当前的价格变动是否伴随足够的成交量支持,以及趋势的强弱程度。

EOM的核心特征

特征说明
理论基础 价格变动与成交量的比值关系
数值形式 围绕零轴波动的震荡指标
开发者 Richard W. Arms, Jr.(阿姆斯)
指标类型 成交量动量指标
与EMV的关系 EOM = Ease of Movement,与EMV(简易波动值)是同一指标

EOM的核心逻辑

  • 正值:价格上涨,且成交量相对较小 → 上涨“轻松”
  • 负值:价格下跌,且成交量相对较小 → 下跌“轻松”
  • 接近零:价格变动需要较大成交量支持 → 变动“费力”

这一逻辑可以用一句话概括:“价格走得远,成交量省得多” 是好事;“走得近,成交量消耗大” 是坏事。

二、EOM的计算方法

1. 核心计算公式

EOM的计算包含三个关键步骤:距离(Distance)、箱体比率(Box Ratio)和一周期EOM值。

2. 详细计算步骤

步骤一:计算中点距离

首先,计算当前周期的中点(最高价与最低价的平均值)与前一周期中点的差值:
Distancet=Hight+Lowt2−Hight−1+Lowt−12
\\mathrm{Distance}_t = \\frac{\\mathrm{High}_t + \\mathrm{Low}_t}{2} – \\frac{\\mathrm{High}_{t-1} + \\mathrm{Low}_{t-1}}{2}
Distancet=2Hight+Lowt2Hight1+Lowt1

其中:

  • Hight\\mathrm{High}_tHight:当前周期的最高价
  • Lowt\\mathrm{Low}_{t}Lowt:当前周期的最低价
  • Hight−1\\mathrm{High}_{t-1}Hight1:前一周期的最高价
  • Lowt−1\\mathrm{Low}_{t-1}Lowt1:前一周期的最高价

这反映了价格重心的移动方向和距离。

步骤二:计算箱体比率(Box Ratio)

箱体比率将成交量标准化并与价格波动范围相结合:
BoxRatiot=Volumet/DivisorHight−Lowt
\\mathrm{BoxRatio}_t = \\frac{\\mathrm{Volume}_t / \\mathrm{Divisor}}{\\mathrm{High}_t – \\mathrm{Low}_t}
BoxRatiot=HightLowtVolumet/Divisor

其中:

  • Volumet\\mathrm{Volume}_tVolumet:当前周期的成交量
  • Divisor\\mathrm{Divisor}Divisor:标准化除数(pandas_ta默认使用1亿)
  • Hight−Lowt\\mathrm{High}_t – \\mathrm{Low}_tHightLowt:当前周期的价格波动范围

步骤三:计算单周期EOM
EOMt=DistancetBoxRatiot
\\mathrm{EOM}_t = \\frac{\\mathrm{Distance}_t}{\\mathrm{BoxRatio}_t}
EOMt=BoxRatiotDistancet

步骤四:平滑处理

对单周期EOM值进行移动平均,以消除噪声:

EOMsmooth=SMA(EOM,length)
\\mathrm{EOM}_\\mathrm{smooth} = \\mathrm{SMA}(\\mathrm{EOM}, \\mathrm{length})
EOMsmooth=SMA(EOM,length)

其中 length\\mathrm{length}length 为平滑周期(默认14)。

3. 完整公式汇总

将上述步骤合并,EOM的完整计算公式为:

EOMt=Hight+Lowt2−Hight−1+Lowt−12Volumet/DHight−Lowt
\\mathrm{EOM}_t = \\cfrac{\\cfrac{\\mathrm{High}_t + \\mathrm{Low}_t}{2} – \\cfrac{\\mathrm{High}_{t-1} + \\mathrm{Low}_{t-1}}{2}}{\\cfrac{\\mathrm{Volume}_t / D}{\\mathrm{High}_t – \\mathrm{Low}_t}}
EOMt=HightLowtVolumet/D2Hight+Lowt2Hight1+Lowt1

其中DDD为除数因子(默认100,000,000100,000,000100,000,000),整理后得:

EOMt=(Hight+Lowt−Hight−1−Lowt−1)×(Hight−Lowt)2×Volumet/D
\\mathrm{EOM}_t = \\frac{(\\mathrm{High}_t + \\mathrm{Low}_t – \\mathrm{High}_{t-1} – \\mathrm{Low}_{t-1}) \\times (\\mathrm{High}_t – \\mathrm{Low}_t)}{2 \\times \\mathrm{Volume}_t / D}
EOMt=2×Volumet/D(Hight+LowtHight1Lowt1)×(HightLowt)

4. panda_ta的简化实现

根据pandas_ta的源代码,该库实现的计算步骤略有简化:

  • 计算距离:distance = hl2(high, low) – hl2(high.shift(drift), low.shift(drift)),其中hl2 = (high + low)/2
  • 计算箱体比率:box_ratio = (volume / divisor) / (high – low)
  • 计算EOM:eom = distance / box_ratio
  • 平滑:EOM = SMA(eom, length)
  • 5. 参数说明

    参数类型默认值说明
    high pd.Series 必需 最高价序列
    low pd.Series 必需 最低价序列
    close pd.Series 必需 收盘价序列
    volume pd.Series 必需 成交量序列
    length int 14 平滑周期,控制指标灵敏度和平滑度
    divisor float 100000000 (1e8) 成交量标准化除数,用于将大成交量数值调整至合适范围
    drift int 1 计算距离时的价格间隔步长
    offset int 0 结果偏移周期数

    关于divisor参数:由于股票成交量的数值通常非常大(动辄数百万、数千万),直接代入计算会导致EOM值过于庞大而失去参考意义。因此需要使用较大的除数将其标准化。默认值1e8(1亿)适用于A股等成交量较大的市场,对于成交量较小的品种可以适当调小,反之亦然。

    三、EOM的使用方法

    1. 零轴分界线——方向判断

    EOM围绕零轴波动,零轴是多空力量的分界线:

    EOM状态市场含义操作倾向
    EOM > 0 价格上涨相对“轻松”,买方力量占优 顺势看多
    EOM < 0 价格下跌相对“轻松”,卖方力量占优 顺势看空
    EOM ≈ 0 价格变动“费力”,趋势不明显 观望

    核心逻辑:EOM的正负取决于距离(Distance)的正负——价格上涨时距离为正,EOM为正;价格下跌时距离为负,EOM为负。

    2. 数值大小——动量强度判断

    EOM的绝对值大小反映了趋势的强度:

    EOM绝对值趋势含义
    较大正值 价格上涨区间较大,成交量较小 → 上涨轻松,动能强劲
    较小正值 价格小幅上涨但成交量较大 → 上涨费力,动能可能衰竭
    较大负值 价格下跌区间较大,成交量较小 → 下跌轻松,空头强劲
    较小负值 价格小幅下跌但成交量较大 → 下跌费力,空头可能衰竭

    核心逻辑:箱体比率(Box Ratio)决定了EOM的强弱——成交量越小或波动范围越大,箱体比率越大,EOM的绝对值越大。

    3. 与成交量配合使用

    EOM不能替代成交量分析,建议与标准成交量指标配合使用:

    配合情况信号含义
    EOM正值上升 + 成交量温和放大 上涨趋势健康,动能持续
    EOM正值下降 + 成交量放大 上涨开始“费力”,动能可能衰竭
    EOM正值上升 + 成交量萎缩 上涨“轻松”,但需警惕缺乏成交量支持的上涨

    4. 与其他技术指标的配合

    配合指标作用具体用法
    移动平均线 确认趋势方向 EOM零轴穿越方向与MA趋势一致时信号更可靠
    RSI 验证超买超卖 结合RSI判断是否处于极端区域
    MACD 确认动能变化 两个指标同时给出信号时可信度更高
    标准成交量指标 验证EOM信号 用单纯成交量确认EOM反映的“轻松/费力”状态

    5. 注意事项与局限性

    使用EOM前需了解以下要点:

    局限性说明
    数值范围敏感 EOM的数值受成交量标准化除数影响较大,不同市场的合适除数不同,需要手动调整
    滞后性质 经过SMA平滑处理,本质上是滞后指标
    震荡市假信号 在横盘整理市场中,EOM可能在零轴附近波动,产生假信号
    不适合极端成交量 当成交量极大或极小时,EOM可能出现极端值,需结合其他指标判断
    不能单独使用 建议与成交量、移动平均线等指标配合使用

    关键提示:EOM对成交量的依赖是其最大的特色也是最大的局限。在使用前必须确保divisor参数的设置与市场成交量水平相匹配,否则指标将失去参考意义。

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

    1. pandas_ta中的EOM函数

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

    2. 函数完整参数

    pandas_ta.volume.eom(high, low, close, volume, length=None, divisor=None, drift=None, offset=None, **kwargs)

    参数详解:

    参数类型默认值说明
    high pd.Series 必需 最高价序列
    low pd.Series 必需 最低价序列
    close pd.Series 必需 收盘价序列
    volume pd.Series 必需 成交量序列
    length int 14 平滑周期
    divisor float 100000000 成交量标准化除数
    drift int 1 价格变化步长
    offset int 0 结果偏移周期数

    返回值:pd.Series——EOM值序列。

    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

    # 生成OHLC和成交量数据
    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, 20000000, n) # 成交量以百万为单位

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

    # ========== 第二步:计算EOM(基础用法) ==========
    # 使用默认参数 length=14, divisor=100000000
    df['EOM'] = ta.eom(df['high'], df['low'], df['close'], df['volume'])

    print("EOM计算结果(最近10行):")
    print(df[['close', 'volume', 'EOM']].tail(10))
    print("\\n" + "=" * 60 + "\\n")

    # ========== 第三步:手动验证EOM计算 ==========
    def manual_eom(high, low, volume, divisor=100000000, length=14):
    """手动计算EOM验证pandas_ta结果"""
    # 计算中点距离
    hl2 = (high + low) / 2
    distance = hl2 hl2.shift(1)

    # 计算箱体比率
    hl_range = high low
    box_ratio = (volume / divisor) / hl_range

    # 计算单周期EOM
    eom_raw = distance / box_ratio

    # 计算SMA平滑
    eom_smooth = eom_raw.rolling(window=length).mean()

    return eom_smooth

    df['EOM_manual'] = manual_eom(df['high'], df['low'], df['volume'])

    # 验证一致性
    diff = (df['EOM'] df['EOM_manual']).abs().max()
    print(f"pandas_ta与手动计算的最大差异:{diff:.10f}")
    print("\\n" + "=" * 60 + "\\n")

    # ========== 第四步:不同参数的EOM对比 ==========
    # 短周期 (9)
    df['EOM_9'] = ta.eom(df['high'], df['low'], df['close'], df['volume'], length=9)

    # 标准周期 (14)
    df['EOM_14'] = ta.eom(df['high'], df['low'], df['close'], df['volume'], length=14)

    # 长周期 (21)
    df['EOM_21'] = ta.eom(df['high'], df['low'], df['close'], df['volume'], length=21)

    # 调整除数(对于成交量较小的品种,可调小除数)
    df['EOM_small_divisor'] = ta.eom(df['high'], df['low'], df['close'], df['volume'], divisor=10000000)

    print("不同参数EOM对比(最近5行):")
    print(df[['close', 'EOM_9', 'EOM_14', 'EOM_21', 'EOM_small_divisor']].tail())
    print("\\n" + "=" * 60 + "\\n")

    # ========== 第五步:生成零轴穿越信号 ==========
    df['EOM_above_zero'] = df['EOM'] > 0
    df['cross_above_zero'] = (df['EOM_above_zero'] == True) & (df['EOM_above_zero'].shift(1) == False)
    df['cross_below_zero'] = (df['EOM_above_zero'] == False) & (df['EOM_above_zero'].shift(1) == True)

    df['signal'] = ''
    df.loc[df['cross_above_zero'], 'signal'] = '买入(上穿零轴)'
    df.loc[df['cross_below_zero'], 'signal'] = '卖出(下穿零轴)'

    print("零轴穿越信号(最近10个):")
    signals = df[df['signal'] != ''].tail(10)
    if not signals.empty:
    print(signals[['close', 'EOM', 'signal']])
    else:
    print("近期无穿越信号")
    print("\\n" + "=" * 60 + "\\n")

    # ========== 第六步:生成EOM强度信号 ==========
    # 定义强度阈值(需根据实际数据范围调整)
    STRONG_POSITIVE = 0.5
    STRONG_NEGATIVE = 0.5

    df['strength_signal'] = ''
    df.loc[df['EOM'] > STRONG_POSITIVE, 'strength_signal'] = '强上升(上涨轻松)'
    df.loc[(df['EOM'] > 0) & (df['EOM'] <= STRONG_POSITIVE), 'strength_signal'] = '弱上升(上涨费力)'
    df.loc[(df['EOM'] < 0) & (df['EOM'] >= STRONG_NEGATIVE), 'strength_signal'] = '弱下降(下跌费力)'
    df.loc[df['EOM'] < STRONG_NEGATIVE, 'strength_signal'] = '强下降(下跌轻松)'

    print("EOM强度信号统计:")
    print(df['strength_signal'].value_counts())
    print("\\n" + "=" * 60 + "\\n")

    # ========== 第七步:策略回测(零轴穿越策略) ==========
    # 策略:EOM > 0 时持仓多头
    df['position'] = (df['EOM'] > 0).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("策略绩效统计(EOM零轴穿越策略回测):")
    print(f"买入持有策略总收益率:{total_return_buyhold:.2%}")
    print(f"EOM零轴穿越策略总收益率:{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[100:], df['close'][100:], label='Close Price',
    linewidth=1.5, color='black')
    plt.title('Price Chart (Last 100 days)', fontsize=14)
    plt.ylabel('Price')
    plt.legend(loc='upper left')
    plt.grid(True, alpha=0.3)

    # 子图2:成交量
    plt.subplot(3, 1, 2)
    plt.bar(df.index[100:], df['volume'][100:], label='Volume',
    color='gray', alpha=0.6, width=0.8)
    plt.title('Volume', fontsize=14)
    plt.ylabel('Volume')
    plt.legend(loc='upper left')
    plt.grid(True, alpha=0.3)

    # 子图3:EOM指标(含零轴)
    plt.subplot(3, 1, 3)
    plt.plot(df.index[100:], df['EOM'][100:], label='EOM (length=14)',
    linewidth=1.5, color='blue')
    plt.axhline(y=0, color='red', linestyle='-', linewidth=1, label='零轴')

    # 强度参考线
    plt.axhline(y=0.5, color='green', linestyle='–', linewidth=0.8, alpha=0.7, label='强上升参考线')
    plt.axhline(y=0.5, color='orange', linestyle='–', linewidth=0.8, alpha=0.7, label='强下降参考线')

    # 填充区域
    plt.fill_between(df.index[100:], 0.5, df['EOM'][100:], alpha=0.1, color='green',
    where=(df['EOM'][100:] > 0.5))
    plt.fill_between(df.index[100:], 0.5, df['EOM'][100:], alpha=0.1, color='red',
    where=(df['EOM'][100:] < 0.5))

    plt.title('EOM (Ease of Movement) – Volume-Based Oscillator', fontsize=14)
    plt.xlabel('Date')
    plt.ylabel('EOM Value')
    plt.legend(loc='upper left')
    plt.grid(True, alpha=0.3)

    plt.tight_layout()
    plt.show()

    # ========== 第九步:不同周期EOM对比 ==========
    plt.figure(figsize=(14, 6))
    plt.plot(df.index[100:], df['EOM_9'][100:], label='EOM (length=9) – Fast',
    linewidth=1, alpha=0.7)
    plt.plot(df.index[100:], df['EOM_14'][100:], label='EOM (length=14) – Standard',
    linewidth=1.5, color='blue')
    plt.plot(df.index[100:], df['EOM_21'][100:], label='EOM (length=21) – Slow',
    linewidth=1.5, color='green')
    plt.axhline(y=0, color='red', linestyle='-', linewidth=0.8)
    plt.title('EOM: Different Periods Comparison (9, 14, 21)', fontsize=14)
    plt.xlabel('Date')
    plt.ylabel('EOM Value')
    plt.legend()
    plt.grid(True, alpha=0.3)
    plt.show()

    # ========== 第十步:divisor参数影响分析 ==========
    print("\\n" + "=" * 60)
    print("divisor参数影响分析:")
    print(f"默认divisor (1e8) – EOM范围:[{df['EOM'].min():.2f}, {df['EOM'].max():.2f}]")
    print(f"较小divisor (1e7) – EOM范围:[{df['EOM_small_divisor'].min():.2f}, {df['EOM_small_divisor'].max():.2f}]")
    print("建议:对于成交量较大的市场(如A股),使用默认divisor=1e8合适")
    print("对于成交量较小的市场(如加密货币),可适当调小divisor")
    print("=" * 60)

    # ========== 第十一步:数据清洗提示 ==========
    nan_count = df['EOM'].isna().sum()
    print(f"\\nEOM初始NaN数量:{nan_count}")
    print("原因:EOM需要至少length+1个周期的数据才能开始计算")
    print("处理建议:df_clean = df.iloc[15:].copy()")

    4. 关键注意事项

    divisor参数的选择:

    # 对于不同市场,建议调整divisor以获得合适的EOM数值范围
    # 股票市场(成交量以百万、千万计)
    df['EOM'] = ta.eom(high, low, close, volume, divisor=100000000)

    # 加密货币市场(成交量较小)
    df['EOM'] = ta.eom(high, low, close, volume, divisor=10000)

    # 期货市场(根据合约大小调整)
    df['EOM'] = ta.eom(high, low, close, volume, divisor=1000000)

    # 观察EOM数值范围,一般应在[-1, 1]或[-2, 2]区间内较为理想
    print(f"EOM范围:[{df['EOM'].min():.2f}, {df['EOM'].max():.2f}]")

    初始NaN值处理:

    # EOM需要足够的数据预热
    df['EOM'] = ta.eom(df['high'], df['low'], df['close'], df['volume'])

    # 查看NaN数量
    nan_count = df['EOM'].isna().sum()
    print(f"初始NaN数量:{nan_count}")

    # 建议丢弃前length+1个不稳定数据点
    df_clean = df.iloc[15:].copy()

    五、总结

    EOM(简易波动指标)是一种融合成交量与价格变动的独特技术指标,其核心价值与定位如下:

    维度特点
    核心创新 将成交量纳入动量分析,反映价格变动的“轻松程度”
    核心公式 中点距离 ÷ 箱体比率 → SMA平滑
    核心信号 零轴穿越、数值大小(强度判断)
    开发者 Richard W. Arms, Jr.(阿姆斯)
    参数关键 length(14)、divisor(1e8)
    最佳应用场景 趋势确认、成交量与价格关系的量化分析
    主要局限 依赖divisor参数设置、滞后性、震荡市假信号

    实战使用三原则:

  • 零轴即方向:EOM的正负值直接反映价格方向——上穿零轴可视为看涨信号,下穿零轴为看跌信号。
  • 强度看大小:EOM的绝对值反映趋势的“轻松程度”——绝对值越大,趋势越轻松强劲;绝对值越小,趋势越“费力”,可能面临衰竭。
  • 配合成交量验证:EOM的价值在于量化价格与成交量的关系,建议与标准成交量指标配合使用,互相验证。
  • 最后提醒:EOM的核心价值在于回答一个关键问题:“当前的价格变动是否有足够的成交量支持?” 当价格上涨时,如果EOM的值也同时上升,说明上涨是“轻松的”,动能健康;反之,如果价格上涨而EOM值却在下降(甚至转为负值),说明上涨越来越“费力”,可能是趋势衰竭的前兆。然而,EOM的数值范围对divisor参数高度敏感,使用前必须根据具体市场的成交量水平进行参数优化。

    赞(0)
    未经允许不得转载:171主机测评 » Volume:EOM(简易波动指标)技术指标详解
    分享到: 更多 (0)

    评论 抢沙发

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