【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

相关推荐
xys_67835 分钟前
JavaEE通关手册:初识Java
java·开发语言
文丑颜不良啊36 分钟前
LeetCode2461~2480题目解析
java·leetcode
2601_967980111 小时前
Python自动化办公:模块、库与样式调整教程
python·excel·自动化办公·openpyxl·样式调整
长沙三为智能科技1 小时前
GEO与传统SEO的5个关键差异:排名与被引用、关键词与答案型的全维度对比
python·aigc·seo·ai搜索·geo优化
ShineWinsu2 小时前
对于Qt:QTableWidget常见属性以及QTableWidgetItem类的解析
开发语言·c++·qt·gui·客户端·表格·qtablewidget
文丑颜不良啊2 小时前
LeetCode2441~2460题目解析
java·leetcode
阿屿2 小时前
从零手搓 AI 伴侣(二):好感度通胀危机,与它的「货币政策」
java·人工智能
灿飞3 小时前
Kotlin与Java虚拟机十一:类与属性:构造顺序、backing field 与显式 backing field
java·后端·kotlin
aiks_yuan3 小时前
40亿个QQ号 1G内存怎么去重:Java BitSet 装不下,long 数组位图 512MB 实测 46 秒(JDK 17)
java
量化分析码农3 小时前
【Python量化趋势跟踪实战 02】突破买入总被假突破坑:通道系统与入场时机
python