Advertisement

Python 量化交易实战

阅读量:

1、Tushare 量化交易神器

1. Tushare 简介

Tushare是一个免费的、开源的财经数据库,可谓是金融业的数据宝库,包含着多方面的数据:股票、指数、基金、期货、债券、外汇、行业大数据,以及提供数字货币行情等区块链数据。这些数据为金融业带来了福音,也为研究人员提供了可靠的参考。

Tushare里面的基础数据都是免费共享的,这对学生党来说简直是打开了新世界的大门。通过注册Tushare账号,便可获得相应的token(由字母和数字组成),即可获得数据权限。

Tushare 旧版官网:

http://tushare.org/index.html

Tushare 新版官网:

https://tushare.pro

Tushare 是一个金融大数据平台,数据内容包含股票、指数、基金、期货、债券、外汇、行业大数据等,同时包括了数字货币行情等区块链数据,为各类金融投资和研究人员提供适用的数据和工具,概览如下:

Tushare采用积分分享模式,不同的板块包含不同的数据,从而也需要不同的积分。总体来说数据量很可靠,同时Tushare为高校老师和学生也提供了一定的福利,通过这些积分福利,大多数的数据都可以使用啦!

Tushare数据调取也是比较简单的,可直接从jupyter notebook或者python中install tushare,然后通过个人的token作为调取凭证,输入对应的代码就可以调取想要的数据了。当然,如果用户还是习惯使用Excel查看数据,也可以通过Tushare将数据导出到本地,从而用Excel进行分析。

2. 注册和使用

Tushare 平台的数据,已全面升级到 tushare pro 了,通常情况下,还是称之为 tushare。
想使用 tushare 中的数据和功能,首先需要进行注册,获得一份 token(一串字母和数字组成的文本),然后才可以获取到数据,大家可以通过以下链接来注册:Tushare数据

在 tushare 中注册后,通过 “个人主页”——“接口 TOKEN” 可以找到自己的 token 值,界面如下:

复制 token 值,然后在代码中进行如下设置:

复制代码
 # 设 置 token

    
 # tushare 注 册 地 址 : https://tushare.pro/register?reg=129033
    
 # 以 上 方 法 只 需 要 在 第 一 次 或 者 token 失 效 后 调 用 , 完 成 调 取 tushare 数 据 凭 证的 设 置 , 正常 情 况 下 不 需 要 重 复 设 置 。
    
 ts.set_token(' 你 的 token 值 ')
    
  
    
 pro = ts.pro_api()
    
    
    
    
    AI助手

在设置好 token 值后,我们就可以开始获取数据。

3. 沪深股票

作为国内的投资,首先要接触的当然就是 A 股,也就是沪深两市的股票。Tushare 在沪深股票方面,提供了大量的功能和接口,可以供咱们来使用,主要包括基础数据、行情数据、财务数据和市场参考数据四个子板块。

每个细分板块都提供一些特定的接口来获取数据,下面分享部分用的较多的功能。

1)获取交易日历信息

通过 trade_cal 可以便捷的获取所有的交易日的日期信息,代码如下:

复制代码
 df = pro.trade_cal(exchange_id='', start_date='20180101', end_date='', fields='pretrade_date', is_open='0')

    
  
    
 df
    
    
    
    
    AI助手

在绘制股票交易图表时,希望能够隐藏周六日和节假日,只显示交易日的交易信息,有了 tushare 的这个功能,就可以快速的对沪深股市进行设置。即从全部日期中剔除交易日,就是想隐藏的周六日和节假日的范畴了。

2)股票列表

获取股票信息列表也是我们经常需要获取的数据之一,在 tushare 中可以有几种方法来获取,包括 pro.stock_basic 和 pro.query。

通过这个功能,可以快速的了解到,当前沪深两市,有 4000 多只股票在正常交易。

也可以通过下面的方法来获取这些信息:

复制代码
 # 查 询 当 前 所 有 正 常 上 市 交 易 的 股 票 列 表

    
 data = pro.query('stock_basic', exchange='', list_status='L', fields='ts_code,symbol,name,area,industry,list_date,list_status')
    
 data
    
    
    
    
    AI助手

3)股票的日线行情数据

获取日线行情数据,这恐怕是平时使用最多的功能了。在 tushare 中,日线行情数据的接口是 daily ,目前在tushare 中可以通过三种方式来获取股票的日线行情数据。

(1)pro.daily() 方式

可以使用下面的代码来获取茅台股份的日线行情数据,如下:

此外,还可以同时获取多只股票的日线行情数据,代码如下:

复制代码
 # 获 取 多 只 股 票 日 线 行 情 数 据

    
 # 茅 台 , 600519.SH
    
 # 300433 蓝 思 科 技
    
 data = pro.daily(
    
     ts_code='600519.SH,300433.SZ',
    
     start_date='20180101',
    
     end_date='20210120')
    
 data
    
    
    
    
    AI助手

pro.daily() 方法还可以获取某个交易日,整个沪深市场所有股票的日线行情数据,如下:

复制代码
 # 获 取 历 史 某 天 全 部 股 票 的 交 易 数 据

    
 df = pro.daily(trade_date='20210120')
    
 df
    
    
    
    
    AI助手

(2)pro.query() 方法

第二种获取日线行情数据的方式是使用 pro.query() ,示例如下:

复制代码
 # 获 取 单 只 股 票 日 线 行 情 数 据

    
 # 300433 蓝 思 科 技
    
 data = pro.query('daily', ts_code='300433.SZ', start_date='20180101', end_date='20210120')
    
  
    
 data
    
    
    
    
    AI助手

(3)ts.pro_bar() 方法

此外,tushare 还提供了通用行情接口 pro_bar ,目前整合了股票(未复权、前复权、后复权)、指数、数字货币、ETF 基金、期货、期权的行情数据。可以通过这个方法实现多种标的数据的获取。

通用行情接口,适合多种数据的获取。

复制代码
 # 获 取 多 个 股 票 的 数 据 , 有 bug ?

    
 data = ts.pro_bar(
    
     ts_code='600519.SH',
    
     start_date='20180101',
    
     end_date='20210120',
    
     adj='qfq',
    
 )
    
 data
    
    
    
    
    AI助手

上面的参数 adj ,复权因子,只适用于股票交易数据,qfq 表示的是前复权。

4. 指数数据

除了股票数据的获取,通常情况下,还会经常关注指数的行情,比如上证综合指数、沪深 300 指数等。

Tushare提供的指数分类信息如下:

1)指数基本信息

通过接口 index_basic 可以获取不同市场指数的基本信息,比如可以设置参数 market='CSI 来获取中证指数的基础信息。

2)指数日线行情数据

指数日线行情数据可以通过 index_daily 接口或通过行情接口来获取数据。

(1)index_daily 接口

index_daily 方法可以设置获取数据的开始日期和结束日期,如下:

复制代码
 # 获 取 沪 深 300 指 数 的 日 线 行 情

    
 # 设 置 开 始 和 结 束 日 期
    
 df = pro.index_daily(ts_code='000300.SH',
    
                  start_date='20180101',
    
                  end_date='20210120')
    
 df
    
    
    
    
    AI助手

(2)通用行情接口

同样的,可以通过通用行情接口来获取指数的数据,设置参数 asset=I ,表示获取的是指数数据(I:index),代码如下:

复制代码
 df = ts.pro_bar(ts_code='000300.SH',

    
             asset='I',start_date='20180101', end_date='20210120')
    
 df
    
    
    
    
    AI助手

5. 使用权限

上面提到的这些接口和功能还只是很小一部分。

Tushare 提供的功能确实很多,而且基础功能都是免费提供的。在本文刚开始的时候,跟大家提到了使用 tushare需要进行注册。

为啥需要注册呢,因为 tushare 进行了不同积分权限的设置,不同的积分,获取数据的权限是不一样的。

上面跟大家介绍的常用的接口和功能,大部分是注册之后就可以使用的。如果想更高频率的获取数据,或者想获取公募基金数据、港股、美股数据等,就需要提高积分权限了。

目前,tushare 官方给的一些提高积分的方法,有那么几种,一种是金额赞助,可以去换积分。另一种是官方给了一些推广的途径,去帮它推广也可以获取积分。

比如大家通过本文的 “注册地址” 来注册 tushare 的话,我可以获得一丢丢的积分,哈,地址如下:

Tushare数据

其实,也是需要理解 tushare 官方的这种做法的,毕竟天下没有免费的午餐。这么好用的工具,如果完全免费,tushare 官方估计也很难维护这样一个社区。

2、Pandas 表格样式设置指南

Python在数据分析以及人工智能领域是越来越火。

这离不开pandas、numpy、sklearn、TensorFlow、PyTorch等数据科学包,尤其是 Pandas,几乎是每一个从事Python数据科学相关的同学都绕不过去的。

