提示:本文为个人原创,仅供技术探讨与交流,对实际投资并不造成建议。
基于大数据技术对基金分析-python
- 前言
- 一、数据获取:python爬虫
- 1).从天天基金数据接口获取数据
- 2).爬虫前期准备
- 3).爬虫具体实现
- 二、数据清洗及计算指标
- 1.过滤数据
- 2.数据字段格式统一
- 3.计算得出字段now_down、all_win
- 4.数据清洗具体实现
- 4.保存数据库
- 三、数据分析:SQL
- 1.昨日涨跌幅、近一月涨跌幅、近一年涨幅、仅三年涨幅 top20 => 当前主题行情分析
- 2.金额总值top50=> 截至2023-6-29最热门抱团方向
- 3. 3月份后涨跌幅为“- -” => 新发基金封闭期3个月=> 当前基金机构看好的行业板块方向 => 未来抱团方向(?)
- 4.now_down最小前20 => 数值越小,跌幅越大
- 5.all_win =>中长期正收益,值得长期持有
- 总结
前言
在当下热销的基金市场中,有着茫茫多的基金,本文希望能以科学的角度,以数据作为依托,更好地挑选基金
本文共用到以下技术:Python爬虫、SQL分析
本文切入角度:
1.希望做到在相对低的净值买入值得长期持有的基金并定投
2.通过已有的数据了解到当下及未来3个月市场热门的板块(抱团)
3.通过基金的表现及近期情况判断市场方向
基金是T+1交易制度,存在明显的市场时间滞后,不适合像股票一样频繁操作。
一、数据获取:python爬虫
1).从天天基金数据接口获取数据
接入天天基金数据接口(所有基金名称列表代码):http://fund.eastmoney.com/js/fundcode_search.js
部分数据截图:
从图中数据可以看出,js文件中的数据以列表格式存放,且按照 => (编号、拼音缩写、基金名称、基金类型、拼音 )的顺序排列,因为信息并不完善,所以还需要再爬取每个基金的详细信息
2).爬虫前期准备
通过观察,天天基金的robots规则为:
每个基金详情页面为
http://fund.eastmoney.com/+基金代号,如下图:
3).爬虫具体实现
获取唯一编号列表
import requests
def get_req_data(url):
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:100.0) Gecko/20100101 Firefox/100.0',
}
res = requests.get(url=url, headers=headers)
res.encoding = 'utf-8'
return res.text
def get_code():
data = get_req_data('http://fund.eastmoney.com/js/fundcode_search.js')
data_list = data.replace("var r = [", "").replace("];", "").replace("],", "];").split(";")
mf_codes = []
for a in data_list:
mf_list = a.split(",")
mf_code = mf_list[0].replace("[", "").replace("\"", "")
mf_codes.append(mf_code)
return mf_codes
二、数据清洗及计算指标
1.过滤数据
据观察,数据中存在已终止基金或已不更新数据的基金 仅保留:昨日涨跌幅、近一月涨跌幅、近三月涨跌幅、近六月涨跌幅、近一年涨跌幅、近三年涨跌幅%、成立以来涨跌幅 共7个字段中为 “–”(基金运作时间未久会以“–”表示,为正常)和存在“%”的数据
2.数据字段格式统一
(便于后续SQL查询)金额字段统一为:仅保留数值(以亿元为单位)
(便于后续SQL查询)统一涨幅字段:百分比 => double小数,“–” => 0.0
3.计算得出字段now_down、all_win
all_win:将3个月、半年、一年、三年、历史总涨幅为正的基金标记为1 => 从数据层面上看,该基金中长期正收益,值得长期持有并定投
now_down:将同时满足昨日涨跌幅<0,仅一月涨跌幅<0,近半年涨跌幅>0,仅一年涨跌幅>0的基金标记,并计算出昨日跌幅+ 仅一月跌幅作为参数 => 数据层面上判断:该基金长期持有为正收益且当前属于回撤调整期,属于相对低值可入手
4.数据清洗具体实现
def parse(codes):
t = len(codes)
for i in range(0, t, 1000):
sqlInfo, end = [], i + 1000
if end > t:
end = t
for j in range(i, end):
url = 'http://fund.eastmoney.com/'+str(codes[j])+'.html'
response = etree.HTML(get_req_data(url))
mf_name =response.xpath('//div[@class="fundDetail-tit"]/div[1]/text()')
check_day = response.xpath('//dl[@class="dataItem02"]/dd[1]/span[1]/text()')
if len(mf_name) == 0 or len(check_day) == 0:
continue
mf_name, mf_num = mf_name[0], codes[j]
mf_type = response.xpath('//td[contains(text(),"基金类型")]/a/text()')[0]
mf_money = check_data(response.xpath('//td[contains(text(),"亿元")]/text()')[0].replace(":", "").split("亿")[0])
mf_manager = response.xpath('//td[contains(text(),"基金经理")]/a/text()')[0]
mf_all = response.xpath('//div[@class="dataOfFund"]/dl[3]/dd[3]/span[2]/text()')
worth, rate, day7, all_win, now_down = 0.0, 0.0, 0.0, 0, 0.0
if len(mf_all) != 0:
worth = check_data(check_day[0])
if(worth == 0.0):
continue
rate = check_data(response.xpath('//dl[@class="dataItem02"]/dd[1]/span[2]/text()')[0].replace("%", "")) * 0.01
mon1 = check_data(response.xpath('//div[@class="dataOfFund"]/dl[1]/dd[2]/span[2]/text()')[0].replace("%", "")) * 0.01
mon3 = check_data(response.xpath('//div[@class="dataOfFund"]/dl[2]/dd[2]/span[2]/text()')[0].replace("%", "")) * 0.01
mon6 = check_data(response.xpath('//div[@class="dataOfFund"]/dl[3]/dd[2]/span[2]/text()')[0].replace("%", "")) * 0.01
mon12 = check_data(response.xpath('//div[@class="dataOfFund"]/dl[1]/dd[3]/span[2]/text()')[0].replace("%", "")) * 0.01
mon36 = check_data(response.xpath('//div[@class="dataOfFund"]/dl[2]/dd[3]/span[2]/text()')[0].replace("%", "")) * 0.01
mon_all = check_data(mf_all[0].replace("%", "")) * 0.01
else:
day7 = check_data(check_day[0].replace("%", "")) * 0.01
mon1 = check_data(response.xpath('//div[@class="dataOfFund"]/dl[1]/dd[1]/span[2]/text()')[0].replace("%", "")) * 0.01
mon3 = check_data(response.xpath('//div[@class="dataOfFund"]/dl[2]/dd[1]/span[2]/text()')[0].replace("%", "")) * 0.01
mon6 = check_data(response.xpath('//div[@class="dataOfFund"]/dl[3]/dd[1]/span[2]/text()')[0].replace("%", "")) * 0.01
mon12 = check_data(response.xpath('//div[@class="dataOfFund"]/dl[1]/dd[2]/span[2]/text()')[0].replace("%", "")) * 0.01
mon36 = check_data(response.xpath('//div[@class="dataOfFund"]/dl[2]/dd[2]/span[2]/text()')[0].replace("%", "")) * 0.01
mon_all = check_data(response.xpath('//div[@class="dataOfFund"]/dl[3]/dd[2]/span[2]/text()')[0].replace("%", "")) * 0.01
# all_win:将3个月、半年、一年、三年、历史总涨幅为正的基金标记为1 => 从数据层面上看,该基金中长期正收益,值得长期持有并定投
if mon3 > 0.0 and mon6 > 0.0 and mon12 > 0.0 and mon36 > 0.0 and mon_all > 0.0:
all_win = 1
# now_down:将同时满足昨日涨跌幅 < 0,仅一月涨跌幅 < 0,近半年涨跌幅 > 0, 仅一年涨跌幅 > 0
# 的基金标记,并计算出昨日跌幅 + 仅一月跌幅作为参数 => 数据层面上判断:该基金长期持有为正收益且当前属于回撤调整期,属于相对低值可入手
if rate < 0.0 and mon1 < 0.0 and mon6 > 0.0 and mon12 > 0.0 and mon36 > 0.0:
now_down = (rate + mon1) * 0.01
sqlInfo.append((mf_name, mf_num, mf_type, float(mf_money), mf_manager, worth, rate, day7, mon1, mon3, mon6, mon12, mon36, mon_all, all_win, now_down))
print(sqlInfo[-1])
insert_data(sqlInfo)
def check_data(mf):
if mf == "--":
mf = 0.0
return float(mf)
4.保存数据库
def insert_data(sqlInfo):
# 批量写入数据库, 打开数据库连接
conn = pymysql.connect(host='127.0.0.1', port=3306, user='root', passwd='123456', db='fund_money', charset='utf8')
sql = 'INSERT INTO `fund`(mf_name, mf_num, mf_type, mf_money, mf_manager, mf_worth, mf_rate, mf_day7, mf_mon1 , mf_mon3 , mf_mon6 , mf_mon12, mf_mon36, mf_all, all_win, now_down) ' \
'VALUES(%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)'
# 使用 cursor() 方法创建一个游标对象 cursor
cursor = conn.cursor()
cursor.executemany(sql, sqlInfo)
conn.commit() # 提交
cursor.close()
conn.close()
三、数据分析:SQL
1.昨日涨跌幅、近一月涨跌幅、近一年涨幅、仅三年涨幅 top20 => 当前主题行情分析
2.金额总值top50=> 截至2023-6-29最热门抱团方向
3. 3月份后涨跌幅为“- -” => 新发基金封闭期3个月=> 当前基金机构看好的行业板块方向 => 未来抱团方向(?)
4.now_down最小前20 => 数值越小,跌幅越大
SELECT * FROM `fund` ORDER BY now_down, mf_mon1 LIMIT 20;
5.all_win =>中长期正收益,值得长期持有
总结
完整代码