来源:Python数据之道 (ID:PyDataLab)

作者:阳哥

01写在前面

大家好,我是阳哥。

大家知道,在财经领域,Python是有着广泛的用途的。2021年,量化基金开始成为弄潮儿。有不少消息称,量化基金贡献了相当比例的成交量,量化基金对成交量的贡献到底有多大,咱也不知道。

我们所知道的是,在金融量化领域,Python 是主力编程语言之一。

在2021年9月,阳哥曾分享过 用 Python 快速获取基金持仓增值与减持情况 这个内容,由于使用的工具 akshare 获取数据的接口发生变化,有不少同学在运行代码时遇到问题,本次将更新下文章内容,以及代码文件。

在财经领域,之前阳哥分享过部分Python实践应用的主题,包括:

  1. 用Python来做一个投资计划

  2. 用Python快速追踪基金的收益情况

  3. 视频:Plotly 和 Dash 在投资领域的应用

  4. 视频:Plotly中绘制股票交易图表

  5. Plotly中绘制三种经典的股票交易图表

  6. 神器Tushare,财经数据必备工具!

  7. 用 Python 读取巴菲特近期持仓数据

大家知道,近几年,不少同学都是经由基金进入到股市中的。2020年就很流行“买股不如买基”的说话,至于股票和基金到底谁更好,这个仁者见仁智者见智,恐怕一时半会儿也说不清楚。

本文中,阳哥给大家分享的主题是 用 Python 来追踪和更新基金的持仓结构以及基金的股票增持和减持情况,涉及到的Python库主要是 pandasakshare

最终实现的效果包括两个方面:

  • 单支基金的不同季度持仓变化情况

  • 多支基金的十大持仓的历史信息

效果如下:

单支基金

多支基金

02akshare 介绍

与 Tushare 类似, akshare 是国内比较的优秀的财经数据工具。

AKShare 是基于 Python 的财经数据接口库, 目的是实现对股票、期货、期权、基金、外汇、债券、指数、加密货币等金融产品的基本面数据、实时和历史行情数据、衍生数据从数据采集、数据清洗到数据落地的一套工具。

在使用 akshare 之前,需要进行安装,使用命令如下:

pip install akshare

在安装好 akshare 后,导入相关 Python库,如下:

import akshare as ak
import pandas as pd
import numpy as npprint(f'pandas version: {pd.__version__}')
print(f'akshare version: {ak.__version__}')# pandas version: 1.3.2
# akshare version: 1.3.41

2022.1.12 更新内容:

本文中, akshare 使用的版本为 1.3.41

03单支基金

对于单支基金的持仓情况变化,主要是通过对比临近的两个季度,来观察基金持仓的股票增持与减持情况,从而来跟踪基金的趋势变化。

由于基金季度报告中,1季度和3季度只披露前十大股票的持仓情况,而半年度(Q2)和年度(Q4)报告会披露全部持仓的情况,因此,也可以通过对更详细信息的对比来挖掘有些趋势信息。

下面,将详细的介绍获取并对比两个季度的持仓数据。

分步骤介绍

首先,咱们通过 akshare 来获取基金的季度持仓数据,以 易方达蓝筹精选基金(基金代码:005827)为例,获取2019-2021年的持仓数据情况,代码如下:

# 易方达蓝筹精选,005827
# 前海开源公共事业股票,005669
years = ['2019','2020','2021']data = pd.DataFrame()
for yr in years:# df_tmp = ak.fund_em_portfolio_hold(code="005827",year=yr) # akshare version: 1.0.91df_tmp = ak.fund_portfolio_hold_em(code="005827",year=yr) # akshare version: 1.3.41data = data.append(df_tmp)data['季度']=data['季度'].apply(lambda x:x[:6])
data['季度'] = data['季度'].str.replace('年','Q')
data['占净值比例'] = pd.to_numeric(data['占净值比例'])
data

2022.1.12 更新内容:

