一、绪论
获取基金数据并展示。
已实现功能:
1、获取基金名称以列表的方式展示,可按照类型筛选,也可以直接搜索;
2、点击左侧基金名称展示日线,移动鼠标竖线跟着移动,并且显示对应日期的基金数据;
3、右上角显示基金的基本信息。
二、代码分享
包含两个.py文件,一个负责展示、一个负责数据获取
JJ_Windows.py
import tkinter as tk
from tkinter import ttk
from tkinter import *
import JJ_InterFace
import pandas as pd
import time
class JJ_Windows:
def __init__(self, jj_interface):
self.window = tk.Tk()
self.window.title('基金看板')
self.window_width = self.window.winfo_screenwidth()
self.window_height = self.window.winfo_screenheight()
self.window.geometry(str(self.window_width) + 'x' + str(self.window_height))
# 基金数据接口类
self.columns = ['基金编号', '基金名称', '基金类型']
self.columns_width = [2, 5, 3]
self.table_width = 300
self.search_height = 90
self.logging_height = 90
self.jj_interface = jj_interface
self.jj_codes, self.jj_types, self.jj_names = self.jj_interface.GetCodes()
self.jj_types_set = set(self.jj_types)
self.jjs_infor = zip(*[self.jj_codes, self.jj_names, self.jj_types])
self.jjs_y_infor = pd.DataFrame(self.jjs_infor, columns=self.columns)
self.jjs_infor = self.jjs_y_infor
self.jj_code = ''
self.net_x = []
self.net_y = []
self.rate_x = 0
self.rate_y = 0
self.InitWindows()
self.window.mainloop()
def InitWindows(self):
"""初始化界面"""
##### 最左侧的表格,并在表格内插入数据
# 创建表格头和摆放位置
self.table = ttk.Treeview(master=self.window, height=40, columns=self.columns, show='headings')
for index, column in enumerate(self.columns):
self.table.heading(column=column, text=column, anchor=CENTER)
self.table.column(column=column, width=int(self.table_width*self.columns_width[index]/10), minwidth=5, anchor=CENTER)
self.table.place(x=0, y=self.search_height)
# 插入表格数据
for index, row in self.jjs_infor.iterrows():
jj_code = row['基金编号']
jj_name = row['基金名称']
jj_type = row['基金类型']
self.table.insert('', END, values=[jj_code, jj_name, jj_type])
self.table.bind('<ButtonRelease-1>', self.TableViewClick)
##### 筛选框
ttk.Label(self.window, text="类型筛选:").place(x=0, y=10)
s_jj_type = tk.StringVar()
self.jj_type_chose_c = ttk.Combobox(self.window, width=15, textvariable=s_jj_type)
self.jj_type_chose_c['values'] = tuple(self.jj_types_set)
self.jj_type_chose_c.place(x=60, y=10)
jj_type_chose_b = tk.Button(self.window, text ="筛选")
jj_type_chose_b.bind('<ButtonRelease-1>', self.TypeChose)
jj_type_chose_b.place(x=200, y=8)
##### 搜索框
ttk.Label(self.window, text="名称搜索:").place(x=0, y=50)
s_jj_name = tk.StringVar()
self.jj_name_search_c = ttk.Entry(self.window, width=15, textvariable=s_jj_name)
self.jj_name_search_c.place(x=60, y=50)
jj_name_search_b = tk.Button(self.window, text ="搜索")
jj_name_search_b.bind('<ButtonRelease-1>', self.NameSearch)
jj_name_search_b.place(x=200, y=48)
##### 右侧画布
self.canvas = Canvas(self.window, width=self.window_width-self.table_width, height=self.window_height, bg="black")
self.canvas.place(x=self.table_width, y=0)
self.canvas.bind('<Motion>', self.XYMotion)
##### 画布上的控件
self.jj_name_c = self.canvas.create_text(self.window_width-self.table_width-100, 10, text='', font = "time 10", fill='white')
self.jj_code_c = self.canvas.create_text(self.window_width-self.table_width-100, 25, text='', font = "time 10", fill='white')
self.syl_1n_c = self.canvas.create_text(self.window_width-self.table_width-100, 40, text='', font = "time 10", fill='white')
self.syl_6y_c = self.canvas.create_text(self.window_width-self.table_width-100, 55, text='', font = "time 10", fill='white')
self.syl_3y_c = self.canvas.create_text(self.window_width-self.table_width-100, 70, text='', font = "time 10", fill='white')
self.syl_1y_c = self.canvas.create_text(self.window_width-self.table_width-100, 85, text='', font = "time 10", fill='white')
self.jj_line_c = []
self.jj_x_c = self.canvas.create_line(0, 0, 0, self.window_height, fill='white')
# self.jj_y_c = self.canvas.create_line(0, 0, self.window_width-self.table_width, 0, fill='white')
self.jj_xtext_c = self.canvas.create_text(0, 0, text='', font = "time 10", fill='white')
self.jj_ytext_c = self.canvas.create_text(0, 0, text='', font = "time 10", fill='white')
def TypeChose(self, event):
"""按照类型进行筛选基金"""
jj_type = self.jj_type_chose_c.get()
if jj_type != '':
self.jjs_infor = self.jjs_y_infor[self.jjs_y_infor['基金类型'] == jj_type]
self.UpdateTable()
def NameSearch(self, event):
"""按照名称搜索基金"""
jj_name = self.jj_name_search_c.get()
if jj_name != '':
self.jjs_infor = self.jjs_y_infor[self.jjs_y_infor['基金名称'].str.contains(jj_name)]
self.UpdateTable()
def UpdateTable(self):
"""更新表格数据"""
# 先删除表格数据
ch_table = self.table.get_children()
for value in ch_table:
self.table.delete(value)
# 插入表格数据
for index, row in self.jjs_infor.iterrows():
jj_code = row['基金编号']
jj_name = row['基金名称']
jj_type = row['基金类型']
self.table.insert('', END, values=[jj_code, jj_name, jj_type])
def XYMotion(self, event):
"""鼠标在画布上滑动,显示竖线,曲线与竖线交叉位置的数据"""
x, y = event.x, event.y
self.canvas.coords(self.jj_x_c, x, 0, x, self.window_height)
# self.canvas.coords(self.jj_y_c, 0, y, self.window_width-self.table_width, y)
index = int(x/self.rate_x)
if index >=0 and index <= len(self.net_y):
x1, y1 = x, self.window_height/2 - (self.net_y[index]-self.net_y_min)*self.rate_y-50
x2, y2 = x, self.window_height/2 - (self.net_y[index]-self.net_y_min)*self.rate_y-70
if y2 < 10:
y1, y2 = 30, 10
self.canvas.coords(self.jj_xtext_c, x1, y1)
self.canvas.coords(self.jj_ytext_c, x2, y2)
self.canvas.itemconfig(self.jj_xtext_c, text=time.strftime("%Y/%m/%d %H:%M:%S", time.localtime(int(self.net_x[index]/1000))))
self.canvas.itemconfig(self.jj_ytext_c, text=str(self.net_y[index]))
def TableViewClick(self, event):
"""获得表格里面选择基金的代码,通过基金代码获得数据,并展示数据"""
# 获得选取的行数据
for item in self.table.selection():
item_text = self.table.item(item,"values")
self.jj_code = item_text[0]
self.jj_name = item_text[1]
# 通过基金代码,获得数据
self.net_x, self.net_y, ACWorth, syl_1n, syl_6y, syl_3y, syl_1y = self.jj_interface.GetWorth(self.jj_code)
# 展示数据
self.canvas.itemconfig(self.jj_name_c, text='基金名称:'+self.jj_name)
self.canvas.itemconfig(self.jj_code_c, text='基金编码:'+self.jj_code)
self.canvas.itemconfig(self.syl_1n_c, text='近1年收益率:'+str(syl_1n))
self.canvas.itemconfig(self.syl_6y_c, text='近6个月收益率:'+str(syl_6y))
self.canvas.itemconfig(self.syl_3y_c, text='近3个月收益率:'+str(syl_3y))
self.canvas.itemconfig(self.syl_1y_c, text='近1个月收益率:'+str(syl_1y))
self.clear_jj_line()
self.draw_jj_line('green')
def clear_jj_line(self):
"""先删除之前的曲线"""
for index in self.jj_line_c:
self.canvas.delete(index)
self.jj_line_c.clear()
def draw_jj_line(self, color):
"""画曲线"""
# 整个曲线的宽度
x_edging = 10
y_edging = 10
jj_line_width = self.window_width - self.table_width - 2*x_edging
# 整个曲线的高度
jj_lin_height = self.window_height/2 - 2*y_edging
# 整个曲线数据中y数据的最小值
self.net_y_min = min(self.net_y)
# 整个曲线数据中y数据的最大值
net_y_max = max(self.net_y)
# 整个曲线数据中y数据的个数
net_y_len = len(self.net_y)
# 相邻x数据之间的像素距离
self.rate_x = jj_line_width/net_y_len
# 相邻y数据之间的像素距离
self.rate_y = jj_lin_height/(net_y_max - self.net_y_min)
# 开始画曲线
for index, value in enumerate(self.net_y):
if index == 0:
x2, y2 = index*self.rate_x + x_edging, jj_lin_height - value*self.rate_y + y_edging
else:
x1, y1, x2, y2 = x2, y2, index*self.rate_x + x_edging, jj_lin_height - (value-self.net_y_min)*self.rate_y + y_edging
self.jj_line_c.append(self.canvas.create_line(x1, y1, x2, y2, fill=color))
# 画两条坐标线
x1, y1, x2, y2 = x_edging, y_edging, x_edging + jj_line_width, y_edging + jj_lin_height
self.jj_line_c.append(self.canvas.create_line(x1, y1, x1, y2, fill='white'))
self.jj_line_c.append(self.canvas.create_line(x1, y2, x2, y2, fill='white'))
pass
jj_interface = JJ_InterFace.JJ_InterFace()
jj_windows = JJ_Windows(jj_interface)
JJ_InterFace.py
注意:由于基金的数据从网上获取的js文件,分享可能有风险,这里未给出,需要的请私信我。
import requests
import time
import execjs
class JJ_InterFace:
def __init__(self) -> None:
pass
def GetUrl(self, jj_code):
"""
函数功能:获得单个基金数据的URL
参数说明:jj_code->单个基金的代码,类似"001794"
"""
head = 'xxxx'
tail = '.js?v='+ time.strftime("%Y%m%d%H%M%S",time.localtime())
return head+jj_code+tail
def GetCodes(self):
"""
函数功能:获得所有基金的代码
参数说明:
jj_codes->所有基金的代码,类似["001794", "001794"]
jj_types->所有基金的类型,类似["混合型-灵活", "货币型"]
url返回的数据:
var r = [["000001","HXCZHH","华夏成长混合","混合型-偏股","HUAXIACHENGZHANGHUNHE"]]
"""
url = 'xxxxx'
content = requests.get(url)
jscontent = execjs.compile(content.text)
rawdata = jscontent.eval('r')
jj_codes = []
jj_types = []
jj_names = []
for code in rawdata:
jj_codes.append(code[0])
jj_types.append(code[3])
jj_names.append(code[2])
return jj_codes, jj_types, jj_names
def GetWorth(self, jj_code):
"""
函数功能:根据基金代码获取净值
参数说明:
jj_code->单个基金的代码,类似"001794"
url返回的数据:
var fS_name = "华夏成长混合";
var fS_code = "000001";
var Data_netWorthTrend = [{"x":1245945600000,"y":1.399,"equityReturn":0.7925,"unitMoney":""},];
var Data_ACWorthTrend = [[1008604800000,1.0],]
url xxxxx
"""
url = self.GetUrl(jj_code)
print(url)
content = requests.get(url)
jsContent = execjs.compile(content.text)
name = jsContent.eval('fS_name')
code = jsContent.eval('fS_code')
#单位净值走势
netWorthTrend = jsContent.eval('Data_netWorthTrend')
#累计净值走势
ACWorthTrend = jsContent.eval('Data_ACWorthTrend')
syl_1n = jsContent.eval('syl_1n')
syl_6y = jsContent.eval('syl_6y')
syl_3y = jsContent.eval('syl_3y')
syl_1y = jsContent.eval('syl_1y')
net_x = []
net_y = []
ACWorth = []
for dayWorth in netWorthTrend[::-1]:
net_x.append(dayWorth['x'])
net_y.append(dayWorth['y'])
for dayACWorth in ACWorthTrend[::-1]:
ACWorth.append(dayACWorth[1])
return net_x, net_y, ACWorth, syl_1n, syl_6y, syl_3y, syl_1y