《中文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可视化操作的应用程序。
好了,欢迎继续关注我的博客。后面我们介绍更多的二次开发技术。
超越自己是我的每一步!我的进步就是你的进步!