获取本项目代码,请扫描下方二维码,发送关键字『基金』,即可获取!
导言
最近股市有点牛市的味道,没有投资股票的朋友,一般也会投资基金,那一般是定投的吧
定投的好处就是不需要额外的时间去管理,自动扣款就好了
然而基金投资有一句话叫做:“越低越投”,基金定投有一个不好的地方就是,如果某一天基金净值大跌了,但恰好这一天又不是你的定投日,那么这个时候加点仓也许是个不错的选择,比如上这周的周五(2020-07-24),大部分基金净值跌了超过 4% ,以这个为例。
那么,我们是否可以写一个程序,在交易日的时候自动采集基金的涨跌情况,设定阈值,然后通知我们,我们再手动决定是否加仓或者卖出
笔者尝试了一下,所以形成了这篇文章,文末有本文代码获取的方法,或者大家可以留言告诉我你想关注的基金代码和你的邮箱(留言我会在后台看到,不会展示出来!),我会在交易日每天 14.50 的时候发邮件提醒你。
下面开始我的表演~~~~
邮箱配置
想要使用自动发邮件的功能,那么就必须有一个发件方,这个发件方的配置信息可以在
邮箱设置中进行配置(这里使用的是 QQ 邮箱,其他邮箱的配置类似)
配置过程如下所示:
生成授权码后复制下来,后面会用到
这里说一个 QQ 邮箱的使用技巧,QQ 邮箱是可以一邮多号的,什么意思呢?
就是同一个邮箱可以有不同的账号,但是最终收件的地方都是同一个,大家可以在 QQ 邮箱/设置/账户中进行配置,我就是 @qq.com
和 @foxmail.com
配合使用的,非常方便。
基金信息获取
配置好邮箱后,我们就来获取我们想要的信息,我们想要每天监控某个基金的涨跌幅信息,可以从基金网站上用爬虫获取,我们使用的是新浪财经(http://stocks.sina.cn/fund/?code=160119&vt=4#)
我们按 F12
进行调试,我们只需要涨跌幅信息,所以爬取起来也比较简单
所以我们只在这个网站上 http://stocks.sina.cn/fund/?code=160119&vt=4# 抓取基金的涨跌幅信息,要获取基金的其他信息,如基金的具体名称的话,我们使用这个网站:http://fund.eastmoney.com/js/fundcode_search.js ,里面涵盖大部分基金的文本信息
代码实现
代码的基本思路是,抓取若干个基金(可以进行配置)的涨跌幅信息,然后设置一个阈值(如涨或者跌超过 1%)就提醒我们。
import smtplib
import email
# 负责构造文本
from email.mime.text import MIMEText
# 负责构造图片
from email.mime.image import MIMEImage
# 负责将多个对象集合起来
from email.mime.multipart import MIMEMultipart
from email.header import Header
import requests
from bs4 import BeautifulSoup
import re
import time
def get_fund_info(fund_code):
"""
获取基金涨跌幅信息:信息来源(新浪财经 http://stocks.sina.cn/fund/)
fund_code:为基金代码,若该基金不存在,返回 False,否则返回 涨跌幅比例
"""
headers = {
"Cookie": 'ustat=__14.28.56.65_1590560309_0.89300000; genTime=1590560309; SINAGLOBAL=4905578110989.475.1590560312465; Apache=3574138427052.4756.1595941642394; ULV=1595941642397:5:2:1:3574138427052.4756.1595941642394:1594733801049; sinaH5EtagStatus=y; vt=99; historyRecord={"href":"http://stocks.sina.cn/fund/","refer":""}',
"Host": "stocks.sina.cn",
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.89 Safari/537.36"
}
url = url = "http://stocks.sina.cn/fund/?code={}&vt=4#".format(fund_code)
try:
r = requests.get(url, headers)
r.encoding="UTF-8"
soup = BeautifulSoup(r.text, "html.parser")
result = soup.findAll(attrs={"class": "j_fund_valExt"})
if len(result) == 1:
pattern = "(?<=>)(.+)(?=<)"
result = re.findall(pattern, str(result[0]))[0]
return float(result.split("%")[0])
else:
return False
except:
return False
def send_mail(fund_code, receiver_mail, thresh=1,user=None):
"""
fund_code:基金代码
receiver_mail:收件人邮箱
thresh:阈值
user:用户名标志
"""
# SMTP服务器,这里使用qq邮箱
mail_host = "smtp.qq.com"
# 发件人邮箱
mail_sender = "xxx@qq.com"
# 邮箱授权码
mail_license = "xxxxx"
# 收件人邮箱,可以为多个收件人
mail_receivers = [receiver_mail] # ["******@qq.com","******@foxmail.com"]
print(mail_receivers)
# 构建 MIMEMultipart 对象代表邮件本身,可以往里面添加文本、图片、附件等
mm = MIMEMultipart('related')
# 邮件主题
subject_content = """基金"""
# 设置发送者,注意严格遵守格式,里面邮箱为发件人邮箱
mm["From"] = "sender_name<xxx@qq.com>"
# 设置接受者,注意严格遵守格式,里面邮箱为接受者邮箱
# 多个接受者 "receiver_1_name<******@qq.com>,receiver_2_name<******@foxmail.com>"
mm["To"] = "receiver_1_name<{}>".format(receiver_mail)
# 设置邮件主题
mm["Subject"] = Header(subject_content, 'utf-8')
body_content = "现在是 {} \n\n".format(time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()))
for code in fund_code:
rate = get_fund_info(code)
# print(date, rate)
# 邮件正文内容
if rate:
##### 根据基金代码获取基金信息
headers = {
"Cookie":
"qgqp_b_id=f8b59df051caea02b176f6d76db75887; EMFUND1=null; EMFUND2=null; EMFUND3=null; EMFUND4=null; EMFUND5=null; EMFUND6=null; EMFUND7=null; st_si=92310565820236; st_asi=delete; searchbar_code=160119; EMFUND0=null; EMFUND8=07-14%2021%3A54%3A31@%23%24%u5357%u65B9%u4E2D%u8BC1500ETF@%23%24510500; EMFUND9=07-25 23:07:36@#$%u5357%u65B9%u4E2D%u8BC1500ETF%u8054%u63A5A@%23%24160119; ASP.NET_SessionId=5ljqqn1s20zpfryhuw5fx4jw; st_pvi=06954122844047; st_sp=2020-05-20%2007%3A32%3A46; st_inirUrl=https%3A%2F%2Fwww.google.com%2F; st_sn=2; st_psi=20200725230736417-0-5210348614",
"User-Agent":
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.89 Safari/537.36"
}
url = "http://fund.eastmoney.com/js/fundcode_search.js"
r = requests.get(url, headers)
info = re.findall("(\[.*?\])", r.text[9:-2])
fund_info = list(filter(lambda x:x.replace("\"", "").replace("[", "").replace("]", "").split(",")[0]==code, info))[0]
if abs(rate) > thresh:
temp = """{} 基金 \n 涨幅为 {}%, \n \t \t ### 涨跌幅超过阈值!!! ### \n\n""".format(fund_info, rate)
else:
temp = """{} 基金 \n 涨幅为 {}% \n\n""".format(fund_info, rate)
else:
temp = """代码为 {} 基金 \n 不存在!!!""".format(code)
body_content += temp
# 构造文本,参数1:正文内容,参数2:文本格式,参数3:编码方式
message_text = MIMEText(body_content, "plain", "utf-8")
# 向MIMEMultipart对象中添加文本对象
mm.attach(message_text)
# 创建SMTP对象
stp = smtplib.SMTP()
# 设置发件人邮箱的域名和端口,端口地址为25
stp.connect(mail_host, 25)
# set_debuglevel(1)可以打印出和SMTP服务器交互的所有信息
stp.set_debuglevel(0)
# 登录邮箱,传递参数1:邮箱地址,参数2:邮箱授权码
stp.login(mail_sender, mail_license)
# 发送邮件,传递参数1:发件人邮箱地址,参数2:收件人邮箱地址,参数3:把邮件内容格式改为str
stp.sendmail(mail_sender, mail_receivers, mm.as_string())
print("邮件发送成功")
# 关闭SMTP对象
stp.quit()
# 基金编码,收件邮箱,阈值,用户名(做标记)
send_mail(fund_code=["511010", "160119"], receiver_mail="xxxxx@foxmail.com", thresh=1, user="LSJ")
服务器部署
写好程序后,我们自然想让他自动操作,所以,我们就把他部署到服务器中,在指定时间给我们发邮件就好了。
笔者本来打算使用 Docker 镜像进行部署的,但是基本的 Python 环境的 Dockers 镜像都好几百 M,运行我们这个程序实在有点浪费,再者,我们程序所用到的包都是基本的 Python 包,不会对我们的环境产生影响,所以,我们就只单纯的使用 Linux 命令来配置程序的定时运行。
笔者使用的是腾讯云服务器,本以为把程序上传到服务器运行就可以了,但是,发现自己还是太年轻
但笔者把程序放到服务器时,发现根本发不了邮件,找了一下原因,是因为发邮件的端口(25)被腾讯封了,如果要使用邮件系统的话,必须解封,其次,腾讯要求不能使用云主机之间 SMTP 发邮件,否则就永久封禁 25 端口,这不就是告诉大家不准在本地架邮件服务器吗。。。。
为了之前那么辛苦调试程序,我决定拿出我的另外一台服务器,秒秒钟解决问题
其实我们的部署步骤非常的简单,只需要在服务器上设置在 周一到周五 14.50 左右运行一下程序即可(周一到周五才有交易,且 15 前交易为当天的净值)
首先,需要安装一下基本的 Python 包
pip install requests
pip install bs4
其次,我们需要设置一下定时任务,使用到的命令是:crontab
一些基本命令如下
export EDITOR="/usr/bin/vim" # 设置 Linux 环境下使用 vim 编辑器编辑 crontab 文件
crontab -e # 创建定时任务
crontab -l # 查看定时任务
service cron restart # 重启crontab 定时任务
sudo /etc/init.d/cron start # 开启定时任务
sudo /etc/init.d/cron stop # 停止定时任务
service cron status # 查看定时任务状态
- 创建定时任务
然后修改下面的信息crontab -e
50 14 * * 1-5 bash /root/fund/fund_code.sh > /root/fund/log.txt 2>&1 # 这句话的意思是 每周一到周五14点50运行一下脚本程序,并把输入写入 log 文件中
其中fund_code.sh
是脚本命令,内容如下,fund_code.py
是我们爬取信息发邮件的程序,/root/anaconda3/bin/python3 的意思是使用 anaconda 的 python 环境来运行我们的程序
注意:设计脚本的时候,涉及到路径的,最好使用绝对路径
上面程序中 50 14 * * 1-5
每个值的含义如下:
* * * * *
- - - - -
| | | | |
| | | | +----- 星期中星期几 (0 - 7) (星期天 为0)
| | | +---------- 月份 (1 - 12)
| | +--------------- 一个月中的第几天 (1 - 31)
| +-------------------- 小时 (0 - 23)
+------------------------- 分钟 (0 - 59)
同时我们也可以使用 网站 https://crontab.guru/ 来帮助我们设计合适的时间
- 重启一下定时任务
注意:笔者在调试的时候,遇到定时任务不执行,大家如果也遇到,首先看看 python 程序是否正确,还是不行的话,可以参考:https://blog.csdn.net/qfikh/article/details/78202449 进行解决service cron restart
参考: