接下来将要讲解的实例是一个简单的量化选股脚本,通过调用Tushare获取股票数据,然后根据不同的选股模型来筛选符合条件的股票,并将结果保存到文件中。用户可以根据自己的需求选择不同的选股模型和参数。请注意,该代码仅供学习和参考,实际应用时需要根据自己的需求和风险管理进行进一步的开发和测试。
实例6-4:量化选股程序(源码路径:daima/6/stock_online_dev.py)
6.3.1 Tushare令牌初始化
编写Tushare令牌初始化函数Initial(),用于设置Tushare令牌,并初始化Tushare客户端。这是获取股票数据的前提步骤。具体实现代码如下所示。
def Initial():#初始化
# 设置Tushare令牌
token = ''
ts.set_token(token)
# 初始化Tushare客户端
pro=ts.pro_api()
return pro
6.3.2 辅助函数
编写辅助函数Stocklist()、Wait(n)和函数Menu(),其中函数Stocklist()用于获取A股市场的股票列表,包括股票代码等信息;Wait(n)是一个简单的辅助函数,用于等待一段时间,以控制访问频率,避免频繁访问Tushare API。函数Menu()用于显示用户菜单,让用户选择不同的选股模型。根据用户选择,它将调用不同的选股策略函数。具体实现代码如下所示。
def Stocklist():#获取股票列表
sl=pro.stock_basic(exchange='',list_status='L',fields='ts_code')
return sl
# print(sl) #调试用
def Wait(n):
i=0
while i<n:
i+=1
def Menu():
print("请选择选股模型:")
print("1.均线金叉模型")
print("2.均线压制K线多时,K线站上均线模型")
print("3.均线拐头模型")
print("4.趋势模型")
print("all:以上所有模型")
choice=input()
if choice=="1":
freq=input("请输入均线周期:")
mas=input("请输入短期均线:")
mal=input("请输入长期均线:")
n=input("请输入跨越的周期长度:")
m=input("几天内金叉:")
result=GoldenCross(freq,int(mas),int(mal),int(n),int(m))
filename=freq+'cross'+mas+mal+'_'+n+'_'+m+'_'+now+'.txt' #文件名
SaveResult(filename,result) #保存结果
elif choice=="2":
freq=input("请输入均线周期:")
mas=input("请输入均线:")
n=input("请输入跨越的周期长度:")
m=input("K线在多长时间内站上均线:")
result=Suppress(freq,int(mas),int(n),int(m))
filename=freq+'suppress'+mas+'_'+n+"_"+m+'_'+now+'.txt' #文件名
SaveResult(filename,result) #保存结果
elif choice=='3':
freq=input("请输入均线周期:")
ma_s=input("请输入均线:")
n=input("请输入跨越的周期长度:")
m=input("均线拐头天数:")
result=Bottom(freq,int(ma_s),int(n),int(m))
filename=freq+'bottom'+ma_s+'_'+n+"_"+m+'_'+now+'.txt' #文件名
SaveResult(filename,result) #保存结果
elif choice=='4':
freq=input("请输入均线周期:")
ma_s=input("请输入均线:")
n=input("请输入跨越的周期长度:")
m=input("均线趋势上扬时间:")
result=Suppress(freq,int(ma_s),int(n),int(m))
filename=freq+'trend'+ma_s+'_'+n+"_"+m+'_'+now+'.txt' #文件名
SaveResult(filename,result) #保存结果
elif choice=='all':
print("正在设定均线金叉模型参数:")
freq1=input("请输入均线周期:")
mas=input("请输入短期均线:")
mal=input("请输入长期均线:")
n1=input("请输入跨越的周期长度:")
result=Cross(freq1,int(mas),int(mal),int(n1))
print("正在设定均均线压制K线多时,K线站上均线模型参数:")
freq2=input("请输入均线周期:")
n2=input("请输入跨越的周期长度:")
result=Suppress(freq2,int(n2))
6.3.3 保存结果
函数SaveResult(filename, result)用于将选股结果保存到指定的文件中,具体实现代码如下所示。
def SaveResult(filename,result):
with open ('G:/result/'+filename,'w') as f:
for i in result:
details=StockDetails(i)
Wait(50000)
for j in details:
f.write(j)
f.write('\n')
f.close()
6.3.4 股票详情
函数StockDetails(ts_code)用于获取特定股票的详细信息,具体实现代码如下所示。
def StockDetails(ts_code):
details=[] #记录股票详细信息
tmp=pro.stock_basic(ts_code=ts_code) #获取DataFrame结构的信息
for i in tmp.loc[0]: #遍历DataFrame中的label:0
if i!=None:
details.append(i+'\t')
return details
6.3.5 选股策略
在本程序中提供了多种选股策略,每种选股策略均有独立的功能函数实现,具体说明如下:
- 函数Cross(freq, mas, mal, n):实现均线交叉选股策略。它会遍历股票列表,检查每支股票的短期均线是否在长期均线上方,并且在过去n个交易日内发生了金叉情况。
- 函数GoldenCross(freq, mas, mal, n, m):实现均线金叉选股策略。它会遍历股票列表,检查每支股票的短期均线是否在长期均线上方,并且在过去n个交易日内发生了金叉情况。此外,还要求金叉后的m天内价格上涨。
- 函数Suppress(freq, mas, n, m):实现K线站上均线选股策略。它会遍历股票列表,检查每支股票的K线是否站上了均线,并且在过去n个交易日内发生了这种情况。
- 函数Trend(freq, ma_s, n):实现单调递增选股策略。它会遍历股票列表,检查每支股票的均线是否在过去n个交易日内保持单调递增。
- 函数Bottom(freq, ma_s, n, m):实现均线拐点选股策略。它会遍历股票列表,检查每支股票的均线是否在过去n个交易日内发生了拐点,并且拐点后的m天内价格上涨。
上述选股函数的具体实现代码如下所示。
def Cross(freq,mas,mal,n): #均线交叉
count=0 #计数
total=len(sl['ts_code']) #总上市股票数
result=[]
for i in sl['ts_code']:
os.system('clear')
count+=1 #每判断一个股票,计数加1
print('进度:'+str(count)+'/'+str(total)) #显示已判断股票数的比例
print('正在比对:'+i) #调试用
data=ts.pro_bar(ts_code=i,freq=freq,adj='qfq',start_date=previous,end_date=now,ma=[mas,mal])
if data is None: #如果没有获取到任何数据,比如刚上市又还没上市的股票
continue
ma_s=data['ma'+str(mas)] #提取短期均线
ma_l=data['ma'+str(mal)] #提取长期均线
if len(ma_s)<n or len(ma_l)<n: #判断是否有空值
# print(i+'上市时间过短')
continue
if (ma_s[0]-ma_l[0])>0 and (ma_s[n]-ma_l[n])<0:
result.append(i)
# print('捕获:'+data['ts_code'])
if freq=='W':
Wait(15000) #等待一段时长,防止频率过快,受限于帐号积分
if freq=='M':
Wait(5000000) #等待一段时长,防止频率过快,受限于帐号积分
# print(ma13)
# plt.plot(data['trade_date'],data['ma13'])
# plt.plot(data['trade_date'],data['ma55'])
# plt.show()
return result
def GoldenCross(freq,mas,mal,n,m): #均线金叉
count=0 #计数
total=len(sl['ts_code']) #总上市股票数
result=[]
for i in sl['ts_code']:
os.system('clear')
count+=1 #每判断一个股票,计数加1
print('进度:'+str(count)+'/'+str(total)) #显示已判断股票数的比例
print('正在比对:'+i) #调试用
data=ts.pro_bar(ts_code=i,freq=freq,adj='qfq',start_date=previous,end_date=now,ma=[mas,mal])
if data is None: #如果没有获取到任何数据,比如刚上市又还没上市的股票
continue
ma_s=data['ma'+str(mas)] #提取短期均线
ma_l=data['ma'+str(mal)] #提取长期均线
if len(ma_s)<n or len(ma_l)<n: #判断是否有空值
continue
if ma_s[0]>ma_l[0]: #判断短期均线是不是在长期均线上方
j=1
crosspoint=0 #初始值为0,假设1天内出现金叉的情况
while j<=n: #判断之前的收盘价是不是在均线下文,以此寻找刚启动的行情
if ma_s[j]>ma_l[j]: #判断短期均线是否还在长期均线上方,不是则交叉点已经出现
crosspoint=j #记录金叉时的天数
j=j+1
else:
if crosspoint<=m: #判断交叉点是否在要求的时间段里,是则继续判断
j+=1
if j==n: #交叉前的N天,短期均线都在长期均线下方,可以断定为金叉
result.append(i)
else:
break
if freq=='W':
Wait(20000) #等待一段时长,防止频率过快,受限于帐号积分
if freq=='M':
Wait(9000000) #等待一段时长,防止频率过快,受限于帐号积分
return result
def Suppress(freq,mas,n,m): #K线站上均线模型
count=0 #计数
total=len(sl['ts_code']) #总上市股票数
result=[]
for i in sl['ts_code']:
os.system('clear')
count+=1 #每判断一个股票,计数加1
print('Suppress进度:'+str(count)+'/'+str(total)) #显示已判断股票数的比例
print('正在比对:'+i) #调试用
data=ts.pro_bar(ts_code=i,freq=freq,adj='qfq',start_date=previous,end_date=now,ma=[mas])
# data1=ts.pro_bar(ts_code=i,freq='D',adj='qfq',start_date=previous,end_date=now,ma=[mas])
# data.to_csv('/tmp/online.csv')
# break
if data is None: #如果没有获取到任何数据,比如刚上市又还没上市的股票
continue
Wait(20000000) #等待一段时长,防止频率过快,受限于帐号积分
close=data['close']
ma=data['ma'+str(mas)]
if len(close)<n or len(ma)<n: #判断是否有空值
continue
if close[0]>ma[0]: #判断最新收盘价是不是在均线上方
j=1
point=0
while j<=n: #判断之前的收盘价是不是在均线下文,以此寻找刚启动的行情
if close[j]>ma[j]: #判断K线是否还在长期均线上方,不是则突破压制
point=j #记录突破时的天数
j=j+1
else:
if point==m: #判断突破是否在要求的时间段里,是则继续判断
if j==n: #突破前的N段时间内,都在均线下方,突破成立
result.append(i)
j+=1
else:
break
return result
def Trend(freq,ma_s,n): #单调递增模型
count=0 #计数
total=len(sl['ts_code']) #总上市股票数
result=[]
for i in sl['ts_code']:
os.system('clear')
count+=1 #每判断一个股票,计数加1
print('进度:'+str(count)+'/'+str(total)) #显示已判断股票数的比例
print('正在比对:'+i) #调试用
data=ts.pro_bar(ts_code=i,freq=freq,adj='qfq',start_date=previous,end_date=now,ma=[ma_s])
if data is None: #如果没有获取到任何数据,比如刚上市又还没上市的股票
continue
ma=data['ma'+str(ma_s)]
if len(ma)<n: #判断是否有空值
continue
j=0 #循环初始化
while ma[j]>ma[j+1]:
if j==n: #n天内,均线单调递增
result.append(i)
break
j+=1
if freq=='W':
Wait(5000000) #等待一段时长,防止频率过快,受限于帐号积分
if freq=='M':
Wait(5000000) #等待一段时长,防止频率过快,受限于帐号积分
return result
def Bottom(sl,freq,ma_s,n,m): #均线拐点
global previous # 使用全局的 previous 变量
global q # 使用全局的 q 变量
count=0 #计数
total=len(sl['ts_code']) #总上市股票数
result=[]
for i in sl['ts_code']:
os.system('clear')
count+=1 #每判断一个股票,计数加1
print('Bottom进度:'+str(count)+'/'+str(total)) #显示已判断股票数的比例
print('正在比对:'+i) #调试用
data=ts.pro_bar(ts_code=i,freq=freq,adj='qfq',start_date=previous,end_date=now,ma=[ma_s])
if data is None: #如果没有获取到任何数据,比如刚上市又还没上市的股票
continue
ma=data['ma'+str(ma_s)] #提取均线
close=data['close'] #提取收盘价
if len(ma)<n: #判断是否有空值
continue
if close[0]<ma[0]: #收盘价在均线上方,如在均线下文,则为弱势
continue
j=0 #初始化
if ma[j]>ma[j+1]: #判断当前均值是否大于前一天,即均线拐头,如不是,则均线向下,不合要求,排除
j=j+1
while ma[j]>ma[j+1]: #向前递归,直到出现拐点
j=j+1
point=j #记录拐点
else:
continue
if point==m: #拐点是否在要求的时间
while ma[j]<=ma[j+1]: #向前递归,拐点前是否单调递减
j=j+1
if j==n: #直到规定的时间内,都是单调递减,则输出
result.append(i)
break #捕捉到致富代码,则退出循环,寻找下一个
if freq=='D':
Wait(5000000) #等待一段时长,防止频率过快,受限于帐号积分
if freq=='W':
Wait(500000) #等待一段时长,防止频率过快,受限于帐号积分
if freq=='M':
Wait(950000000) #等待一段时长,防止频率过快,受限于帐号积分
q.put(result)
return result
6.3.6 主程序
__main__ 部分是本实例的主程序部分,包含了代码的执行流程。首先初始化Tushare客户端,获取股票列表,然后根据用户菜单选择的不同选股策略调用相应的策略函数。在本例中,以 Bottom 为例进行演示。具体实现代码如下所示。
####主程序####
if __name__ == '__main__':
pro=Initial() #初始化
q=Queue()
now=time.strftime("%Y%m%d") #当前日期
#now='20201230'
previous=int(now)-30000 #一年前的日期
previous=str(previous) #转换成字符串
sl=Stocklist() #股票列表
# sl=sl[1:20] #调试用,限制股票数量以减短时间
# global result
# result=[]#全局变量,记录结果
start=time.time()
t1=Process(target=Bottom,args=(sl[100:200],'D',13,15,1))
t1.start()
t1.join()
end=time.time()
result=q.get()
print(end-start)
print(result)
# Menu()
#result=Cross('W',13,55,2) #调试用
#result=Suppress('M',8,10) #调试用
#result=GoldenCross('D',8,21,3,1) #调试用
#result=Trend('W',55,50) #调试用
#result=Bottom('D',55,10,2) #调试用
#SaveResult('test',result) #调试用
另外,在主函数的注释掉的测试代码中,有一些示例代码用于测试不同的选股策略函数。这些示例代码以不同的策略调用函数,并且用于演示如何使用各个选股策略。以下是对被注释掉示例代码的说明:
- result=Cross('W',13,55,2):这行代码演示了如何使用Cross策略函数,其中参数包括选股的频率为周线('W'),短期均线周期为13,长期均线周期为55,跨越的周期长度为2。这个示例会检查过去的周线数据,查找金叉情况。
- result=Suppress('M',8,10):这行代码演示了如何使用Suppress策略函数,其中参数包括选股的频率为月线('M'),均线周期为8,跨越的周期长度为10。这个示例会检查过去的月线数据,查找K线是否站上均线的情况。
- result=GoldenCross('D',8,21,3,1):这行代码演示了如何使用GoldenCross策略函数,其中参数包括选股的频率为日线('D'),短期均线周期为8,长期均线周期为21,跨越的周期长度为3,金叉后的价格上涨天数为1。这个示例会检查过去的日线数据,查找金叉情况,并要求金叉后的第一天价格上涨。
- result=Trend('W',55,50):这行代码演示了如何使用Trend策略函数,其中参数包括选股的频率为周线('W'),均线周期为55,跨越的周期长度为50。这个示例会检查过去的周线数据,查找均线是否保持单调递增的情况。
- result=Bottom('D',55,10,2):这行代码演示了如何使用Bottom策略函数,其中参数包括选股的频率为日线('D'),均线周期为55,跨越的周期长度为10,均线拐头天数为2。这个示例会检查过去的日线数据,查找均线是否发生了拐点,并要求拐点后的价格上涨。
上述被注释掉的示例代码可以帮助用户了解如何使用不同的选股策略函数,并根据自己的需求进行自定义配置。在实际使用时,可以取消注释这些代码,并根据具体的选股需求进行调整和测试。