Pandas是一种高效的数据处理库,它以 dataframe 和 series 为基本数据类型,呈现出类似excel的二维数据。

在 Jupyter 中(jupyter notebook 或者 jupyter lab),可以对数据表格按照条件进行个性化的设置,方便形象的查看和使用数据。

Pandas提供了 DataFrame.style 属性,它会返回 Styler对象,用于数据样式的设置。

基于 Pandas提供的方法,主要内容概括如下:

本次使用的环境如下:

MacOS系统

Python 3.8

Jupyter Notebook

Pandas 和 Numpy 的版本为:

复制代码
 pandas version:1.3.0rc1

    
 numpy version:1.19.2
    
    
    
    
    AI助手

首先导入 pandas 和 numpy 库,这次咱们本次需要用到的两个 Python 库,如下:

复制代码
 import pandas as pd

    
 import numpy as np
    
  
    
 print(f'pandas version:{pd.__version__}')
    
 print(f'numpy version:{np.__version__}')
    
    
    
    
    AI助手

本次咱们使用的两份数据是关于主动基金以及消费行业指数基金的数据,本次演示用的数据仅为展示Pandas图表美化功能,对投资没有参考建议哈。

消费行业指数基金相关的数据,导入如下:

复制代码
 df_consume = pd.read_csv('./data/fund_consume.csv',index_col=0,parse_dates=['上任日期','规模对应日期'])

    
 df_consume = df_consume.sort_values('基金规模(亿)',ascending=False).head(10)
    
 df_consume = df_consume.reset_index(drop=True)
    
 df_consume
    
    
    
    
    AI助手

主动基金数据,导入如下:

复制代码
 df_fund = pd.read_csv('./data/fund-analysis.csv',index_col=0,parse_dates=['上任日期','规模对应日期'])

    
 df_fund = df_fund.sort_values('基金规模(亿)',ascending=False).head(10)
    
 df_fund = df_fund.reset_index(drop=True)
    
 df_fund.head(2)
    
    
    
    
    AI助手

主要使用第一份数据。

1. 隐藏索引

用 hide_index() 方法可以选择隐藏索引,代码如下:

复制代码
    df_consume.style.hide_index()
    
    AI助手

效果如下:

2. 隐藏列

用 hide_columns() 方法可以选择隐藏一列或者多列,代码如下:

复制代码
    df_consume.style.hide_index().hide_columns(['性别','基金经理','上任日期','2021'])
    
    AI助手

效果如下:

3. 设置数据格式

在设置数据格式之前,需要注意下,所在列的数值的数据类型应该为数字格式,如果包含字符串、时间或者其他非数字格式,则会报错。

可以用 DataFrame.dtypes 属性来查看数据格式。

复制代码
    df_consume.dtypes
    
    AI助手

格式如下:

复制代码
 基金名称               object

    
 基金代码               object
    
 基金经理               object
    
 性别                 object
    
 上任日期       datetime64[ns]
    
 基金公司               object
    
 管理费               float64
    
 托管费               float64
    
 基金规模(亿)           float64
    
 规模对应日期     datetime64[ns]
    
 2018              float64
    
 2019              float64
    
 2020              float64
    
 2021              float64
    
 dtype: object
    
    
    
    
    AI助手

从上面来看,数据格式主要包括字符串、数字和时间这三种常见的类型,此外,空值(NaN,NaT等)也是我们需要处理的数据类型之一。

对于字符串类型,一般不要进行格式设置;

对于数字类型,是格式设置用的最多的,包括设置小数的位数、千分位、百分数形式、金额类型等;

对于时间类型,经常会需要转换为字符串类型进行显示;

对于空值,可以通过 na_rep 参数来设置显示内容;

Pandas 中可以通过 style.format() 函数来对数据格式进行设置。

复制代码
 format_dict = {'基金规模(亿)': '¥{0:.1f}',

    
            '管理费': '{0:.2f}', 
    
            '托管费': '{0:.2f}', 
    
            '规模对应日期':lambda x: "{}".format(x.strftime('%Y%m%d')),
    
            '2018': '{0:.1%}', 
    
            '2019': '{0:.1%}', 
    
            '2020': '{0:.1%}', 
    
            '2021': '{0:.1%}'
    
             }
    
  
    
 df_consume.style.hide_index()\
    
             .hide_columns(['性别','基金经理','上任日期','2021'])\
    
             .format(format_dict)
    
    
    
    
    AI助手

4. 空值设置

使用 na_rep 设置空值的显示,一般可以用 -、/、MISSING 等来表示:

复制代码
 df_consume.style.hide_index()\

    
             .hide_columns(['性别','基金经理','上任日期','2021'])\
    
             .format(format_dict,na_rep='-')
    
    
    
    
    AI助手

5. 颜色高亮设置

对于最大值、最小值、NaN等各类值的颜色高亮设置,pandas 已经有专门的函数来处理,配合 axis 参数可以对行或者列进行应用:

highlight_max()

highlight_min()

highlight_null()

highlight_between()

1)highlight_max

通过 highlight_max()来高亮最大值,其中 axis=0 是按列进行统计:

复制代码
 df_consume.style.hide_index()\

    
             .hide_columns(['性别','基金经理','上任日期',])\
    
             .format(format_dict)\
    
             .highlight_max(axis=0,subset=['2018','2019','2020'])
    
    
    
    
    AI助手

2)highlight_min

通过 highlight_min()来高亮最小值,其中 axis=1 是按行进行统计:

复制代码
 df_consume.style.hide_index()\

    
             .hide_columns(['性别','基金经理','上任日期',])\
    
             .format(format_dict)\
    
             .highlight_min(axis=1,subset=['2018','2019','2020'])
    
    
    
    
    AI助手

3)highlight_null

通过 highlight_null()来高亮空值(NaN值):

复制代码
 df_consume.style.hide_index()\

    
             .hide_columns(['性别','基金经理','上任日期',])\
    
             .format(format_dict)\
    
             .highlight_null()
    
    
    
    
    AI助手

效果如下:

4)highlight_between

highlight_between() 函数,对处于范围内的数据进行高亮显示。

highlight_between() 函数的使用参数如下:

复制代码
    Styler.highlight_between(subset=None, color='yellow', axis=0, left=None, right=None, inclusive='both', props=None)
    
    AI助手

highlight_between() 函数,对处于范围内的数据进行高亮显示,通过 left 和 right 参数来设置两边的范围。

需要注意下,highlight_between() 函数从 pandas 1.3.0版本开始才有,旧的版本可能不能使用哦。

下面示例中 对2018年至2020年的年度涨跌幅度 -20%~+20% 范围内的数据进行高亮标注:

复制代码
 df_consume.style.hide_index()\

    
             .hide_columns(['性别','基金经理','上任日期',])\
    
             .format(format_dict)\
    
             .highlight_between(left=-0.2,right=0.2,subset=['2018','2019','2020'])
    
    
    
    
    AI助手

效果如下:

也可以分别对不同年度的不同涨跌范围进行设置,比如下面示例中:

  • 2018年的年度涨跌幅度 -15%~+0% 范围;
  • 2019年的年度涨跌幅度 0%~20%% 范围;
  • 2020年的年度涨跌幅度 0%~40% 范围;
复制代码
 df_consume.style.hide_index()\

    
             .hide_columns(['性别','基金经理','上任日期',])\
    
             .format(format_dict)\
    
             .highlight_between(left=[-0.15,0,0],right=[0,0.2,0.4],subset=['2018','2019','2020'],axis=1)
    
    
    
    
    AI助手

效果如下:

5)个性化设置

highlight_max()、highlight_min()、highlight_null() 等函数的默认颜色设置,我们不一定满意,可以通过 props 参数来进行修改。

字体颜色和背景颜色设置:

复制代码
 df_consume.style.hide_index()\

    
             .hide_columns(['性别','基金经理','上任日期',])\
    
             .format(format_dict)\
    
             .highlight_min(axis=1,subset=['2018','2019','2020','2021'],props='color:black;background-color:#99ff66')\
    
             .highlight_max(axis=1,subset=['2018','2019','2020','2021'],props='color:black;background-color:#ee7621')\
    
             .highlight_null(props='color:white;background-color:darkblue')
    
    
    
    
    AI助手

效果如下:

字体加粗以及字体颜色设置:

复制代码
 df_consume.style.hide_index()\

    
             .hide_columns(['性别','基金经理','上任日期',])\
    
             .format(format_dict)\
    
             .highlight_between(left=-0.2,right=0.2,subset=['2018','2019','2020'],props='font-weight:bold;color:#ee7621')
    
    
    
    
    AI助手

效果如下:

类似的个性化设置,在本文后续内容中也是适用的。

6. 色阶颜色设置

1)背景色阶颜色设置

使用 background_gradient() 函数可以对背景颜色进行设置。

该函数的参数如下:

复制代码
    Styler.background_gradient(cmap='PuBu', low=0, high=0, axis=0, subset=None, text_color_threshold=0.408, vmin=None, vmax=None, gmap=None)
    
    AI助手

使用如下:

复制代码
 df_consume.style.hide_index()\

    
             .hide_columns(['性别','基金经理','上任日期'])\
    
             .format(format_dict)\
    
             .background_gradient(cmap='Blues')
    
    
    
    
    AI助手

效果如下:

如果不对 subset 进行设置,background_gradient 函数将默认对所有数值类型的列进行背景颜色标注。

对 subset 进行设置后,可以选择特定的列或特定的范围进行背景颜色的设置。

复制代码
 # 对基金规模以色阶颜色进行标注

    
  
    
 df_consume.style.hide_index()\
    
             .hide_columns(['性别','基金经理','上任日期'])\
    
             .format(format_dict)\
    
             .background_gradient(subset=['基金规模(亿)'],cmap='Blues')
    
    
    
    
    AI助手

效果如下:

此外,可以通过对 low 和 high 值的设置,可以来调节背景颜色的范围,low 和 high 分别是压缩 低端和高端的颜色范围,其数值范围一般是 0~1 ,各位可以调试下。

复制代码
 # 对基金规模以色阶颜色进行标注

    
 # 通过对 low 和 high 值的设置,可以来调节背景颜色的范围
    
 # low 和 high 分别是压缩 低端和高端的颜色范围,其数值范围一般是 0~1 ,各位可以调试下
    
  
    
 df_consume.style.hide_index()\
    
             .hide_columns(['性别','基金经理','上任日期'])\
    
             .format(format_dict)\
    
             .background_gradient(subset=['基金规模(亿)'],cmap='Blues',low=0.3,high=0.9)
    
    
    
    
    AI助手

效果如下:

当数据范围比较大时,可以通过设置 vmin 和 vmax 来设置最小和最大的颜色的设置起始点。

比如下面,基金规模在20亿以下的,颜色最浅,规模70亿以上的,颜色最深,20~70亿之间的,颜色渐变。

复制代码
 # 对基金规模以色阶颜色进行标注

    
 df_consume.style.hide_index()\
    
             .hide_columns(['性别','基金经理','上任日期'])\
    
             .format(format_dict)\
    
             .background_gradient(subset=['基金规模(亿)'],cmap='Blues',vmin=20,vmax=70)
    
    
    
    
    AI助手

通过 gmap 的设置,可以方便的按照某列的值,对行进行全部的背景设置:

复制代码
 df_consume.style.hide_index()\

    
             .hide_columns(['性别','基金经理','上任日期'])\
    
             .format(format_dict)\
    
             .background_gradient(cmap='Blues',gmap=df_consume['基金规模(亿)'])
    
    
    
    
    AI助手

效果如下:

gmap 还可以以矩阵的形式对数据进行样式设置,如下:

复制代码
 df_gmap = df_consume.loc[:2,['基金名称','管理费','基金规模(亿)','2020']]

    
  
    
 gmap = np.array([[1,2,3], [2,3,4], [3,4,5]])  # 3*3 矩阵,后面需要进行颜色设置的形状也需要是 3*3,需要保持一致
    
 df_gmap.style.background_gradient(axis=None, gmap=gmap,
    
     cmap='Blues', subset=['管理费','基金规模(亿)','2020']
    
 )
    
    
    
    
    AI助手

效果如下:

上面 gmap 是 33 矩阵,后面需要进行颜色设置的形状也需要是 33,需要保持一致。

需要注意的是 颜色设置是根据 gmap中的值来设置颜色深浅的,而不是根据 DataFrame 中的数值来的。

这个在某些特定的情况下可能会用到。

2)文本色阶颜色设置

类似于背景色阶颜色设置,文本也是可以进行颜色设置的。

使用 text_gradient() 函数可以实现这个功能,其参数如下:

复制代码
    Styler.text_gradient(cmap='PuBu', low=0, high=0, axis=0, subset=None, vmin=None, vmax=None, gmap=None)
    
    AI助手

text_gradient() 函数的用法跟 background_gradient() 函数的用法基本是一样的。

下面演示两个使用案例,其他的用法参考 background_gradient() 函数。

某列的文本色阶显示:

复制代码
 # 对基金规模以色阶颜色进行标注

    
  
    
 df_consume.style.hide_index()\
    
             .hide_columns(['性别','基金经理','上任日期'])\
    
             .format(format_dict)\
    
             .text_gradient(subset=['基金规模(亿)'],cmap='RdYlGn')
    
    
    
    
    AI助手

效果如下:

全部表格的文本色阶显示:

复制代码
 # 通过 `gmap` 的设置,可以方便的按照某列的值,对行进行全部的文本颜色设置

    
  
    
 df_consume.style.hide_index()\
    
             .hide_columns(['性别','基金经理','上任日期'])\
    
             .format(format_dict)\
    
             .text_gradient(cmap='RdYlGn',gmap=df_consume['基金规模(亿)'])
    
    
    
    
    AI助手

效果如下:

7. 数据条显示

数据条的显示方式,可以同时在数据表格里对数据进行可视化显示,这个功能咱们在 Excel 里也是经常用到的。

在 pandas 中,可以使用 DataFrame.style.bar() 函数来实现这个功能,其参数如下:

复制代码
    Styler.bar(subset=None, axis=0, color='#d65f5f', width=100, align='left', vmin=None, vmax=None)
    
    AI助手

示例代码如下:

复制代码
 df_consume.style.hide_index()\

    
             .hide_columns(['性别','基金经理','上任日期'])\
    
             .format(format_dict)\
    
             .bar(subset=['基金规模(亿)','2018','2021'],color=['#99ff66','#ee7621'])
    
    
    
    
    AI助手

效果如下:

1)设置对其方式

上面这个可视化效果,对于正负数值的区别,看起来总是有点别扭。

可以通过设置 aligh 参数的值来控制显示方式:

left:最小值从单元格的左侧开始。

zero:零值位于单元格的中心。

mid:单元格的中心在(max-min)/ 2,或者如果值全为负(正),则零对齐于单元格的右(左)。

将显示设置为 mid 后,符合大部分人的视觉审美观,代码如下:

复制代码
 df_consume.style.hide_index()\

    
             .hide_columns(['性别','基金经理','上任日期'])\
    
             .format(format_dict)\
    
             .bar(subset=['基金规模(亿)','2018','2021'],color=['#99ff66','#ee7621'],align='mid')
    
    
    
    
    AI助手

效果如下:

关于颜色设置,color=['#99ff66','#ee7621'], color可以设置为单个颜色,所有的数据只显示同一个颜色,也可以设置为包含两个元素的list或tuple形式,左边的颜色标注负数值,右边的颜色标注正数值。

8. 自定义函数的使用

通过 apply 和 applymap 函数,用户可以使用自定义函数来进行样式设置。

其中:

  • apply 通过axis参数,每一次将一列或一行或整个表传递到DataFrame中。对于按列使用 axis=0, 按行使用 axis=1, 整个表使用 axis=None。
  • applymap 作用于范围内的每个元素。

1)apply

先自定义了函数max_value(),用来找到符合条件的最大值,apply 使用的示例代码如下。

按列设置样式:

复制代码
 def max_value(x, color='red'):

    
     return np.where(x == np.nanmax(x.to_numpy()), f"color: {color};background-color:yellow", None)
    
  
    
 # axis =0 ,按列设置样式
    
 df_consume.style.hide_index()\
    
             .hide_columns(['性别','基金经理','上任日期',])\
    
             .format(format_dict)\
    
             .apply(max_value,axis=0,subset=['2018','2019','2020','2021'])
    
    
    
    
    AI助手

效果如下:

按行设置样式:

复制代码
 # axis =1 ,按行设置样式

    
 df_consume.style.hide_index()\
    
             .hide_columns(['性别','基金经理','上任日期',])\
    
             .format(format_dict)\
    
             .apply(max_value,axis=1,subset=['2018','2019','2020','2021'])
    
    
    
    
    AI助手

效果如下:

按整个表格设置样式:

按整个表格设置样式时,需要注意的是,整个表格的数据类型需要是一样的,不然会报错。

复制代码
 # axis = None ,按整个表格设置样式

    
 # 注意,整个表格的数据类型需要是一样的,不然会报错
    
  
    
 df_consume_1 = df_consume[['2018','2019','2020','2021']]
    
 # df_consume_1
    
 df_consume_1.style.hide_index().apply(max_value,axis=None)
    
    
    
    
    AI助手

效果如下:

2)applymap

继续上面的数据表格,我们来自定义一个函数,对于基金的年度涨跌幅情况,年度上涨以橙色背景标注,下跌以绿色背景标注,NaN值以灰色背景标注。

由于 applymap 是作用于每个元素的,因此该函数不需要 axis 这个参数来进行设置,示例代码如下:

复制代码
 def color_returns(val):

    
     if val >=0:
    
     color = '#EE7621'  # light red
    
     elif val <0:
    
     color =  '#99ff66' # light green  '#99ff66'
    
     else:
    
     color = '#FFFAFA'  # ligth gray
    
     return f'background-color: {color}'
    
  
    
 format_dict = {'基金规模(亿)': '¥{0:.1f}', 
    
            '管理费': '{0:.2f}', 
    
            '托管费': '{0:.2f}', 
    
            '规模对应日期':lambda x: "{}".format(x.strftime('%Y%m%d')),
    
            '2018': '{0:.1%}', 
    
            '2019': '{0:.1%}', 
    
            '2020': '{0:.1%}', 
    
            '2021': '{0:.1%}'
    
             }
    
  
    
 df_consume.style.hide_index()\
    
             .hide_columns(['性别','基金经理','上任日期',])\
    
             .format(format_dict)\
    
             .applymap(color_returns,subset=['2018','2019','2020','2021'])
    
    
    
    
    AI助手

效果如下:

9. 颜色设置范围选择

在使用 Style 中的函数对表格数据进行样式设置时,对于有 subset 参数的函数,可以通过设置 行和列的范围来控制需要进行样式设置的区域。

对行(row)进行范围设置:

复制代码
    df_consume_1.style.applymap(color_returns,subset=pd.IndexSlice[1:5,])
    
    AI助手

效果如下:

对列(column)进行范围设置:

复制代码
    df_consume_1.style.applymap(color_returns,subset=['2019','2020'])
    
    AI助手

效果如下:

对行和列同时进行范围设置:

复制代码
 df_consume.style.hide_index()\

    
             .hide_columns(['性别','基金经理','上任日期',])\
    
             .format(format_dict)\
    
             .applymap(color_returns,subset=pd.IndexSlice[1:5,['2018','2019','2020']])
    
    
    
    
    AI助手

效果如下:

10. 共享样式

对于pandas 中样式设置后的共享复用,目前支持通过 Styler.export() 导出样式,然后通过 Styler.use() 来使用导出的样式。

不过经过测试,简单的样式导出与使用是可以的。但稍微复杂一些的情况,目前的pandas版本是不太好用的。

1)简单样式

示例如下,先保存当前样式:

复制代码
 df_consume_1 = df_consume[['2018','2019','2020','2021']]

    
 # df_consume_1
    
 style1 = df_consume_1.style.hide_index()\
    
             .highlight_min(axis=1,subset=['2018','2019','2020','2021'],props='color:black;background-color:#99ff66')\
    
             .highlight_max(axis=1,subset=['2018','2019','2020','2021'],props='color:black;background-color:#ee7621')\
    
             .highlight_null(props='color:white;background-color:darkblue')
    
 style1
    
    
    
    
    AI助手

保存的样式的效果如下:

使用保存的样式:

复制代码
 df_fund_1 = df_fund[['2018','2019','2020','2021']]

    
  
    
 df_fund_1.style.use(style1.export())
    
    
    
    
    AI助手

效果如下:

由于后面的数据表格是没有空值的,所以两者的样式实际是一样的。

2)复杂样式

当样式设置较多时,比如同时隐藏索引、隐藏列、设置数据格式、高亮特定值等,这个时候有些操作在导出后使用时并没有效果。

测试如下,先保存样式:

复制代码
 style3 = df_consume.style.hide_index()\

    
             .hide_columns(['性别','基金经理','上任日期',])\
    
             .format(format_dict)\
    
             .highlight_min(axis=1,subset=['2018','2019','2020','2021'],props='color:black;background-color:#99ff66')\
    
             .highlight_max(axis=1,subset=['2018','2019','2020','2021'],props='color:black;background-color:#ee7621')\
    
             .highlight_null(props='color:white;background-color:darkblue')
    
 style3
    
    
    
    
    AI助手

保存样式的效果如下:

使用保存的样式:

复制代码
    df_fund.style.use(style3.export())
    
    AI助手

效果如下:

从上面来看,我们希望的样式效果,并没有很好的实现。

所以,针对较为复杂的样式,还是乖乖的复制代码使用吧。

11. 导出样式到Excel

导出样式到 Excel 中,这个功能还是比较实用的。

DataFrames 使用 OpenPyXL 或XlsxWriter 引擎可以将样式导出到 Excel 工作表。

不过,这个功能目前也还是处于不断完善过程中,估计有时候有些内容会没有效果。

大家可以在使用过程中来发现其中的一些问题。

来看一个案例:

复制代码
 df_consume.style.hide_index()\

    
             .hide_columns(['性别','基金经理','上任日期',])\
    
             .format(format_dict)\
    
             .highlight_min(axis=1,subset=['2018','2019','2020','2021'],props='color:black;background-color:#99ff66')\
    
             .highlight_max(axis=1,subset=['2018','2019','2020','2021'],props='color:black;background-color:#ee7621')\
    
             .highlight_null(props='color:white;background-color:darkblue')\
    
             .to_excel('style_export.xlsx',engine = 'openpyxl')
    
    
    
    
    AI助手

上面的案例内容导出到 excel 后,我从 excel 中打开查看了下效果如下:

可以看出,跟共享样式里有些相同的问题,比如隐藏索引、隐藏列、设置数据格式等效果并没有实现。

3、用 Python 来做一个投资计划

我也是一个坚持在股市投资的小韭菜,在投资之前,一般会思考一个初步的计划。

最近,在进一步研究各个品种的投资配置计划,类似如下表格:

对于单个股票或 ETF 或基金,我以前用的是 Excel 表格,但最近发现,如果同时计算多个品种的投资计划时,Excel 的粘贴复制并不好用,原因是 Excel 表格中有许多表格单元格需要按 F4 固定,在复制使用时需要逐个修改,表格多了后,经常会忘记修改,这对投资而言,就很不严谨,如果数据计算错误,就很有问题了。

于是,就想着用 Python 来制作上述表格,并尽量将数据简化,将计算过程进行封装。

1. 表格制作过程

在 Python 中制作上述表格,咱们可以用 Pandas 来实现。

Pandas 是一种高效的数据处理库,它以 dataframe 和 series 为基本数据类型,呈现出类似 excel 的二维数据。

我们先来观察上面的 Excel 表格,在表格中,价格系数以及仓位比例这两列数据,是由咱们主观来控制的;此外,首次开始购买时的价格,也是主观控制的;首次购买份数,是由咱们总资金情况,根据仓位比例计算出来的,在这里,首次购买数量,也可以由咱们来主观控制输入。

因此,在 Python 中制作这个表格,可以先将主观控制的内容定义为变量。

复制代码
 import pandas as pd

    
  
    
 # 标 的 名 称
    
 name = ' 中 概 互 联 -513050'
    
  
    
 # 首 次 购 入 价 格
    
 price_init = 2.0
    
  
    
 # 首 次 加 仓 比 例
    
 rate_init = 0.01
    
  
    
 # 首 次 购 入 份 额 数 量
    
 shares_init = 2000
    
  
    
 data = {
    
     ' 价 格 系 数 ':[1,0.95,0.9,0.85,0.8,0.75,0.7,0.65],
    
     ' 加 仓 比 例 ':[rate_init,0.02,0.03,0.04,0.05,0.05,0.05,0.05],
    
     }
    
  
    
 df = pd.DataFrame(data=data)
    
  
    
 df
    
    
    
    
    AI助手

将已有变量输入到 DataFrame 中后,得到下面的表格:

基于已有数据和变量来计算其他数据,包括 加仓批次 、品种名称 name 和 价格 列,同时设置其他列为空值(shares 为加仓数量,money 为加仓金额) 。

复制代码
 df[' 加 仓 批 次 '] = list(range(1,len(df)+1))

    
 df['name'] = name
    
 df[' 价 格 '] = df[' 价 格 系 数 '] * price_init
    
 df['shares'] = ''
    
 df['money'] = ''
    
 df
    
    
    
    
    AI助手

计算后,得到的表格如下:

由于首次买入份额数量是由咱们自己控制的,可以先将首次购买的数量输入,如下:

复制代码
 df.loc[df[' 加 仓 批 次 ']==1, 'shares'] = shares_init

    
 df
    
    
    
    
    AI助手

同时计算首次购买的金额,如下:

复制代码
 df.loc[df[' 加 仓 批 次 ']==1, 'money'] = df.loc[df[' 加 仓 批 次 ']==1, ' 价 格 '] * df.loc[df[' 加 仓 批 次 ']==1, 'shares']

    
 df
    
    
    
    
    AI助手

这里,在 Pandas 中,是通过 .loc 筛选出具体的行,通过控制单个单元格来实现的。

接下来,需要计算 加仓批次 2 到 8 的每一行的份额数量以及金额,每一行的计算方法都是一样的,咱们以第 2 行为例来说明。

