https://uqer.io/community/share/5784b3d1228e5b8a09932d9e

Fama-French 三因子在A股市场的实证研究

Fama-French三因子模型无疑是量化领域最经典的模型之一,该模型的提出是在论文《commom risk factors in returns on bonds and stocks>里,本帖本着学习的精神对其进行了学习,并使用论文中的方法在中国A股市场上进行了实证。\begin{align}E(R(t))=R_f(t)+bE(R_M(t)-R_f(t))+sE(SMB(t))+hE(HML(t))\end{align}

RM−Rf表示的是市场因子,SMB表示的是规模(市值)因子,HML表示账面市值比因子

一.背景

资本资产定价模型(CAPM)问世以后,许多学者对其进行了实证研究,如Black和Scholes(1972)及Fama(1973)的检验证明,对1969年以前的数据而言,资本资产定价模型是有效的,而对之后的数据,却缺乏说服力。在横截面数据里,股票的平均收益和市场β相关性很低,因而更多影响股票收益的因素亟待发掘。

Fama和French(1992)研究了市场β, 市值(size), 账面市值比(book-to-market equity),财务杠杆(leverage) 和市盈率(E/P)对平均收益的影响。横截面回归后发现,在独立检验四者对平均收益的影响时,四者都表现出了很强的解释能力,而β很弱;在进行多变量回归时,市值和账面市值比这两个因子吸收了另两个因子的解释能力,成为了解释平均收益的决定性变量

1993年,Fama和French的论文《commom risk factors in returns on bonds and stocks〉正式标志着三因子模型的建立。在该论文里,他们不仅研究了影响股票收益的因子模型,还研究了对债券收益的因子模型;更重要的是,不同于以往的横截面回归,该论文使用了Black,Jensen和Scholes的时间序列回归方法,对影响股票收益的市场超额收益,规模和账面市值比三个因子进行了实证研究。

下面,本帖就论文里对股票三因子模型的研究思路,在优矿平台上对中证800成分股从2007.6到2016.5的数据进行了实证研究,以验证三因子模型的有效性。

二.研究思路

1.Black-Jensen-Scholes时间序列回归

横截面回归大家都非常熟悉,无论是单变量还是多变量回归,都是在研究解释变量对响应变量的解释能力。Black-Jensen-Scholes时间序列回归的方法是Black,Jensen和Scholes所提出来验证CAPM的。早期的验证方法是先使用一个单只股票的时间序列回归估计贝塔,再用横截面回归验证CAPM推出的假设。但是这样回归会有误差项存在相关性,贝塔非平稳等问题,时间序列回归则避免了这些问题。即根据前一期估计的贝塔值对股票排序再进行分组,分别估计各投资组合的阿尔法和贝塔,每五年重新估计贝塔,然后检验各个投资组合的阿尔法是否显著为0,从而验证CAPM\begin{align}E(R(t))=R_f(t) +\beta(E(R_M(t))-R_f(t))\end{align}

2.解释变量

解释变量就是我们需要验证的三个因子,市场超额收益,规模和账面市值比。我们要按照论文里的思路对其进行处理。

1)分组

把股票按每年5月末时的市值(size)大小进行排序,按照50%分位值把股票分为S(small)和B(big)两组;

再依据5月末时的账面市值比(我们取1/PB)大小对800只股票进行排序,分为L(low,30%),M(medium,40%),H(high,30%)三组;

再分别对S,B和L,M,H取交集,股票即被分为了SL,SM,SH,BL,BM,BH六组。

也就是说,分组每年5月末进行一次,800只股票每次被重新分为了SL,SM,SH,BL,BM,BH六组,前一年6月到第二年5月重新分组时的投资组合都是一样的

这里为什么要按市值分为两组,按账面市值比分为三组呢?是因为账面市值比有更强的作用,我们要把它分得更细。

(PS:论文里是6月末按照市值大小分组,账面市值比依据的是前一年末时的数据,个人以为5月末也没事)

1
#定义了一个进行分组的函数get_6groups,方便对每年的数据进行分组

2
#这里的账面市值比用的是1/PB

3
import numpy as np

4
import pandas as pd 

5
def get_6groups(breakpoint):   #breakpoint是每年进行分组的时间点,这里是每年5月末

6
    C=DataAPI.MktEqudGet(ticker='000028',beginDate=str(int(breakpoint)-20),endDate=breakpoint,field=u"ticker,tradedate")

7
    breakpoint=filter(lambda x:x.isdigit(),C.iat[len(C)-1,1])                         #取breakpoint前最近一个交易日日期

8
    universe = set_universe('000906.ZICN',date=breakpoint)                           #取当时的中证800股指成分股

9
    ME=DataAPI.MktEqudGet(tradeDate=breakpoint,secID=universe,field=u"ticker,marketValue").dropna()   #取当时的市值

10
    ME50=np.percentile(ME['marketValue'],50)                                     #算出市值大小的50%分位值

11
    S=ME[ME['marketValue']<=ME50]['ticker'].tolist()                                #按市值大小分为两组,存为列表

12
    B=ME[ME['marketValue']>ME50]['ticker'].tolist()

