(二十六)Fama-French三因素模型及应用
Fama-French三因素模型简介
Fama-French三因素模型在CAPM的基础上增加了两个因子:市值因子和账面市值比因子。其中市场风险溢酬因子E(RM)-Rf对应了市场投资组合的收益率,市值因子SMB对应了做多市值较小的公司与做空市值较大的公司的投资组合带来的收益率(规模因子),账面市值比因子HML对应的是做多高book-to-market ratio公司、做空低book-to-market ratio公司的投资组合带来的收益率(做多价值股做空成长股,价值因子)。三因素模型的形式为:
其中Rit为我们要研究的投资组合或个股收益率序列;Rmt为市场投资组合收益率序列,常用合适的大盘指数来替代;αi代表超额收益。
方法很简单,通过多元线性回归估计出各个因子前的系数就可得到完整的三因素模型。在投资组合管理中,透过系数可以看出该投资组合的投资风格,比如估计出的hi为负,就代表该投资组合在做空价值股、做多成长股;若Rf前的系数为负,代表该策略在加杠杆买大盘;超额收益率α越大代表投资组合越优秀等等。
实际上难点在于SMB和HML的计算。类似与Fama-French(1993)的构造方法,首先,我们按照公司的市值与帐面市值比的大小形成6个组合;然后我们利用这6个组合来模拟“规模因子”与“价值因子” 带来的收益率。具体方法如下:
第一步,在每个交易日内对所有的股票按其市值进行排序,用中位数把样本内的股票分成两个两组,即小的(S)与大(B)的两组。同样我们也按账面市值比的大小进行排序,按最小的%30(L)、中间的40%(M)、最大的30%(H)来取分界点。这样我们通过把上面的两种分类方法就可以构造出6个组合(S/L,S/M,S/H,B/L,B/M,B/H),以等权重来计算出6个组合的收益。
第二步,利用已经构造的6个组合来计算SMB与HML,计算方法如下:
新引入函数介绍
query()函数对数据框进行挑选行的操作,主要格式为按条件提取df.query(‘(a列==“b”) and (b列 >=“c”)’)或列之间的比较df.query(‘a列<b列’) 。尤其注意多条件提取时内外引号不能相同,比如里面单引号外面双引号,否则会报错。
map()将一个自定义函数应用于Series结构中的每个元素,表现为一种映射关系。比如df[‘a’] = df[‘a’].map(lambda x :"%.3f"%x)表示将df数据框的a列改成保留小数点后三位。
apply()将一个函数作用于DataFrame中的每个行或者列,也就是说fun的参数x指的是某列或某行。比如想对数据框中某两行或两列相加:
df = pd.DataFrame({'a1':np.arange(5),'a2':np.arange(5,10),'key1':['a','a','b','b','a']})
df['key1']=df['key1'].map({'b':'c','a':'d'});df#map函数修改key1列的数据
Out[7]: a1 a2 key1
0 0 5 d
1 1 6 d
2 2 7 c
3 3 8 c
4 4 9 d
#对列元素(每行)求和
df['rowsum']=df[['a1','a2']].apply(lambda x:sum(x),axis=1);df
Out[9]: a1 a2 key1 rowsum
0 0 5 d 5
1 1 6 d 7
2 2 7 c 9
3 3 8 c 11
4 4 9 d 13
#对行元素(每列)求和
df.loc['colsum']=df[['a1','a2']].apply(lambda x:sum(x),axis=0);dfa1 a2 key1 rowsum
0 0.0 5.0 d 5.0
1 1.0 6.0 d 7.0
2 2.0 7.0 c 9.0
3 3.0 8.0 c 11.0
4 4.0 9.0 d 13.0
colsum 10.0 35.0 NaN NaN
下面以研究个股收益率为例建立三因素模型。
step1:计算SMB和HML
引入需要的库:
import pandas as pd
import tushare as ts
import datetime
pro=ts.pro_api('你的token')#登录tushare
定义计算SMB和HML的函数。先根据流通市值进行大市值小市值分组,再求出每一个公司的账面市值比,并分为三组。然后组合成六组并求每一组的按市值加权平均收益率,代入公式即可。
def cal_smb_hml(df):# 根据流通市值circ_mv划分大小市值公司,映射到SB列df['SB'] = df['circ_mv'].map(lambda x: 'B' if x >= df['circ_mv'].median() else 'S')# 求账面市值比:PB的倒数df['BM'] = 1 / df['pb']# 划分高、中、低账面市值比公司border_down, border_up = df['BM'].quantile([0.3, 0.7])df['HML'] = df['BM'].map(lambda x: 'H' if x >= border_up else 'M')df['HML'] = df.apply(lambda x: 'L' if x['BM'] <= border_down else x['HML'], axis=1)#这里的x可看做df,按行处理# 组合划分为6组df_SL = df.query('(SB=="S") & (HML=="L")')df_SM = df.query('(SB=="S") & (HML=="M")')df_SH = df.query('(SB=="S") & (HML=="H")')df_BL = df.query('(SB=="B") & (HML=="L")')df_BM = df.query('(SB=="B") & (HML=="M")')df_BH = df.query('(SB=="B") & (HML=="H")')# 计算各组按市值加权平均收益率:Σ百分比变化×流通市值/Σ流通市值,除以100是因为pct_chg乘了100R_SL = (df_SL['pct_chg'] * df_SL['circ_mv'] / 100).sum() / df_SL['circ_mv'].sum()R_SM = (df_SM['pct_chg'] * df_SM['circ_mv'] / 100).sum() / df_SM['circ_mv'].sum()R_SH = (df_SH['pct_chg'] * df_SH['circ_mv'] / 100).sum() / df_SH['circ_mv'].sum()R_BL = (df_BL['pct_chg'] * df_BL['circ_mv'] / 100).sum() / df_BL['circ_mv'].sum()R_BM = (df_BM['pct_chg'] * df_BM['circ_mv'] / 100).sum() / df_BM['circ_mv'].sum()R_BH = (df_BH['pct_chg'] * df_BH['circ_mv'] / 100).sum() / df_BH['circ_mv'].sum()# 计算SMB, HMLsmb = (R_SL + R_SM + R_SH - R_BL - R_BM - R_BH) / 3hml = (R_SH + R_BH - R_SL - R_BL) / 2return smb, hml
挑选出样本区间内的所有股票信息,包括每日行情和每日指标,计算出SMB和HML:
data = []
#取出交易日信息,选出上交所的交易日
df_cal = pro.trade_cal(start_date='20190101', end_date='20200101');df_cal.head()
Out[4]: exchange cal_date is_open
0 SSE 20190101 0
1 SSE 20190102 1
2 SSE 20190103 1
3 SSE 20190104 1
4 SSE 20190105 0
df_cal = df_cal.query('(exchange=="SSE") & (is_open==1)')
for date in df_cal.cal_date:df_daily = pro.daily(trade_date=date)#通过日期取出某一天所有股票的行情数据df_basic = pro.daily_basic(trade_date=date)#通过日期取某一天的全部基本指标#根据股票代码合并数据df = pd.merge(df_daily,df_basic,on='ts_code',how='inner')smb, hml = cal_smb_hml(df)data.append([date,smb,hml])print(date,smb,hml)
'''20190102 0.012469284877061207 -0.0030494986538882407
20190103 -0.00208332807907743 0.015438815398099406
20190104 0.002506068642702431 3.796338743873237e-05......'''
df_tfm = pd.DataFrame(data, columns=['trade_date', 'SMB', 'HML'])
df_tfm['trade_date'] = pd.to_datetime(df_tfm.trade_date)
#将日期设为索引之前先转换为日期格式
df_tfm = df_tfm.set_index('trade_date')
df_tfm.to_csv('df_three_factor_model.csv')
df_tfm.head()
Out[11]: SMB HML
trade_date
2019-01-02 0.012469 -0.003049
2019-01-03 -0.002083 0.015439
2019-01-04 0.002506 0.000038
2019-01-07 0.010351 -0.005321
2019-01-08 0.001529 0.000247
step2:挑选股票和基准指数
这里研究的个股是贵州茅台,选取的市场指数是国证A指。
maotai = pro.daily(ts_code='600519.SH',start_date='20190101',end_date='20200101')
gzA = pro.index_daily(ts_code='399317.SZ',start_date='20190101',end_date='20200101')
#用日期作为index并按照日期升序排序
stock_list = [maotai,gzA]
for stock in stock_list:stock.index = pd.to_datetime(stock.trade_date)
df_stock = pd.concat([stock.pct_chg/100 for stock in stock_list],axis=1)
#concat下的两个数据框的index取并集合并
df_stock.columns = ['maotai','gzA']
df_stock = df_stock.sort_index(ascending=True)
df_stock.head()
Out[13]: maotai gzA
trade_date
2019-01-02 0.015203 -0.009442
2019-01-03 -0.014992 -0.003467
2019-01-04 0.020339 0.024723
2019-01-07 0.005797 0.012932
2019-01-08 -0.001156 -0.001923
step3:合并数据并回归
df = pd.merge(df_stock,df_tfm,left_index=True,right_index=True,how='inner')
#left_index=True,right_index=True表示合并索引
df = df.fillna(0)
rf = 1.032**(1/360)-1#每一天的Rf
df.maotai=df.maotai-rf;df.gzA=df.gzA-rf
df.head()
Out[14]: maotai gzA SMB HML
trade_date
2019-01-02 0.015115 -0.009530 0.012469 -0.003049
2019-01-03 -0.015080 -0.003555 -0.002083 0.015439
2019-01-04 0.020251 0.024635 0.002506 0.000038
2019-01-07 0.005709 0.012844 0.010351 -0.005321
2019-01-08 -0.001244 -0.002011 0.001529 0.000247
import statsmodels.api as sm
model=sm.OLS(df['maotai'],sm.add_constant(df[['gzA', 'SMB', 'HML']])).fit()
result=model.summary()
result
<class 'statsmodels.iolib.summary.Summary'>
"""OLS Regression Results
==============================================================================
Dep. Variable: maotai R-squared: 0.565
Model: OLS Adj. R-squared: 0.559
Method: Least Squares F-statistic: 103.8
Date: Thu, 06 Feb 2020 Prob (F-statistic): 4.24e-43
Time: 21:35:56 Log-Likelihood: 721.01
No. Observations: 244 AIC: -1434.
Df Residuals: 240 BIC: -1420.
Df Model: 3
Covariance Type: nonrobust
==============================================================================coef std err t P>|t| [0.025 0.975]
------------------------------------------------------------------------------
const -0.0013 0.001 -1.413 0.159 -0.003 0.000
x1 0.9123 0.065 13.950 0.000 0.784 1.041
x2 -1.1363 0.114 -10.010 0.000 -1.360 -0.913
x3 -0.9687 0.142 -6.838 0.000 -1.248 -0.690
==============================================================================
Omnibus: 19.671 Durbin-Watson: 2.001
Prob(Omnibus): 0.000 Jarque-Bera (JB): 26.343
Skew: 0.561 Prob(JB): 1.90e-06
Kurtosis: 4.154 Cond. No. 175.
==============================================================================model.params
Out[15]:
const -0.001258
x1 0.912341
x2 -1.136315
x3 -0.968734
dtype: float64
注意在fama的研究中是先按t-1时的数据分组再计算t时间的加权收益率,本文为了说明大致思路做了一个简化,未体现如此轮换。由上述结果可得茅台集团股票收益率的Fama-French三因素模型表达式为Ri=-0.0013+0.9123x1-1.1363x2-0.9687x3。
参考文献
https://zhuanlan.zhihu.com/p/55071842
(二十六)Fama-French三因素模型及应用相关推荐
- 2021年大数据Hadoop(二十六):YARN三大组件介绍
全网最详细的Hadoop文章系列,强烈建议收藏加关注! 后面更新文章都会列出历史文章目录,帮助大家回顾知识重点. 目录 本系列历史文章 前言 Yarn三大组件介绍 ResourceManager No ...
- 模板方法模式 Template method 行为型 设计模式(二十六)
模板方法模式 Template method 上图为网上百度的一份简历模板截图 相信大家都有求职的经历,那么必然需要简历,写简历的时候,很可能你会网上检索一份简历模板,使用此模板的格式,然后替换为你的 ...
- 窗口消息——Windows核心编程学习手札之二十六
窗口消息 --Windows核心编程学习手札之二十六 Windows允许一个进程至多建立10000个不同类型的用户对象(user object):图符.光标.窗口类.菜单.加速键表等,当一个线程调用一 ...
- OpenCV学习笔记(二十六)——小试SVM算法ml OpenCV学习笔记(二十七)——基于级联分类器的目标检测objdect OpenCV学习笔记(二十八)——光流法对运动目标跟踪Video Ope
OpenCV学习笔记(二十六)--小试SVM算法ml 总感觉自己停留在码农的初级阶段,要想更上一层,就得静下心来,好好研究一下算法的东西.OpenCV作为一个计算机视觉的开源库,肯定不会只停留在数字图 ...
- 2008R2Win7管理二十六ADRMS客户端使用及侦错
2008R2Win7管理二十六ADRMS客户端使用及侦错 预计我以后都没太多时间专研新技术和写文啦,尽量挤时间吧,有一篇放一篇吧,呵呵 本篇介绍在win7客户端使用adrms来进行权限管理和侦错,在使 ...
- SAP UI5 初学者教程之二十六 - OData 服务配合 Mock 服务器的使用步骤详解试读版
一套适合 SAP UI5 初学者循序渐进的学习教程 教程目录 SAP UI5 本地开发环境的搭建 SAP UI5 初学者教程之一:Hello World SAP UI5 初学者教程之二:SAP UI5 ...
- 微信小程序把玩(二十六)navigator组件
微信小程序把玩(二十六)navigator组件 原文:微信小程序把玩(二十六)navigator组件 navigator跳转分为两个状态一种是关闭当前页面一种是不关闭当前页面.用redirect属性指 ...
- 第一百二十六节,JavaScript,XPath操作xml节点
第一百二十六节,JavaScript,XPath操作xml节点 学习要点: 1.IE中的XPath 2.W3C中的XPath 3.XPath跨浏览器兼容 XPath是一种节点查找手段,对比之前使用标准 ...
- FreeSql (二十六)贪婪加载 Include、IncludeMany、Dto、ToList
贪婪加载顾名思议就是把所有要加载的东西一次性读取. 本节内容为了配合[延时加载]而诞生,贪婪加载和他本该在一起介绍,开发项目的过程中应该双管齐下,才能写出高质量的程序. Dto 映射查询 Select ...
最新文章
- 安卓bochs模拟linux_云电脑?不需要的,有了这个模拟器,手机轻松变电脑
- 网络计算机统考报名网,2017年《计算机应用基础》统考试题及答案
- LightOJ 1088 - Points in Segments 二分
- 数据结构图之二(最小生成树--克鲁斯卡尔算法)
- 关于选择排序和快速排序--2017-09-24
- 学习练习 java 二分查找法
- 红米k30 android版本,Redmi K30 Pro 推送 MIUI 12.2.1 稳定版:为安卓跨版本升级
- duration java_Java Duration类| ofDays()方法与示例
- servlet3.1
- 华为招聘公关总监:接触近10位路透资深记者 年薪高达20万美元
- django模块导入/函数/中间件/MVC和MTV/CSRF
- Android平台Native开发与JNI机制详解
- 洛谷P1120【小木棍】(搜索+剪枝)
- UCOS II怎么查找当前就绪表中,优先级最高的任务
- Activiti 单环节多人办理一件
- python pandas中文手册-Pandas速查手册中文版(转)
- 机器人对话常用语模板_电话机器人的销售能力
- arduino 智能车组装步骤_Arduino智能小车硬件安装说明
- kodi树莓派_【树莓派】树莓派与XBMC及Kodi、LibreELEC插件(二)
- 元旦给计算机老师发贺词,给老师的元旦祝福语