python 回归 statsmodels_FamaMacbeth 回归和NeweyWest调整

1. 综述

Fama Macbeth是一种通过回归方法做因子检验,并且可以剔除残差截面上自相关性的回归方法,同时为了剔除因子时序上的自相关性,可以通过Newey West调整对回归的协方差进行调整。

2. 原理

2.1 系数估计

Fama Macbeth回归分为两步,第一步是横截面回归 ,在截面上用股票收益率对各因子暴露做回归,得到各因子的收益率;第二部是对系数的时间序列取平均得到作为参数的估计值,并进行t检验,t检验用到的标准误经过Newey West调整,总结如下

python 回归 statsmodels_FamaMacbeth 回归和NeweyWest调整_第1张图片

2.2 标准误估计

  • 简单估计

d4deb1b294192d2801e60ba067a6aeb7.png

其中,分子上为回归系列的标准差,可以直接计算,也可以进行Newey West调整消除异方差和序列自相关。

  • Newey West 调整

 Newey West的原理主要参考了石川大佬的文章[2][3],简单说明,考虑一个线性模型

python 回归 statsmodels_FamaMacbeth 回归和NeweyWest调整_第2张图片

当残差不存在异方差和自相关性时,残差协方差阵为单位阵的倍数,回归系数的协方差估计是一致估计量,当残差存在异方差或自相关性时,协方差阵估计有问题,可以通过Newey West调整解决,具体来说是估计上式中的

11f73a0c22bb29f255debbe536373ad5.png

Newey West调整即对Q进行估计,最终给出的估计量具有一致性,表达式如下,用S表示

python 回归 statsmodels_FamaMacbeth 回归和NeweyWest调整_第3张图片

上式中,括号中第一项为仅有异方差时的调整,后面一项为针对自相关的调整,其中,e为样本残差,L为计算自相关性影响的最大滞后阶数,w_l是滞后期l的系数,从公式来看,随着滞后期数的增加,影响减小。将S带入系数协方差阵的估计可以得到协方差的Newey West估计量

628af6c72dc71f6abde7416bc35eff7d.png

其中,L常用的取法有很多种,python的famamacbeth函数的取法包括

python 回归 statsmodels_FamaMacbeth 回归和NeweyWest调整_第4张图片

以上是对于OLS的Newey West调整,对于Fama Macbeth回归,是对已经回归出来的一堆beta系数序列的方差进行调整,跟回归有一定差别,可以做一个转换:用回归出来的所有beta做因变量,1做自变量,做一个回归,这样回归出来的系数是所有beta的均值,残差也捕捉了beta中的异方差性和自相关性,对这个回归方程做newey west即可,这个在石川大佬的文章中有更细致的说明。

3. Python实现

Python的linearmodels中自带FamaMacBeth函数,本文一方面调用这一函数,另一方面自己写,用两种方法实现Fama Macbeth回归,确保结果的准确性。

数据使用2010-2018年全A股的市值、动量、PB、roe因子进行测试。

3.1 数据载入

from linearmodels import FamaMacBeth
import numpy as np
import pandas as pd

import datetime

import statsmodels.api as sm
import statsmodels.formula.api as sml

def getICSeries(factors,ret,method):
    

    icall = pd.DataFrame()
    fall = pd.merge(factors,ret,left_on = ['tradedate','stockcode'],right_on = ['tradedate','stockcode'])
    icall = fall.groupby('tradedate').apply(lambda x:x.corr(method = method)['ret']).reset_index()
    icall = icall.drop(['ret'],axis = 1).set_index('tradedate')

    return icall


def ifst(x):
    if pd.isnull(x.entry_dt):
        return 0
    elif (x.tradedate < x.entry_dt) |(x.tradedate > x.remove_dt):
        return 0
    else:
        return 1


def ols_coef(x,y):
    x = x.astype('float64')
    y = y.astype('float64')

    return sm.OLS(y,sm.add_constant(x),missing = 'drop').fit().params



def ols_coef1(x,formula):
    return sml.ols(formula, data=x).fit(missing = 'drop').params
price = pd.read_csv('price.csv',index_col = 0)
pb = pd.read_csv('pb.csv',index_col = 0)
roe = pd.read_csv('roe.csv',index_col = 0)
mkt = pd.read_csv('mkt.csv',index_col = 0)
ST = pd.read_excel('ST.xlsx')

'''
收益率计算
'''

price['tradedate'] = price.tradedate.apply(getdate)
ret_m = getRet(price,freq ='m',if_shift = True)
mom1 = getRet(price,freq ='m',if_shift = False)
mom1 = mom1.rename(columns = {'ret':'mom1'})

fall = pd.merge(pb,roe,left_on = ['tradedate','stockcode'],right_on = ['tradedate','stockcode'])
fall = pd.merge(fall,mkt,left_on = ['tradedate','stockcode'],right_on = ['tradedate','stockcode'])
fall['tradedate'] = fall.tradedate.apply(getdate)
fall = pd.merge(fall,mom1,left_on = ['tradedate','stockcode'],right_on = ['tradedate','stockcode'])
del fall['rptdate']




