问题描述
用talib.macd算出来的dif值和同花顺/自己算的dif值不一样
查了一下talib官网,好像也没有给出计算公式
https://mrjbq7.github.io/ta-lib/func_groups/momentum_indicators.html
https://hexdocs.pm/talib/TAlib.Indicators.MACD.html
多谢
解决方案
在使用 Python 进行量化分析时,很多开发者会发现 talib.MACD 计算出的结果与国内主流炒股软件(如同花顺、通达信)显示的值存在差异。这主要是由于 TA-Lib 底层对 EMA(指数移动平均)的初始值处理方式 以及 MACD 柱状图的乘数定义 与国内软件不同导致的。
以下是详细的差异分析及解决方案:
1. TA-Lib 中 MACD 的返回值对应关系
首先,talib.MACD(close, fastperiod=12, slowperiod=26, signalperiod=9) 会返回三个值:
- macd:对应国内软件的 DIF (快线 EMA - 慢线 EMA)
- macdsignal:对应国内软件的 DEA (DIF 的 EMA)
- macdhist:对应国内软件的 MACD 柱状图
2. 为什么计算结果不一样?
差异主要来源于以下两点:
差异一:MACD 柱状图的乘数
- TA-Lib:
macdhist = macd - macdsignal(即 DIF - DEA) - 同花顺/通达信:
MACD柱 = (DIF - DEA) * 2
国内软件为了让柱状图更明显,通常会乘以 2。如果你发现 DIF 和 DEA 差不多,但柱子差了一倍,就是这个原因。
差异二:EMA 的初始值计算逻辑(导致 DIF/DEA 不同的根本原因)
MACD 的核心是 EMA(指数移动平均线)。EMA 的计算公式为:
EMA(today) = alpha * Price(today) + (1 - alpha) * EMA(yesterday),其中 alpha = 2 / (N + 1)。
关键在于 第一天的 EMA(yesterday) 是多少?
- 同花顺/通达信:直接将时间序列的第一个有效收盘价作为第一个 EMA 值。即
EMA[0] = Close[0]。 - TA-Lib:TA-Lib 在计算 EMA 时,前 N 个周期是没有 EMA 值的,它会先计算前 N 个周期的 SMA(简单算术平均),将其作为第 N 天的 EMA 初始值,然后再开始应用 EMA 公式。这就导致了 TA-Lib 的 EMA 序列在起始阶段与国内软件完全不同。
注意:由于 EMA 是一个无限递归的平滑指标,初始值的差异会随着时间推移(数据量增加)而逐渐衰减。如果你传入的数据量足够大(例如 500 根以上的 K 线),TA-Lib 算出的最新 DIF 值会无限趋近于同花顺的值。但如果传入的数据量较少,差异就会非常明显。
3. 如何用 Python 实现与同花顺/通达信完全一致的 MACD?
为了在量化策略中得到与国内软件完全一致的 MACD 信号,建议放弃使用 talib.MACD,改用 pandas 的 ewm 方法来实现。pandas.ewm(adjust=False) 的计算逻辑与国内软件的 EMA 逻辑完全一致。
Python 实现代码如下:
import pandas as pd
import numpy as np
def calc_macd_cn(close_series, fastperiod=12, slowperiod=26, signalperiod=9):
"""
计算与同花顺/通达信一致的 MACD 指标
:param close_series: 收盘价的 Pandas Series
:return: DIF, DEA, MACD_HIST
"""
# 计算快线 EMA12 和慢线 EMA26
# adjust=False 保证了初始值的处理与国内软件一致 (EMA[0] = Close[0])
ema_fast = close_series.ewm(span=fastperiod, adjust=False).mean()
ema_slow = close_series.ewm(span=slowperiod, adjust=False).mean()
# 计算 DIF
dif = ema_fast - ema_slow
# 计算 DEA (DIF 的 9日 EMA)
dea = dif.ewm(span=signalperiod, adjust=False).mean()
# 计算 MACD 柱状图 (国内习惯乘以 2)
macd_hist = (dif - dea) * 2
return dif, dea, macd_hist
# --- 测试示例 ---
# 假设 df 是包含 'close' 列的 DataFrame
# df = get_price('000001.XSHE', end_date='2023-10-01', count=100, fields=['close'])
# dif, dea, macd = calc_macd_cn(df['close'])
# df['DIF'] = dif
# df['DEA'] = dea
# df['MACD'] = macd
# print(df.tail())
总结
如果你在 JoinQuant 平台上编写策略,并且策略信号严格依赖于与同花顺/通达信绝对一致的 MACD 数值(尤其是近期上市的新股,数据量不足时),强烈建议使用上述基于 pandas.ewm 的自定义函数 calc_macd_cn,而不是直接调用 talib.MACD。