【Python量化技术指标实战 02】MACD金叉也分强弱?手工算一次 DIF/DEA 就懂了

本文你将得到什么:

  1. 不调用任何指标库,纯手工用 EMA 算出 MACD 三要素
  2. 理解 DIF、DEA、MACD 柱各自的含义与联动关系
  3. 区分"零轴上金叉"和"零轴下金叉"的实战差异
  4. 把 MACD 信号画成三面板图(价格 + DIF/DEA + MACD柱)
  5. 一段可直接复制的完整代码

痛点:为什么你不能只会看软件上的 MACD

行情软件里的 MACD 都是现成的,但很多人用了几年也没搞清楚:

  • DIF 和 DEA 到底谁减谁?
  • MACD 柱为什么是"(DIF-DEA)×2"?
  • 同样的金叉,为什么有的涨、有的不涨?

答案藏在计算过程里。本文带你手工算一遍,算完你就明白"零轴上金叉"和"零轴下金叉"为什么强度完全不同。


环境准备

bash 复制代码
pip install mairui pandas numpy matplotlib

第一步:拉取数据

python 复制代码
from mairui import Client

client = Client("LICENCE-66D8-9F96-0C7F0FBCD073")
bars = client.stock_history(code="600519", period="d", dividend="f")

import pandas as pd
df = pd.DataFrame(bars)
df = df.rename(columns={"t": "date", "o": "o", "h": "h", "l": "l", "c": "c"})
df = df.sort_values("date").reset_index(drop=True)
closes = df["c"].astype(float)

第二步:手工计算 EMA

MACD 的底层是三个 EMA:

  • EMA(12):快速平滑
  • EMA(26):慢速平滑
  • EMA(9):对 DIF 再平滑一次得到 DEA
python 复制代码
def calc_ema(series, span):
    """指数移动平均。"""
    return series.ewm(span=span, adjust=False).mean()

ema12 = calc_ema(closes, 12)
ema26 = calc_ema(closes, 26)

EMA 和 MA 的区别:EMA 给近期价格更高权重,对最新变化更敏感。


第三步:算 DIF、DEA、MACD 柱

python 复制代码
# DIF = 快EMA - 慢EMA
dif = ema12 - ema26

# DEA = DIF 的 9日EMA
dea = calc_ema(dif, 9)

# MACD柱 = (DIF - DEA) * 2
macd_hist = (dif - dea) * 2

print(f"DIF 最新: {dif.iloc[-1]:.4f}")
print(f"DEA 最新: {dea.iloc[-1]:.4f}")
print(f"MACD柱 最新: {macd_hist.iloc[-1]:.4f}")

输出示例(演示数据):

复制代码
DIF 最新: -0.0105
DEA 最新: 0.0243
MACD柱 最新: -0.0696

解读:

  • DIF < DEA,说明短期动能弱于中期平滑值,当前为空头状态
  • MACD 柱为负,且 DIF 在零轴下方,整体偏空

第四步:检测 MACD 交叉信号

python 复制代码
def find_macd_signals(df):
    signals = []
    for i in range(1, len(df)):
        prev = df.iloc[i - 1]
        curr = df.iloc[i]
        if prev["dif"] < prev["dea"] and curr["dif"] > curr["dea"]:
            zero_info = "零轴上" if curr["dif"] > 0 else "零轴下"
            signals.append((curr["date"], "golden", curr["dif"], zero_info))
        elif prev["dif"] > prev["dea"] and curr["dif"] < curr["dea"]:
            zero_info = "零轴上" if curr["dif"] > 0 else "零轴下"
            signals.append((curr["date"], "death", curr["dif"], zero_info))
    return signals

signals = find_macd_signals(df)
for date, typ, dval, zinfo in signals:
    label = "金叉" if typ == "golden" else "死叉"
    print(f"{date} {label} DIF={dval:.4f} ({zinfo})")

输出示例:

复制代码
2025-09-30 金叉 DIF=-0.1524 (零轴下)
2025-10-31 死叉 DIF=-0.0922 (零轴下)
2025-11-05 金叉 DIF=-0.0565 (零轴下)
2025-11-07 死叉 DIF=-0.0728 (零轴下)
2025-11-10 金叉 DIF=-0.0238 (零轴下)
2025-11-25 死叉 DIF=0.0372 (零轴上)

注意最后一组:死叉发生在零轴上方,属于"多头中的回调信号",和零轴下方的死叉含义不同。


第五步:三面板可视化

python 复制代码
import matplotlib.pyplot as plt
plt.rcParams["font.sans-serif"] = ["SimHei", "Microsoft YaHei"]
plt.rcParams["axes.unicode_minus"] = False

fig, axes = plt.subplots(3, 1, figsize=(12, 10),
                         gridspec_kw={"height_ratios": [2, 1, 1]})
x = range(len(df))