fall = pd.merge(fall,ST,left_on = 'stockcode',right_on = 'stockcode',how = 'left')
fall['if_st'] = fall.apply(ifst,axis = 1)
fall = fall.loc[fall.if_st == 0].reset_index(drop = True)
fall = fall.drop(['if_st','entry_dt','remove_dt'],axis = 1)


alldata = pd.merge(fall,ret_m,left_on = ['tradedate','stockcode'],right_on = ['tradedate','stockcode'])
alldata['mktcap'] = np.log(alldata.mktcap)

3.2 计算因子IC

alldata中包含所有的因子和收益率数据,并且已经剔除了ST,用alldata做测试。首先用alldata计算因子的IC,ICIR结果如下

ics = getICSeries(fall,ret_m,'spearman')
icir = ics.mean()/ics.std()*np.sqrt(12)
ics.cumsum().plot()

python 回归 statsmodels_FamaMacbeth 回归和NeweyWest调整_第5张图片

3.3 Fama macbeth回归

接下来用这四个因子做FamaMacbeth回归,首先用python自带的函数FamaMacbeth

python 回归 statsmodels_FamaMacbeth 回归和NeweyWest调整_第6张图片

这个函数用法和statsmodels中的OLS基本一致,输入因变量(dependent),自变量(exog)和样本权重(weights),需要注意的是因变量、自变量都是面板数据,或者用MultiIndex的dataframe,日期在前,个体编码在后。生成FamaMacbeth对象后用fit方法进行系数估计,fit的输入参数较多

python 回归 statsmodels_FamaMacbeth 回归和NeweyWest调整_第7张图片

python 回归 statsmodels_FamaMacbeth 回归和NeweyWest调整_第8张图片

  • cov_type表示是否对方差进行调整,'unadjusted'表示不调整,默认为不调整。'kernel'表示调整,即用Newey West方法进行调整

  • debiased:是否对协方差进行自由度调整,即分母用n还是n-1

  • bandwitdh:窗宽,即上文NW调整中的L,如果不设置会通过算法自动生成最优的,也可以根据上面L的公式手动输入。

关于这两个函数的细致说明可以参考[1],这里首先给出不进行调整的回归结果

fmdata = alldata.set_index(['stockcode','tradedate'])
fm = FamaMacBeth(dependent = fmdata['ret'],
                 exog = sm.add_constant(fmdata[['pb','mktcap','mom1','roe_ttm']]))
res_fm = fm.fit(debiased=False)
res_fm

python 回归 statsmodels_FamaMacbeth 回归和NeweyWest调整_第9张图片

手动回归并与上述结果相对比

res = alldata.dropna().groupby('tradedate').apply(ols_coef1,'ret ~ 1 + pb + mktcap + mom1 + roe_ttm')
res.mean()

res.std(ddof = 0)/np.sqrt(res.shape[0])
  • 回归系数

python 回归 statsmodels_FamaMacbeth 回归和NeweyWest调整_第10张图片

  • 回归标准误

python 回归 statsmodels_FamaMacbeth 回归和NeweyWest调整_第11张图片

接下来看Newey West调整后的结果,这里根据上面的公式计算最优的L

T = res.shape[0]
L = int(np.ceil(4 * (T / 100) ** (2 / 9)))

取bandwidth = 5,NW调整后的回归结果如下

res_fm = fm.fit(cov_type= 'kernel',debiased = False,bandwidth = 5)
res_fm

python 回归 statsmodels_FamaMacbeth 回归和NeweyWest调整_第12张图片

手动回归,用beta序列对1进行回归,对结果做NW调整,这里回归用statsmodels中的OLS函数构造辅助函数ols_nw_se完成,最大滞后阶数也设置为5,返回标准误

def ols_nw_se(y):
    return sm.OLS(y.values,np.ones(len(y))).fit(missing = 'drop',cov_type = 'HAC',cov_kwds={'maxlags':5}).bse

res.apply(ols_nw_se,axis = 0)

fb7cefe5649d8658849850dccaeaed20.png

最后对回归结果做简单分析,从FM结果来看,市值和动量通过了显著性检验,方向与IC的方向一致,表明因子对股票收益率有一定的解释力,pb、roe未能通过检验,表明这两个因子中的信息有跟其他两个重叠了,没有信息增益。但是要说明本文建模整个过程都比较粗糙,只是为实现FM回归,结论不具有一般性。

获取数据源码,后台回复"famamacbeth"

参考文献

[1]https://bashtage.github.io/linearmodels/panel/models.html#linearmodels.panel.model.FamaMacBeth 

[2] https://zhuanlan.zhihu.com/p/40984029 

[3] https://zhuanlan.zhihu.com/p/54913149

[4] Fama E F, MacBeth J D. Risk, return, and equilibrium: Empirical tests[J]. Journal of political economy, 1973, 81(3): 607-636.

你可能感兴趣的:(python,回归,statsmodels)