问题描述
各位前辈好,
我看函数库里关于MACD背离的策略基本上都是copy的如下版本
def doDecide(stock):
fast = 11
slow = 26
sign = 5
rows = (fast + slow + sign) * 5
suit = {'dif':0, 'dea':0, 'macd':0, 'gold':False, 'dead':False}
grid = attribute_history(stock, rows, fields=['close']).dropna()
stock = '600010.XSHG.XSHE' #自己设定的
try:
grid['dif'], grid['dea'], grid['macd'] = talib.MACD(grid['close'].values, fast, slow, sign)
grid = grid.dropna()
# 底背离----------------------------------------------------------------
mask = grid['macd']>0 #金叉
mask = mask[mask==True][mask.shift(1)==False]
key2 = mask.keys()[-2]
key1 = mask.keys()[-1]
#1.本次(key1指定的日期)金叉股价低于上一次(不是昨天,是key2指定的日期)金叉股价
#2.本次(key1指定的日期)金叉Dif值大于上一次(不是昨天,是key2指定的日期)金叉Dif值
#3.金叉出现,即MACD由前天的负值变为昨天的正值
suit['gold'] = grid.close[key2]>grid.close[key1] and \
grid.dif[key2]< grid.dif[key1]< 0 and \
grid.macd[-2]< 0< grid.macd[-1]
# 顶背离----------------------------------------------------------------
mask = grid['macd']< 0 #死叉
mask = mask[mask==True][mask.shift(1)==False]
key2 = mask.keys()[-2]
key1 = mask.keys()[-1]
suit['dead'] = grid.close[key2]< grid.close[key1] and \
grid.dif[key2]>grid.dif[key1]>0 and \
grid.macd[-2]>0>grid.macd[-1]
except:
#如果找不到金叉,则执行key2 = mask.keys()[-2]或会抛出异常,
#说明这么多行【rows = (fast + slow + sign) * 5】数据中找不到金叉
#直接返回suit
pass
return suit
code
在下小白刚刚接触量化,仅在网上看了不到一个周的python教程
看了上面代码之后有不少问题,总结了以下几条,希望各位大神可以不吝赐教,非常感谢!
Q1
rows = (fast + slow + sign) * 5
code
请问为什么要取这些天数的历史数据?是基于经验的结果吗?
===
Q2
suit = {'dif':0, 'dea':0, 'macd':0, 'gold':False, 'dead':False}
grid = attribute_history(stock, rows, fields=['close']).dropna()
try:
grid['dif'], grid['dea'], grid['macd'] = talib.MACD(grid['close'].values, fast, slow, sign)
code
第一行创建了一个suit字典,这只了dif、dea、macd等key的默认值
第二行把某股票rows天数的历史收盘价赋予了grid
请问最后一行对grid的再次赋值怎么理解?
是把第二行里rows天收盘价对应的dif、dea和MACD值,通过talib.MACD函数又给了grid吗?
这里我理解是对上面suit字典里各个key重新赋value,但之前的字典不是suit吗?为什么要用grid['dif']这种形式而不是suit['dif']?
还是用grid添加到之前suit这个字典里?
======
Q3
mask = grid['macd']>0 #金叉
mask = mask[mask==True][mask.shift(1)==False]
code
请问第二行表代码语法意思是什么?
两个中括号并列是and判断条件是否同时成立吗?
也就是先判断mask(也就是grid['macd'])是否大于零,并且mask下一个的值是否不大于零?
另外我百度.shift()函数代表的意思是一个矩阵里向下移动1行,这里的矩阵就是指grid在attribute_history里形成的以天为单位(省略了参数‘1d’)收盘价形成的矩阵吗?
===========
Q4
key2 = mask.keys()[-2]
key1 = mask.keys()[-1]
code
请问这里[-1]和[-2]的位置是指mask这个矩阵(也就是grid以attribute_history按天赋予的收盘价矩阵)的倒数第一/二行,还是倒数第一/二列?
==========
Q5
suit['gold'] = grid.close[key2]>grid.close[key1] and \
grid.dif[key2]< grid.dif[key1]< 0 and \
grid.macd[-2]< 0< grid.macd[-1]
code
请问为什么这里 grid.close和grid.dif用的是key1、key2来确定的位置,而grid.macd是直接用的[-1][-2]来确认的位置?
二者有什么区别?
而且这里-1、-2不就是grid这个集合里倒数第二个和倒数第三个的值吗?为什么在代码的文字说明部分却说“不是昨天,是key2指定的日期”呢?
如果我理解是对的话,请问有代码可以判断并找出“上一个低点(不是第二小的值)”的位置吗?
=======
Q6
如果我想看看 grid['dif'], grid['dea'], grid['macd']和mask的值各是什么(以600010或其他任意股票为背景),请问如何实现?
我输入print (grid['dif'])之后,点击编译运行,系统会报错……
感谢各位大神前辈
解决方案
你好!欢迎来到量化交易的世界。这段代码结合了 pandas 数据处理和 talib 技术指标库,对于刚接触 Python 不久的新手来说,确实包含了一些进阶的用法。下面为你逐一详细解答这 6 个问题:
Q1:为什么要取 rows = (fast + slow + sign) * 5 这么多天的历史数据?
解答:
这是基于数学计算原理和经验的结果。MACD 的核心是 EMA(指数移动平均线)。EMA 的计算公式会包含历史上所有的数据,越久远的数据权重越小。理论上需要无限长的数据才能算出绝对准确的 EMA,但在实际编程中,只要取周期之和的 3 到 5 倍长度的数据,早期数据的权重就会衰减到几乎为 0,从而保证算出来的 MACD 值与同花顺、通达信等行情软件上的数值基本一致。如果取的数据太短,算出来的 MACD 会有较大的初始误差。
Q2:grid['dif'] 是对 suit 字典赋值吗?
解答:
不是的。这里涉及两个完全不同的数据结构:
suit是一个 Python 字典 (dict),它的作用是作为这个函数的最终返回值,告诉策略这只股票当前的状态(是否金叉、是否背离等)。grid是一个 Pandas DataFrame(数据框,类似于 Excel 表格)。attribute_history返回的就是一个 DataFrame。
grid['dif'] = ... 的意思是:在 grid 这个表格中,新增一列,列名叫做 'dif',并把 talib.MACD 计算出来的一整列 DIF 数据填进去。这和 suit 字典没有任何关系,grid 只是用来做中间计算的草稿纸。
Q3:mask = mask[mask==True][mask.shift(1)==False] 是什么意思?
解答:
这行代码的目的是找出发生“金叉”的那一天。
mask = grid['macd'] > 0:这会生成一列布尔值(True/False),MACD 大于 0 的天数是 True,否则是 False。mask.shift(1):shift(1)是 Pandas 的函数,意思是把数据往下平移一行。所以mask.shift(1)代表的是昨天的 MACD 是否大于 0。mask[mask==True]:选出今天 MACD > 0 的日子。[mask.shift(1)==False]:在上面的基础上,进一步筛选出昨天 MACD <= 0 的日子。
总结: 今天 MACD > 0 且 昨天 MACD <= 0,这就是 MACD 由负转正的瞬间,也就是金叉日。两个中括号连在一起是 Pandas 的链式索引筛选,相当于“满足条件 A 的数据中,再筛选出满足条件 B 的数据”。
Q4:key2 = mask.keys()[-2] 中的 [-1] 和 [-2] 是指什么?
解答:
经过 Q3 的筛选,mask 里现在只剩下发生过金叉的那些天了。mask.keys() 获取的是这些金叉日的日期索引(时间戳)。
[-1]表示 Python 列表/数组中的倒数第一个元素,也就是**最近一次(本次)**发生金叉的日期。[-2]表示倒数第二个元素,也就是上一次发生金叉的日期。
Q5:grid.close[key2] 和 grid.macd[-2] 的区别是什么?
解答:
这是两种不同的定位方式:
- 按日期标签定位:
key1和key2是具体的日期(比如 2023-10-15)。grid.close[key2]的意思是:去grid表格里,把key2这一天(上一次金叉日)的收盘价取出来。 - 按绝对位置定位:
grid.macd[-1]和grid.macd[-2]中的-1和-2是指grid这个表格的最后一行(通常是今天/昨天)和倒数第二行(昨天/前天)。
为什么文字说明说“不是昨天,是key2指定的日期”?
文字说明解释的是 grid.close[key2] > grid.close[key1] 这一句,意思是比较“上一次金叉日”和“本次金叉日”的收盘价,而不是比较昨天和今天的收盘价。
而 grid.macd[-2] < 0 < grid.macd[-1] 这一句,确实是在判断最近两天(倒数第二天和最后一天)是否刚刚发生了金叉。
Q6:如何打印 grid['dif'] 和 mask 的值?系统报错怎么办?
解答:
系统报错很可能是因为你在代码里加了这一句:
stock = '600010.XSHG.XSHE'
股票代码的后缀写错了,JoinQuant 里的上汽集团代码应该是 '600010.XSHG',多了一个 .XSHE 会导致 attribute_history 找不到数据从而报错。
正确的打印方法:
把那行错误的 stock 赋值删掉(或者改成正确的 '600010.XSHG'),然后在 try 语句块里面使用 print 或者 log.info:
# 修正股票代码
stock = '600010.XSHG'
grid = attribute_history(stock, rows, fields=['close']).dropna()
try:
grid['dif'], grid['dea'], grid['macd'] = talib.MACD(grid['close'].values, fast, slow, sign)
grid = grid.dropna()
# 打印查看数据
print("DIF数据:\n", grid['dif'].tail()) # tail() 只打印最后5行,避免数据太长
print("MACD数据:\n", grid['macd'].tail())
mask = grid['macd']>0
mask = mask[mask==True][mask.shift(1)==False]
print("金叉日期:\n", mask)
在 JoinQuant 的研究环境(Jupyter Notebook)或回测日志中,你就可以看到打印出来的具体数值了。
祝你量化学习之路顺利!多用 print 打印中间变量是学习 Python 数据处理最好的方法。