第 2 行的份额数量的计算公式为:(第1行的金额 * 第2行的加仓比例 )/(第1行的加仓比例 * 第2行的价格)

由于交易数量是 100 的整数倍,因此还需要稍微调整下实际的份额数量,如下:

round((第1行的金额 * 第2行的加仓比例 )/(第1行的加仓比例 * 第2行的价格*100),0)

Python 代码如下:

复制代码
 money_init = df.loc[df[' 加 仓 批 次 ']==1, 'money'].values[0]

    
  
    
 df.loc[df[' 加 仓 批 次 ']==2, 'shares'] = round(money_init*df.loc[df[' 加 仓 批 次 ']==2, ' 加 仓 比 例 ']/\
    
     (rate_init * df.loc[df[' 加 仓 批 次 ']==2, ' 价 格 ']*100),0)*100
    
  
    
 df    
    
    
    
    
    AI助手

第 2 行的金额的计算如下:

复制代码
 df.loc[df[' 加 仓 批 次 ']==2, 'money'] = df.loc[df[' 加 仓 批 次 ']==2, ' 价 格 '] * df.loc[df[' 加 仓 批 次 ']==2, 'shares']

    
 df
    
    
    
    
    AI助手

其他行的计算也是类似的,咱们可以用 for 循环来实现,如下:

复制代码
 for i in range(len(df)-1):

    
     n = i+2
    
     df.loc[df[' 加 仓 批 次 ']==n, 'shares'] = round(money_init*df.loc[df[' 加 仓 批 次 ']==n, ' 加 仓 比 例 ']/\
    
     (rate_init * df.loc[df[' 加 仓 批 次 ']==n, ' 价 格 ']*100),0)*100
    
 df.loc[df[' 加 仓 批 次 ']==n, 'money'] = df.loc[df[' 加 仓 批 次 ']==n, ' 价 格 '] * df.loc[df[' 加 仓 批 次 ']==n, 'shares']
    
 df
    
    
    
    
    AI助手

这样,就可以计算出其他行的购买数量以及购买金额的数据了,得到的表格如下:

在上面表格的基础上,咱们还会经常要知道一些累计的数据,比如累计仓位,累计购买份额数量,累计购买金额,以及计算当前的投资成本等,在 pandas 中,累计求和使用 cumsum()函数,实现如下:

复制代码
 df[' 累 计 仓 位 '] = df[' 加 仓 比 例 '].cumsum()

    
 df[' 累 计 份 额 '] = df['shares'].cumsum()
    
 df[' 累 计 金 额 '] = df['money'].cumsum()
    
 df[' 成 本 '] = df[' 累 计 金 额 ']/df[' 累 计 份 额 ']
    
 df
    
    
    
    
    AI助手

在 Excel 表格中,还有一列 下跌幅度 ,其数据类型为字符串型,在 Python 中,这个也是可以根据价格系数来计算出来的,比 Excel 中的手动修改还要方便些,如下:

复制代码
 df[' 下 跌 幅 度 '] = 1- df[' 价 格 系 数 ']

    
 df[' 下 跌 幅 度 '] = df[' 下 跌 幅 度 '].apply(lambda x:f' 下 跌 {int(round(x*100,0))}%')
    
 df.loc[df[' 加 仓 批 次 ']==1, ' 下 跌 幅 度 '] = ' 首 次 '
    
 df
    
    
    
    
    AI助手

最后,就是调整显示的顺序,以及数据格式,如下:

复制代码
 df = df[['name',' 加 仓 批 次 ', ' 下 跌 幅 度 ', ' 价 格 系 数 ',' 价 格 ', ' 加 仓 比 例 ', ' 累 计 仓位 ', 'shares', 'money',' 累 计 份 额 ', ' 累 计 金 额 ',' 成 本 ']]

    
  
    
 format_dict = {'money': ' ¥ {0:.0f}',
    
            ' 累 计 金 额 ': ' ¥ {0:.0f}',
    
            ' 价 格 ': '{0:.3f}',
    
            ' 成 本 ': '{0:.3f}',
    
            'shares': '{0:.0f}',
    
            ' 累 计 份 额 ': '{0:.0f}',
    
            ' 价 格 系 数 ': '{0:.0%}',
    
            ' 加 仓 比 例 ': '{0:.2%}',
    
            ' 累 计 仓 位 ': '{0:.2%}',
    
             }
    
  
    
 df.style.hide_index().format(format_dict)
    
    
    
    
    AI助手

至此,针对单一品种的投资计划表格就出来了。

2. 函数封装

上面是详细的分步骤的介绍,在实际使用过程中,咱们会针对多个品种进行核算,不同品种计划投资的仓位比例,价格间隔区间,可能都不一样,咱们可以通过将上面的实现过程封装成函数的形式,来适应各种需求,函数封装如下:

复制代码
 def plan(name,price_init,rate_init,shares_init,data):

    
     df = pd.DataFrame(data=data)
    
     df[' 加 仓 批 次 '] = list(range(1,len(df)+1))
    
     df['name'] = name
    
     df[' 价 格 '] = df[' 价 格 系 数 '] * price_init
    
     df['shares'] = ''
    
     df['money'] = ''
    
  
    
     df.loc[df[' 加 仓 批 次 ']==1, 'shares'] = shares_init
    
     df.loc[df[' 加 仓 批 次 ']==1, 'money'] = df.loc[df[' 加 仓 批 次 ']==1, ' 价 格 '] * df.loc[df[' 加 仓 批 次 ']==1, 'shares']
    
     money_init = df.loc[df[' 加 仓 批 次 ']==1, 'money'].values[0]
    
     for i in range(len(df)-1):
    
     n = i+2
    
     df.loc[df[' 加 仓 批 次 ']==n, 'shares'] = round(money_init*df.loc[df[' 加 仓 批 次 ']==n, ' 加 仓 比 例 ']/\
    
         (rate_init * df.loc[df[' 加 仓 批 次 ']==n, ' 价 格 ']*100),0)*100
    
      df.loc[df[' 加 仓 批 次 ']==n, 'money'] = df.loc[df[' 加 仓 批 次 ']==n, ' 价 格 ']*df.loc[df[' 加 仓 批 次 ']==n, 'shares']
    
  
    
     df[' 累 计 仓 位 '] = df[' 加 仓 比 例 '].cumsum()
    
     df[' 累 计 份 额 '] = df['shares'].cumsum()
    
     df[' 累 计 金 额 '] = df['money'].cumsum()
    
     df[' 成 本 '] = df[' 累 计 金 额 ']/df[' 累 计 份 额 ']
    
     df[' 下 跌 幅 度 '] = 1- df[' 价 格 系 数 ']
    
     df[' 下 跌 幅 度 '] = df[' 下 跌 幅 度 '].apply(lambda x:f' 下 跌 {int(round(x*100,0))}%')
    
     df.loc[df[' 加 仓 批 次 ']==1, ' 下 跌 幅 度 '] = ' 首 次 '
    
     df = df[['name',' 加 仓 批 次 ', ' 下 跌 幅 度 ', ' 价 格 系 数 ',' 价 格 ', ' 加 仓 比 例 ', ' 累计 仓 位 ', 'shares', 'money',' 累 计 份 额 ', ' 累 计 金 额 ',' 成 本 ']]
    
     return df
    
    
    
    
    AI助手

上面的自定义函数涉及五个变量:

  • name、price_init、rate_init、shares_init、data;
  • 对应的是:品种名称,初始买入价格,初始买入持仓比例,初始买入份额,列表数据(由价格系数和加仓比例组成);

对于咱们计划投资的品种,设置好变量的初始值后,就可以通过上述的自定义函数来计算出投资计划表了,如下:

复制代码
 # 标 的 名 称

    
 name = ' 中 概 互 联 -513050'
    
  
    
 # 首 次 购 入 价 格
    
 price_init = 2.0
    
  
    
 # 首 次 加 仓 比 例
    
 rate_init = 0.01
    
  
    
 # 首 次 购 入 份 额 数 量
    
 shares_init = 2000
    
  
    
 data = {
    
     ' 价 格 系 数 ':[1,0.95,0.9,0.85,0.8,0.75,0.7,0.65],
    
     ' 加 仓 比 例 ':[rate_init,0.02,0.03,0.04,0.05,0.05,0.05,0.05],
    
     }
    
 df = plan(name,price_init,rate_init,shares_init,data)
    
  
    
 format_dict = {'money': ' ¥ {0:.0f}',
    
            ' 累 计 金 额 ': ' ¥ {0:.0f}',
    
            ' 价 格 ': '{0:.3f}',
    
            ' 成 本 ': '{0:.3f}',
    
            'shares': '{0:.0f}',
    
            ' 累 计 份 额 ': '{0:.0f}',
    
            ' 价 格 系 数 ': '{0:.0%}',
    
            ' 加 仓 比 例 ': '{0:.2%}',
    
            ' 累 计 仓 位 ': '{0:.2%}',
    
             }
    
 df.style.hide_index().format(format_dict)
    
    
    
    
    AI助手

