量化之macd的计算
股市从9月底开始,似乎开始活过来了,于是又开始研究了一下量化。想试试macd相关策略的回测,结果发现,用talib算出来的macd和券商的通达信软件的数据差别挺大的,于是对这个算法做了点“深入”研究。
在知乎上搜到了一篇不错的文章:
具体看原文,这里主要记录自己的应用。
递归式
macd计算中的关键就是EMA的计算,而根据EMA的算法定义可以推出一个递归的公式,借图如下:

这个公式很好理解,计算起来也很方便,问题在于我们需要一个Xn-1的初始值。
原理式
原文作者为了讲解对其原理的理解,推导出一个新的公式,正好可以用于计算初始值:
EMA = (1*X1 + 2*X2 + ... + n*Xn)/(1+2+...+n)
所以EMA的原理其实就是每个参数值依据所在位置,权重不同,再计算出平均值。
上面的“递归式”和“原理式”是我自己编的一个名词,便于后文叙述。
算法实现
有了上面两个公式,用代码实现macd就很简单了,具体思路就是,在初始值没出现前,使用原理式计算出初始值,有了初始值之后,用递归式计算后续数据。
计算结果测试
简单测试计算结果后,看到的效果是:
在数据量较小时,本算法的计算结果与通达信的偏差相对talib的macd函数要小一点
在数据量较多(约300个数值)时,本算法和talib的macd公式的结果都很接近通达信数据,偏差不超过0.02%
关于算法的应用
既然数据量较大时,talib的结果完全可用,我何必造个新轮子呢,主要在于两点考虑:
出于多年开发工作的习惯,喜欢节省计算资源,talib由于是个黑箱,每次计算必须给它足够多的数据来计算,而自己写的函数,可以在初次算好一批数据后,新的数据只需要拿前一天的数据代入即可算出新的macd值(但量化平台似乎并不在乎这点计算资源)。
由于对python不那么熟练,在量化平台上写代码,出错在所难免,出了错有时候报错信息并不那么清晰,我想要在自己的python环境里把大部分代码调通了,再上量化平台写点取数据的代码就能通,但talib本地安装的依赖还挺多的,有点不想折腾安装。
我知道有些量化平台支持本地安装,但一般都是需要付费的,我目前还在对策略的学习研究阶段,等到能靠它赚钱再付费不迟。
小结
虽然自己搞macd的计算似乎不是非常必要,但在特定情况下多少还有点用,并且加深了一点数学知识的理解,因此小记一篇。
附 - 代码
# close: a list
def macd(close, short=12, long=26, signal=9):
ema_short, ema_long, dif, dea, bar = [], [], [], [], []
for index, item in enumerate(close):
if index < short - 1:
ema_short.append(np.nan)
if index == short - 1:
ema_short.append(ema1(close, index, short))
if index > short - 1:
ema_short.append(ema2(item, ema_short[index-1], short))
if index < long - 1:
ema_long.append(np.nan)
dif.append(np.nan)
if index == long - 1:
ema_long.append(ema1(close, index, long))
dif.append(ema_short[index] - ema_long[index])
if index > long - 1:
ema_long.append(ema2(item, ema_long[index-1], long))
dif.append(ema_short[index] - ema_long[index])
if index < long + signal - 2:
dea.append(np.nan)
bar.append(np.nan)
if index == long + signal - 2:
dea.append(ema1(dif, index, signal))
bar.append((dif[index] - dea[index]) * 2)
if index > long + signal - 2:
dea.append(ema2(dif[index], dea[index-1], signal))
bar.append((dif[index] - dea[index]) * 2)
return ema_short, ema_long, dif, dea, bar
def ema1(close, last_index, n):
#分子 分母
accn, accm = 0, 0
for i in range(n):
val = close[last_index - i]
accn = accn + val * (n - i)
accm = accm + i + 1
# print(i, val, accn, accm)
return accn / accm
def ema2(val, last_ema, n):
return (2 * val + (n - 1) * last_ema) / (n + 1)