Fama-French三因子模型理論知識
模型介紹
Fama和French 1992年對美國股票市場決定不同股票回報率差異的因素的研究發現,股票的市場的beta值不能解釋不同股票回報率的差異,而上市公司的市值、賬面市值比、市盈率可以解釋股票回報率的差異。Fama and French認為,上述超額收益是對CAPM 中β未能反映的風險因素的補償。
模型表達式為:
其中Rit代表資產收益率,rf代表無風險收益率;Rit-rf為超額市場收益率;SMBt代表市值規模因子,HMLt代表賬面市值比因子,β1i、β2i、β3i分別為Rit-rf、SMBt、HMLt的系數,εit為殘差項,αi為截距項。
三因子構建方法
首先先根據上市公司的市值,按照其大小值進行排序並分為兩組,記為S、B,S、B分別表示為小市場規模股和大市場規模股。然后再根據年末上市公司賬面市值比,按照33%、33%、33%的比例排序,記為L、M、H,L、M、H分別為低價值、中等價值、高價值;最后即可得到股票交叉組合,並通過加權平均(以總市值為權重)計算它們的月收益率{SL,SM,SH,BL,BM,BH}。
通過以上6個組合的月收益率數據即可構造出市值規模因子(SMB),具體計算公式如下:
通過該方法得到的市值規模因子體現出市值規模小的投資組合與市值規模大的投資組合之間的收益率差異,剔除賬面市值比因素所造成的影響。
同理,得到賬面市值比因子也保證了解釋變量只考慮賬面市值比所產生的影響,反映賬面市值比高的投資組合與賬面市值比低的投資組合之間的收益率差異。
理論假設
在探討Fama—French三因子模型的應用時,是以“有限理性”理論假設為基礎。並在此基礎上得出若干基本假定: (1)存在着大量投資者; (2)所有投資者都在同一證券持有期計划自己的投資資產組合; (3)投資者投資范圍僅限於公開金融市場上交易的資產; (4)不存在證券交易費用(佣金和服務費用等)及稅賦; (5)投資者們對於證券回報率的均值、方差及協方差具有相同的期望值; (6)所有投資者對證券的評價和經濟局勢的看法都一致。
統計假設
從模型的表達式可以看出,FF模型屬於多元回歸模型。其基本假設為: (1)(Rm − Rf)、SMB、HML與隨機誤差項u不相關; (2)零均值假定:E(εi)=0; (3)同方差假定,即 的方差為一常量:Var(εi)=S^2; (4)無自相關假定:cov(εi,εj)=0,i≠j; (5)解釋變量之間不存在線性相關關系。即兩個解釋變量之間無確切的線性關系; (6)假定隨機誤差項 服從均值為零,方差為S2正態分布,即εi~N(0,S^2)。
Python實現
本次案例數據來自銳思數據庫,選取2019年創業板所有股票作為樣本。
第一部分:導入數據
import pandas as pd data = pd.read_csv('F:\\python\\【案例】數據分析)
第二部分:計算三因子
首先,我們需要計算每一天的三因子,在此之前我們就需要提取每個交易日日期。 讀取數據中的‘date’列,並去重:
date = data['date'] date.drop_duplicates(keep='first',inplace=True)
參數說明: data.drop_duplicates(subset=['A','B'],keep='first',inplace=True) 1、代碼中subset對應的值是列名,表示只考慮這兩列,將這兩列對應值相同的行進行去重。默認值為subset=None表示考慮所有列; 2、keep='first'表示保留第一次出現的重復行,是默認值。keep另外兩個取值為"last"和False,分別表示保留最后一次出現的重復行和去除所有重復行; 3、inplace=True表示直接在原來的DataFrame上刪除重復項,而默認值False表示生成一個副本。
接下來我們利用循環語句依次提取每一天中所有股票數據,並進行計算(這里以‘2019/1/3’為例,循環代碼就不在此展示,可在完整代碼中查看) 提取‘2019/1/3’的數據,即data1:
data = data.reset_index(drop=True) data1 = data[data['date'] == date[1]]
說明: 在循環語句中.reset_index(drop=True) 這句代碼,必須要加drop=True,否則會報錯。因為在.reset_index()重置索引時,會插入列'index'或'level_0',如果它們之一/(兩者都已經被占用),那么它會報錯。所以需要使用“drop”選項,這將刪除具有相同名稱的現有索引,並使用新的重置索引替換它。 疑問: 這兩句代碼中,如果沒有‘data = data.reset_index(drop=True)’這句代碼,那么第二句代碼便會報錯,並不知道具體理由,還望大神指點!
緊接着,我們依次按照市值和賬面市值比進行排序,先按照市值分為大小兩組,再在每組中按照賬面市值比分為高中低三組。 按市值排序,即data2:
data2 = data1.sort_values('market value')
參數說明: sort_values(by=’’,axis=0,ascending=False)#排序 1、by是指定需要排序的列的標簽名; 2、axis為需要排序的軸,0代表行,1代表列; 3、ascending表示排序方法,True表示升序,False表示降序。
按市值分組,並按賬面市值比進行升序排序:
data2_big = data2[0:round(len(data2)/2)].sort_values(by='BM',axis=0,ascending=True) data2_small = data2[round(len(data2)/2):len(data2)].sort_values(by='BM',axis=0,ascending=True)
按賬面市值比分組:
data2_big_low = data2_big[0:round(len(data2_big)/6)] data2_big_medium = data2_big[round(len(data2_big)/6):round(len(data2_big)/3)] data2_big_high = data2_big[round(len(data2_big)/3):len(data2_big)] data2_small_low = data2_small[0:round(len(data2_small)/6)] data2_small_medium = data2_small[round(len(data2_small)/6):round(len(data2_small)/3)] data2_small_high = data2_small[round(len(data2_small)/3):len(data2_small)]
計算三因子:
R_market = sum(data1['yields']*data1['market value'])/sum(data1['market value']) SL = sum(data2_small_low['yields']*data2_small_low['market value'])/sum(data2_small_low['market value']) SM = sum(data2_small_medium['yields']*data2_small_medium['market value'])/sum(data2_small_medium['market value']) SH = sum(data2_small_high['yields']*data2_small_high['market value'])/sum(data2_small_high['market value']) BL = sum(data2_big_low['yields']*data2_big_low['market value'])/sum(data2_big_low['market value']) BM = sum(data2_big_medium['yields']*data2_big_medium['market value'])/sum(data2_big_medium['market value']) BH = sum(data2_big_high['yields']*data2_big_high['market value'])/sum(data2_big_high['market value']) SMB = (SL+SM+SH)/3-(BL+BM+BH)/3 HML = (BH+SH)/2-(BL+SL)/2
最后我們創建一個用於存放三因子的數組‘TF’,將計算出來的每日三因子存放進去:
TF = pd.DataFrame(columns=['date','R_market','SMB','HML']) new = pd.DataFrame([[date[1],R_market,SMB,HML]],columns=['date','R_market','SMB','HML']) TF = TF.append(new,ignore_index=True)
疑問: 看過我之前文章的小伙伴一定發現,在之前文章中使用.append()並沒有賦值,但在此次案例中,如果沒有賦值將會出錯,這一點仍存有疑惑。
將上述步驟用循環語句整合在一起便可計算出每一天的三因子,在這里就不進行代碼展示,大家可在完整代碼中查看。
我們根據Fama-French三因子模型,整理出我們所需的自變量和因變量。 整理回歸數據:
TF_hg = pd.DataFrame(columns=['date','R_market','SMB','HML']) for code_date in code['date']: choose = TF[TF['date']==code_date] TF_hg = TF_hg.append(choose,ignore_index=True) TF_hg['return']=code['yields']-code['risk-free rate']
緊接着,我們就可以導入所需的庫進行回歸了:
import numpy as np import statsmodels.api as sm
最后,我們分別將自變量和因變量賦值給x,y后,進行建模:
x = np.column_stack((TF_hg['R_market'], TF_hg['SMB'], TF_hg['HML'])) y = TF_hg['return'] X = sm.add_constant(x) model = sm.OLS(y, X).fit() print(model.summary())
說明: 1、在.OLS()的參數中,數據的類型要求為‘numpy.ndarray’,所以需要加載numpy庫,我們之前的數組都是‘DataFrame’,無法直接使用; 2、.fit()是將模型中所有參數進行整理,如果不加這句,summary()、params()便無法調用。
最后再給大家介紹兩個常用函數——summary()、params(),以便提取所需內容。 summary()是概括模型信息,上圖以進行展示; params()是自變量系數:
model.params['x1']