Bootstrap

《中文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,status
        global happ,hbegin,xma,xmb
        self.root=root
        self.i=1        
        self.x=x
        self.y=y
        self.width=width
        self.height=height
        self.x2=self.x+width
        self.y2=self.y+height        
        self.name=text
        self.cd='200'  #数据长度
        self.zq=4   #数据周期        
        self.bg=bg
        self.bd=bd
        self.font=font
        self.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,status
        global 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)"%l
            for 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

        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']="股票池(%d)"%l
            for 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

        def 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.bk


        def 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)"%l
                    for m,c in bk2:
                        self.bk.append((m,c))
                        line='('+str(m)+','+str(c)+')'
                        self.lb.insert(tk.END,line)                    
            except:
                pass
            self.bk2=self.bk

                
        def getwc(event=None): #清空股票池
            import wencai
            global 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=1
                       else:
                           m=0
                       bk2.append((m,c[0:6]))  
                   self.lb.delete(0,tk.END)
                   l=len(bk2)
                   self['text']="股票池(%d)"%l
                   for m,c in bk2:
                        self.bk.append((m,c))
                        line='('+str(m)+','+str(c)+')'
                        self.lb.insert(tk.END,line)                    
            except:
                print('问财出错!')
                pass
            self.bk2=self.bk
            
        def scgp(event=None):
            self.lb.delete(0,tk.END)
            self.bk=[]
            self.bk2=self.bk
            self['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,status
        global 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=560
        height=480
        f2.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.hczqa
        self.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.cd
            xma[self.i]['zq']=self.zq
            xma[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=root
        self.i=i
        self.x=x
        self.y=y
        self.width=width
        self.height=height
        self.x2=self.x+width
        self.y2=self.y+height
        self.name=text
        self.bg=bg
        self.bd=bd
        self.font=font
        self.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)"%l
            for 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

        def 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.bk

        def scgp(event=None):   #清空股票池
            self.lb.delete(0,tk.END)
            self.bk=[]
            self.bk2=self.bk
            self['text']=self.name

        def jg2(event=None):   #获取上级模块计算出的股票池
            if self.i>1:
                x=xm2[self.i-1]
                bk2=x.bk2
                l=len(bk2)
                self['text']=self.name+"(%d)"%l
                for 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 

        #代码设计独狼,微信18578755056
        def thswt(bk,num=200):  #同花顺委托
            global xm,xm2,xmi,xmm,gx,gy,gx2,gy2,mroot,top,gxb,gyb,gx2b,gy2b,status
            global happ,hbegin,xma,xmb      
            global ip,port
            htdx.TdxInit(ip=ip,port=port) 
            ##初始化同花顺软件
            ths.connect( "C:\\同花顺软件\\同花顺\\xiadan.exe")
            ths.enable_type_keys_for_editor()
            资金状况=ths.balance
            zjye=资金状况['资金余额']
            for m,c in bk:
                pk=htdx.get_security_quotes2(m,c)
                ask1=pk[0]['ask1']
                je=ask1*num*1.001
                if zjye>je:
                    ths.buy(c, price=ask1, amount=num) 
                
            status.text(2,'同花顺委托完成!')       
                
        def thsxd2(event=None):    #同花顺下单
            bk=self.bk
            num=int(self.cd)
            thswt(bk,num)

        def to_qq(event=None):   #发QQ信息
            bk=self.bk
            num=int(self.cd)
            txl=self.gs
            txl2=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.bk
            num=int(self.cd)
            txl=self.gs
            txl2=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可视化操作的应用程序。
好了,欢迎继续关注我的博客。后面我们介绍更多的二次开发技术。

超越自己是我的每一步!我的进步就是你的进步!

;