假设你要投资的是其他品种,比如万科,可以修改初始变量,快捷的得到投资计划,如下:

复制代码
 # 标 的 名 称

    
 name = ' 万 科 '
    
  
    
 # 首 次 购 入 价 格
    
 price_init = 22
    
  
    
 # 首 次 加 仓 比 例
    
 rate_init = 0.01
    
  
    
 # 首 次 购 入 份 额 数 量
    
 shares_init = 300
    
  
    
 data = {
    
     ' 价 格 系 数 ':[1,0.95,0.9,0.85,0.8,0.75,0.7,0.65],
    
     ' 加 仓 比 例 ':[rate_init,0.02,0.03,0.04,0.05,0.05,0.05,0.05],
    
 }
    
 df = plan(name,price_init,rate_init,shares_init,data)
    
  
    
 format_dict = {'money': ' ¥ {0:.0f}',
    
            ' 累 计 金 额 ': ' ¥ {0:.0f}',
    
            ' 价 格 ': '{0:.3f}',
    
            ' 成 本 ': '{0:.3f}',
    
            'shares': '{0:.0f}',
    
            ' 累 计 份 额 ': '{0:.0f}',
    
            ' 价 格 系 数 ': '{0:.0%}',
    
            ' 加 仓 比 例 ': '{0:.2%}',
    
            ' 累 计 仓 位 ': '{0:.2%}',
    
             }
    
 df.style.hide_index().format(format_dict)
    
    
    
    
    AI助手

得到的表格如下:

这个表格的实现,其实还是比较简洁的,用 Excel 也是可以较为方便的实现的,只是如果同时实现多个的话,会要麻烦一些,并且可能经常没有注意到个别单元格内容而出现错误。

这个投资计划表格,在实际投资中,还是有些作用的,看似很简单,但就是这种常识性的内容,往往很多人没有在投资之前就做好计划和设想。导致在实际投资过程中,买的比较随意,并且经常追高被套。

此外,由于事前没有做好计划,在投资组合的资产配置以及仓位管理方面,也是一片空白。经常会遇到下跌时发现没钱继续买了,这也是很尴尬的。

比如,上面的万科,如果按计划走的话,第一次买入 300 股,花费 6600 元,钱也不多,但如果市场真要大幅度下跌,下跌达到 35% 以后,按计划投资,这个品种需要投资将近 20 万,持仓占比 30% ,估计是很多人都没有想过的。

经过大跌,才开始反思自己投资计划的情况(发现没钱来大规模加仓了),之前有些品种买的也是有些随意。如果能够事先做好功课,在下跌时泰然处之,不慌乱的清仓,估计投资结果会好很多。

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

用 Python 来追踪和更新基金的收益情况,涉及到的 Python 库主要是 pandas 和 tushare 。

最终实现的效果如下:

上面表格中的信息,主要涉及四个方面:

  1. 基金基础信息,包括基金名称、基金费率、基金公司、基金成立时间等;
  2. 基金经理的信息,包括姓名、性别、任职时间等;
  3. 基金规模,体现出来的是基金金额的规模,是通过基金份额以及基金净值计算出来的;
  4. 基金年度收益情况,一般是计算基金近几年的收益情况。上面的这些信息,在财经工具 tushare 中,目前是都已经提供了的;

1. 基金信息获取

首先,导入本次需要用到的 python 库,并设置好 tushare ,如下:

复制代码
 import pandas as pd

    
 import datetime
    
 import numpy as np
    
 import tushare as ts
    
  
    
 # token='XXXXXXX'
    
 # ts.set_token(token)
    
  
    
 pro = ts.pro_api()
    
    
    
    
    AI助手

需要说明的是,tushare 中不同的数据获取需要的权限是不一样的,如果权限不够,可能获取不到数据哦。

1)获取基金基础信息

首先,咱们来获取基金基础信息,包括基金名称、基金费率、基金公司、基金成立时间等。在 tushare 中,提供了fund_basic() 接口来获取这些信息。

该接口有两个参数,咱们主要关注的是第一个参数 market,由于基金有场内基金(ETF 和 LOF)和场外基金,因此,全部的基金是这两者之和。

所有基金的基础信息获取代码如下:

复制代码
 # 获 取 基 金 中 文 名 称 信 息

    
 df_e = pro.fund_basic(market='E')
    
 df_o = pro.fund_basic(market='O')
    
 # df_fund_info = df_e.append(df_o)
    
 df_fund_info = pd.concat([df_e,df_o])
    
 df_fund_info = df_fund_info[['ts_code','name', 'management','m_fee', 'c_fee', 'found_date']]
    
 df_fund_info.columns = ['fund_code','fund_name', 'management','m_fee', 'c_fee', 'found_date']
    
 df_fund_info = df_fund_info.reset_index(drop=True)
    
 df_fund_info
    
    
    
    
    AI助手

结果如下:

这个数据框中,有 1.6 万多支基金,是不是超出你的想象了啊。

实际上,目前整个市场上总数应该多于这个数,tushare 中有部分信息还没有纳入进来。

当然了,这是包括了混合型、股票型、债券型、货币基金类等全部的基金。

2)获取基金经理信息

对于主动型基金而言,挑选基金时,选择一个好的基金经理是很重要的因素,因此,在追踪基金的收益情况时,是有必要将基金经理的信息一并获取的。

在 tushare 中,可通过接口 fund_manager 来获取某只基金过往基金经理的信息。

在获取基金经理信息后,可以跟之前获取基金基础信息进行合并。

这个过程,写了一个自定义函数,如下:

复制代码
 # 获 取 单 个 基 金 的 信 息

    
  
    
 def get_fund(fund_code,df_fund_info):
    
     # 获 取 基 金 经 理 信 息 , 以 及 开 始 管 理 该 基 金 的 日 期
    
     df_manager = pro.fund_manager(ts_code=fund_code)
    
     df_manager = df_manager[df_manager['end_date'].isna()]
    
     df_manager = df_manager.sort_values('begin_date',ascending=True).head(1)
    
     begin_date = df_manager['begin_date'].values[0] # 开 始 管 理 该 基 金 的 日 期
    
     df_manager = df_manager[['ts_code', 'name', 'gender','begin_date']]
    
     df_manager.columns = ['fund_code', 'manager_name', 'gender','begin_date']
    
  
    
     # 合 并
    
     df_fund = pd.merge(df_manager,df_fund_info,how='left',on='fund_code')
    
  
    
     return df_fund
    
    
    
    
    AI助手

假如你想获取易方达优质精选(以前叫易方达中小盘)的信息,可以基于上面的自定义函数 get_fund ,使用如下的代码:

复制代码
 # 110011.OF, 易 方 达 优 质 精 选 ( 易 方 达 中 小 盘 )

    
  
    
 df_fund = get_fund('110011.OF',df_fund_info)
    
 df_fund
    
    
    
    
    AI助手

获取的信息如下:

3)计算基金规模和收益情况

接下来,咱们还需要获取两个信息,基金规模和基金近几年的收益情况。

基金规模,可以从一个角度来观察该基金在市场上的受欢迎度,一般来说,规模较大的基金,说明该基金过往的表现应该还是可以的。不过这里也有两点需要注意:

  1. 基金过往业绩并不代表未来依然会如此;
  2. 基金规模小,并不一定说明该基金未来表现会不好。

除了基金规模,另一个咱们经常关注的,也是基金营销机构经常拿出来宣传的,就是基金的过往收益情况。

基金规模的计算公式如下:

基金规模 (亿元) = 基金份额数量 X 基金单位净值

在 tushare 中基金份额数量由函数 fund_share 来获取,基金单位净值(unit_nav)数据由函数 fund_nav 来获取。

此外,在计算基金的过往年度收益时,也是基于其累计净值(accum_nav)来实现的。

因此,将基金规模获取过程和基金近几年收益情况计算合并在一个自定义函数里,代码如下:

复制代码
 # 获 取 单 支 基 金 的 年 度 收 益 , 基 金 规 模 等 信 息

    
  
    
 def get_returns(fund_code,start_year):
    
     """ 获 取 单 支 基 金 的 年 度 收 益 , 基 金 规 模 等 信 息
    
     Keyword arguments:
    
     fund_code -- 基 金 代 码 , 如 : 110011.OF
    
     start_year -- 开 始 获 取 基 金 数 据 的 年 份
    
     """
    
     df_fund_nav = pro.fund_nav(ts_code=fund_code)
    
     df_fund_nav['date'] = pd.to_datetime(df_fund_nav['nav_date'])
    
     df_fund_nav = df_fund_nav.set_index('date').sort_index()
    
  
    
     years = df_fund_nav['accum_nav'].resample('AS').sum().to_period('A')
    
     # 获 取 年 度 数 据 , 类 型 为 pandas 的 period
    
     years = years.index.tolist()
    
  
    
     # 有 些 基 金 的 开 始 日 期 晚 于 2018 年 , 需 要 对 齐 列 , 补 空 白
    
      year_list = [yr.year for yr in years if yr.year>=start_year]
    
  
    
     # 将 period 时 间 数 据 转 为 string 的 年 度 数 据
    
     years = [str(yr.year) for yr in years]
    
  
    
     df_yrs_index = pd.DataFrame()
    
     for yr in years:
    
     df_yr_index = df_fund_nav.loc[yr].tail(1)
    
     # df_yrs_index = df_yrs_index.append(df_yr_index) # append 将 被 替 代
    
     df_yrs_index = pd.concat([df_yrs_index,df_yr_index])
    
     # df_yrs = df_fund_nav.head(1).append(df_yrs_index)
    
     df_yrs = pd.concat([df_fund_nav.head(1),df_yrs_index])
    
  
    
     # 计 算 每 年 的 收 益 率 , 即 涨 跌 幅 度
    
     df_yrs['returns'] = df_yrs['accum_nav'].pct_change()
    
  
    
     # 删 除 收 益 率 为 NA 的 行 ( 第 1 天 有 数 据 记 录 的 日 期 )
    
     df_yrs = df_yrs.dropna(subset=['returns'])
    
  
    
     # 筛 选 自 开 始 年 份 以 来 的 数 据
    
     df_yrs = df_yrs.loc[str(start_year):]
    
  
    
     df_yrs = df_yrs.reset_index()
    
     df_yrs['year'] = df_yrs['date'].dt.year
    
  
    
     # 透 视 表
    
     df_yr_returns = pd.pivot_table(
    
     df_yrs, index=['ts_code'],
    
     values=['returns'], columns=['year'], fill_value=""
    
     )
    
  
    
     # 将 多 层 索 引 转 变 为 单 层 索 引
    
     df_yr_returns = df_yr_returns['returns']
    
     df_yr_returns = df_yr_returns.reset_index()
    
     df_yr_returns.columns.name = None
    
  
    
     df_yr_returns = df_yr_returns.rename(columns={'ts_code':'fund_code'})
    
  
    
     # 基 金 份 额
    
     df_fund_share = pro.fund_share(ts_code=fund_code).head(1)
    
     df_fund_share.columns = ['fund_code', 'fd_share_date', 'fd_share', 'fund_type', 'market']
    
     # fd_share, 单 位 是 万 份
    
     fd_share_date = df_fund_share['fd_share_date'].values[0] # 份 额 对 应 的 日 期
    
  
    
     # 份 额 日 期 的 净 值 数 据
    
     df_ann_nav = df_fund_nav.loc[fd_share_date:fd_share_date].sort_values('update_flag',ascending=False).head(1)
    
     df_ann_nav = df_ann_nav[['ts_code', 'accum_nav','unit_nav']]
    
     df_ann_nav.columns = ['fund_code', 'accum_nav','unit_nav']
    
  
    
     # 计 算 基 金 规 模 , amount , 单 位 : 亿 元
    
 df_fund_amount = pd.merge(df_fund_share,df_ann_nav,how='left',on='fund_code')
    
     df_fund_amount['amount'] = df_fund_amount['fd_share'] * df_fund_amount['unit_nav']/10000
    
     df_fund_amount = df_fund_amount[['fund_code','amount','fd_share_date']]
    
  
    
     # 合 并 数 据
    
     df_yr_returns = pd.merge(df_fund_amount,df_yr_returns,how='left',on='fund_code')
    
  
    
     for yr in year_list:
    
     if yr not in df_yr_returns.columns.tolist():
    
         df_yr_returns[yr]=np.nan
    
  
    
     return df_yr_returns
    
    
    
    
    AI助手

假如你想获取易方达优质精选基金的 2018 年以来的收益情况信息,可以基于上面的自定义函数 get_returns ,使用如下的代码:

复制代码
 df_return = get_returns('110011.OF',2018)

    
 df_return
    
    
    
    
    AI助手

获取的信息如下:

4)同时获取多只基金的信息

上面已经实现了获取单只基金所需要的信息,接下来,咱们需要拼接之前获取的信息。

同时,我们一般会同时关注多只基金,因此同时获取多只基金的信息,也是基本必备的需求。

实现的代码如下:

复制代码
 # 获 取 多 只 基 金 的 信 息

    
  
    
 def get_data_fund(df_fund_info,fund_code_short,start_year):
    
     df_filter_info = pd.DataFrame()
    
     for code in fund_code_short:
    
     df_tmp = df_fund_info[df_fund_info['fund_code'].str.contains(code)].head(1)
    
     # df_filter_info = df_filter_info.append(df_tmp)
    
     df_filter_info = pd.concat([df_filter_info,df_tmp])
    
     df_filter_info = df_filter_info.reset_index(drop=True)
    
     fund_codes = df_filter_info['fund_code'].tolist()
    
     # fund_codes
    
  
    
     # df = pd.DataFrame()
    
     frames = [] # 用 来 装 载 df_fund_merge
    
     i=0
    
     for code in fund_codes:
    
     print(f'i:{i},code:{code}')
    
     if i<78:
    
         # print(code)
    
         # print(f'i:{i},code:{code}')
    
         df_fund = get_fund(code,df_fund_info)
    
         df_return = get_returns(code,start_year)
    
         df_fund_merge = pd.merge(df_fund,df_return,how='left',on='fund_code')
    
         frames.append(df_fund_merge)
    
         i +=1
    
     else:
    
         i=0
    
         time.sleep(61)
    
     # df = df.append(df_fund_merge)
    
     df = pd.concat(frames)
    
     df = df.reset_index(drop=True)
    
  
    
     df = df.rename(columns={
    
     'fund_code':' 基 金 代 码 ','manager_name':' 基 金 经 理 ',
    
     'gender':' 性 别 ','begin_date':' 上 任 日 期 ',
    
     'fund_name':' 基 金 名 称 ','management':' 基 金 公 司 ',
    
     'm_fee':' 管 理 费 ','c_fee':' 托 管 费 ',
    
     'found_date':' 成 立 时 间 ','amount':' 基 金 规 模 ( 亿 元 )',
    
     'fd_share_date':' 规 模 对 应 日 期 '
    
     })
    
  
    
     # 调 整 列 的 排 序
    
     cols = df.columns.tolist()
    
     col_1 = cols[:4]
    
     col_2 = cols[4:5]
    
     col_3 = cols[5:]
    
     cols = col_2 + col_1 + col_3
    
     df = df[cols]
    
  
    
     return df
    
    
    
    
    AI助手

在上面的函数 get_data_fund 中,还对最后显示的列的顺序也进行了调整。

基于自定义函数 get_data_fund,设置好函数所需要的参数后,就可以获取多只基金的信息了,具体参数如下:

复制代码
 # 需 要 获 取 的 基 金 代 码 简 称 列 表

    
 fund_code_short = ['001938', '162605', '005911', '005827', '007119', '110011', '161005', '000800', '519736', '163402', '003095', '163406', '002190', '164908', '000772']
    
  
    
 # 设 置 基 金 收 益 开 始 的 年 份
    
 start_year = 2018
    
 # 设 置 基 金 收 益 截 止 的 年 份
    
 end_year = 2022
    
 1year_list = list(range(start_year,end_year+1))
    
  
    
 df_fund_final = get_data_fund(df_fund_info,fund_code_short,start_year)
    
 df = df_fund_final.sort_values(' 基 金 经 理 ',ascending=True,ignore_index=True)
    
 # df.to_csv('./data/fund_manager.csv',index=False)
    
 df
    
    
    
    
    AI助手

得到的结果如下:

Pandas 中,可以通过 Style 对表格样式进行设置,对收益情况进行红涨绿跌的设置。

代码如下:

复制代码
 def color_returns(val):

    
     if val >=0:
    
     color = '#EE7621' # light red
    
     elif val <0:
    
     color = '#99ff66' # light green
    
     else:
    
     color = '#FFFAFA' # ligth gray
    
     return f'background-color: {color}'
    
  
    
 format_dict = {' 基 金 规 模 ( 亿 元 )': ' ¥ {0:.1f}',
    
            ' 管 理 费 ': '{0:.1f}',
    
            ' 托 管 费 ': '{0:.2f}',
    
             2017: '{0:.1%}',
    
             2018: '{0:.1%}',
    
             2019: '{0:.1%}',
    
             2020: '{0:.1%}',
    
             2021: '{0:.1%}',
    
             2022: '{0:.1%}',
    
             }
    
  
    
  
    
 df.style.hide(axis='index')\
    
                 .hide([' 规 模 对 应 日 期 ',' 成 立 时 间 '],axis='columns')\
    
                 .format(format_dict)\
    
                 .applymap(color_returns,subset=year_list)\
    
                 .background_gradient(subset=[' 基 金 规 模 ( 亿 元 )'],cmap='Blues'
    
         )
    
    
    
    
    AI助手

