3 分钟快速生成代码
输入想法,AI 即刻生成可运行代码
这是一个在量化交易中非常经典且常见的问题。在使用 JoinQuant(或其他量化平台)调用 talib 或 jqlib 计算 MACD、EMA、WR 等技术指标时,结果与通达信、同花顺等传统看盘软件产生巨大差异,通常是由以下几个核心原因造成的:
MACD 的核心是计算 EMA(指数移动平均线)。EMA 的计算公式是递归的:
EMA(今日) = α * 今日收盘价 + (1 - α) * EMA(昨日)
这意味着,今天的 EMA 值依赖于昨天的 EMA 值,而昨天的又依赖于前天的,一直追溯到这只股票上市的第一天。
history(slowperiod*20, ...) 大约只获取了过去 520 天的数据。talib 在接收到这 520 天的数据时,只能把第 1 天的数据当作 EMA 的初始值。由于丢失了前几年的历史数据积累,初始值不同,导致后面计算出的 EMA、DIF、DEA 都会产生误差。解决办法:虽然理论上需要所有历史数据,但 EMA 的权重是呈指数衰减的。通常,获取过去 250 天到 300 天以上的数据作为“预热期”,计算到今天的误差就已经小到可以忽略不计了。建议将获取数据的长度加大,例如 history(300, '1d', 'close', ...)。
fq='pre')。前复权是以当前日期(回测的 current_dt 或当天的真实日期)为基准往回算。如果你在 JoinQuant 回测 2020 年的数据,它的前复权价格是基于 2020 年的基准;而你今天打开通达信看 2020 年的 K 线,它是基于今天的基准进行前复权的。如果这期间发生了分红派息,两边的历史价格就会完全不同。history 参数设置为 fq=None(不复权),同时把通达信也切换到“不复权”模式进行对比,这样排除了复权因子的干扰。talib.MACD 返回的第三个值(MACD histogram)是 DIF - DEA。(DIF - DEA) * 2。macd * 2,这一步是正确的,但很多新手会忽略这一点。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 值与通达信的误差会缩小到小数点后三位以内。