当前位置:   article > 正文

用Python搞了个基金查询机器人,还可以拓展!

用Python搞了个基金查询机器人,还可以拓展!

一、说点东西

老早就想搞个基金监控机器人了,方便自己查看自己关注基金的各种指数涨跌情况,及时进行止损或者止盈,从今天开始,我们先建楼基,手把手带大家实现一个基金查询机器人,目前主要可以查询基金指定日期段数据和查看基金净值走势图,后面慢慢新增功能。

二、开始动手动脑

2.1 环境准备

  • Linux、Mac、Windows 都可以

  • python 3.7及以上

  • 相关第三方包:pandas(数据处理)、requests(爬取数据)、re(文本内容解析)、akshare(获取基金股票数据)、matplotlib(数据可视化)、dataframe-image(dataframe表格转成图片)

2.2 获取指定日期段基金数据

基金数据可以从一些金融相关的网站获取到,比如天天基金网、新浪基金网等,可以自己写爬虫程序获取网站数据,也可以使用现成的工具包获取数据,比如:一行代码获取股票、基金数据,并绘制K线图里用到的akshare

这里我们同时介绍下两种方法:

2.2.1 回顾下akshare获取基金数据

目前akshare不支持获取指定日期范围内的基金净值数据,但是可以一次获取到基金历史净值数据,调用函数fund_em_open_fund_info获取基金历史数据,然后自己根据日期选取时间断进行分析。

import akshare as ak   fund_data = ak.fund_em_open_fund_info(fund='005827', indicator='单位净值走势')      print(fund_data)   
  • 1

自己调用现成数据接口

本质上akshare也是从一些金融相关的网站获取到数据,我们也可以自己写代码进行获取,通过浏览器我们很快能搜索到基金数据接口,来自东方财富的天天基金网。

f'http://fund.eastmoney.com/f10/F10DataApi.aspx?type=lsjz&code={code}&page={page}&sdate={start_date}&edate={end_date}&per={per}'      code - 基金代码   page - 基金数据页码   start_date - 数据开始日期   end_date - 数据结束日期   per - 每页展现数据量,最多40   
  • 1

根据指定参数,浏览器会返回指定参数,一段js赋值代码,包括了 基金数据(content)、总记录条数(records)、总页数(pages)、当前页数(curpage)。

格式非常规整,我们可以直接通过正则提取数据,

'''   获取单页面 基金数据   '''   def get_html(code, start_date, end_date, page=1, per=40):       url = f'http://fund.eastmoney.com/f10/F10DataApi.aspx?type=lsjz&code={code}&page={page}&sdate={start_date}&edate={end_date}&per={per}'       # print(url)       rsp = requests.get(url)       html = rsp.text              return html   
  • 1

通过返回的数据可以发现,基金数据部分是一个由table标签包裹的html代码,那我们可以直接使用pandas的read_html来解析数据。

# 从html中解析出数据表部分 并解析成df   def parses_table(html):       # 获取基金数据表       pattern = 'content:"<table(.*)</table>",'       table = re.search(pattern, html).group(1)       table = '<table' + table + '</table>'       fund_data = pd.read_html(table)[0]       return fund_data   
  • 1

前面有提到,基金数据接口返回数据每页最多展示40条,所以要想获取所有数据,我们可能需要遍历每一页,那么我们还需要通过正则将总页数pages获取到,然后遍历调用get_htmlparses_table函数解析出所有数据。

# 获取指定日期内 累计净值 等数据   def get_fund_data(code, start_date, end_date):       first_page = get_html(code, start_date, end_date)       # 获取总页数       pattern = 'pages:(.*),'       pages = re.search(pattern, first_page).group(1)       # 转成int数据       try:           pages = int(pages)       except Exception as e:           r = f'【错误信息】{e}'           # print(r)           return r               # 存放每页获取到的基金数据 dataframe格式 便于后面合并       fund_df_list = []              # 循环便利所有页面       for i in range(pages):            if i == 0:               fund_data = parses_table(first_page)           else:               page_html = get_html(code, start_date, end_date, page=i+1)               fund_data = parses_table(page_html)           fund_df_list.append(fund_data)              # 将每页的数据合并到一起       fund_df = pd.concat(fund_df_list)              return fund_df   
  • 1