13
    BP=DataAPI.MktStockFactorsOneDayGet(tradeDate=breakpoint,secID=universe,field=u"ticker,PB").dropna() 

14
    BP=BP[BP>0].dropna()                                                  #去掉PB值为负的股票

15
    BP[['PB']]=1/BP[['PB']]                                                #取1/PB,为账面市值比

16
    BP30=np.percentile(BP['PB'],30)

17
    BP70=np.percentile(BP['PB'],70)

18
    L=BP[BP['PB']<=BP30]['ticker'].tolist()                                      #按1/PB大小分为三组

19
    H=BP[BP['PB']>BP70]['ticker'].tolist()

20
    M=list(set(BP['ticker'].tolist()).difference(set(L+H)))

21
    SL=list(set(S).intersection(set(L)))                                       #对S组和L组的股票取交集,作为SL组的股票组合

22
    SM=list(set(S).intersection(set(M)))

23
    SH=list(set(S).intersection(set(H)))

24
    BL=list(set(B).intersection(set(L)))

25
    BM=list(set(B).intersection(set(M)))

26
    BH=list(set(B).intersection(set(H)))

27
    return SL,SM,SH,BL,BM,BH

查看全部

下面我们要计算每个投资组合的月收益率,计算投资组合的月收益率时,要算市值加权的收益率,这是为了最小化方差(风险)

1
#得到投资组合x从Year的6月到Year+1的5月的月收益率序列

2
def get_returnMonthly(x,Year):

3
    #先用交易日日历得到Year的5月到Year+1的5月的月末交易日日期

4
    from CAL.PyCAL import *

5
    data=DataAPI.TradeCalGet(exchangeCD=u"XSHG",beginDate=str(Year*10000+501),endDate=str((Year+1)*10000+601),field=['calendarDate','isMonthEnd'])

6
    data = data[data['isMonthEnd'] == 1]

7
    date= map(lambda x: x[0:4]+x[5:7]+x[8:10], data['calendarDate'].values.tolist())

8
    #调用投资组合x每只股票每个月末的市值,收盘价用来计算收盘价

9
    returnMonthly=np.zeros(12)

10
    for i in range(12):

11
        inf1=DataAPI.MktEqudAdjGet(tradeDate=date[i],ticker=x,field=u"ticker,closePrice").set_index('ticker')  #前一个月的收盘价

12
        inf2=DataAPI.MktEqudAdjGet(tradeDate=date[i+1],ticker=x,field=u"ticker,marketValue,closePrice").set_index('ticker')   #当月的收盘价和市值

13
        Return=pd.concat([inf2,inf1],axis=1)

14
        Return.columns=['Weight','Return','WReturn']                   #计算每只股票收益率和市值加权的权重以及两者的乘积

15
        Return['Weight']=Return['Weight']/Return['Weight'].sum()

16
        Return['Return']=Return['Return']/Return['WReturn']-1

17
        Return['WReturn']=Return['Weight']*Return['Return']

18
        returnMonthly[i]=Return['WReturn'].sum()

19
    return returnMonthly

查看全部

2)因子

市值因子:\begin{align}SMB = 1/3(SL+SM+SH)-1/3(BL+BM+BH)\end{align}表示的是由于公司规模不同造成的风险溢价

账面市值比因子:\begin{align}HML = (SH+BH)/2-(SL+BL)/2\end{align}表示由于账面市值比不同造成的风险溢价

可以看出因子的值是一个市值加权月收益率序列,我们研究了九年的数据,所以因子的长度是9*12=108

1
#计算每年的SMB和HML因子,合在一起

2
SMB=[]

3
HML=[]

4
r_groups=pd.DataFrame()  #用于存储每个组合的月收益率序列,方便我们之后查看

5
r_groups['SL']=np.zeros(108)

6
r_groups['SM']=np.zeros(108)

7
r_groups['SH']=np.zeros(108)

8
r_groups['BL']=np.zeros(108)

9
r_groups['BM']=np.zeros(108)

10
r_groups['BH']=np.zeros(108)

11
for Year in [2007,2008,2009,2010,2011,2012,2013,2014,2015]:

12
    SL,SM,SH,BL,BM,BH=get_6groups(str(Year*10000+531))  #依据当年5月末的市值和账面市值比分组

13
    r_SL=get_returnMonthly(SL,Year)       #得到当年5月末到次年的市值加权月收益率序列

14
    r_SM=get_returnMonthly(SM,Year)

15
    r_SH=get_returnMonthly(SH,Year)

16
    r_BL=get_returnMonthly(BL,Year)

17
    r_BM=get_returnMonthly(BM,Year)

18
    r_BH=get_returnMonthly(BH,Year)

19
    

20
    r_groups.iloc[(Year-2007)*12:(Year-2006)*12,[0]]=r_SL.reshape(12,1)   #把组合SL当年5月末到次年的市值加权月收益率序列

21
    r_groups.iloc[(Year-2007)*12:(Year-2006)*12,[1]]=r_SM.reshape(12,1)

22
    r_groups.iloc[(Year-2007)*12:(Year-2006)*12,[2]]=r_SH.reshape(12,1)

