提示:本文为个人原创,仅供技术探讨与交流,对实际投资并不造成建议。
基于大数据技术对基金分析-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 requestsdef 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.textdef 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 + 1000if end > t:end = tfor 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:continuemf_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.0if len(mf_all) != 0:worth = check_data(check_day[0])if(worth == 0.0):continuerate = check_data(response.xpath('//dl[@class="dataItem02"]/dd[1]/span[2]/text()')[0].replace("%", "")) * 0.01mon1 = check_data(response.xpath('//div[@class="dataOfFund"]/dl[1]/dd[2]/span[2]/text()')[0].replace("%", "")) * 0.01mon3 = check_data(response.xpath('//div[@class="dataOfFund"]/dl[2]/dd[2]/span[2]/text()')[0].replace("%", "")) * 0.01mon6 = check_data(response.xpath('//div[@class="dataOfFund"]/dl[3]/dd[2]/span[2]/text()')[0].replace("%", "")) * 0.01mon12 = check_data(response.xpath('//div[@class="dataOfFund"]/dl[1]/dd[3]/span[2]/text()')[0].replace("%", "")) * 0.01mon36 = check_data(response.xpath('//div[@class="dataOfFund"]/dl[2]/dd[3]/span[2]/text()')[0].replace("%", "")) * 0.01mon_all = check_data(mf_all[0].replace("%", "")) * 0.01else:day7 = check_data(check_day[0].replace("%", "")) * 0.01mon1 = check_data(response.xpath('//div[@class="dataOfFund"]/dl[1]/dd[1]/span[2]/text()')[0].replace("%", "")) * 0.01mon3 = check_data(response.xpath('//div[@class="dataOfFund"]/dl[2]/dd[1]/span[2]/text()')[0].replace("%", "")) * 0.01mon6 = check_data(response.xpath('//div[@class="dataOfFund"]/dl[3]/dd[1]/span[2]/text()')[0].replace("%", "")) * 0.01mon12 = check_data(response.xpath('//div[@class="dataOfFund"]/dl[1]/dd[2]/span[2]/text()')[0].replace("%", "")) * 0.01mon36 = check_data(response.xpath('//div[@class="dataOfFund"]/dl[2]/dd[2]/span[2]/text()')[0].replace("%", "")) * 0.01mon_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.01sqlInfo.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.0return 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() 方法创建一个游标对象 cursorcursor = 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 =>中长期正收益,值得长期持有
总结
完整代码