🚀 AI 一键生成 joinquant 策略代码
立即体验
👉 AI 生成代码 👈

JoinQuant 平台调用 talib 计算 MACD 等技术指标与通达信差异巨大的原因及解决方案

joinquant | 发布时间: 2026/6/24
以下内容由 EasyQuant 生成。

问题描述

为什么调用stalib 或者jqlib.technical_analysis计算macd wr等,结果都是跟通达信天差地别?求解,谢谢哈

def MACD(security_list, fastperiod=12, slowperiod=26, signalperiod=9):

# 修复传入为单只股票的情况
if isinstance(security_list, str):
security_list = [security_list]
# 计算 MACD
security_data = history(slowperiod*20, '1d', 'close' , security_list, df=False, skip_paused=True)
macd_DIF = {}; macd_DEA = {}; macd_HIST = {}
for stock in security_list:
nan_count = list(np.isnan(security_data[stock])).count(True)
if nan_count == len(security_data[stock]):
log.info("股票 %s 输入数据全是 NaN,该股票可能已退市或刚上市,返回 NaN 值数据。" %stock)
macd_DIF[stock] = array([np.nan])
macd_DEA[stock] = array([np.nan])
macd_HIST[stock]= array([np.nan])
else:
macd_DIF[stock], macd_DEA[stock], macd = talib.MACDEXT(security_data[stock], fastperiod=fastperiod, fastmatype=1, slowperiod=slowperiod, slowmatype=1, signalperiod=signalperiod, signalmatype=1)
macd_HIST[stock] = macd * 2
return macd_DIF, macd_DEA, macd_HIST
macd_DIF, macd_DEA, macd_HIST=MACD(stocks, fastperiod=12, slowperiod=26, signalperiod=9)
print(macd_DIF, macd_DEA, macd_HIST)

Img

通达信同一天的值是-0.22.
用jqlib.technical_analysis 也试过,结果也是差非常多,望大家解惑,谢谢!

解决方案

这是一个在量化交易中非常经典且常见的问题。在使用 JoinQuant(或其他量化平台)调用 talibjqlib 计算 MACD、EMA、WR 等技术指标时,结果与通达信、同花顺等传统看盘软件产生巨大差异,通常是由以下几个核心原因造成的:

1. 核心原因:EMA 算法的“长记忆性”与数据截断(最主要原因)

MACD 的核心是计算 EMA(指数移动平均线)。EMA 的计算公式是递归的:
EMA(今日) = α * 今日收盘价 + (1 - α) * EMA(昨日)

这意味着,今天的 EMA 值依赖于昨天的 EMA 值,而昨天的又依赖于前天的,一直追溯到这只股票上市的第一天

  • 通达信的做法:通达信在计算 MACD 时,通常是加载了该股票上市以来的所有历史 K 线数据,从第一天开始往后递推计算 EMA。
  • 你的代码的做法:在你的代码中,history(slowperiod*20, ...) 大约只获取了过去 520 天的数据。talib 在接收到这 520 天的数据时,只能把第 1 天的数据当作 EMA 的初始值。由于丢失了前几年的历史数据积累,初始值不同,导致后面计算出的 EMA、DIF、DEA 都会产生误差。

解决办法:虽然理论上需要所有历史数据,但 EMA 的权重是呈指数衰减的。通常,获取过去 250 天到 300 天以上的数据作为“预热期”,计算到今天的误差就已经小到可以忽略不计了。建议将获取数据的长度加大,例如 history(300, '1d', 'close', ...)

2. 复权方式与基准日的差异

  • 复权基准日:JoinQuant 默认使用前复权(fq='pre')。前复权是以当前日期(回测的 current_dt 或当天的真实日期)为基准往回算。如果你在 JoinQuant 回测 2020 年的数据,它的前复权价格是基于 2020 年的基准;而你今天打开通达信看 2020 年的 K 线,它是基于今天的基准进行前复权的。如果这期间发生了分红派息,两边的历史价格就会完全不同。
  • 解决办法:在对比数据时,建议将 JoinQuant 的 history 参数设置为 fq=None(不复权),同时把通达信也切换到“不复权”模式进行对比,这样排除了复权因子的干扰。

3. MACD 柱子的倍数问题

  • talib.MACD 返回的第三个值(MACD histogram)是 DIF - DEA
  • 国内软件(通达信/同花顺)的 MACD 柱子公式是 (DIF - DEA) * 2
  • 注:你的代码中已经写了 macd * 2,这一步是正确的,但很多新手会忽略这一点。

4. 停牌数据的处理

  • 你的代码中使用了 skip_paused=True,这与通达信的处理方式是一致的(通达信 K 线图上通常没有停牌日)。这一点保持即可。

修正后的代码示例

为了让结果尽可能贴近通达信,我们需要拉长获取数据的窗口,并确保对比时复权方式一致。以下是优化后的代码:

import talib
import numpy as np

def MACD_Optimized(security_list, fastperiod=12, slowperiod=26, signalperiod=9):
    if isinstance(security_list, str):
        security_list = [security_list]
        
    # 关键修改1:获取足够长的数据(例如 300 天),让 EMA 充分“预热”衰减误差
    # 关键修改2:对比时建议先用 fq=None 排除复权干扰。实盘/回测中可改回 fq='pre'
    data_length = 300 
    security_data = history(data_length, '1d', 'close', security_list, df=True, skip_paused=True, fq='pre')
    
    macd_DIF = {}
    macd_DEA = {}
    macd_HIST = {}
    
    for stock in security_list:
        close_prices = security_data[stock].values
        
        # 剔除全 NaN 的情况
        if np.isnan(close_prices).all():
            macd_DIF[stock] = np.nan
            macd_DEA[stock] = np.nan
            macd_HIST[stock]= np.nan
            continue
            
        # 剔除前面的 NaN 值,保留有效数据
        valid_closes = close_prices[~np.isnan(close_prices)]
        
        # 如果有效数据太短,talib 会报错或全返回 nan
        if len(valid_closes) < slowperiod + signalperiod:
            macd_DIF[stock] = np.nan
            macd_DEA[stock] = np.nan
            macd_HIST[stock]= np.nan
            continue
            
        # 使用标准的 talib.MACD 即可,默认就是 EMA
        dif, dea, macd = talib.MACD(valid_closes, 
                                    fastperiod=fastperiod, 
                                    slowperiod=slowperiod, 
                                    signalperiod=signalperiod)
        
        # 取最后一个值作为当天的指标值
        macd_DIF[stock] = dif[-1]
        macd_DEA[stock] = dea[-1]
        # 关键修改3:乘以 2 对齐通达信
        macd_HIST[stock] = macd[-1] * 2 
        
    return macd_DIF, macd_DEA, macd_HIST

总结
只要你把 history 获取的数据长度拉长到 250~300 天以上,并且确保 JoinQuant 和通达信处于相同的复权模式(强烈建议用不复权对比),你会发现计算出来的 MACD 值与通达信的误差会缩小到小数点后三位以内。