23
    r_groups.iloc[(Year-2007)*12:(Year-2006)*12,[3]]=r_BL.reshape(12,1)

24
    r_groups.iloc[(Year-2007)*12:(Year-2006)*12,[4]]=r_BM.reshape(12,1)

25
    r_groups.iloc[(Year-2007)*12:(Year-2006)*12,[5]]=r_BH.reshape(12,1)

26
    

27
    SMBr=(r_SL+r_SM+r_SH)/3-(r_BL+r_BM+r_BH)/3                         #当年的SMB和HML因子,存为list

28
    HMLr=(r_SH+r_BH)/2-(r_SL+r_BL)/2   

29
    SMB += SMBr.tolist()

30
    HML += HMLr.tolist()

31
SMB=np.array(SMB)

32
HML=np.array(HML)

查看全部

1
#加载画图需要用的包

2
import matplotlib as mpl

3
import matplotlib.pyplot as plt

4
mpl.style.use('ggplot')

5
import seaborn as sns

查看全部

下面我们先看一看我们得到的六个组合的市值加权月收益率的情况,直观上符合常理

1
from CAL.PyCAL import *

2
data=DataAPI.TradeCalGet(exchangeCD=u"XSHG",beginDate=str(2007*10000+501),endDate=str((2015+1)*10000+601),field=['calendarDate','isMonthEnd'])

3
data = data[data['isMonthEnd'] == 1]

4
date= map(lambda x: x[0:4]+x[5:7]+x[8:10], data['calendarDate'].values.tolist())

5
r_groups.index=date[1:]

6
r_groups.plot(figsize=[12,7])

查看全部

<matplotlib.axes._subplots.AxesSubplot at 0x7a79650>

市场因子:\begin{align}R_M-R_f\end{align}RM取的就是中证800指数的收益,Rf取的是银行间质押式回购利率_同业拆借中心R007

1
#先用交易日日历得到Year的5月到Year+1的5月的月末交易日日期

2
from CAL.PyCAL import *

3
data=DataAPI.TradeCalGet(exchangeCD=u"XSHG",beginDate='20070501',endDate='20160601',field=['calendarDate','isMonthEnd'])

4
data = data[data['isMonthEnd'] == 1]

5
date = map(lambda x: x[0:4]+x[5:7]+x[8:10], data['calendarDate'].values.tolist())

6
RmMonthly=np.zeros(108)

7
RfMonthly=np.zeros(108)

8
for i in range(108):

9
    index1=DataAPI.MktIdxdGet(tradeDate=date[i],indexID=u"000906.ZICN",field=u"closeIndex") #上月指数收盘

10
    index2=DataAPI.MktIdxdGet(tradeDate=date[i+1],indexID=u"000906.ZICN",field=u"closeIndex")  #当月指数收盘

11
    RmMonthly[i]=index2['closeIndex'][0]/index1['closeIndex'][0]-1

12
    rf=DataAPI.ChinaDataInterestRateInterbankRepoGet(indicID=u"M120000068",beginDate=date[i+1],endDate=date[i+1],field=u"dataValue")  #当月无风险收益

13
    RfMonthly[i]=rf['dataValue'][0]/100/12    #给出的是年化无风险收益,这里需要转化成月的

14
MF=RmMonthly-RfMonthly  #市场因子

查看全部

三个因子我们都得到了,再来看看三个因子的状况:

1
factor=pd.DataFrame()

2
factor['MF']=MF

3
factor['SMB']=SMB

4
factor['HML']=HML

5
factor.index=date[1:]

6
factor.plot(figsize=[12,7])

7
factor.describe()

查看全部

  MF SMB HML
count 108.000000 108.000000 108.000000
mean 0.001237 0.014489 -0.001268
std 0.096189 0.058929 0.049805
min -0.262845 -0.209948 -0.157205
25% -0.063134 -0.014343 -0.025673
50% 0.005296 0.014957 -0.004887
75% 0.058838 0.051484 0.022718
max 0.192618 0.214916 0.248741

到这里,我们的三个因子就处理完了,三个解释变量都做成了数组,下面可以分析一下各个因子之间的相关系数

1
x=np.zeros((3,108))

2
x[0]=MF

3
x[1]=SMB

4
x[2]=HML

5
Correlations=pd.DataFrame(np.corrcoef(x))

6
Correlations.columns=['MF','SMB','HML']

7
Correlations.index=['MF','SMB','HML']

8
Correlations

查看全部

  MF SMB HML
MF 1.000000 0.212499 -0.031912
SMB 0.212499 1.000000 -0.424261
HML -0.031912 -0.424261 1.000000

3. 响应变量

首先我们将股票按之前的方法分为25个组合,即:

在每年5月末,按照市值大小将股票排序并分为5组,然后按照账面市值比大小把股票分为5组,交叉取交集,得到5*5=25个股票组合

也就是说,我们将作25个回归,每次回归时的解释变量都一样,响应变量不同

1
#每年5月末,按照市值大小将股票排序分为5组

2
def get_25groups(breakpoint):

3
    universe = set_universe('000906.ZICN',date=breakpoint)

4
    C=DataAPI.MktEqudGet(ticker='000028',beginDate=str(int(breakpoint)-20),endDate=breakpoint,field=u"ticker,tradedate")

5
    breakpoint=filter(lambda x:x.isdigit(),C.iat[len(C)-1,1])                             #取breakpoint前最近一个交易日日期

6
    ME=DataAPI.MktEqudGet(tradeDate=breakpoint,secID=universe,field=u"ticker,marketValue").dropna()

7
    MEq=np.zeros(6) #用于存储ME的分位值

8
    ME_5=pd.Series([]) #用于存储依据市值分好的5个组

9
    BP=DataAPI.MktStockFactorsOneDayGet(tradeDate=breakpoint,secID=universe,field=u"ticker,PB").dropna() 

10
    BP=BP[BP>0].dropna()                                                  #去掉PB值为负的股票

11
    BP[['PB']]=1/BP[['PB']]                                               #取1/PB,为账面市值比

12
    BPq=np.zeros(6)  #用于存储1/PB的分位值

13
    BP_5=pd.Series([]) #用于存储依据账面市值比分好的5个组

14
    for i in range(5):

15
        MEq[i+1]=np.percentile(ME['marketValue'],(i+1)*20)                           #算出市值大小的20%,40%,60%,80%分位值

16
        BPq[i+1]=np.percentile(BP['PB'],(i+1)*20)

17
        D=ME[(ME['marketValue']>MEq[i]) & (ME['marketValue']<=MEq[i+1])]['ticker'].tolist()     #取市值处于相应分位值区间的股票

18
        ME_5=pd.concat([ME_5,pd.Series(D)],axis=1)                                 #存于dataframe里

19
        E=BP[(BP['PB']>BPq[i]) & (BP['PB']<=BPq[i+1])]['ticker'].tolist()

20
        BP_5=pd.concat([BP_5,pd.Series(E)],axis=1)

21
    ME_5.columns=range(5) #重命名列名

22
    BP_5.columns=range(5)

23
    Group25=pd.Series([]) #用于存著交叉取交集后的25个股票组合

24
    for i in range(5):

25
        for j in range(5):

26
            s1=ME_5[i].dropna().tolist()

27
            s2=BP_5[j].dropna().tolist() 

28
            stocks=pd.Series(list(set(s1).intersection(set(s2)))) #取交集

29
            Group25=pd.concat([Group25,stocks],axis=1)

30
    Group25.columns=range(25)

31
    return Group25

查看全部

计算25个股票组合,每个组合的市值加权月收益率序列

1
EReturn=np.zeros((25,12*9)) #用于存储25个组合的超额收益序列

2
for i in range(25):

3
    a=[]

4
    for Year in [2007,2008,2009,2010,2011,2012,2013,2014,2015]:

5
        Group25=get_25groups(str(Year*10000+531))   #每年进行分组

6
        a=a+(get_returnMonthly(Group25[i].dropna().tolist(),Year)).tolist()   #收益率转化为list,方便每年相加

7
    EReturn[i]=np.array(a)-RfMonthly

查看全部

我们看一下25个组合平均每年的公司数:

1
number=np.zeros((25,9)) 

2
for i in range(25):

3
    for j in range(9):

4
        Group25=get_25groups(str((j+2007)*10000+531))   #每年进行分组

5
        number[i][j]=len(Group25[i].dropna().tolist())

6
number_mean=np.zeros(25)

7
for i in range(25):

8
    number_mean[i]=number[i].mean()

9
numbers=pd.DataFrame(number_mean.reshape(5,5))

10
numbers.columns=['small_BE/ME','1','2','3','big_BE/ME']

11
numbers.index=['small_size','1','2','3','big_size']

12
numbers

查看全部

  small_BE/ME 1 2 3 big_BE/ME
small_size 12.888889 27.888889 35.777778 41.333333 41.888889
1 25.333333 34.666667 36.777778 34.555556 28.666667
2 39.555556 34.222222 34.444444 25.555556 25.555556
3 44.111111 36.666667 27.333333 28.000000 23.888889
big_size 38.111111 26.222222 25.111111 30.222222 40.000000

还可以看一下25个组合平均每年的总市值大小,验证一下分组的正确性:

1
MarketValue=np.zeros((25,9)) 

2
for i in range(25):

3
    for j in range(9):

4
        breakpoint=str((j+2007)*10000+531)

5
        Group25=get_25groups(breakpoint)  #每年进行分组

6
        C=DataAPI.MktEqudGet(ticker='000028',beginDate=str(int(breakpoint)-20),endDate=breakpoint,field=u"ticker,tradedate")

7
        breakpoint=filter(lambda x:x.isdigit(),C.iat[len(C)-1,1])                             #取breakpoint前最近一个交易日日期

8
        data=DataAPI.MktEqudGet(tradeDate=breakpoint,ticker=Group25[i].dropna().tolist(),field=u"ticker,marketValue").dropna()

9
        MarketValue[i][j]=data['marketValue'].sum()

10
MarketValue_mean=np.zeros(25)

11
for i in range(25):

12
    MarketValue_mean[i]=MarketValue[i].mean()

13
MV=pd.DataFrame(MarketValue_mean.reshape(5,5))

14
MV.columns=['small_BE/ME','1','2','3','big_BE/ME']

15
MV.index=['small_size','1','2','3','big_size']

16
MV

查看全部

  small_BE/ME 1 2 3 big_BE/ME
small_size 6.007957e+10 1.175193e+11 1.455794e+11 1.798913e+11 1.646767e+11
1 1.745471e+11 2.368089e+11 2.355071e+11 2.167016e+11 1.717474e+11
2 3.920921e+11 3.309705e+11 3.278397e+11 2.391956e+11 2.118269e+11
3 7.007655e+11 5.851388e+11 4.300714e+11 4.208436e+11 4.202868e+11
big_size 2.054059e+12 2.041996e+12 2.416426e+12 3.877098e+12 7.510095e+12

上面的股票组合从左到右,账面市值比越来越大;从上往下,市值越来越大,说明我们的分组是正确的

看一下25个组合超额收益的均值和方差:

1
EReturn_mean=np.zeros(25)

2
for i in range(25):

3
    EReturn_mean[i]=EReturn[i].mean()

4
mean=pd.DataFrame(EReturn_mean.reshape(5,5))

5
mean.columns=['small_BE/ME','1','2','3','big_BE/ME']

6
mean.index=['small_size','1','2','3','big_size']

7
mean

查看全部

  small_BE/ME 1 2 3 big_BE/ME
small_size 0.025433 0.022799 0.043428 0.022999 0.019819
1 0.019233 0.022419 0.023366 0.022356 0.016953
2 0.016595 0.015579 0.017392 0.017074 0.020958
3 0.019433 0.012199 0.014942 0.013659 0.009995
big_size 0.006133 0.000801 0.006065 0.007580 0.005248
1
EReturn_std=np.zeros(25)

2
for i in range(25):

3
    EReturn_std[i]=EReturn[i].std()

4
std=pd.DataFrame(EReturn_std.reshape(5,5))

5
std.columns=['small_BE/ME','1','2','3','big_BE/ME']

6
std.index=['small_size','1','2','3','big_size']

7
std

查看全部

  small_BE/ME 1 2 3 big_BE/ME
small_size 0.125990 0.111912 0.216930 0.111223 0.109084
1 0.110347 0.113726 0.110196 0.116036 0.111958
2 0.104115 0.105876 0.110242 0.112561 0.110588
3 0.106525 0.106846 0.107491 0.102590 0.110776
big_size 0.097351 0.097236 0.100561 0.102553 0.092128

三.回归和结果

1.回归一

CAPM回归模型:\begin{align}R(t)-R_f(t) = a+b(R_M(t)-R_f(t))+e(t)\end{align}这就是经典的CAPM模型,我们可以检验一下它在中国A股市场的有效性:

1
#作25次回归

2
import numpy as np

3
from sklearn import linear_model

4
a1=np.zeros(25)   #a项

5
b1=np.zeros(25)   #市场因子项系数

6
e1=np.zeros((25,108))   #残差项

7
R2_1=np.zeros(25)   #R2相关系数平方

8
tb1=np.zeros(25)

9
ta1=np.zeros(25)

10
import statsmodels.api as sm

11
from statsmodels.sandbox.regression.predstd import wls_prediction_std

12
ap1=np.zeros(25)  #a显著性检验的P值,下面类同

13
bp1=np.zeros(25)

14
for i in range(25):

15
    X=np.zeros((1,108))

16
    X[0]=MF

17
    X=X.T

18
    X = sm.add_constant(X,has_constant='skip')

19
    y=EReturn[i]

20
    model = sm.OLS(y, X)

21
    results = model.fit()

22
    a1[i] = results.params[0]

23
    b1[i] = results.params[1]

24
    ap1[i]=results.pvalues[0]

25
    bp1[i]=results.pvalues[1]

26
    R2_1[i] = results.rsquared

27
    e1[i] = results.resid

28
    tb1[i] = results.tvalues[1]

29
    ta1[i] = results.tvalues[0]

查看全部

先看一下这25个回归的判定系数R2,它度量了拟合程度的好坏。

1
R2inf1=pd.DataFrame(R2_1.reshape(5,5))

2
R2inf1.columns=['small_BE/ME','1','2','3','big_BE/ME']

3
R2inf1.index=['small_size','1','2','3','big_size']

4
R2inf1

查看全部

  small_BE/ME 1 2 3 big_BE/ME
small_size 0.594353 0.714150 0.350486 0.824708 0.825360
1 0.716048 0.731942 0.791529 0.828343 0.859161
2 0.718364 0.745072 0.884344 0.860049 0.804825
3 0.745227 0.870885 0.906794 0.895085 0.878805
big_size 0.795297 0.894826 0.834521 0.783604 0.786575

25个回归的R2大多处于0.7~0.9之间,已经是比较好的结果了,这点可以和其它的回归模型对比。

下面看一下市场因子的系数β:

1
binf1=pd.DataFrame(b1.reshape(5,5))

