前言
如果有对于股票数据分析或者量化交易经典策略–双均线策略不熟悉的读者,可以参考一下我之前写的文章: [python金融数据分析及可视化] [Python量化交易策略及回测系统] (好像不让放超链接,大家可以到我的主页上去看>_<)
一、概念
GUI(Graphical User Interface)意为图形用户界面,在python中,可以利用tkinter模块来开发用户操作界面,然后通过键盘和鼠标等输入设备,操作屏幕上的文本框或命令框等控件来完成一些任务。
双均线策略:构建周期不同的两根移动平均线,如5日移动平均线与30日移动平均线;然后利用两者的交叉点进行股票买卖,如当5日移动平均线从下往上穿过30日移动平均线时,买入股票并持有,当5日移动平均线从上往下穿过30日移动平均线时,卖出股票。
本文利用GUI编程开发用户操作界面,以此来绘制动态的K线及双均线,实现界面动态交互效果
二、tkinter的使用
在下面实战案例中,主要利用的GUI编程知识有:tkinter的基本用法——带标签的文本框、带标签的按钮以及弹出框的显示。在进入案例前先学习一下这些功能的实现
1、模块的导入
import tkinter
import tkinter.messagebox
首先是导入tkinter模块来实现GUI界面的开发,而tkinter.messagebox 是用于弹出框的显示
2、窗口和文本框的创建
loginwin = tkinter.Tk()
loginwin.geometry('220x120')
loginwin.title('登录窗口')
其次是窗口的创建,以及窗口大小、窗口标题的定义
tkinter.Label(loginwin,text='用户名:').place(x=10,y=20)
tkinter.Label(loginwin,text='密 码:').place(x=10,y=50)
接下来是创建两个标签对象用于提示文本框输入的信息,以及这两个标签的文本、位置的设定
userval = tkinter.StringVar()
pwdval = tkinter.StringVar()
userentry = tkinter.Entry(loginwin, textvariable=userval)
userentry.place(x=65,y=20)
pwdentry = tkinter.Entry(loginwin,textvariable=pwdval,show='*')
pwdentry.place(x=65,y=50)
然后tkinter.Entry() 是文本框的定义,并指定文本框输入的内容存放于tkinter.StringVal() 数据类型的对象userval和pwdval中,且将密码的显示改为*
3、处理函数的定义
def check():
username = userval.get()
pwd = pwdval.get()
print('用户名:' + username)
print('密 码:' + pwd)
if(username == 'aaa' and pwd == '123'):
tkinter.messagebox.showinfo('提示','登录成功')
else:
if(username == 'aaa' and pwd != '123'):
tkinter.messagebox.showinfo('提示','密码错误')
else:
tkinter.messagebox.showinfo('提示','登录失败')
随后自定义函数check()用于连接按钮,执行点击按钮后的对应操作,其中tkinter.messagebox.showinfo() 是弹出窗口定义
4、按钮的创建
tkinter.Button(loginwin, text='登录',width=12,command=check).place(x=10,y=85)
tkinter.Button(loginwin, text='退出',width=12,command=loginwin.quit).place(x=120,y=85)
tkinter.mainloop()
最后tkinter.Botton() 用于创建命令按钮,参数定义了按钮的文本、大小以及调用的函数,即单击该按钮后触发的函数。tkinter.mainloop() 是开启主循环,用于监听键盘、鼠标等输入设备操作事件,当事件(点击)发生触发相应方法(登录/退出)。
5、运行结果
运行之后就可以得到: 输入正确的账号密码: 输入密码错误: 输入的账号不存在:
三、实战案例——根据界面输入绘制动态K线和均线图
1、模块的导入
import pandas as pd
import akshare as ak
import matplotlib.pyplot as plt
from mpl_finance import candlestick2_ochl
import tkinter
import tkinter.messagebox
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
以上的大部分模块对我们来说已经是非常熟悉了,除了最后一个FigureCanvasTkAgg ,该模块用于创建canvas控件,整合matplotlib库和tkinter库,使matplotlib库的图形可以导入tkinter库的窗口界面之中。
2、窗口界面和文本框的创建
和前面一样,导入模块之后就是创建窗口界面和文本框了。
win = tkinter.Tk()
win.geometry('625x600')
win.title('股票K线均线')
tkinter.Label(win, text='股票代码:').place(x=10,y=20)
tkinter.Label(win, text='开始时间:').place(x=10,y=50)
tkinter.Label(win, text='结束时间:').place(x=10,y=80)
根据需要,设置窗口的大小;以及文本框的标签的文本、位置
stockcodeval = tkinter.StringVar()
startdateval = tkinter.StringVar()
enddateval = tkinter.StringVar()
stockcodeentry = tkinter.Entry(win, textvariable=stockcodeval)
stockcodeentry.place(x=70,y=20)
stockcodeentry.insert(0,"sh000001")
startdateentry = tkinter.Entry(win, textvariable=startdateval)
startdateentry.place(x=70,y=50)
startdateentry.insert(0,"20190101")
enddateentry = tkinter.Entry(win, textvariable=enddateval)
enddateentry.place(x=70,y=80)
enddateentry.insert(0,"20190630")
大家别看文本框创建的代码就有十几行而看不下去,其实它就只有4行,然后3个文本框重复操作而显得有点复杂而已(下面也有相类似的地方),在这里多了一个insert()函数用于设置文本框的默认值
3、处理函数的定义
以下自定义了6个函数,其中4个用于连接最后定义的4个按钮,其他两个用于辅助操作的实现。
3.1、获取股票数据
def getstockdata(stockcode, startdate, enddate):
stock_df = ak.stock_zh_a_daily(symbol=stockcode, start_date=startdate, end_date=enddate)
filename = 'D:\\' + stockcode + startdate + enddate + '.csv'
stock_df.to_csv(filename)
有看过之前的文章的读者一定不陌生,这个函数中ak.stock_zh_a_daily() 是用于获取新浪财经A股个股的历史行情数据的。在这里我们将获取的数据储存起来,以便后面读取数据。
3.2、【绘制】按钮的处理函数
以下两个自定义函数是可以合成一个的,但为了分析就拆成了两个
def draw():
plt.clf()
stockcode = stockcodeval.get()
startdate = startdateval.get()
enddate = enddateval.get()
drawline(stockcode, startdate, enddate)
第一个自定义函数是用于获取文本框输入的内容,并将信息传给下一个函数用于绘制对应的图形
def drawline(stockcode, startdate, enddate):
filename = 'D:\\' + stockcode + startdate + enddate + '.csv'
getstockdata(stockcode, startdate, enddate)
df = pd.read_csv(filename, encoding='gbk')
ax = figure.add_subplot(111)
candlestick2_ochl(ax=ax, opens=df['open'].values, closes=df['close'].values,
highs=df['high'].values, lows=df['low'].values, width=0.75,colorup='red',colordown='green')
df['close'].rolling(window=5).mean().plot(label='5日均线')
df['close'].rolling(window=20).mean().plot(label='20日均线')
plt.legend(loc='best')
ax.grid(True)
plt.title("股票{}的K线图".format(stockcode))
plt.rcParams['font.sans-serif']=['SimHei']
major_index = df.index[df.index%10==0]
major_xticks = df['date'][df.index%10==0]
plt.xticks(major_index,major_xticks)
plt.setp(plt.gca().get_xticklabels(),rotation=30)
canvas.draw()
在得到要绘制的图形的信息之后,自定义函数drawline()先调用第一个函数获取股票数据,然后根据数据绘制K线图和均线图(想必大家都已通读前置文章了解清楚了,我就不过多的赘述了)
最后对图形的x轴数据进行设置,并利用canvas控件展示在窗口界面中。
3.3、【计算买卖点】按钮的处理函数
下面就以【买点提示】按钮的处理函数为例讲解分析,【卖点提示】按钮的处理函数也是相类似的,只需要修改其判断条件即可
def printbuypoint():
stockcode = stockcodeval.get()
startdate = startdateval.get()
enddate = enddateval.get()
filename = 'D:\\' + stockcode + startdate + enddate + '.csv'
getstockdata(stockcode, startdate, enddate)
stock_df = pd.read_csv(filename, encoding='gbk')
stock_df_date = stock_df['date'].values
stock_df.index = stock_df_date
stock_df_close = stock_df['close']
s_short = stock_df_close.rolling(window=5, min_periods=5).mean()
s_long = stock_df_close.rolling(window=20, min_periods=20).mean()
stock_df_len = len(stock_df)
buydate = ''
for i in range(stock_df_len):
if pd.isna(s_long[i]):
continue
elif s_short[i-1] < s_long[i-1] and s_short[i] > s_long[i]:
buydate = buydate + stock_df_date[i] + ','
else:
continue
tkinter.messagebox.showinfo('提示买点',buydate)
print('提示买点'+buydate)
与【绘制】按钮第一步操作一样,先得到文本框输入的信息,利用信息获取股票数据,然后计算长短两个周期的移动平均线,利用其交叉点进行买卖的判断(前置文章中有详细介绍)
最后就是利用tkinter.messagebox.showinfo() 将得到的信息在弹出窗口中显示,其中,第一个参数为弹出窗口的标题,第二个为弹出窗口中显示的内容。
3.4、【重置】按钮的处理函数
def reset():
stockcodeentry.delete(0,tkinter.END)
stockcodeentry.insert(0,"sh000001")
startdateentry.delete(0,tkinter.END)
startdateentry.insert(0,"20190101")
enddateentry.delete(0,tkinter.END)
enddateentry.insert(0,"20190630")
plt.clf()
canvas.draw()
自定义函数reset()就是在点击【重置】按钮后,删除文本框的信息同时插入自定义的股票代码、开始时间、结束时间的默认值。
4、按钮的创建及canvas控件
接下来就是按钮的创建了,根据需要创建了【绘制】、【重置】、【计算买点】和【计算卖点】四个按钮,并且调整它们的宽度、位置等参数
tkinter.Button(win,text='绘制',width=12,command=draw).place(x=200,y=50)
tkinter.Button(win,text='重置',width=12,command=reset).place(x=200,y=80)
tkinter.Button(win,text='计算买点',width=12,command=printbuypoint).place(x=300,y=50)
tkinter.Button(win,text='计算卖点',width=12,command=printsellpoint).place(x=300,y=80)
最后就是利用控件canvas整合了基于matplotlib库的figure对象和面向GUI界面的win对象,然后设置控件canvas的大小及位置;当然不要忘记开启循环!
figure = plt.figure()
canvas = FigureCanvasTkAgg(figure, win)
canvas.get_tk_widget().config(width=575,heigh=500)
canvas.get_tk_widget().place(x=0,y=100)
win.mainloop()
5、运行结果
运行之后会出现: 点击【绘制】按钮之后: 点击【计算买点】按钮之后: 点击【计算卖点】按钮之后: 修改信息之后,点击【绘制】按钮: 点击【重置】按钮之后:
结语及源代码
分享的过程也是学习的过程,在写完这篇博客之后,我对GUI与图形库交互的技巧的掌握也更进一步,学明白了基于tkinter库的GUI交互界面的开发,包括标签、文本框、按钮和canvas等控件的用法,并结合以前所学的知识:股票数据的分析和可视化,对两者进行整合,最终体会到掌握新技能的快乐。
最后附上源代码,希望大家也可以一起学习进步
1、“登录窗口”源代码
import tkinter
import tkinter.messagebox
loginwin = tkinter.Tk()
loginwin.geometry('220x120')
loginwin.title('登录窗口')
tkinter.Label(loginwin,text='用户名:').place(x=10,y=20)
tkinter.Label(loginwin,text='密 码:').place(x=10,y=50)
userval = tkinter.StringVar()
pwdval = tkinter.StringVar()
userentry = tkinter.Entry(loginwin, textvariable=userval)
userentry.place(x=65,y=20)
pwdentry = tkinter.Entry(loginwin,textvariable=pwdval,show='*')
pwdentry.place(x=65,y=50)
def check():
username = userval.get()
pwd = pwdval.get()
print('用户名:' + username)
print('密 码:' + pwd)
if(username == 'aaa' and pwd == '123'):
tkinter.messagebox.showinfo('提示','登录成功')
else:
if(username == 'aaa' and pwd != '123'):
tkinter.messagebox.showinfo('提示','密码错误')
else:
tkinter.messagebox.showinfo('提示','登录失败')
tkinter.Button(loginwin, text='登录',width=12,command=check).place(x=10,y=85)
tkinter.Button(loginwin, text='退出',width=12,command=loginwin.quit).place(x=120,y=85)
tkinter.mainloop()
2、“股票K线均线”源代码
import pandas as pd
import akshare as ak
import matplotlib.pyplot as plt
from mpl_finance import candlestick2_ochl
import tkinter
import tkinter.messagebox
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
win = tkinter.Tk()
win.geometry('625x600')
win.title('股票K线均线')
tkinter.Label(win, text='股票代码:').place(x=10,y=20)
tkinter.Label(win, text='开始时间:').place(x=10,y=50)
tkinter.Label(win, text='结束时间:').place(x=10,y=80)
stockcodeval = tkinter.StringVar()
startdateval = tkinter.StringVar()
enddateval = tkinter.StringVar()
stockcodeentry = tkinter.Entry(win, textvariable=stockcodeval)
stockcodeentry.place(x=70,y=20)
stockcodeentry.insert(0,"sh000001")
startdateentry = tkinter.Entry(win, textvariable=startdateval)
startdateentry.place(x=70,y=50)
startdateentry.insert(0,"20190101")
enddateentry = tkinter.Entry(win, textvariable=enddateval)
enddateentry.place(x=70,y=80)
enddateentry.insert(0,"20190630")
def getstockdata(stockcode, startdate, enddate):
stock_df = ak.stock_zh_a_daily(symbol=stockcode, start_date=startdate, end_date=enddate)
filename = 'D:\\' + stockcode + startdate + enddate + '.csv'
stock_df.to_csv(filename)
def draw():
plt.clf()
stockcode = stockcodeval.get()
startdate = startdateval.get()
enddate = enddateval.get()
drawline(stockcode, startdate, enddate)
def drawline(stockcode, startdate, enddate):
filename = 'D:\\' + stockcode + startdate + enddate + '.csv'
getstockdata(stockcode, startdate, enddate)
df = pd.read_csv(filename, encoding='gbk')
ax = figure.add_subplot(111)
candlestick2_ochl(ax=ax, opens=df['open'].values, closes=df['close'].values,
highs=df['high'].values, lows=df['low'].values, width=0.75,colorup='red',colordown='green')
df['close'].rolling(window=5).mean().plot(label='5日均线')
df['close'].rolling(window=20).mean().plot(label='20日均线')
plt.legend(loc='best')
ax.grid(True)
plt.title("股票{}的K线图".format(stockcode))
plt.rcParams['font.sans-serif']=['SimHei']
major_index = df.index[df.index%10==0]
major_xticks = df['date'][df.index%10==0]
plt.xticks(major_index,major_xticks)
plt.setp(plt.gca().get_xticklabels(),rotation=30)
canvas.draw()
def printbuypoint():
stockcode = stockcodeval.get()
startdate = startdateval.get()
enddate = enddateval.get()
filename = 'D:\\' + stockcode + startdate + enddate + '.csv'
getstockdata(stockcode, startdate, enddate)
stock_df = pd.read_csv(filename, encoding='gbk')
stock_df_date = stock_df['date'].values
stock_df.index = stock_df_date
stock_df_close = stock_df['close']
s_short = stock_df_close.rolling(window=5, min_periods=5).mean()
s_long = stock_df_close.rolling(window=20, min_periods=20).mean()
stock_df_len = len(stock_df)
buydate = ''
for i in range(stock_df_len):
if pd.isna(s_long[i]):
continue
elif s_short[i-1] < s_long[i-1] and s_short[i] > s_long[i]:
buydate = buydate + stock_df_date[i] + ','
else:
continue
tkinter.messagebox.showinfo('提示买点',buydate)
print('提示买点'+buydate)
def printsellpoint():
stockcode = stockcodeval.get()
startdate = startdateval.get()
enddate = enddateval.get()
filename = 'D:\\' + stockcode + startdate + enddate + '.csv'
getstockdata(stockcode, startdate, enddate)
stock_df = pd.read_csv(filename, encoding='gbk')
stock_df_date = stock_df['date'].values
stock_df.index = stock_df_date
stock_df_close = stock_df['close']
s_short = stock_df_close.rolling(window=5, min_periods=5).mean()
s_long = stock_df_close.rolling(window=20, min_periods=20).mean()
stock_df_len = len(stock_df)
selldate = ''
for i in range(stock_df_len):
if pd.isna(s_long[i]):
continue
elif s_short[i-1] > s_long[i-1] and s_short[i] < s_long[i]:
selldate = selldate + stock_df_date[i] + ','
else:
continue
tkinter.messagebox.showinfo('提示卖点',selldate)
print('提示卖点'+selldate)
def reset():
stockcodeentry.delete(0,tkinter.END)
stockcodeentry.insert(0,"sh000001")
startdateentry.delete(0,tkinter.END)
startdateentry.insert(0,"20190101")
enddateentry.delete(0,tkinter.END)
enddateentry.insert(0,"20190630")
plt.clf()
canvas.draw()
tkinter.Button(win,text='绘制',width=12,command=draw).place(x=200,y=50)
tkinter.Button(win,text='重置',width=12,command=reset).place(x=200,y=80)
tkinter.Button(win,text='计算买点',width=12,command=printbuypoint).place(x=300,y=50)
tkinter.Button(win,text='计算卖点',width=12,command=printsellpoint).place(x=300,y=80)
figure = plt.figure()
canvas = FigureCanvasTkAgg(figure, win)
canvas.get_tk_widget().config(width=575,heigh=500)
canvas.get_tk_widget().place(x=0,y=100)
win.mainloop()
以上代码也可以在我上传的资源上下载.py、.ipynb两种类型的文件
最后的最后,大家如果觉得不错的话记得点赞、收藏、关注三连哦~
|