要点:
1、数据的分析与爬取
2、pyecharts绘图
数 据 源
数据来源我们选择新浪财经基金板块:https://finance.sina.com.cn/fund/
我们随便选一个基金,就选第一个吧:660001,农银行业成长混合A
老办法,监控Network
通过分析,我们发现了三个request:
今日的实时数据从下面的连接返回
https://app.xincai.com/fund/api/jsonp.json/var%20t1fu_660001=/XinCaiFundService.getFundYuCeNav?symbol=660001&___qn=3
返回信息格式如下
var t1fu_660001=({"yes":"2.6775","detail":"09:30,2.6822,09:31,...,15:03,2.7189"});
历史数据从下面的连接返回
https://finance.sina.com.cn/fund/api/xh5Fund/nav/660001.js
返回信息格式如下
xh5Fund({"data":"20200220,2.6775,3.2775,4.15436,82.38,81.8,56.24#
20200219,2.6223,3.2223,4.06871,84.26,81.19,55.47#...#20080804,1,1,1,,,","symbol":"660001","fhday":"20091123,20100426","fhvalue":"0.4,0.2","fhchaifen":"0,0"})
数据顺序为:日期、单位净值、累积净值、历史回报、排位百分比、排位百分比、排位百分比
查询时间下的基金综合数据
https://hq.sinajs.cn/list=fu_660001
返回信息
var hq_str_fu_660001="农银行业成长混合A
,15:04:00,2.6722,2.6223,3.2223,0.0262,1.9029,2020-02-20";
爬取并解析数据
了解了以上信息,我们就可以用requests获得返回的数据。
我们可以先定义header,假装是浏览器访问网站
header = {
"User-Agent":"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.25 Safari/537.36 Core/1.70.3704.400 SLBrowser/10.0.3929.400",
}
实时数据的解析
解析返回的实时数据字符串,按照 左括号 ( 与右括号 ) 拆分,将提取出的大括号{}中的数据利用ast.literal_eval转换为字典。yes对应的字符串直接转换为float,detail对应的字符串按照 逗号 , 拆分,每两个数据一组,第一个为“时间”保持字符串即可,第二个为“实时数据”转换为float。将解析好的数据再次打包为字典即可备用。
历史数据的解析
历史数据的解析,与实时数据基本相似,也是先通过对左右括号的拆分,提取出需要的字典字符串数据,而后利用ast.literal_eval将字符串转为字典。字典的data中是每日数据,这里用 # 号拆分历史每日数据字符串,再将每一天的数据字符串用 逗号, 拆分,依照数据顺序转换为float打包为最终字典即可。
定义抓取数据函数代码,返回 实时数据字典 与 每日数据字典
# -*- coding:utf-8 -*-
import requests
import re
import ast
header = {
"User-Agent":"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.25 Safari/537.36 Core/1.70.3704.400 SLBrowser/10.0.3929.400",
}
today_url_format = "https://app.xincai.com/fund/api/jsonp.json/var%20t1fu_{}=/XinCaiFundService.getFundYuCeNav?symbol={}&___qn=3"
both_url_format = "https://finance.sina.com.cn/fund/api/xh5Fund/nav/{}.js"
name_url_format = "https://hq.sinajs.cn/list=fu_{}"
def catchData(code):
today_url = today_url_format.format(code,code)
both_url = both_url_format.format(code)
name_url = name_url_format.format(code)
#获得当天数据,步长为分钟
today_response = requests.get(today_url,headers=header)
today_str = re.split("[()]",today_response.text)[1]#通过()拆分字符串
today_dict_one = ast.literal_eval(today_str)#初步生成字典
minute_list_strs = today_dict_one['detail'].split(',')#拆分字典中detail的字符串
minute_dict = {}
for i in range(0,len(minute_list_strs),2):
minute_dict[minute_list_strs[i]] = float(minute_list_strs[i+1])
today_dict = {"yes":float(today_dict_one["yes"]),"detail":minute_dict}
#获得全部数据,步长为天
both_response = requests.get(both_url,headers=header)
both_str = re.split("[()]",both_response.text)[1]
both_dict_one = ast.literal_eval(both_str)
day_list_strs = both_dict_one['data'].split('#')
day_list = {}
for i in range(len(day_list_strs)):
str = day_list_strs[i];
str_list = str.split(',')
day_list[str_list[0]] = {"dwjz":str2float(str_list[1]),"jljz":str2float(str_list[2]),
"lshb":str2float(str_list[3]),"mper":str2float(str_list[4]),
"qper":str2float(str_list[5]),"yper":str2float(str_list[6])}
both_dict = {"symbol":both_dict_one["symbol"],"fhday":both_dict_one["fhday"],
"fhvalue":both_dict_one["fhvalue"],"fhchaifen":both_dict_one["fhchaifen"],
"data":day_list}
#获取基金名称
name_response = requests.get(name_url,headers=header)
name_str = name_response.text.split('="')[1].split(',')[0]
today_dict["name"] = name_str
both_dict["name"] = name_str
return today_dict,both_dict
def str2float(str):
return 0 if ''==str else float(str)
调用函数
我们只需要给出基金代码,既可以获得数据字典
code = "660001"
today_dict,both_dict = catchData(code)
绘制数据曲线
pyecharts是非常强大的绘图工具,非常好用。分为 v0.5.X 和 v1 两个大版本,v0.5.X 和 v1 间不兼容,v1 是一个全新的版本。目前官方已经不再维护v0.5.X版本,因此本文建议读者使用v1版本。
注:v1版本仅支持 Python3.6+
特别注意:本文使用的环境是一款神器jupyter lab,在该环境下可以使用
render_notebook()直接绘制pyecharts图形。但是因为其无法得知用户的具体开发环境,因此需要指定开发环境:
CurrentConfig.NOTEBOOK_TYPE = NotebookType.JUPYTER_LAB
如果在调用render_notebook()时绘制的依然为空白,说明环境没有正确加载echarts.js等文件,只需要在绘制前调用:load_javascript()即可。
在jupyter lab环境中,调用load_javascript()与render_notebook()需要和设置图形的代码段分开。
具体详见官方文档:
https://pyecharts.org/#/zh-cn/notebook?id=jupyter-lab
绘制实时数据折线图
下面介绍一下绘制实时数据折线图的关键点:
1)通过is_smooth=True可以平滑折线图
2)通过extend_axis函数定义第二个y轴,并设置其格式:formatter="{value} %"
3)字典的keys方法可以将字典的所有索引提取为数组
4)字典的values方法可以将字典的所有值提取为数组
from pyecharts.globals import CurrentConfig, NotebookType
CurrentConfig.NOTEBOOK_TYPE = NotebookType.JUPYTER_LAB
import pyecharts.options as opts
from pyecharts.faker import Faker
from pyecharts.charts import Line
import numpy as np
yes = today_dict["yes"]
xaxis = list(today_dict["detail"].keys())#获得x轴数据
yaxis = list(today_dict["detail"].values())#获得y轴数据
yaxis_plus_yes = [yes-x for x in yaxis]
dy_max_abs = np.max(np.abs(yaxis_plus_yes))#用来计算百分比和坐标轴范围
def today_line_smooth() -> Line:
c = (
Line()
.add_xaxis(xaxis)
.add_yaxis("净值预测", yaxis , is_smooth=True,is_hover_animation=True,yaxis_index=0)
.extend_axis(
yaxis=opts.AxisOpts(
axislabel_opts=opts.LabelOpts(formatter="{value} %"),
min_=round(-dy_max_abs/yes*100,2),
max_=round(dy_max_abs/yes*100,2)
)
)
.set_global_opts(title_opts=opts.TitleOpts(title="Line-smooth"),
yaxis_opts=opts.AxisOpts(min_=round(yes-dy_max_abs,4),max_=round(yes+dy_max_abs,4)))
)
return c
line = today_line_smooth()
line.load_javascript()
line.render_notebook()
绘制历史单位净值及积累净值曲线
主要需要注意如下几点:
- 配置AxisOpts,min_=min,max_=max,使曲线上只提示最大值和最小值
2)配置TooltipOpts,使鼠标移动显示十字交叉线,并提示关键信息:
trigger="axis",trigger_on="mousemove",axis_pointer_type="cross"
3)通过函数形式,返回JsCode字符串设置自定义Tooltip文字
3.1) 需要包含from pyecharts.commons.utils import JsCode
3.2)JsCode字符串中的字符串必须用单引号'包扩,否则会报错
3.3)函数的参数是个列表,具体形式可以通过console.log(params)查看,自定义文字所需要的数据均在params中,可以通过拼接字符串的型式,将python中的变量引入到JsCode中。
def getToolip():
a1 = yaxis_dwjz[0]
a2 = yaxis_jljz[0]
return JsCode("""function (params) {
var dwjz_per = (params[0].value[1]-"""+str(a1)+""")/"""+str(a1)+""";
dwjz_per = (dwjz_per*100).toFixed(2);
var jljz_per = (params[1].value[1]-"""+str(a2)+""")/"""+str(a2)+""";
jljz_per = (jljz_per*100).toFixed(2);
return params[0].axisValue+'
'+
params[0].seriesName+' : '+params[0].value[1]+'('+dwjz_per+'%)
'+
params[1].seriesName+' : '+params[1].value[1]+'('+jljz_per+'%)
';
}""")
定义曲线
from pyecharts.commons.utils import JsCode
datas = both_dict["data"]
xaxis_all = list(datas.keys())
xaxis_all.sort()#升序排列,返回的数据是降序的
beginX = "20191113"#设置绘制的开始时间
endX = "20200221"#设置绘制的结束时间
xaxis = []
yaxis_dwjz = []
yaxis_jljz = []
for key in xaxis_all:#提取y轴数据
if keyendX:
continue
data = datas[key]
xaxis.append(key)
yaxis_dwjz.append(data["dwjz"])
yaxis_jljz.append(data["jljz"])
#计算坐标轴范围
max = round(1.05*np.max([yaxis_dwjz,yaxis_jljz]),1)
min = round(0.95*np.min([yaxis_dwjz,yaxis_jljz]),1)
dy_min = (min-yaxis_dwjz[0])/yaxis_dwjz[0]*100
dy_max = (max-yaxis_dwjz[0])/yaxis_dwjz[0]*100
def both_line_smooth_1() -> Line:
c = (
Line()
.add_xaxis(xaxis)
.add_yaxis("单位净值", yaxis_dwjz)
.add_yaxis("积累净值", yaxis_jljz)
.extend_axis(
yaxis=opts.AxisOpts(
axislabel_opts=opts.LabelOpts(formatter="{value} %"),
min_=round(dy_min,1),
max_=round(dy_max,1)
)
)
.set_global_opts(title_opts=opts.TitleOpts(title="Line-smooth"),
yaxis_opts=opts.AxisOpts(min_=min,max_=max,interval=round((max-min)/8,1)),
tooltip_opts=opts.TooltipOpts(is_show=True,trigger="axis",
trigger_on="mousemove",axis_pointer_type="cross",
formatter=getToolip()))
.set_series_opts(label_opts = opts.LabelOpts(is_show = False), is_smooth=True,is_hover_animation=True,
markpoint_opts=opts.MarkPointOpts(data=[opts.MarkPointItem(type_ = "max",name = "max"),
opts.MarkPointItem(name = "min",type_ = "min")]))
)
return c
line = both_line_smooth_1()
line.render_notebook()
通过同样的方式,绘制 历史回报曲线、以及排位百分比曲线
总结
本文为处理量化投资数据,做了最基本的事情。爬取数据,解析,并且研究了如何美观的绘制折线图,希望对您有所帮助!