2
binf1.columns=['small_BE/ME','1','2','3','big_BE/ME']

3
binf1.index=['small_size','1','2','3','big_size']

4
binf1

查看全部

  small_BE/ME 1 2 3 big_BE/ME
small_size 1.014500 0.987796 1.341376 1.054965 1.035088
1 0.975273 1.016236 1.023992 1.103041 1.083893
2 0.921680 0.954528 1.082809 1.090298 1.036224
3 0.960486 1.041443 1.069108 1.013754 1.084644
big_size 0.906779 0.960705 0.959496 0.948184 0.853405

我们可以看到β大多处于1左右。下面我们来检验其显著性,回归系数的显著性检验用的是t检验:原假设为t=0,若t统计量的值大于给定显著水平下的t分位值,则拒绝原假设,说明该系数显著大于0

1
import scipy.stats as stats

2
t107=stats.t.isf(0.025,106)  #自由度为n-p,显著水平5%下的t分位值

3
t107

查看全部

1.9825972617102912
1
tbinf1=pd.DataFrame(tb1.reshape(5,5))

2
tbinf1.columns=['small_BE/ME','1','2','3','big_BE/ME']

3
tbinf1.index=['small_size','1','2','3','big_size']

4
tbinf1

查看全部

  small_BE/ME 1 2 3 big_BE/ME
small_size 12.462385 16.273421 7.562993 22.331745 22.382173
1 16.349394 17.012826 20.061476 22.616582 25.428921
2 16.443012 17.601218 28.469459 25.522643 20.906988
3 17.608419 26.738941 32.113328 30.072274 27.724048
big_size 20.293403 30.030898 23.120656 19.591878 19.765164

我们可以看到所有回归里β的t统计量的值都大于临界值,我们应该拒绝原假设,即表明β系数显著

以上,说明资本资产定价模型是有效的,市场因子的影响是显著的

2.回归二

如上,我们检验了CAPM模型的有效性,现在我们不妨检验一下另外两个因子对股票超额收益的解释作用。\begin{align}R(t)-R_f(t) = a+sSMB(t)+hHML(t)+e(t)\end{align}SMB和HML分别代表规模(市值)因子和账面市值比因子。

1
#作25次回归

2
import numpy as np

3
from sklearn import linear_model

4
a2=np.zeros(25)   #a项

5
s2=np.zeros(25)   #规模因子项系数

6
h2=np.zeros(25)   #账面价值比项系数

7
e2=np.zeros((25,108))   #残差项

8
R2_2=np.zeros(25)   #R2相关系数平方

9
ta2=np.zeros(25)

10
tb2=np.zeros(25)

11
import statsmodels.api as sm

12
from statsmodels.sandbox.regression.predstd import wls_prediction_std

13
ap2=np.zeros(25)  #a显著性检验的P值,下面类同

14
sp2=np.zeros(25)

15
hp2=np.zeros(25)

16
for i in range(25):

17
    X=np.zeros((2,108))

18
    X[0]=SMB

19
    X[1]=HML

20
    X=X.T

21
    X = sm.add_constant(X,has_constant='skip')

22
    y=EReturn[i]

23
    model = sm.OLS(y, X)

24
    results = model.fit()

25
    ap2[i]=results.pvalues[0]

26
    sp2[i]=results.pvalues[1]

27
    hp2[i]=results.pvalues[2]

28
    a2[i] = results.params[0]

29
    s2[i] = results.params[1]

30
    h2[i] = results.params[2]

31
    R2_2[i] = results.rsquared

32
    e[i] = results.resid

33
    tb2[i] = results.tvalues[1]

34
    ta2[i] = results.tvalues[0]

查看全部

同样,我们看一下25个回归的判定系数R2的情况:

1
R2inf2=pd.DataFrame(R2_2.reshape(5,5))

2
R2inf2.columns=['small_BE/ME','1','2','3','big_BE/ME']

3
R2inf2.index=['small_size','1','2','3','big_size']

4
R2inf2

查看全部

  small_BE/ME 1 2 3 big_BE/ME
small_size 0.429206 0.393804 0.121499 0.298528 0.280941
1 0.373788 0.387774 0.291721 0.291967 0.232686
2 0.398290 0.319392 0.222978 0.207927 0.249022
3 0.242946 0.190158 0.152270 0.161753 0.129395
big_size 0.116128 0.005802 0.004471 0.062431 0.124630

从R2可以看到,基本都在0.5以下,这个回归的结果比回归一差了很多,这个模型并不好,也就是说只用市值因子和账面市值比因子来解释股票超额收益是不合适的

3.回归三

这里的回归模型就是我们经典的三因子模型

\begin{align}R(t)-R_f(t) = a+b(R_M(t)-R_f(t))+sSMB(t)+hHML(t)+e(t)\end{align}

R(t)−Rf(t):市场因子

SMB(t):规模(市值)因子

HML(t) :账面市值比因子

1
#作25次回归

2
import numpy as np

3
from sklearn import linear_model

4
a=np.zeros(25)   #a项

5
b=np.zeros(25)   #市场因子项系数