上面两种方法都可以获取到基金净值数据,最后我选择了akshare方式获取,设置一个定时任务,每天三点更新自己关注的基金所有数据,存储到本地,后面要查询的时候直接读取本地文件查询即可。

  • 定时任务:每天早上3点获取所有关注的基金历史数据,存储到本地
# 定时任务:每天早上3点获取所有关注的基金历史数据,存储到本地   def get_all():       try:           # 从文件读取 自己关注的基金代码列表           with open('./FD/funds.txt') as f:               funds = [i.strip() for i in f.readlines()]           # 遍历 一个个更新数据           for fund in funds:               fund_df = ak.fund_em_open_fund_info(fund, indicator='单位净值走势')               fund_df = fund_df.sort_values(by=['净值日期'], ascending=False)               fund_df.to_csv(f"./FD/DATA/F{fund}_data.csv", index=False)               # print(f"./FD/DATA/F{fund}_data.csv")               time.sleep(random.randint(1,5))           return '基金数据更新完成'       except Exception as e:           r = f"【错误信息】{e}"           return r   
  • 1
  • 获取指定基金 指定日期段 净值数据
# 获取指定基金 指定日期段 净值数据   def get_fund_data(fund, start_d, end_d):       fund_df = pd.read_csv(f'./FD/DATA/{fund}_data.csv')       result_df = fund_df.query(f"'{start_d}'<=净值日期<='{end_d}'")       return result_df   
  • 1

2.3 返回数据呈现方式

目前先简单点,设置规则如下:

  • 1)如果数据量小于等于30条,就返回原始数据图

原始数据图就是直接将获取到的数据转成图片的方式发送给用户,这里我们使用dataframe-image这个第三方包,使用非常简单,pip安装后,直接调用export函数即可快速将datafrmae数据转成图片。

# 将dtaframe表格转变成图片   def df_to_img(fund_df, fund, start_d, end_d):       if fund_df.shape[0] <=1:           dfi.export(fund_df, f'./FD/IMG/{fund}_{start_d}_{end_d}_data.png')           return               # 格式化表格 凸显最大最小值       fund_df = fund_df.style.highlight_max(subset=['单位净值'], color='red')\                .highlight_min(subset=['单位净值'], color='green')\                .format({'日增长率': '{:}%'})              dfi.export(fund_df, f'./FD/IMG/{fund}_{start_d}_{end_d}_data.png')   
  • 1

为了图片数据更好看,我们还使用了df.style设置数据表格样式(单位净值最大值、最小值高亮和日增长率添加百分号)。

  • 2)如果数据量大于30条,就返回原始数据趋势图

原始数据趋势图就是将数据可视化下,然后返回给用户,这里我们选择绘制数据的走(趋)势图,使用matplotlib进行绘制。

# 绘制基金单位净值走势图   def draw_fund_line(fund_df, fund, start_d, end_d):       plt.rcParams['figure.figsize'] = (8.0, 4.0) # 设置figure_size尺寸       plt.rcParams['savefig.dpi'] = 300 #保存图片分辨率          # 不显示右、上边框       ax=plt.gca()        ax.spines['right'].set_color('none')       ax.spines['top'].set_color('none')          # 设置坐标网格       plt.grid(axis="y", color='gray')            # 计算最大值 最小值坐标 并标注到图中       fund_max = fund_df.loc[fund_df['单位净值'].idxmax()]       fund_min = fund_df.loc[fund_df['单位净值'].idxmin()]          ax.annotate(f'({fund_max[0]},{fund_max[1]})', xy=(fund_max[0], fund_max[1]), color='red')       ax.annotate(f'({fund_min[0]},{fund_min[1]})', xy=(fund_min[0], fund_min[1]), color='green')          # 画图       plt.plot(fund_df['净值日期'],fund_df['单位净值'], color="c")       plt.title('基金单位净值走势图')       plt.xticks(rotation=30)       plt.xlabel('净值日期')       plt.ylabel('单位净值')       plt.savefig(f'./FD/IMG/{fund}_{start_d}_{end_d}_data.png')   
  • 1

