*注:所有产生的数据可在主页资源中“中欧医疗健康混合C相关基金数据”*中查看与使用
1 问题背景分析
2022年1月末,年前股票大跌,相应的基金连续跌了近一周的时间。我作为一个资深的“韭菜”,于2021年12月初购买了中欧医疗健康混合C基金。基金经理是葛兰,据说是医疗板块最优秀的基金经理,并且医疗板块是民生大计,我当时认为购买这只基金可以获取相当多的收益。但自我购买以来,这只基金连续跌了一个月,直到年前我的这只基金跌了近15%。为了扭转局势,我不断的学习基金知识,并希望通过数据分析来量化购买决策。购买更加科学就会带来更多的收益。
分析一只基金除了看基金经理的资历之外,还需要看这只基金的业绩,以及大家对这只基金的评价。所以,我需要这只基金的历史净值、日涨幅以及购买这只基金的用户的评论。
数据来源是天天基金网,天天基金是中国A股上市的财经门户–东方财富网旗下全资子公司,同时也是证监会批准的首批独立基金销售机构,天天基金网具有最全面的基金数据。
2 数据采集/清洗
数据来源:天天基金网(https://fund.eastmoney.com/)
所需数据:
1)中欧医疗健康混合C的历史净值以及日涨幅数据;
2)同类型基金(医疗)的历史净值的均值以及日涨幅数据;
3)沪深300指数基金历史净值以及日涨幅数据;
4)中欧医疗健康混合C用户评论数据;
针对中欧医疗健康混合C的历史净值以及日涨幅、同类型基金(医疗)的历史净值的均值以及日涨幅、沪深300指数基金历史净值以及日涨幅数据利用Python的requests库对天天基金网进行爬取。点击“基金档案”并搜索“中欧医疗健康混合C”可以看到基金净值的表格,网站截图如下所示:
并且通过天天基金网的板块检索选择与中欧医疗相似的七只基金,基金代码分别是007613、001563、003581、005043、007111、005044、110023。具体操作如下图所示:
数据采集:
#导入相应的包 import requests import re import json import pandas as pd
#爬取中欧医疗数据 df_list = [] for i in range(1,65): url = "http://api.fund.eastmoney.com/f10/lsjz?callback=jQuery18304159015262110892_1643544350831&fundCode=003096&pageIndex={}&pageSize=20&startDate=&endDate=&_=1643545365216".format(i) headers = { "Referer":"http://fundf10.eastmoney.com/jjjz_003096.html", "User-Agent":'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:92.0) Gecko/20100101 Firefox/92.0', } resp = requests.get(url,headers=headers) html = resp.text res = re.findall('\((.*?)\)',html) datas = json.loads(res[0])["Data"]["LSJZList"] df = pd.DataFrame(datas) df_list.append(df) df_data = pd.concat(df_list) df_data.to_csv('中欧医疗健康混合C (003096).csv',index=False,encoding="utf_8_sig")
#爬取同医疗类型基金数据并计算净值均值方便以后对比 same_type = ["007613","001563","003581","005043","007111","005044","110023"] pages = [25,64,62,50,34,50,64] df_list = [] for index in range(1,65): lis = [] for i in range(0,7): if pages[i] >= index: lis.append(same_type[i]) else: pass df1 = [] for i in lis: url = "http://api.fund.eastmoney.com/f10/lsjz?callback=jQuery18304159015262110892_1643544350831&fundCode={}&pageIndex={}&pageSize=20&startDate=&endDate=&_=1643545365216".format(i,index) headers = { "Referer":"http://fundf10.eastmoney.com/jjjz_{}.html".format(i), "User-Agent":'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:92.0) Gecko/20100101 Firefox/92.0', } resp = requests.get(url,headers=headers) html = resp.text res = re.findall('\((.*?)\)',html) datas = json.loads(res[0])["Data"]["LSJZList"] df = pd.DataFrame(datas) df1.append(df) df_new = pd.concat(df1,keys=lis) df_list.append(df_new) df_data = pd.concat(df_list) df_data.to_csv('同类基金.csv',index=True,encoding="utf_8_sig")
#爬取沪深300指数净值 df_list = [] for i in range(1,65): url = "http://api.fund.eastmoney.com/f10/lsjz?callback=jQuery18304159015262110892_1643544350831&fundCode=050002&pageIndex={}&pageSize=20&startDate=&endDate=&_=1643545365216".format(i) headers = { "Referer":"http://fundf10.eastmoney.com/jjjz_050002.html", "User-Agent":'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:92.0) Gecko/20100101 Firefox/92.0', } resp = requests.get(url,headers=headers) html = resp.text res = re.findall('\((.*?)\)',html) datas = json.loads(res[0])["Data"]["LSJZList"] df = pd.DataFrame(datas) df_list.append(df) df_data = pd.concat(df_list) df_data.to_csv('博时沪深300指数A (050002).csv',index=False,encoding="utf_8_sig")
数据清洗:
import os import re import math import pandas as pd
df_base = pd.read_csv('中欧医疗健康混合C (003096).csv') df_avg = pd.read_csv('同类医疗基金均值.csv') df_300 = pd.read_csv('博时沪深300指数A (050002).csv')
def data_extraction(df,col): #df_avg的col=3,其余为6 df_flashback = df.reindex(index=df.index[::-1]) cumulative_growth = [] sum_up = 0 for i in range(0,1280): cod = float(df_flashback.iloc[i,col]) sum_up += cod cumulative_growth.append(sum_up) data = { "FSRQ":pd.Series(df_flashback["FSRQ"].values), "DWJZ":pd.Series(df_flashback["DWJZ"].values), "JZZZL":pd.Series(df_flashback["JZZZL"].values), "DWZZL":pd.Series(cumulative_growth) } new_df = pd.DataFrame(data) return new_df
3 净值可视化
这里使用pyecharts+DataFrame。pyecharts提供的方法可以使用python直接画echarts图,但是需要数行代码,而平时做数据分析时,数据多存在pandas的DataFrame里,DataFrame的plot方法可以使用matplotlib做后端,直接通过调用实例化的类的方法来绘制图像。 因此,无需在DataFrame外创建pyecharts对象,而是直接用eplot方法通过pyecharts后端来画交互图。
中欧医疗健康混合C历史净值数据可视化
from eplot import eplot df1 = pd.Series(data_extraction(df_base,6)['DWJZ'].values.tolist(), index= data_extraction(df_base,6)['FSRQ'].values.tolist()) df1.eplot()
可见,自2016年11月8日中欧医疗健康混合C成立之初,该只基金上涨一个多点,在2021年5月左右达到顶峰,净值达到了约4.5。但是,今年以来连续下跌,这也是我亏损的原因。
中欧医疗健康混合C与其他基金对比
(1)中欧医疗成立以来涨幅对比
from pyecharts.charts import Line #导入折线图 from pyecharts import options as opts line=( Line() .set_global_opts( tooltip_opts=opts.TooltipOpts(is_show=False), xaxis_opts=opts.AxisOpts(type_="category"), yaxis_opts=opts.AxisOpts( type_="value", axistick_opts=opts.AxisTickOpts(is_show=True), splitline_opts=opts.SplitLineOpts(is_show=True), ), ) .add_xaxis(xaxis_data=data_extraction(df_base,6)["FSRQ"].values.tolist()) .add_yaxis( series_name="中欧医疗", y_axis=data_extraction(df_base,6)["DWZZL"].values.tolist(), symbol="emptyCircle", is_symbol_show=True, label_opts=opts.LabelOpts(is_show=False), ) .add_yaxis( series_name="沪深300", y_axis=data_extraction(df_300,6)["DWZZL"].values.tolist(), symbol="emptyCircle", is_symbol_show=True, label_opts=opts.LabelOpts(is_show=False), ) .add_yaxis( series_name="同类均值", y_axis=data_extraction(df_avg,3)["DWZZL"].values.tolist(), symbol="emptyCircle", is_symbol_show=True, label_opts=opts.LabelOpts(is_show=False), ) ) line.render_notebook()
从图可见中欧医疗的涨幅已经远远超过了同类均值以及沪深300指数。由此可见,中欧医疗的业绩是非常可观的。
(2)近一年的涨幅对比
def data_extraction_year(df,col): #df_avg的col=3,其余为6 df = df[0:366] df_flashback = df.reindex(index=df.index[::-1]) cumulative_growth = [] sum_up = 0 for i in range(0,366): cod = float(df_flashback.iloc[i,col]) cumulative_growth.append(sum_up) sum_up += cod data = { "FSRQ":pd.Series(df_flashback["FSRQ"].values), "DWJZ":pd.Series(df_flashback["DWJZ"].values), "JZZZL":pd.Series(df_flashback["JZZZL"].values), "DWZZL":pd.Series(cumulative_growth) } new_df = pd.DataFrame(data) return new_df
from pyecharts.charts import Line #导入折线图 x = data_extraction_year(df_base,6)["FSRQ"].values.tolist() y1 = data_extraction_year(df_base,6)["DWZZL"].values.tolist() y2 = data_extraction_year(df_300,6)["DWZZL"].values.tolist() y3 = data_extraction_year(df_avg,3)["DWZZL"].values.tolist() line=( Line() .set_global_opts( tooltip_opts=opts.TooltipOpts(is_show=False), xaxis_opts=opts.AxisOpts(type_="category"), yaxis_opts=opts.AxisOpts( type_="value", axistick_opts=opts.AxisTickOpts(is_show=True), splitline_opts=opts.SplitLineOpts(is_show=True), ), ) .add_xaxis(xaxis_data=x) .add_yaxis( series_name="中欧医疗", y_axis=y1, symbol="emptyCircle", is_symbol_show=True, label_opts=opts.LabelOpts(is_show=False), ) .add_yaxis( series_name="沪深300", y_axis=y2, symbol="emptyCircle", is_symbol_show=True, label_opts=opts.LabelOpts(is_show=False), ) .add_yaxis( series_name="同类均值", y_axis=y3, symbol="emptyCircle", is_symbol_show=True, label_opts=opts.LabelOpts(is_show=False), ) ) line.render_notebook()
从图可见中欧医疗涨跌已经趋近于同类均值以及市场均值。说明近一年来的业绩已经不如前几年的业绩。虽然总体上比同类均值以及市场均值涨幅可观,但是业绩下滑是不争的事实。
(3)近三个月的涨幅对比
def data_extraction_month3(df,col): #df_avg的col=3,其余为6 df = df[0:80] df_flashback = df.reindex(index=df.index[::-1]) cumulative_growth = [] sum_up = 0 for i in range(0,80): cod = float(df_flashback.iloc[i,col]) cumulative_growth.append(sum_up) sum_up += cod data = { "FSRQ":pd.Series(df_flashback["FSRQ"].values), "DWJZ":pd.Series(df_flashback["DWJZ"].values), "JZZZL":pd.Series(df_flashback["JZZZL"].values), "DWZZL":pd.Series(cumulative_growth) } new_df = pd.DataFrame(data) return new_df
from pyecharts.charts import Line #导入折线图 x = data_extraction_month3(df_base,6)["FSRQ"].values.tolist() y1 = data_extraction_month3(df_base,6)["DWZZL"].values.tolist() y2 = data_extraction_month3(df_300,6)["DWZZL"].values.tolist() y3 = data_extraction_month3(df_avg,3)["DWZZL"].values.tolist() line=( Line() .set_global_opts( tooltip_opts=opts.TooltipOpts(is_show=False), xaxis_opts=opts.AxisOpts(type_="category"), yaxis_opts=opts.AxisOpts( type_="value", axistick_opts=opts.AxisTickOpts(is_show=True), splitline_opts=opts.SplitLineOpts(is_show=True), ), ) .add_xaxis(xaxis_data=x) .add_yaxis( series_name="中欧医疗", y_axis=y1, symbol="emptyCircle", is_symbol_show=True, label_opts=opts.LabelOpts(is_show=False), ) .add_yaxis( series_name="沪深300", y_axis=y2, symbol="emptyCircle", is_symbol_show=True, label_opts=opts.LabelOpts(is_show=False), ) .add_yaxis( series_name="同类均值", y_axis=y3, symbol="emptyCircle", is_symbol_show=True, label_opts=opts.LabelOpts(is_show=False), ) ) line.render_notebook()
从图可见中欧医疗的业绩已经远远不如同类均值以及市场均值,与此同时中欧医疗的基金规模不断的扩大。说明散户都认为这时是底部,大家都进行了抄底,但是由于美联储加息政策的出台,造成了情绪恐慌,市场下跌严重。而中欧医疗抗跌能力远远不及同类均值,造成了大量用户损失,这也是我损失的原因。
(4)近一个月的涨幅对比
def data_extraction_month(df,col): #df_avg的col=3,其余为6 df = df[0:32] df_flashback = df.reindex(index=df.index[::-1]) cumulative_growth = [] sum_up = 0 for i in range(0,32): cod = float(df_flashback.iloc[i,col]) cumulative_growth.append(sum_up) sum_up += cod data = { "FSRQ":pd.Series(df_flashback["FSRQ"].values), "DWJZ":pd.Series(df_flashback["DWJZ"].values), "JZZZL":pd.Series(df_flashback["JZZZL"].values), "DWZZL":pd.Series(cumulative_growth) } new_df = pd.DataFrame(data) return new_df
from pyecharts.charts import Line #导入折线图 x = data_extraction_month(df_base,6)["FSRQ"].values.tolist() y1 = data_extraction_month(df_base,6)["DWZZL"].values.tolist() y2 = data_extraction_month(df_300,6)["DWZZL"].values.tolist() y3 = data_extraction_month(df_avg,3)["DWZZL"].values.tolist() line=( Line() .set_global_opts( tooltip_opts=opts.TooltipOpts(is_show=False), xaxis_opts=opts.AxisOpts(type_="category"), yaxis_opts=opts.AxisOpts( type_="value", axistick_opts=opts.AxisTickOpts(is_show=True), splitline_opts=opts.SplitLineOpts(is_show=True), ), ) .add_xaxis(xaxis_data=x) .add_yaxis( series_name="中欧医疗", y_axis=y1, symbol="emptyCircle", is_symbol_show=True, label_opts=opts.LabelOpts(is_show=False), ) .add_yaxis( series_name="沪深300", y_axis=y2, symbol="emptyCircle", is_symbol_show=True, label_opts=opts.LabelOpts(is_show=False), ) .add_yaxis( series_name="同类均值", y_axis=y3, symbol="emptyCircle", is_symbol_show=True, label_opts=opts.LabelOpts(is_show=False), ) ) line.render_notebook()
从图可见医疗板块在本次市场下跌中是下跌比较严重的板块,并且中欧医疗的抗跌能力非常差,由此可以推出这只基金最大回撤率非常的高。这是股票型基金的特点——大涨大跌。根据我稳健型的投资风格,我可能会放弃这只基金,转站债券、指数基金等具有相对稳定收益的理财产品。