基金持仓数据获取的接口,在 akshare 1.3.41 版本中,更新为 fund_portfolio_hold_em

结果如下:

基金持仓历史数据

假设咱们需要对比2021年1季度和2021年2季度的持仓变化情况,则只需要在上面的数据上选择 2021Q12021Q2 即可。

数据选取如下:

s1 = '2021Q1'
s2 = '2021Q2'
s1_share = s1+'持股数'
s2_share = s2+'持股数'
s1_value = s1+'持仓市值'
s2_value = s2+'持仓市值'
s1_ratio = s1+'持仓比例'
s2_ratio = s2+'持仓比例'df1 =data[data['季度']==s1]
df1 = df1[['股票代码', '股票名称','持股数','持仓市值','占净值比例']]
df1 = df1.rename(columns={'持股数':s1_share,'持仓市值':s1_value,'占净值比例':s1_ratio})
df2 =data[data['季度']==s2]
df2 = df2[['股票代码', '股票名称','持股数','持仓市值','占净值比例']]
df2 = df2.rename(columns={'持股数':s2_share,'持仓市值':s2_value,'占净值比例':s2_ratio})
df1

2021年1季度数据结果如下:

2021Q1

在获取了两个季度的数据后,将数据按 "股票代码" 进行拼接,在得到的 dataframe 中,可以对持股的增减情况进行判断。

需要注意的是,由于Q2 和 Q4,即半年度和年度报告,是需要披露全部持仓的,因此在拼接后的dataframe 中,对应列的 NaN 数据应为 0,这一点可以细细品味下。

df_merge = pd.merge(df1,df2,on='股票代码',how='outer')# Q2 和 Q4,即半年度和年度报告,是需要披露全部持仓的
# 合并后,在dataframe 中 NaN 的数据应为 0
if s1.endswith('Q2') or s1.endswith('Q4'):df_merge[s1_share] = df_merge[s1_share].fillna(0)df_merge[s1_value] = df_merge[s1_value].fillna(0)df_merge[s1_ratio] = df_merge[s1_ratio].fillna(0)if s2.endswith('Q2') or s2.endswith('Q4'):df_merge[s2_share] = df_merge[s2_share].fillna(0)df_merge[s2_value] = df_merge[s2_value].fillna(0)df_merge[s2_ratio] = df_merge[s2_ratio].fillna(0)df_merge['持股数变化'] = df_merge[s2_share] - df_merge[s1_share]
df_merge = df_merge.sort_values(s2_value,ascending=False)df_merge['股票名称'] = df_merge['股票名称_y']
# df_merge['股票名称'] = df_merge['股票名称'].fillna('0')
# df_merge.loc[df_merge['股票名称']=='0','股票名称'] = df_merge.loc[df_merge['股票名称']=='0','股票名称_x']
df_merge.loc[df_merge['股票名称'].isna(),'股票名称'] = df_merge.loc[df_merge['股票名称'].isna(),'股票名称_x']
df_merge = df_merge[['股票代码', '股票名称', s1_share, s1_value, s1_ratio, s2_share,s2_value, s2_ratio, '持股数变化']]df_merge

结果如下:

持仓对比

上面的结果中,如果没有对 Q2 和 Q4 季度进行判断,则上图中红框内的数据会是 NaN,同时也计算不出减持的结果(结果会显示 NaN)。

因此,在进行调整后,可以真实的看到基金减持的情况。在2021年2季度,易方达蓝筹精选基金清仓了中炬高新等股票。

针对上述表格,咱们也可以通过 pandas 的样式设置,来较为清晰的查看基金的增减持情况,代码如下:

format_dict = {s1_share: '{0:.2f}', s2_share: '{0:.2f}', s1_value: '{0:.0f}', s2_value: '{0:.0f}',s1_ratio: '{0:.2f}', s2_ratio: '{0:.2f}', '持股数变化': '{0:.2f}'}df_merge.style.hide_index()\.hide_columns(['股票代码'])\.format(format_dict)\.bar(subset=['持股数变化'],color=['#99ff66','#ee7621'],align='mid')