这里使用的是折线图,有一些对图片样式的设置,比如:大小、边框、最大/小值标注,但依然不是很美观,后面继续优化。

  • 完整调用
# 返回数据   def response_data(fund, start_d, end_d):       # 本地查看 查询结果是否已存在       imgs = os.listdir('./FD/IMG/')       if f'{fund}_{start_d}_{end_d}_data.png' in imgs:           return f'./FD/IMG/{fund}_{start_d}_{end_d}_data.png'              # 获取数据       fund_df = get_fund_data(fund, start_d, end_d)              # 如果数据量小于等于30条,就返回原始数据图       if fund_df.shape[0]<= 30:           df_to_img(fund_df, fund, start_d, end_d)       else:           # 否则返回数据趋势图           fund_df = fund_df.sort_values(by=['净值日期'])           draw_fund_line(fund_df, fund, start_d, end_d)          return f'./FD/IMG/{fund}_{start_d}_{end_d}_data.png'   
  • 1

2.4 对接钉钉机器人设置守护程序

目前项目中使用到了之前介绍过的两种机器人:钉钉群机器人、企业机器人,相关配置方法和代码可以查看之前的文章:如何用Python发送告警通知到钉钉?如何打造一个能自动回复的钉钉机器人,非常详细。

钉钉群机器人主要用来汇报每天自动汇报基金数据更新情况,后面还可以加基金涨跌检测情况等。

企业机器人主要用来做基金数据查询自动回复功能,也可以拓展主动发消息给用户,后面研究研究。

2.5 遇到问题及解决方法

2.5.1 Linux上datafrmae-image转图片出错

最开始是提示没有chrom,然后按网上教程安装了google chrom。

参考:https://segmentfault.com/a/1190000022425145

安装后,运行代码提示SyntaxError: not a PNG file

看错误提示以为是Pillow和matplotlib的问题,修改到了和本地一样的版本也不行。

最后看了源码,发现可以转换方法除了使用chrom,还可以用matplotlib,修改后,确实可以正常生成图片了,但是没有格式!!!

最后改回默认table_conversion,仔细看,发现有提示以下内容,大概意思linux下不能直接使用root用户权限允许谷歌chrome,最简单的方法就是创建一个普通用户。

[0209/162730.350381:ERROR:zygote_host_impl_linux.cc(90)] Running as root without --no-sandbox is not supported. See https://crbug.com/638180   
  • 1

在root权限下,新建一个用户od,并将/root目录权限授予给它,然后输入su指令切换到新用户下。

useradd od   chown -R od /root   su od   
  • 1

再次运行确实能解决图片生成和数据格式问题,但是有新问题:表头中文无法显示。。。

百般搜索,看源码调试、看项目仓库问题都没解决,最后,最后我突然想到,我本地可以,两个包的版本又是一样,应该不是代码问题,会不会是因为linux里没有安装中文字体,所以无法显示中文?

root用户权限下,先创建一个目录,存放中文字体,创建好后,可以直接利用宝塔将本地的SimHei字体上传到对应目录即可。

mkdir -p /usr/share/fonts/my_fonts   
  • 1

可以通过下面指令查看中文字体是否安装成功,

fc-list :lang=zh   
  • 1

再次运行代码,生成的图片就正常啦~开心!

2.5.2 matplotlib图片中文显示问题

之前有写过详细解决方法,可以直接查看之前文章:永久解决matplotlib中文乱码

2.5.3 钉钉机器人无法直接传输图片