6
s=np.zeros(25)   #规模因子项系数

7
h=np.zeros(25)   #账面价值比项系数

8
e=np.zeros(25)   #残差项

9
R2=np.zeros(25)   #R2相关系数平方

10
for i in range(25):

11
    x=np.zeros((3,108))

12
    x[0]=MF

13
    x[1]=SMB

14
    x[2]=HML

15
    y=EReturn[i]

16
    x=np.mat(x).T

17
    y=np.mat(y).T

18
    regr = linear_model.LinearRegression()

19
    regr.fit(x,y)

20
    b[i]=regr.coef_[0][0]

21
    s[i]=regr.coef_[0][1]

22
    h[i]=regr.coef_[0][2]

23
    a[i]=regr.intercept_[0]

24
    e[i]=regr.residues_

25
    R2[i]=regr.score(x,y)

查看全部

1
import statsmodels.api as sm

2
from statsmodels.sandbox.regression.predstd import wls_prediction_std

3
ap=np.zeros(25)  #a显著性检验的P值

4
ta=np.zeros(25)

5
tb=np.zeros(25)

6
ts=np.zeros(25)

7
th=np.zeros(25)

8
for i in range(25):

9
    X=np.zeros((3,108))

10
    X[0]=MF

11
    X[1]=SMB

12
    X[2]=HML

13
    X=X.T

14
    X = sm.add_constant(X,has_constant='skip')

15
    y=EReturn[i]

16
    model = sm.OLS(y, X)

17
    results = model.fit()

18
    ap[i]=results.pvalues[0]

19
    ta[i] = results.tvalues[0]

20
    tb[i] = results.tvalues[1]

21
    ts[i] = results.tvalues[2]

22
    th[i] = results.tvalues[3]    

查看全部

我们先看一下回归的R2:

1
R2inf3=pd.DataFrame(R2.reshape(5,5))

2
R2inf3.columns=['small_BE/ME','1','2','3','big_BE/ME']

3
R2inf3.index=['small_size','1','2','3','big_size']

4
R2inf3

查看全部

  small_BE/ME 1 2 3 big_BE/ME
small_size 0.850504 0.932359 0.401501 0.952128 0.942172
1 0.928505 0.947654 0.928649 0.949936 0.943393
2 0.960149 0.924252 0.962277 0.926240 0.903547
3 0.916107 0.945470 0.944791 0.933945 0.924949
big_size 0.932492 0.929273 0.901979 0.933192 0.942885

我们可以看到R2基本上都在0.9以上,三因子模型的拟合程度非常好,说明三因子模型是比CAPM更有效的模型

4.回归结果分析

如上三因子模型的有效性已经得到验证,确实能够解释股票收益来源,那么A股市场对这些因子有什么偏好呢?比如:长期来看,小盘股跑赢大盘股,还是大盘股能跑赢小盘股呢?

我们可以从第一个回归(CAPM)的截距项找到答案:

1
ainf1=pd.DataFrame(a1.reshape(5,5))

2
ainf1.columns=['small_BE/ME','1','2','3','big_BE/ME']

3
ainf1.index=['small_size','1','2','3','big_size']

4
tainf1=pd.DataFrame(ta1.reshape(5,5))

5
tainf1.columns=['small_BE/ME','1','2','3','big_BE/ME']

6
tainf1.index=['small_size','1','2','3','big_size']

7
print '第一个回归的a值:'

8
print ainf1.to_html()

9
print '自由度为n-p,显著水平5%下的t分位值:'+str(stats.t.isf(0.025,106))

10
print '第一个回归的a的t检验统计量:'

11
print tainf1.to_html()

查看全部

第一个回归的a值:
  small_BE/ME 1 2 3 big_BE/ME
small_size 0.024177 0.021577 0.041768 0.021694 0.018538
1 0.018026 0.021162 0.022099 0.020991 0.015612
2 0.015455 0.014398 0.016052 0.015725 0.019676
3 0.018244 0.010911 0.013619 0.012405 0.008653
big_size 0.005011 -0.000388 0.004878 0.006406 0.004192

自由度为n-p,显著水平5%下的t分位值:1.98259726171第一个回归的a的t检验统计量:

  small_BE/ME 1 2 3 big_BE/ME
small_size 3.101816 3.712481 2.459500 4.795915 4.186454
1 3.156042 3.699929 4.521662 4.495019 3.825185
2 2.879519 2.772733 4.407769 3.844395 4.145978
3 3.493139 2.925629 4.272353 3.843262 2.309965
big_size 1.171278 -0.126530 1.227596 1.382485 1.013901

可以看到25个回归里只有最后5个回归的a值是显著为0的,其余a值我们可以认为是不显著为0的,也就是说超额收益没有被完全解释,这也是三因子模型存在的必要。

我们从市值的角度来分析一下,以上结果,从上到下组合的市值越来越大,a值越来越小,a代表的是超额收益,也就是说市值越小的股票越容易获得超额收益,这点和我们的认知相同

从左到右,组合的账面市值比越来越大,a值从趋势上是越来越大,但也有很多反转,也就是说账面市值比越高的组合越容易获得超额收益这个结论并不准确。