由于数据行数较多,只截取了部分,效果如下:

持仓对比

在上面的表格中,nan 数据结果,是由于数据披露的情况,有些数据咱们是获取不到的,因此也计算不了持仓增减情况,这些数据,在实际判断中,意义是不大的。

因此,咱们在显示数据结果的时候,也可以选择隐藏这些数据,如下:

df_merge_update = df_merge[~(df_merge['持股数变化'].isna())]
df_merge_update.style.hide_index()\.hide_columns(['股票代码'])\.format(format_dict)\.bar(subset=['持股数变化'],color=['#99ff66','#ee7621'],align='mid')

结果如下:

隐藏nan值

这样,咱们就可以得到,从两个季度信息对比,可以确定的增减持股票情况了(注意,不是全部的增减持,因为有些季度,有些持仓数据是不披露的)。

关于 Pandas表格样式设置的详细内容,请前往下面的链接:

  • Pandas表格样式配置指南

函数封装

将上面的详细过程封装成一个自定义函数,这样咱们就可以在后续重复使用该函数来获取不同基金的持仓变化信息了。

函数封装如下:

def compare(code,years,s1,s2):"""code,str,基金代码;years,list,年份列表,['yr1','yr2','……'];s1,str,靠前的季度, 格式为 'YYYYQ1',例如: '2021Q2';s2,str,靠后的季度, 格式为 'YYYYQ1',例如: '2021Q2';注意,s1和s2的年份应在 years 里"""s1_share = s1+'持股数's2_share = s2+'持股数's1_value = s1+'持仓市值's2_value = s2+'持仓市值's1_ratio = s1+'持仓比例's2_ratio = s2+'持仓比例'data = pd.DataFrame()for yr in years:# df_tmp = ak.fund_em_portfolio_hold(code=code,year=yr) # akshare version: 1.0.91df_tmp = ak.fund_portfolio_hold_em(code=code,year=yr) # akshare version: 1.3.41data = data.append(df_tmp)data['季度']=data['季度'].apply(lambda x:x[:6])data['季度'] = data['季度'].str.replace('年','Q')data['占净值比例'] = pd.to_numeric(data['占净值比例'])df1 =data[data['季度']==s1]df1 = df1[['股票代码', '股票名称','持股数','持仓市值','占净值比例']]df1 = df1.rename(columns={'持股数':s1_share,'持仓市值':s1_value,'占净值比例':s1_ratio})df2 =data[data['季度']==s2]df2 = df2[['股票代码', '股票名称','持股数','持仓市值','占净值比例']]df2 = df2.rename(columns={'持股数':s2_share,'持仓市值':s2_value,'占净值比例':s2_ratio})df_merge = pd.merge(df1,df2,on='股票代码',how='outer')# Q2 和 Q4,即半年度和年度报告,是需要披露全部持仓的# 合并后,在dataframe 中 NaN 的数据应为 0 if s1.endswith('Q2') or s1.endswith('Q4'):df_merge[s1_share] = df_merge[s1_share].fillna(0)df_merge[s1_value] = df_merge[s1_value].fillna(0)df_merge[s1_ratio] = df_merge[s1_ratio].fillna(0)if s2.endswith('Q2') or s2.endswith('Q4'):df_merge[s2_share] = df_merge[s2_share].fillna(0)df_merge[s2_value] = df_merge[s2_value].fillna(0)df_merge[s2_ratio] = df_merge[s2_ratio].fillna(0)df_merge['持股数变化'] = df_merge[s2_share] - df_merge[s1_share]df_merge = df_merge.sort_values(s2_value,ascending=False)df_merge['股票名称'] = df_merge['股票名称_y']# df_merge['股票名称'] = df_merge['股票名称'].fillna('0')# df_merge.loc[df_merge['股票名称']=='0','股票名称'] = df_merge.loc[df_merge['股票名称']=='0','股票名称_x']df_merge.loc[df_merge['股票名称'].isna(),'股票名称'] = df_merge.loc[df_merge['股票名称'].isna(),'股票名称_x']df_merge = df_merge[['股票代码', '股票名称', s1_share, s1_value, s1_ratio, s2_share,s2_value, s2_ratio, '持股数变化']]return df_merge