钉钉机器人目前只支持传输:普通文本、markdown文本、连接、actionCard消息和feedCard消息类型。

如果我想要将生成的基金数据图发送给用户,最好的方法是和之前一样,先将图片转成链接,然后通过markdown形式传输。

如果系统仅个人使用,数据量不大,我们不必选择网络上现有的图床工具(这样我们还要写接口对接代码),可以直接开放个http端口去共享我们的图片,本身企业机器人就使用到了flask,所以我们可以更简单的实现这个功能。

app = Flask(__name__, static_folder='xxx/FD/IMG', static_url_path='/static')   
  • 1

在初始化flask app时,指定静态文件所在目录和静态文件路由后缀即可,这样,我们就可以通过:http://服务器IP地址:端口号/static/图片文件名,访问到对应图片了。

然后将图片链接嵌入到markdown中,即可正常返回给用户了。

2.6 最终效果图

  • 指定查询

查看某基金某个时间段内的基金净值数据。(30条以内数据,表格展示;大于30条,趋势图展示)

查询格式: F基金代码 起始日期 结束日期,如:F005827 2021-12-03 2022-02-10

效果图

  • 普通查询

查看某基金近10天内净值和日增长率数据+趋势图

查询格式: F基金代码,如:F005827

最近10天内,只有两个交易日

三、后言后语

这项目说大不大,说小也不小,百行代码,本机测试还是很顺畅的,主要是迁移到Linux上后出现一些问题,从最开始的python版本问题(安装了一个3.7.9),到datafrmae-image问题,延展出来的Linux安装谷歌、设置新用户、分配权限,以及源码测试学习。

遇到问题、解决问题的过程确实花费了我很长时间,一度还让我很苦恼,但是,这个过程也让我觉得很有益,是一个不断积累、不断练习、不断巩固的过程,解决问题后更会为自己欢呼。

目前基金监测机器人还比较简陋,甚至都没有监测功能(目前只支持数据查询和更新),但是这个楼基很稳、很深,后面添加其他功能会简单、便捷许多,欢迎大家评论区留言,说说你想为这个机器人添加的功能。

代码在下个版本文章发布时一起分享出来,这里招聘5个‘产品经理’,一起来体验这个基金查询机器人,给反馈、提需求,有兴趣的可以微信私聊我(需要有2年以上的基金或者股票投资经验或者理财资产超过10w)。

以上就是“用Python搞了个基金查询机器人,还可以拓展!”的全部内容,希望对你有所帮助。

关于Python技术储备

学好 Python 不论是就业还是做副业赚钱都不错,但要学会 Python 还是要有一个学习规划。最后大家分享一份全套的 Python 学习资料,给那些想学习 Python 的小伙伴们一点帮助!

一、Python所有方向的学习路线

Python所有方向的技术点做的整理,形成各个领域的知识点汇总,它的用处就在于,你可以按照上面的知识点去找对应的学习资源,保证自己学得较为全面。

在这里插入图片描述

二、Python必备开发工具

img

三、Python视频合集

观看零基础学习视频,看视频学习是最快捷也是最有效果的方式,跟着视频中老师的思路,从基础到深入,还是很容易入门的。

img

四、实战案例

光学理论是没用的,要学会跟着一起敲,要动手实操,才能将自己的所学运用到实际当中去,这时候可以搞点实战案例来学习。

img

五、Python练习题

检查学习结果。

img

六、面试资料

我们学习Python必然是为了找到高薪的工作,下面这些面试题是来自阿里、腾讯、字节等一线互联网大厂最新的面试资料,并且有阿里大佬给出了权威的解答,刷完这一套面试资料相信大家都能找到满意的工作。

img

最后祝大家天天进步!!

上面这份完整版的Python全套学习资料已经上传至CSDN官方,朋友如果需要可以直接微信扫描下方CSDN官方认证二维码免费领取【保证100%免费】。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/羊村懒王/article/detail/167985
推荐阅读
相关标签
  

闽ICP备14008679号