得到的结果如下:

至此,咱们就获取了本次想要得到的信息,这个表格可以定期更新,用于给其他人员分享基金的基本信息以及年度收益情况。

5、用 Python 快速获取基金的持仓变化情况

Python 来追踪和更新基金的持仓结构以及基金的股票增持和减持情况,涉及到的 Python 库主要是 pandas 和 akshare 。

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

  • 单只基金的不同季度持仓变化情况;
  • 多只基金的十大持仓的历史信息;

效果如下:

1. akshare 介绍

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

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

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

复制代码
    pip install akshare
    
    AI助手

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

复制代码
 import akshare as ak

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

2. 单支基金

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

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

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

1)分步骤介绍

首先,咱们通过 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.91
    
     df_tmp = ak.fund_portfolio_hold_em(code="005827",year=yr) # akshare version: 1.3.41
    
     data = data.append(df_tmp)
    
  
    
 data[' 季 度 ']=data[' 季 度 '].apply(lambda x:x[:6])
    
 data[' 季 度 '] = data[' 季 度 '].str.replace(' 年 ','Q')
    
 data[' 占 净 值 比 例 '] = pd.to_numeric(data[' 占 净 值 比 例 '])
    
 data
    
    
    
    
    AI助手

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

结果如下:

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

数据选取如下:

复制代码
 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
    
    
    
    
    AI助手

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

在获取了两个季度的数据后,将数据按 “股票代码” 进行拼接,在得到的 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
    
    
    
    
    AI助手

结果如下:

上面的结果中,如果没有对 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')
    
    
    
    
    AI助手

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

在上面的表格中,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')
    
    
    
    
    AI助手

结果如下:

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

2)函数封装

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

函数封装如下:

复制代码
 1 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.91
    
     df_tmp = ak.fund_portfolio_hold_em(code=code,year=yr) # akshare version: 1.3.41
    
     data = 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
    
    
    
    
    AI助手

基金持仓数据获取的接口,在 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')
    
    
    
    
    AI助手

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

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

比如下面的招行:

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

3. 多支基金

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

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

复制代码
 # 获 取 单 支 基 金 的 十 大 股 票 名 称 信 息

    
 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.91
    
     df_tmp = ak.fund_portfolio_hold_em(code=code,year=yr) # akshare version: 1.3.41
    
     data = 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=None
    
     df = df.reset_index()
    
     # df.index.name = None
    
     df[' 基 金 代 码 '] = code
    
     cols = df.columns.tolist()
    
     cols = cols[:1] + cols[-1:] + cols[1:-1] # 将 基 金 代 码 列 名 放 前 面
    
     df = df[cols]
    
     return df
    
    
    
    
    AI助手

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

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

复制代码
 years = ['2019','2020','2021']

    
 df3 = fund_stock_holding(years,'005827')
    
 df3
    
    
    
    
    AI助手

结果如下:

通过自定义函数 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
    
    
    
    
    AI助手

结果如下:

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

基金名称信息获取如下:

复制代码
 # 获 取 基 金 名 称 信 息

    
 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
    
    
    
    
    AI助手

效果如下:

有了基金名称信息后,可以用 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
    
    
    
    
    AI助手

结果如下:

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

6、用 Python 读取巴菲特最近的持仓数据

1. 数据表格情况

持仓情况的披露文件发布在美国证监会网站上,在美国,13F 文件是机构公布季度持仓时需要披露的文件。

这个文件可能跟国内的有点不一样,我们一起来看看吧。

在官方网站上,是以网页格式进行披露的(html 和 xml),点击上图中红色箭头所指的 “html” 文件后,在会在网页格式中打开持仓情况。如下:

从上面的表格中,可以清晰的看出伯克希尔的持仓情况。如果我们想对伯克希尔的持仓情况进行分析,则可以将数据保存到本地,然后进行分析,尤其是涉及多份数据进行对比和分析的时候,对这些数据的读取和梳理就有必要了。

2. 数据读取

下面我们来看看如何用 Python 进行读取。首先,我们将下面红色箭头所指的 “xml” 文件下载到本地,名称为“test.xml” 。

初步观察网页格式上的数据,可以看出,这个数据表格是一个嵌套型的表格,我们在数据分析的时候,最好进行转化为二维的 dataframe 。

本来我是想用 Pandas 直接进行数据读取的,后来发现是嵌套表格之后,有点麻烦。于是改用 BeautifulSoup 来读取,可以将嵌套的数据也单独提取出来,方便后续分析。

用 BeautifulSoup 进行解析的时候,思路其实比较简单:

  1. 读取 XML 文件的内容;
  2. 构造一个 pandas 的 dataframe ,用来保存解析出来的数据 ;

3. xml 格式内容读取

我们知道,BeautifulSoup 可以用来解析 html、xml 等网页的标记语言内容。因此,对于 XML 文件,我们可以直接进行读取。

下面的代码中的 “test.xml” 文件是我们保存的本地的文件,在本文结尾处也提供了获取方式。

复制代码
 # -*- coding: utf-8 -*-

    
 from bs4 import BeautifulSoup
    
 import pandas as pd
    
  
    
 path = r'./test.xml'
    
  
    
 htmlfile = open(path, encoding='utf-8')
    
  
    
 htmlhandle = htmlfile.read()
    
  
    
 soup = BeautifulSoup(htmlhandle, 'xml')
    
  
    
 print(soup)
    
    
    
    
    AI助手

读取标记语言的内容后,我们再来通过 BeautifulSoup 的 find_all 和 find 功能来读取所需要的数据即可。

对数据进行封装和保存的方法有多种,我们这里采取的是构造一个空的 Pandas 的 DataFrame 来进行封装,然后用 Pandas 的 to_excel 来保存文件,这种方法还是比较方便的,有兴趣的同学可以研究下。

复制代码
 count = 0

    
 # 构 造 一 个 空 的 dataframe
    
 result = pd.DataFrame({}, index=[0])
    
  
    
 # 根 据 持 仓 数 据 表 格 , 添 加 需 要 保 存 的 字 段 ( dataframe 的 列 名 称 )
    
 result['nameOfIssuer'] = ''
    
 result['titleOfClass'] = ''
    
 result['cusip'] = ''
    
 result['value'] = ''
    
 result['sshPrnamt'] = ''
    
 result['sshPrnamtType'] = ''
    
 result['investmentDiscretion'] = ''
    
 result['otherManager'] = ''
    
 result['votingAuthority-Sole'] = ''
    
 result['votingAuthority-Share'] = ''
    
 result['votingAuthority-None'] = ''
    
  
    
 new = result
    
 for item in soup.find_all('infoTable'):
    
     nameOfIssuer = item.find('nameOfIssuer').get_text()
    
     new['nameOfIssuer'] = nameOfIssuer
    
     titleOfClass = item.find('titleOfClass').get_text()
    
     new['titleOfClass'] = titleOfClass
    
     cusip = item.find('cusip').get_text()
    
     new['cusip'] = cusip
    
     value = item.find('value').get_text()
    
     new['value'] = value
    
     sshPrnamt = item.find('shrsOrPrnAmt').find('sshPrnamt').get_text()
    
     new['sshPrnamt'] = sshPrnamt
    
     sshPrnamtType = item.find('shrsOrPrnAmt').find('sshPrnamtType').get_text()
    
     new['sshPrnamtType'] = sshPrnamtType
    
     investmentDiscretion = item.find('investmentDiscretion').get_text()
    
     new['investmentDiscretion'] = investmentDiscretion
    
     otherManager = item.find('otherManager').get_text()
    
     new['otherManager'] = otherManager
    
     Sole = item.find('votingAuthority').find('Sole').get_text()
    
     new['votingAuthority-Sole'] = Sole
    
     Shared = item.find('votingAuthority').find('Shared').get_text()
    
     new['votingAuthority-Share'] = Shared
    
     Non = item.find('votingAuthority').find('None').get_text()
    
     new['votingAuthority-None'] = Non
    
  
    
     count += 1
    
     result = result.append(new, ignore_index=True)
    
  
    
 print(count)
    
  
    
 print(result)
    
  
    
 # 保 存 数 据 到 Excel 表 格
    
 result.to_excel('BRKA-2019-Q4.xlsx')
    
    
    
    
    AI助手

如果后续需要对数据进行分析和可视化,可以借助 Pandas、Matplotlib、Plotly 等 Python 库来处理。
当然,如果你只是想看看或分析这一份文件,也可以直接从网页上复制内容到 Excel 表格中即可,获取数据的方法有多种,选择适合自己的就好哈。

全部评论 (0)

还没有任何评论哟~