2022.1.12 更新内容:

基金持仓数据获取的接口,在 akshare 1.3.41 版本中,更新为 fund_portfolio_hold_em

自定义函数 compare() 涉及4个参数,参数的说明如下:

  • code,str,基金代码;

  • years,list,年份列表,['yr1','yr2','……'];

  • s1,str,靠前的季度, 格式为 'YYYYQ1',例如: '2021Q2';

  • s2,str,靠后的季度, 格式为 'YYYYQ1',例如: '2021Q2';

  • 注意,s1和s2的年份应在 years 里

使用该自定义函数之前,先要设置好参数的具体数值,还是以 易方达蓝筹精选基金(基金代码:005827)为例,获取 2020Q3 和 2021Q1 的持仓数据情况,代码如下:

code = "005827"
years = ['2020','2021']
s1 =  '2020Q3'
s2 = '2021Q1'
s1_share = s1+'持股数'
s2_share = s2+'持股数'
s1_value = s1+'持仓市值'
s2_value = s2+'持仓市值'
s1_ratio = s1+'持仓比例'
s2_ratio = s2+'持仓比例'df_merge = compare(code,years,s1,s2)format_dict = {s1_share: '{0:.2f}', s2_share: '{0:.2f}', s1_value: '{0:.0f}', s2_value: '{0:.0f}',s1_ratio: '{0:.2f}', s2_ratio: '{0:.2f}', '持股数变化': '{0:.2f}'}df_merge.style.hide_index()\.hide_columns(['股票代码'])\.format(format_dict)\.bar(subset=['持股数变化'],color=['#99ff66','#ee7621'],align='mid')

demo

从数据计算结果来看,有 nan 的数据行,是不能直接判断出结果的。

但如果是在最新的季度出现,并且是在十大持仓里靠前的位置,大概率是增值了,或者是完全新增的股票持仓。

比如下面的招行:

nan值

注:一般是直接比较两个临近的季度,这里的 2020Q3 和 2021Q1 之间,还隔个一个 2020Q4 。

04多支基金

同时获取多支基金的十大股票持仓信息时,在这里只获取每个季度股票名称的信息。

首先,来获取单支基金的十大持仓股票名称信息,自定义函数如下:

# 获取单支基金的十大股票名称信息
def fund_stock_holding(years,code):data = pd.DataFrame()for yr in years:# df_tmp = ak.fund_em_portfolio_hold(code=code,year=yr) # akshare version: 1.0.91df_tmp = ak.fund_portfolio_hold_em(code=code,year=yr) # akshare version: 1.3.41data = data.append(df_tmp)#     data['季度']=data['季度'].apply(lambda x:x[:8])data['季度']=data['季度'].apply(lambda x:x[:6])data['季度'] = data['季度'].str.replace('年','Q')data['占净值比例'] = pd.to_numeric(data['占净值比例'])# 序号 中,有些是字符串,并且包含字符 “*”,需要替换,最后转换为数字data['序号'] = data['序号'].astype(str)data['序号'] = data['序号'].str.replace('\*','',regex=True)data['序号'] = pd.to_numeric(data['序号'])data = data.sort_values(['季度','持仓市值'],ascending=[True,False])df = data.set_index(['序号','季度']).stack().unstack([1,2]).head(10)df = df.loc[:,(slice(None), '股票名称')]  # 只选取 股票名称df = df.droplevel(None,axis=1)df.columns.name=Nonedf = df.reset_index()
#     df.index.name = Nonedf['基金代码'] = codecols = df.columns.tolist()cols = cols[:1] + cols[-1:] + cols[1:-1]  # 将基金代码列名放前面df = df[cols]return df

