《中文Python穿云箭量化平台二次开发技术10》基于Tkinter的可视化股票池量化平台开发技术
《中文Python穿云箭量化平台二次开发技术10》基于Tkinter的可视化股票池量化平台开发技术
在上一篇【《中文Python穿云箭量化平台二次开发技术09》设计一个可视化股票池量化平台项目用于实现选股和自动交易】,我们介绍了可视化量化平台的开发技术。我们利用穿云箭模块,花费了4天就完成了全部代码的设计,这篇我们给出部分关键代码的实现方法。
声明:本文中代码是【小白量化股票池】的部分代码,仅供个人学习参考,不得直接商用或销售。
在整个软件中,我们充分利用了穿云箭量化的现成金融模块,以及Python自带的GUI模块Tkinter很快能搭建出任何量化工具。
在股票池菜单设计中,我们要实现这些功能。【‘打开自选股文件’,‘增加自选股’,‘保存自选股文件’,‘板块股票’,‘问财选股’,‘清空股票池’】
实现的完整代码如下。
class gpc(tk.LabelFrame): #股票池def __init__(self, root=None,x=0,y=0,width=140, height=80,text='股票池',bg='#AA8888',bd=0,font='Helvetic 12',tag='kaishi',**options):global xm,xm2,xmi,xmm,gx,gy,gx2,gy2,mroot,top,gxb,gyb,gx2b,gy2b,statusglobal happ,hbegin,xma,xmbself.root=rootself.i=1 self.x=xself.y=yself.width=widthself.height=heightself.x2=self.x+widthself.y2=self.y+height self.name=textself.cd='200' #数据长度self.zq=4 #数据周期 self.bg=bgself.bd=bdself.font=fontself.bk=[]self.bk2=[]self.gs=''self.ty='gpc'tk.LabelFrame.__init__(self, root,text=text,bg=bg,bd=bd,font=font,**options) self.scrollbar=tk.Scrollbar(self)self.scrollbar.pack(side=tk.RIGHT,fill=tk.Y)self.lb=tk.Listbox(self,selectmode=tk.BROWSE,yscrollcommand=self.scrollbar.set,font =font) #height=5,self.lb.pack(expand=tk.YES,fill=tk.BOTH)self.scrollbar.config(command=self.lb.yview)self.place(x=self.x, y=self.y, width=self.width, height=self.height)self.init()def init(self):global xm,xm2,xmi,xmm,gx,gy,gx2,gy2,mroot,top,gxb,gyb,gx2b,gy2b,statusglobal happ,hbegin,xma,xmb def ldzxg(event=None): #打开自选股文件'filename = askopenfilename(defaultextension ='.blk',filetypes=[("板块文件","*.blk"),("txt文件","*.txt"),("板块文件","*.ebk")])if len(filename)==0:return(filepath,tempfilename) = os.path.split(filename)(filename2,extension) = os.path.splitext(tempfilename)extension=extension.lower() self.bk=[]if extension=='.blk':bk2=htdx.getzxgfile(filename)elif extension=='.txt':bk2=htdx.gettxtfile(filename)self.lb.delete(0,tk.END)l=len(bk2)self['text']="股票池(%d)"%lfor m,c in bk2:self.bk.append((m,c))line='('+str(m)+','+str(c)+')'self.lb.insert(tk.END,line)os.chdir(hg.mainpath)self.bk2=self.bkdef ldzxg2(event=None): #增加自选股filename = askopenfilename(defaultextension ='.blk',filetypes=[("板块文件","*.blk"),("txt文件","*.txt"),("板块文件","*.ebk")])if len(filename)==0:return(filepath,tempfilename) = os.path.split(filename)(filename2,extension) = os.path.splitext(tempfilename)extension=extension.lower() if extension=='.blk':bk3=htdx.getzxgfile(filename)elif extension=='.txt':bk3=htdx.gettxtfile(filename)bk4=set(bk3)-set(self.bk)bk2=list(bk4)#self.lb.delete(0,tk.END)l=len(bk2)+len(self.bk)self['text']="股票池(%d)"%lfor m,c in bk2:self.bk.append((m,c))line='('+str(m)+','+str(c)+')'self.lb.insert(tk.END,line)os.chdir(hg.mainpath)self.bk2=self.bkdef savezxg(event=None): #保存自选股文件filename = asksaveasfilename(defaultextension ='.blk',filetypes=[("板块文件","*.blk")])if len(filename)==0:return#print(filename)htdx.putzxgfile(self.bk,filename)os.chdir(hg.mainpath)self.bk2=self.bkdef getbk(event=None): #获取通达信板块中股票ss=simpledialog.askstring ("", "请输入通达信板块名",initialvalue='近期强势')try:if len(ss)>1:hq=htdx.TdxInit() ##初始化通达信bk2=htdx.getblockx(bk=ss)self.lb.delete(0,tk.END)self.bk=[]l=len(bk2)self['text']="股票池(%d)"%lfor m,c in bk2:self.bk.append((m,c))line='('+str(m)+','+str(c)+')'self.lb.insert(tk.END,line) except:passself.bk2=self.bkdef getwc(event=None): #清空股票池import wencaiglobal wctj,mroot#wctj=simpledialog.askstring ('问财选挂',"问财条件",initialvalue=wctj,parent=mroot)wctj=show_dialog()try:if len(wctj)>1:self.bk=[]data = wencai.get_wc(query=wctj)bk2=[]cc=data.股票代码.to_list()for c in cc:if c[7:9]=='SZ':m=0 elif c[7:9]=='SH': m=1else:m=0bk2.append((m,c[0:6])) self.lb.delete(0,tk.END)l=len(bk2)self['text']="股票池(%d)"%lfor m,c in bk2:self.bk.append((m,c))line='('+str(m)+','+str(c)+')'self.lb.insert(tk.END,line) except:print('问财出错!')passself.bk2=self.bkdef scgp(event=None):self.lb.delete(0,tk.END)self.bk=[]self.bk2=self.bkself['text']=self.name# 创建弹出菜单self.menubar=tk.Menu(self.lb)toolbarName2 = ('打开自选股文件','增加自选股','保存自选股文件','板块股票','问财选股','清空股票池')toolbarCommand2 = (ldzxg,ldzxg2,savezxg,getbk,getwc,scgp) def addPopButton(name,command):for (toolname ,toolcom) in zip(name,command):self.menubar.add_command(label=toolname,command=toolcom)def pop(event=None):# Menu 类里面有一个 post 方法,它接收两个参数,即 x 和y 坐标,它会在相应的位置弹出菜单。self.menubar.post(event.x_root,event.y_root)addPopButton(toolbarName2,toolbarCommand2) #创建弹出菜单self.lb.bind("<Button-3>",pop)
在策略处理模块中,我们实现的菜单很简单,【‘设置策略’,‘增加自选股’,‘保存自选股文件’,‘清空股票池’】,实现方法可以参考上面代码。
主要介绍设置【设置策略】的实现代码。
计算周期可以选:‘5分钟’,‘15分钟’, ‘30分钟’,‘1小时’,‘日线’,‘周线’,‘月线’,‘1分钟’,‘日线A’,‘季线’,'年线’等。
计算周期是获取的K线数量。
这个代码实现如下:
def szcl(self): #设置策略global xm,xm2,xmi,xmm,gx,gy,gx2,gy2,mroot,top,gxb,gyb,gx2b,gy2b,statusglobal happ,hbegin,xma,xmb,ico self.f2=f2=tk.Toplevel(mroot) #可视化子窗f2.iconbitmap(ico)f2.title('设置'+self.name) #Tkinter中设置窗口标题方法f2.attributes('-topmost',1) #参数1,设置顶层窗口,覆盖其它窗口。width=560height=480f2.geometry('{}x{}+{}+{}'.format(width,height, 320+610, 500)) #改变窗口位置和大小htk.setCenter(f2, width,height)v2=tk.LabelFrame(f2, text="策略参数")v2.pack(side=tk.TOP, fill=tk.X)label1=tk.Label(v2,text='计算周期:')label1.grid(row=0,column=0,padx=1,pady=1,sticky=tk.NSEW)self.zqvar = tk.StringVar()self.zqChosen = ttk.Combobox(v2, width=10, textvariable=self.zqvar)self.hczqa=('5分钟','15分钟', '30分钟','1小时','日线','周线','月线','1分钟','1分钟A','日线A','季线','年线')self.zqChosen['values'] = self.hczqaself.zqChosen.grid(row=0, column=1,padx=1, pady=1,sticky=tk.NSEW)self.zqChosen.current(self.zq) #设置初始显示值,值为元组['values']的下标self.zqChosen.config(state='readonly') #设为只读模式 label3=tk.Label(v2,text='周期数量:')label3.grid(row=1,column=0,padx=1,pady=1,sticky=tk.NSEW)self.cd2= tk.StringVar('')self.cd2.set(self.cd)self.entry2 = tk.Entry(v2,width=20, textvariable=self.cd2)self.entry2.grid(row=1,column=1,padx=1, pady=1,sticky=tk.NSEW)v3=tk.LabelFrame(f2, text="策略指标公式")v3.pack(side=tk.TOP, fill=tk.X)self.text=tk.Text(v3,height=16)self.text.pack(expand=1, fill=tk.BOTH)self.text.delete(1.0,tk.END)self.text.insert(1.0,self.gs)def qd(): #确定self.zq=self.zqChosen.current()self.cd=self.cd2.get()self.gs=self.text.get(1.0,tk.END+"-1c")xma[self.i]['cd']=self.cdxma[self.i]['zq']=self.zqxma[self.i]['gs']=self.gs print('确认操作')self.f2.destroy() def qx(): #取消print('取消操作')self.f2.destroy() v4=tk.Frame(f2)v4.pack(side=tk.TOP, fill=tk.X)bt1=tk.Button(v4,width=6,text='取消', command=qx,cursor='hand2')bt1.pack(side=tk.LEFT)lb2=tk.Label(v4,width=2,text=' ')lb2.pack(side=tk.LEFT)bt2=tk.Button(v4,width=6,text='确认', command=qd,cursor='hand2')bt2.pack(side=tk.RIGHT)
在输出模块中,要实现的菜单【‘读取上级计算结果’,‘保存自选股文件’,‘发邮件’,‘发微信’,‘发QQ’,‘发钉钉’,‘同花顺下单’,‘清空股票池’,‘设置’】
实现代码如下:
class sc(tk.LabelFrame): #输出方案def __init__(self, root=None,i=0,x=0,y=0,width=140, height=80,text='策略1',bg='#8888FF',bd=0,font='Helvetic 12',tag='kaishi',**options):self.root=rootself.i=iself.x=xself.y=yself.width=widthself.height=heightself.x2=self.x+widthself.y2=self.y+heightself.name=textself.bg=bgself.bd=bdself.font=fontself.cd='200' #数据长度self.zq=3 #数据周期 self.ty='sc'self.gs='' self.bk=[]self.bk2=[] self.CheckVar1 = tk.IntVar()self.CheckVar2 = tk.IntVar()self.CheckVar3 = tk.IntVar()self.CheckVar4 = tk.IntVar()self.CheckVar5 = tk.IntVar()self.CheckVar6 = tk.IntVar() tk.LabelFrame.__init__(self, root,text=text,bg=bg,bd=bd,font=font,**options) self.scrollbar=tk.Scrollbar(self)self.scrollbar.pack(side=tk.RIGHT,fill=tk.Y)self.lb=tk.Listbox(self,selectmode=tk.BROWSE,yscrollcommand=self.scrollbar.set,font =font) #height=5,self.lb.pack(expand=tk.YES,fill=tk.BOTH)self.scrollbar.config(command=self.lb.yview)self.place(x=self.x, y=self.y, width=self.width, height=self.height)self.init() self.update()def init(self):def ldzxg2(event=None): #获取自选股文件filename = askopenfilename(defaultextension ='.blk',filetypes=[("板块文件","*.blk"),("txt文件","*.txt"),("板块文件","*.ebk")])if len(filename)==0:return(filepath,tempfilename) = os.path.split(filename)(filename2,extension) = os.path.splitext(tempfilename)extension=extension.lower() if extension=='.blk':bk3=htdx.getzxgfile(filename)elif extension=='.txt':bk3=htdx.gettxtfile(filename)bk4=set(bk3)-set(self.bk)bk2=list(bk4)#self.lb.delete(0,tk.END)l=len(bk2)+len(self.bk)self['text']=self.name+"(%d)"%lfor m,c in bk2:self.bk.append((m,c))line='('+str(m)+','+str(c)+')'self.lb.insert(tk.END,line)os.chdir(hg.mainpath)self.bk2=self.bkdef savezxg(event=None): #保存自选股文件filename = asksaveasfilename(defaultextension ='.blk',filetypes=[("板块文件","*.blk")])if len(filename)==0:return#print(filename)htdx.putzxgfile(self.bk,filename)os.chdir(hg.mainpath)self.bk2=self.bkdef scgp(event=None): #清空股票池self.lb.delete(0,tk.END)self.bk=[]self.bk2=self.bkself['text']=self.namedef jg2(event=None): #获取上级模块计算出的股票池if self.i>1:x=xm2[self.i-1]bk2=x.bk2l=len(bk2)self['text']=self.name+"(%d)"%lfor m,c in bk2:self.bk.append((m,c))line='('+str(m)+','+str(c)+')'self.lb.insert(tk.END,line)os.chdir(hg.mainpath)self.bk2=self.bk #代码设计独狼,微信18578755056def thswt(bk,num=200): #同花顺委托global xm,xm2,xmi,xmm,gx,gy,gx2,gy2,mroot,top,gxb,gyb,gx2b,gy2b,statusglobal happ,hbegin,xma,xmb global ip,porthtdx.TdxInit(ip=ip,port=port) ##初始化同花顺软件ths.connect( "C:\\同花顺软件\\同花顺\\xiadan.exe")ths.enable_type_keys_for_editor()资金状况=ths.balancezjye=资金状况['资金余额']for m,c in bk:pk=htdx.get_security_quotes2(m,c)ask1=pk[0]['ask1']je=ask1*num*1.001if zjye>je:ths.buy(c, price=ask1, amount=num) status.text(2,'同花顺委托完成!') def thsxd2(event=None): #同花顺下单bk=self.bknum=int(self.cd)thswt(bk,num)def to_qq(event=None): #发QQ信息bk=self.bknum=int(self.cd)txl=self.gstxl2=txl.split('\n')for who in txl2:who=who.strip()#print(who)if len(who)>1:for m,c in bk:hts.send_qq(who,'发出信号:'+c)def to_wx(event=None): #发微信bk=self.bknum=int(self.cd)txl=self.gstxl2=txl.split('\n')for who in txl2:who=who.strip()print(who)if len(who)>1:for m,c in bk:hts.send_wx(who,'发出信号:'+c)# 创建弹出菜单self.menubar=tk.Menu(self.lb)toolbarName2 = ('读取上级计算结果','保存自选股文件','发邮件','发微信','发QQ','发钉钉','同花顺下单','清空股票池','设置')toolbarCommand2 = (jg2,savezxg,None,None,to_qq,None,thsxd2,scgp,self.szsc) def addPopButton(name,command):for (toolname ,toolcom) in zip(name,command):self.menubar.add_command(label=toolname,command=toolcom)def pop(event=None):# Menu 类里面有一个 post 方法,它接收两个参数,即 x 和y 坐标,它会在相应的位置弹出菜单。self.menubar.post(event.x_root,event.y_root)addPopButton(toolbarName2,toolbarCommand2) #创建弹出菜单self.lb.bind("<Button-3>",pop)
输出设置如下:
实现方法类似处理模块的设置,这里省略。
要注意一点,在使用Tkinter中一定要使用多线程来处理。
不然在执行策略时,应用窗口会卡住。我们这里把【执行策略】的代码,在一个新线程中运行。因此在策略执行过程中,可以任意移动模块位置,以及修改模块的大小。
正在执行的策略,变为红色。我们仍然可以移动或设置模块位置和大小。
本文介绍的知识,均可以在穿云箭量化中策略或执行代码中。
前面几篇博客,我们介绍了利用《中文Python穿云箭量化平台》的Python模块,打造自己新一代的量化工具。这些工具包括行情软件,量化框架平台,中文Python代码集成开发工具,以及任意Tkinter可视化操作的应用程序。
好了,欢迎继续关注我的博客。后面我们介绍更多的二次开发技术。
超越自己是我的每一步!我的进步就是你的进步!