文章目录
【更新日志】
8/6/2020 对代码中计算择时与择股收益公式的错误进行了修正,并将数据结构框架补充在了附录中
1. 引言
在上文中,我们已经介绍了Brinson归因模型的基本思想和基本方法。在这篇文章中,就让我们动起手来实现这一方法吧!
在Brinson多期归因模型实现的过程中,本文使用模块化方法,将整个实现过程划分为如下五个功能板块:
Module 1: 基准数据读取模块(功能函数:benchmark_load)
Module 2: 持仓数据读取模块(功能函数:positions_load)
Module 3: 持仓收益与基准计算模块(功能函数:w_r_calculation)
Module 4: Brinson多期归因模型核心算法(功能函数:Brinson_Multiple)
Module 5: 主函数模块
注1:由于每个人数据都不一样,因此读者可以仅参考自己认为有用的模块(比如Brinson模型的核心算法)。
注2:由于本人自己的持仓数据比较敏感,在此仅展示代码实现框架。读者可以将自己的数据稍作处理后套用与本模板。
2. Brinson多期归因模型的Python实现
2.1 基准数据读取模块
# 0. 导入pandas库
import pandas as pd
# 1. 基准数据读取函数
'''
· 函数功能:读取基准数据
· 输入变量:基准数据存储的地址路径,格式为str
· 输出变量:基准的权重矩阵和收益矩阵,格式为DataFrame的list
· 数据样式介绍:
1) 基准的权重存储于某xlsx或xls文件中,共有两个sheets,分别命名为"Weights"和"Return";
2) 数据的第一列为交易日日期,列名为“日期”,其列格式为“datatime64”;
3) 数据表头为各行业名称。
'''
def benchmark_load(benchmark_path):
benchmark_weights = pd.read_excel(benchmark_path, sheet_name='Weights', index_col='日期')
benchmark_returns = pd.read_excel(benchmark_path, sheet_name='Return', index_col='日期')
return [benchmark_weights, benchmark_returns]
注:除本地导入基准数据外,可以通过API接口直接获取基准数据,如wind数据库、tushare数据库等
2.2 持仓数据读取模块
# 2. 读取持仓数据
'''
· 函数功能:读取持仓数据
· 输入变量:持仓数据存储的地址路径,格式为str
· 输出变量:标准持仓数据,格式为DataFrame,columns为数据名称,index为对应股票的持仓日期
· 数据样式介绍:
1) 全部的持仓数据,包括:持仓股票代码,股票所属行业,持仓日期、市值、当日卖出金额、当日买入金额等;
2) 数据表头为各数据名称;
3) 原始数据可以不按照交易日先后顺序排列。
'''
def positions_load(positions_path):
pf_data= pd.read_excel(positions_path)
pf_data.drop_duplicates() # 数据去重
pf_data = pf_data[['持仓日期', '市值', '行业', '当日卖出金额', '当日买入金额']] # 选定需要使用的列
pf_data['持仓日期'] = pf_data['持仓日期'].astype('datetime64') # 转化日期格式
return pf_data
2.3 持仓收益与基准计算模块
# 3. calculate the weights and the returns of the portfolio
'''
· 函数功能:计算所持仓位的权重和收益矩阵
· 输入变量:
1) trading_dates:从基准值提取的交易日信息,格式为list
2) sectors: 从基准中提取行业名称(本码为申万一级),格式为list
3) pf_data:使用读取函数读取到的数据
· 输出变量:持仓的权重矩阵和收益矩阵,格式为DataFrame的list
'''
def w_r_calculation(trading_dates, sectors, pf_data):
# sectors: 从基准中提取行业名称(申万一级)
# trading_dates 从基准值提取交易日信息
# Part 1: 初始化一些存储用的中间变量
pf_weights = pd.DataFrame(0, columns=sectors, index=td_dates).astype('float') # 存储持仓权重