2022.1.10 更新内容:

基金持仓数据获取的接口,在 akshare 1.3.41 版本中,更新为 fund_portfolio_hold_em

以 易方达蓝筹精选基金(基金代码:005827)为例,该基金2019-2021的十大持仓信息如下:

years = ['2019','2020','2021']
df3 = fund_stock_holding(years,'005827')
df3

结果如下:

单支基金持仓信息

通过自定义函数 fund_stock_holding(years,code) 可以同时获取多支基金的十大持仓信息,代码如下:

codes = ['005669','006281','003956','001532','001714','001054','001809','003853','004890','001125','001808']
df = pd.DataFrame()
for code in codes:df_tmp = fund_stock_holding(years,code)df = df.append(df_tmp)
df

结果如下:

多支基金持仓信息

上面数据中,显示的是基金代码,一般情况下,咱们还是习惯看基金名称,因此可以把基金代码换成基金名称。

基金名称信息获取如下:

# 获取基金名称信息
df_fund_info = ak.fund_em_fund_name()
df_fund_info = df_fund_info.query('基金代码 in @codes')
df_fund_info = df_fund_info[['基金代码','基金简称']]
df_fund_info = df_fund_info.reset_index(drop=True)
df_fund_info

效果如下:

基金名称信息

有了基金名称信息后,可以用 pandas 通过拼接的方式,将基金名称信息显示出来,代码如下:

df_fund = pd.merge(df,df_fund_info,on='基金代码')
cols = df_fund.columns.tolist()
cols = cols[:1] + cols[-1:] + cols[2:-1]  # 将基金简称放前面
df_fund =df_fund[cols]
df_fund

结果如下:

多支基金持仓信息

05小结

至此,咱们就获取了本次想要得到的信息。针对单支基金或者多支基金,咱们可以定期来获取信息,可以方便快捷的给其他人员分享基金的持仓信息,以及增减持情况。

在今天的这个财经案例里,需求其实是很常见的。在这个案例里,主要用到的是 Pandas 和 akshare 工具。

对于 Pandas 而言,用到的知识点还是挺多的,包括数据筛选、数据合并、数据排序、字符换和数值转换、样式设置等,虽然都是基础知识点,但要做到熟练运用,也是需要一定功底的。

财经工具,本次主要使用的是 akshare,使用 tushare 或者其他的财经工具应该也是可以的。

在代码编写好后,复用性还是挺好的。

我自己也是经常性在另一个公号「价值前瞻」里分享读书和投资的一些经验与教训。

本文的代码,大家可以点击下面的公众号卡片,回复 “pyfin” 来获取代码。


风险提示: 文章主要目的是给大家分享 Python 在财经领域的应用,文中提到的品种或标的,仅作为文中技术实现之用。投资有风险,入市需谨慎,文中内容不构成投资建议,抄作业请理性分析市场。


大家读完顺手点下右下角的  “在看” ,就是最大的鼓励和支持了。