# 面板1:收盘价
axes[0].plot(x, closes, color="black", linewidth=0.8)
axes[0].set_title("收盘价")
axes[0].set_ylabel("价格")
axes[0].grid(True, alpha=0.3)

# 面板2:DIF / DEA + 交叉标记
axes[1].plot(x, dif, label="DIF", color="blue", linewidth=1)
axes[1].plot(x, dea, label="DEA", color="orange", linewidth=1)
axes[1].axhline(y=0, color="gray", linestyle="--", alpha=0.5)
for date, typ, dval, zinfo in signals:
    idx = df[df["date"] == date].index[0]
    color = "red" if typ == "golden" else "green"
    marker = "^" if typ == "golden" else "v"
    axes[1].scatter([idx], [dval], color=color, marker=marker, s=80)
axes[1].set_title("MACD 金叉/死叉")
axes[1].set_ylabel("DIF / DEA")
axes[1].legend()
axes[1].grid(True, alpha=0.3)

# 面板3:MACD柱
 colors = ["red" if v >= 0 else "green" for v in macd_hist]
axes[2].bar(x, macd_hist, color=colors, width=0.8)
axes[2].axhline(y=0, color="gray", linestyle="-", alpha=0.5)
axes[2].set_title("MACD 柱状图(红=正,绿=负)")
axes[2].set_ylabel("MACD")
axes[2].grid(True, alpha=0.3)

plt.tight_layout()
plt.savefig("macd_chart.png", dpi=150)

三面板的好处:价格、DIF/DEA 走势、MACD 柱变化一屏看完,不用在软件里切来切去。


关键认知:金叉的强弱怎么看

场景 位置 含义 强度
零轴上金叉 DIF>0, DEA>0 多头趋势中的加速信号 强
零轴下金叉 DIF<0, DEA<0 空头趋势中的反弹信号 弱
零轴附近金叉 DIF≈0 趋势转折临界点 不确定
死叉同理 --- --- ---

本文演示数据中的金叉全部发生在零轴下方,属于"弱势反弹"型金叉,不能和零轴上的强势金叉等量齐观。实战中建议:零轴上金叉才考虑做多,零轴下金叉只当反弹对待。


常见坑与注意

  1. MACD 的滞后比 MA 更严重:因为 DIF 本身就是 EMA 的差,DEA 又是 DIF 的 EMA,两次平滑导致信号更慢。
  2. 零轴是多空分界线:DIF 在零轴上方 = 多头区域,下方 = 空头区域。很多人只看金叉死叉,忽略零轴位置,容易误判。
  3. 背离才是 MACD 的精髓:价格创新高但 DIF 不创新高(顶背离),或价格创新低但 DIF 不创新低(底背离),比单纯的金叉死叉更可靠。后续篇章会单独讲背离检测。
  4. 演示数据 vs 真实数据:本文使用演示证书跑通逻辑,换正式证书后即为真实行情,代码无需修改。

小结

本文带你手工算了一遍 MACD 的完整计算链路:

  • EMA(12) 和 EMA(26) → DIF
  • DIF 的 EMA(9) → DEA
  • (DIF-DEA)×2 → MACD 柱

并强调了一个实战要点:金叉不是都一样的,零轴位置决定强度。下一篇,我们把 KDJ 和 RSI 拉进来,做一个"超买超卖双确认"系统。


免责声明

本文仅为技术教学,不构成任何投资建议。投资有风险,入市需谨慎。


代码与文档:https://github.com/MaiRuiApi

相关推荐
讳疾忌医丶1 小时前
编译器眼中的 C++ 循环:从指针别名、IEEE 754 结合律到 SIMD 语义证明
开发语言·c++
m0_639906941 小时前
MyBatis 核心体系:核心接口、动态 SQL 原理、插件底层机制
java·sql·mybatis
重生六道1 小时前
B站视频时长统计:一段JS脚本搞定分P/合集总时长(附倍速计算)
开发语言·javascript·音视频
魔都吴所谓1 小时前
【我的开源】Python实现一个离线 PDF 工具箱:架构、骑缝章与踩坑记录
python·pdf·开源
用户0332126663671 小时前
Python 自动化办公:如何轻松在 PDF 中导入与导出表单数据
python
XiYang-DING1 小时前
P5528.带过期时间的 LRU 缓存
java·开发语言·缓存
SimonKing1 小时前
DeepSeek 新模型免费白嫖,夯夯夯,赶紧用起来
java·后端·程序员
小徐不徐说1 小时前
C++ 机器视觉入门:从零搭建 AOI 开发环境(OpenCV + CMake)
开发语言·c++·opencv
MoRanzhi12031 小时前
第5篇(上)· 使用 PyQt5 实现 2048 算法求解版:五类搜索算法内核
python·启发式算法·强化学习·搜索算法·蒙特卡洛树搜索·2048游戏·expectimax