四.总结

通过对三个模型的回归进行对比,我们可以看到,从R2来看,拟合结果最差的是第二个模型(只用市值因子和账面市值比),拟合结果最好的是第三个模型,也就是fama三因子模型。

综上,我们验证了Fama三因子模型在中国A股市场是有效的,也印证了市值小的股票更容易获得超额收益这一点。

本帖重在过程,学习交流。

Fama-French 三因子模型在A股市场的实证研究相关推荐

  1. 一文教你看懂Fama-French三因子模型

    Fama-French三因子模型概述 Fama-French三因子模型(Fama-French 3-factor model,简称FF3) Fama和French 1992年对美国股票市场决定不同股票 ...

  2. 数据分析---Fama-French三因子模型

    大家好,今天带给大家一篇金融模型方面的python应用文章,在这篇文章中将会给大家介绍pandas和statsmodels.api,此外还会介绍Fama-French三因子模型的理论知识. 目录 Fa ...

  3. python量化:如何利用tushare构造FF三因子模型?

    Python量化:如何利用tushare构造FF三因子模型? FF三因子模型介绍 代码实现 从tushare调取数据 利用数据构建因子 总结 笔者是一枚大二菜狗,最近刚上完学院开的python金融量化 ...

  4. 全市场等权中位数_A股市场周内效应研究

    引言 >>> 研究目的 本文参考东吴证券研报<A股市场的周内效应>内容,对A股市场的日历效应在周内表现进行探索.日历效应,是一个鱼龙混杂的题目.有诙谐逗笑的,如马克吐温老 ...

  5. fama matlab源码_基于优化算法改造的Fama-French三因子模型

    基于光大证券金融工程研报<站在巨人的肩膀上,从牛基组合到牛股发现 --FOF 专题研究系列之十六 >中提及的Carhart四因子Alpha优化模型,本文在Fama-French三因子模型上 ...

  6. 【量化】通过Fama-French三因子模型选股,收益能达到多少?

    作者:幻好 来源: 恒生LIGHT云社区 Fama-French三因子模型 基本概念 Fama和French 1993年指出可以建立一个三因子模型来解释股票 回报率 .模型认为,一个 投资组合 (包括 ...

  7. 中国版的FamaFrench三因子模型了解一下

    转 中国版的 Fama-French 三因子模型,了解一下? 作者:石川,量信投资创始合伙人,清华大学学士.硕士,麻省理工学院博士:精通各种概率模型和统计方法,擅长不确定性随机系统的建模及优化.(已获 ...

  8. Fama—French五因子模型在中国股票市场适用吗?

    资产定价历来是金融经济学研究的核心问题.自Sharpe,Lintner,Treynor等于1964年提出资本资产定价模型(CAPM)以来,通过单一风险因素确定资产收益率受到理论和市场等多方面的挑战.为 ...

  9. Fama-French 三因子模型介绍、修改与框架搭建

    一.概述 Fama-French三因子模型(以下简称"三因子模型")是法玛和法兰奇在1990年代初提出来的,它认为理想状态下,资产的超额收益由市场收益.规模收益.价值收益三个部分组 ...

最新文章

  1. 今年你的双11包裹,也是自动驾驶卡车送来的吗?
  2. HTML DOM Event 对象
  3. jpa避免n+1_JPA技巧:避免N + 1选择问题
  4. unknown chromium error 400
  5. tornado celery mysql_tornado中使用celery实现异步MySQL操作
  6. treeview 如何从多个数据表中获取数据动态生成 [提问]
  7. rust-vmm 学习
  8. android webview 跳转到浏览器,Android WebView或手机浏览器打开连接问题解决办法总结...
  9. 教你如何卷积操作进行边缘检测,基础必备
  10. CodeForces 1556C :Compressed Bracket Sequences 思维
  11. 微型计算机硬盘接口种类,各种类型的固态硬盘接口形态分类
  12. 在matlab上利用fft进行信号频谱分析_使用示波器进行信号频谱分析(FFT)的设置教学...
  13. 公历农历显示节日节气星期等万年历
  14. L2-001 城市间紧急救援
  15. Excel VBA(04)数组和字典
  16. 怎么学习Android
  17. websocket连接不成功的原因
  18. 简单解析 兼顾高效与安全的ConcyrrentHashmap
  19. Ubuntu下VS code空格间距很小解决办法
  20. 一款数据库合并工具的中文版更新

热门文章

  1. Docker网络测试;集群搭建;数据卷使用
  2. Android Studio虚拟机手机连接显示
  3. 如何用爬虫爬取精美的图片!
  4. 我用ChatGPT写2023年新课标II卷高考作文
  5. CBOW中关于 NLLLoss 的含义
  6. Python实现极限学习机ELM【hpelm库】(内涵源代码)
  7. 第三章继承与多态3.2.3
  8. 【LeetCode】Maximize Sum Of Array After K Negations(K 次取反后最大化的数组和)
  9. 搭建嵌入式双系统启动
  10. Kaldi学习笔记——The Kaldi Speech Recognition Toolkit(Kaldi语音识别工具箱)(上)