如何用 Python 计算 EMV 简易波动指标?

一句话回答: EMV(Ease of Movement,简易波动指标)把「价格移动的幅度」和「推动它所需的成交量」结合起来,衡量股价上涨/下跌是不是「轻松」------放小量就能涨说明上行轻松(EMV 高),放大量才涨一点说明滞涨(EMV 低)。用 AlphaFeed 取回前复权日线 的 high/low/volume 后,几行 pandas 就能算出 EMV 与其均线,不需要 TA-Lib。

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

只看涨跌幅看不出「涨得费不费力」。同样涨 2%,缩量上涨(轻松)和放巨量才涨(滞涨、可能出货)意义完全不同。EMV 用量价关系量化这种「难易度」。坑在于:一是要用前复权价 ,否则除权跳空污染幅度;二是成交量量纲影响 EMV 数值大小,需要缩放,判断时看方向与零轴而非绝对值。

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

第 1 步:取前复权日线

python 复制代码
from alphafeed import AlphaFeed
import pandas as pd

af = AlphaFeed()  # 读取 ALPHAFEED_API_KEY

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

EMV 依赖每日最高/最低价,务必用 adjust="forward"(前复权),避免除权跳空。

第 2 步:计算 EMV 与信号均线

python 复制代码
def emv(df: pd.DataFrame, n: int = 14, vol_scale: float = 1e5):
    mid = (df["high"] + df["low"]) / 2
    distance = mid - mid.shift(1)                       # 中价移动幅度
    box = (df["volume"] / vol_scale) / (df["high"] - df["low"])  # 单位量能推动的"阻力"
    emv1 = distance / box                               # 单期 EMV
    emv_line = emv1.rolling(n).mean()                   # N 日平滑
    return emv_line

df["EMV"] = emv(df)
df["EMV_ma"] = df["EMV"].rolling(9).mean()              # 信号线
print(df[["trade_date", "close", "EMV", "EMV_ma"]].tail(5).to_string(index=False))

参数与含义:

参数/字段 含义 常用值
n EMV 平滑周期 14
vol_scale 成交量缩放(让数值适中) 按品种调,如 1e5
EMV > 0 上行轻松(缩量能涨) 偏多
EMV < 0 下行轻松/上行滞涨 偏空
零轴上穿 由滞涨转为轻松上行 转强信号

第 3 步:解读信号(零轴与背离)

python 复制代码
prev, cur = df["EMV"].iloc[-2], df["EMV"].iloc[-1]
if prev <= 0 < cur:
    print("EMV 上穿零轴:上行变轻松(偏多)")
elif prev >= 0 > cur:
    print("EMV 下穿零轴:上行滞涨/下行轻松(偏空)")

# 价格创新高但 EMV 未同步走高 → 放量滞涨,警惕
window = 20
price_high = df["close"].iloc[-1] >= df["close"].tail(window).max()
emv_high = df["EMV"].iloc[-1] >= df["EMV"].tail(window).max()
if price_high and not emv_high:
    print("疑似放量滞涨:价格新高但 EMV 未创新高")

第 4 步:全市场批量筛「上行轻松」的票

python 复制代码
codes = ["600519.SH", "000001.SZ", "601318.SH", "000858.SZ", "300750.SZ"]
data = af.klines.batch(codes, period="1d", count=60,
                       adjust="forward", to_dataframe=True)  # dict{symbol: DataFrame}

hits = []
for sym, k in data.items():
    k = k.sort_values("trade_date")
    e = emv(k)
    if pd.notna(e.iloc[-1]) and e.iloc[-1] > 0:
        hits.append((sym, round(float(e.iloc[-1]), 2)))
print("EMV>0(上行轻松):", hits)

关键坑与注意事项

  • 必须用前复权价:除权跳空会让中价移动幅度失真,EMV 出错。
  • 看方向不看绝对值 :EMV 数值受 vol_scale 影响,不同品种/缩放下量级不同,关注正负与零轴穿越、趋势。
  • 除以零风险 :当日 high == low(一字板/停牌)会让 box 分母为 0,需剔除或置 NaN。
  • EMV 是辅助指标:它擅长揭示「放量滞涨/缩量上行」,但不宜单独择时,配合趋势与量能一起看。
  • 成交量单位是「手」 :本 SDK volume 以手计,缩放系数按品种调到 EMV 数值适中即可。
  • 样本要足够 :n=14 + 均线需要预热,count 建议 ≥ 40,忽略头部 NaN。

常见问题(FAQ)

Q:EMV 和 OBV 有什么区别? A:OBV 累加带方向的成交量看资金进出;EMV 衡量「单位成交量能推动多大价格移动」,侧重上涨/下跌的难易度。二者可互补。

Q:EMV 数值忽大忽小正常吗? A:正常,它对量价比敏感。判断时看正负、零轴穿越与相对趋势,不要纠结绝对数值。

Q:vol_scale 怎么定? A:它只是把成交量缩放到合适量级、方便观察,不改变符号与趋势。按品种调到 EMV 在个位/十位量级即可。

Q:需要付费吗? A:单只历史日线有免费额度;全市场批量算 EMV(klines.batch + universes)需 Starter 及以上。详见定价页。

小结

EMV 简易波动指标的本质是「价格移动幅度 ÷ 单位成交量」,量化股价涨跌的难易度,特别适合识别放量滞涨与缩量上行。实现只需 pandas,不依赖 TA-Lib。关键是用前复权价、看方向而非绝对值、处理 high==low 的除零。用 AlphaFeed 取回前复权日线,几行就能算出 EMV 并做全市场筛选。

参考

相关推荐
第25小时记录1 小时前
[2026] Linux安装教程
运维·python
前端 贾公子3 小时前
LangGraph == 图的状态(State)管理 (中)
开发语言·数据库·python
the局外人4 小时前
FastAPI中jwt的基本使用与实践
后端·python·fastapi
mklpo1474 小时前
为不支持sse4.2命令集的老旧设备制作Win11 26H2系统镜像,2026-10-11。
运维·windows·python
GoldenSpider.AI4 小时前
用 AI 改流程之前先做「四桶」体检:9 次返工里 8 次靠规则就能消掉(附 Python 发布前校验器)
人工智能·python·自动化·ai智能体
小白快快跑哦4 小时前
python-字符串全解(六):正则表达式-修饰符
python·正则表达式·修饰符
niucloud-admin4 小时前
JAVA V6 多商户商城 开发文档——项目启动
java·开发语言·python
CCIE-Yasuo4 小时前
个人电脑搭建AI-Qwen3-8B-学习记录
人工智能·python·学习