用 Python 快速获取基金持仓增减情况 | 更新版相关推荐

  1. 用 Python 快速获取基金持仓增值与减持情况

    来源:Python数据之道 (ID:PyDataLab) 作者:阳哥 大家好,我是阳哥. 大家知道,在财经领域,Python是有着广泛的用途的,这不,最近A股成交量明显扩大,有不少消息说是量化类的基金 ...

  2. Python爬虫获取基金持仓股票数据

    Python爬虫小实战 . 通过Python爬虫获取基金持仓股票数据,按照基金持仓金额对股票进行排行,并储存在本地数据库. from urllib import request from bs4 im ...

  3. 用 Python 快速追踪基金的收益情况(2022年4月更新) | Python财经实践

    来源:Python数据之道 (ID:PyDataLab) 作者:阳哥 大家好,我是阳哥. 大家知道,近几年,不少同学都是经由基金进入到股市中的.至于股票和基金到底谁更好,这个仁者见仁智者见智,恐怕一时 ...

  4. 用 Python 快速追踪基金的收益情况!

    来源:Python数据之道 (ID:PyDataLab) 作者:阳哥 大家知道,近几年,不少同学都是经由基金进入到股市中的.至于股票和基金到底谁更好,这个仁者见仁智者见智,恐怕一时半会儿也说不清楚. ...

  5. python爬虫获取基金数据2

    用sklearn分析基金数据<1> python爬虫获取基金数据<2> 数据预处理:数据清洗.生成样本数据<3> 用sklearn训练样本数据<4> 用 ...

  6. 使用Python快速获取公众号文章定制电子书(二)

    原文链接 我的GitHub博客地址 接上篇文章使用Python快速获取公众号文章定制电子书(一).我们现在已经成功的将公众号历史消息的前十条文章给爬取了出来,使用 content_url 这个关键字段 ...

  7. Python快速获取桌面路径

    python 快速获取桌面路径 import os desk = os.path.join(os.path.expanduser("~"), 'Desktop') + '\\'输出 ...

  8. Python批量获取基金季报

    大家好,我是小一 最近研究基金的时候,突然对今年来收益最高的基金有点好奇,于是去翻了翻该基金的业绩报告. 好家伙,今年来收益接近30%了,这还是最近一段时间回调了,如果在9月初那会,净值最高冲到了2. ...

  9. 星球年度汇总-爬虫获取基金持仓明细(附GUI可拿来就用}

    量化交易是一个多技术综合的项目,学习完书籍<Python股票量化交易从入门到实践>我们提供了升级的学习内容--知识星球<玩转股票量化交易> 在星球中我们深入分享包括Python ...

最新文章

  1. 《本人娶刘亦菲的可行性报告》原文
  2. 数据结构与算法(8-2)有序表查找(折半查找(二分查找)、插值查找)
  3. Mybatis集成二级缓存与同时使用缓存与事务存在的坑
  4. Boosting(提升方法)之XGBoost
  5. SpringBoot2.0 以上 WebMvcConfigurerAdapter 方法过时 解决办法
  6. html(2)标签(1)
  7. Android Studio运行报错:无法访问XXX......请删除该文件或确保该文件位于正确的类路径子目录中
  8. ( ̄▽ ̄) 关于河北ETC记账卡的默认密码
  9. Simple Polygon Embedding CodeForces - 1354C1(计算几何)
  10. JavaScript之闭包
  11. 【转】c#中@的3种作用
  12. java重写面试题_Java面试题:重写了equals方法,为什么还要重写hashCode方法?
  13. C语言函数如何返回字符串
  14. web服务器、Apache 和 tomcat 的关系
  15. rpcbind.service启动失败
  16. 把ts自动合并 下载网页视频并自动合成视频
  17. html字体播放,Html 字体
  18. 短视频制作难度大吗?怎么剪辑短视频?
  19. 山西最新五大姓氏排名发布,排名第一的是王,第二的竟是……
  20. Room的基本使用(一)

热门文章

  1. 怎么退出自适应巡航_吉利ICON ACC自适应巡航系统
  2. 蚂蚁金服高级技术专家李福喜 :蚂蚁开放平台技术路线及行业实践
  3. Unity获取摄像头权限
  4. 我本以为你们会写简历
  5. 致谢词大全字C语言,致谢词范文
  6. groovy if 判断字符串_Groovy快速入门看这篇就够了
  7. 我国成功发射第七颗北斗导航卫星
  8. 对青浦区专利工作试点和示范企业给予20万元和30万元资助
  9. mysql数据库被勒索删库怎么办
  10. 为什么印度人能占领硅谷,1万清华毕业生只能给他们打工?