手把手教你實現法瑪三因子模型

數量技術宅發表於2023-09-26

更多精彩內容,歡迎關注公眾號:數量技術宅,也可新增技術宅個人微訊號:sljsz01,與我交流。

關於法瑪三因子模型

法瑪三因子模型(Fama-French Three-Factor Model)是一種資本資產定價模型(Capital Asset Pricing Model,CAPM)的擴充套件,用於解釋股票回報的變異性。該模型由尤金·法瑪(Eugene Fama)和肯尼斯·法rench(Kenneth French)於1992年提出。

該模型考慮了三個因子對股票回報的影響:市場風險因子、市值因子和價值因子。市場風險因子指的是股票市場整體的風險,可以用市場指數來代表。市值因子是指公司的市值大小,可以用市值指數來代表。價值因子是指公司的估值水平,可以用賬面市值比(B/M)來代表。這三個因子被認為是影響股票回報的重要因素。

法瑪三因子模型認為,股票的預期回報可以透過以下公式計算:

E(Ri) = Rf + βi(Market Risk Premium) + si(SMB) + hi(HML)

其中,E(Ri)表示股票i的預期回報,Rf表示無風險收益率,βi表示股票i的市場風險係數,Market Risk Premium表示市場風險因子的回報率減去無風險收益率,si表示股票i的市值因子係數,SMB表示市值因子的超額回報,hi表示股票i的價值因子係數,HML表示價值因子的超額回報。

透過考慮市值和價值因子,法瑪三因子模型可以更好地解釋股票回報的變異性。該模型被廣泛應用於投資組合管理、風險管理和資產定價等領域。

Python程式碼實現

由於上述模型需要使用股票的市值和賬面市值比等資料,我們需要先獲取這些資料。以下是一種獲取中國股票市值和賬面市值比資料的方法:

import pandas as pd
import tushare as ts

# 獲取股票列表
stock_list = ts.get_stock_basics().index.tolist()

# 獲取股票市值資料
market_cap = ts.get_stock_basics().loc[:, 'totalAssets']

# 獲取股票賬面市值比資料
book_to_market = ts.get_stock_basics().loc[:, 'bvps'] / ts.get_stock_basics().loc[:, 'pb']

# 將市值和賬面市值比資料合併到一個DataFrame中
data = pd.concat([market_cap, book_to_market], axis=1)
data.columns = ['market_cap', 'book_to_market']
data.index.name = 'code'
data = data.dropna()

接下來,我們可以使用以上獲取的市值和賬面市值比資料,以及股票收益率資料,來實現法瑪三因子模型的計算。以下是一個簡單的實現:

import numpy as np
import statsmodels.api as sm

# 獲取股票收益率資料
start_date = '2020-01-01'
end_date = '2020-12-31'
return_data = pd.DataFrame()
for code in stock_list:
    try:
        stock_return = ts.pro_bar(ts_code=code, start_date=start_date, end_date=end_date, adj='qfq', 
                                   factors=['tor']).set_index('trade_date')['pct_chg']
        return_data[code] = stock_return
    except:
        pass

# 計算市場指數收益率
market_return = ts.pro_bar(ts_code='000001.SH', start_date=start_date, end_date=end_date, 
                           adj='qfq', factors=['tor']).set_index('trade_date')['pct_chg']
market_return.name = 'market_return'

# 將股票收益率和市場指數收益率合併到一個DataFrame中
data = pd.concat([return_data, market_return], axis=1)
data = data.dropna()

# 計算超額收益率
data = data.sub(data['market_return'], axis=0)

# 將市值和賬面市值比資料合併到一個DataFrame中
data = pd.concat([data, market_cap, book_to_market], axis=1)
data.columns = stock_list + ['market_return', 'market_cap', 'book_to_market']

# 計算因子收益率
factor_data = pd.DataFrame()
factor_data['market_factor'] = market_return - ts.pro_bar(ts_code='000016.SH', start_date=start_date, 
                                                          end_date=end_date, adj='qfq', 
                                                          factors=['tor']).set_index('trade_date')['pct_chg']
factor_data['size_factor'] = sm.OLS(np.array(data.mean()), sm.add_constant(np.log(data['market_cap']))).fit().resid
factor_data['value_factor'] = sm.OLS(np.array(data.mean()), sm.add_constant(np.log(data['book_to_market']))).fit().resid

# 計演算法瑪三因子模型的引數
X = sm.add_constant(factor_data)
model = sm.OLS(np.array(data.mean()), X)
results = model.fit()
print(results.summary())

  

以上程式碼中,我們使用了tushare庫獲取股票資料,並使用statsmodels庫進行迴歸分析。具體來說,我們首先獲取了股票收益率、市場指數收益率、股票市值和賬面市值比等資料,然後計算了超額收益率,並將這些資料合併到一個DataFrame中。接著,我們計算了市場因子、規模因子和價值因子的收益率,並利用OLS迴歸分析計算了法瑪三因子模型的引數。

需要注意的是,以上程式碼只是一個簡單的示例,實際應用中還需要考慮很多其他因素,如資料處理、缺失值處理、資料標準化等。此外,法瑪三因子模型也有其侷限性,如不能很好地解釋一些股票市場現象等。因此,在實際應用中需要根據具體情況進行調整和改進。

因子有效性檢驗

當使用法瑪三因子模型時,我們需要確認市場因子、規模因子和價值因子是否有效。以下是幾種在Python中進行因子有效性檢驗的方法:

1 因子收益率的t檢驗

首先,我們可以使用t檢驗來檢驗市場因子、規模因子和價值因子的收益率是否顯著不為零。我們可以透過計算每個因子收益率的t統計量,並檢驗其顯著性水平,來判斷該因子是否有效。以下是一個簡單的示例:

import pandas as pd
import numpy as np
import statsmodels.api as sm

# 獲取股票收益率資料
start_date = '2020-01-01'
end_date = '2020-12-31'
return_data = pd.DataFrame()
for code in stock_list:
    try:
        stock_return = ts.pro_bar(ts_code=code, start_date=start_date, end_date=end_date, adj='qfq', 
                                   factors=['tor']).set_index('trade_date')['pct_chg']
        return_data[code] = stock_return
    except:
        pass

# 計算市場指數收益率
market_return = ts.pro_bar(ts_code='000001.SH', start_date=start_date, end_date=end_date, 
                           adj='qfq', factors=['tor']).set_index('trade_date')['pct_chg']
market_return.name = 'market_return'

# 將股票收益率和市場指數收益率合併到一個DataFrame中
data = pd.concat([return_data, market_return], axis=1)
data = data.dropna()

# 計算超額收益率
data = data.sub(data['market_return'], axis=0)

# 將市值和賬面市值比資料合併到一個DataFrame中
data = pd.concat([data, market_cap, book_to_market], axis=1)
data.columns = stock_list + ['market_return', 'market_cap', 'book_to_market']

# 計算因子收益率
factor_data = pd.DataFrame()
factor_data['market_factor'] = market_return - ts.pro_bar(ts_code='000016.SH', start_date=start_date, 
                                                          end_date=end_date, adj='qfq', 
                                                          factors=['tor']).set_index('trade_date')['pct_chg']
factor_data['size_factor'] = sm.OLS(np.array(data.mean()), sm.add_constant(np.log(data['market_cap']))).fit().resid
factor_data['value_factor'] = sm.OLS(np.array(data.mean()), sm.add_constant(np.log(data['book_to_market']))).fit().resid

# 計算t檢驗的p值
ttest = sm.stats.ttest_ind(factor_data, np.zeros(factor_data.shape), axis=0)

# 列印檢驗結果
print(ttest)

  

以上程式碼中,我們首先計算了每個因子的收益率,然後使用OLS迴歸分析計算了每個因子的殘差,即每個因子收益率中的alpha值。接著,我們計算了每個因子收益率的t統計量和p值,並輸出檢驗結果。

2 因子迴歸的 $R^2$ 值

除了t檢驗之外,我們還可以使用因子迴歸的 $R^2$ 值來評估市場因子、規模因子和價值因子的有效性。$R^2$ 值表示模型中因子收益率對總收益率的解釋程度,值越高則說明因子對總收益率的解釋程度越大。以下是一個簡單的示例:

import pandas as pd
import numpy as np
import statsmodels.api as sm

# 獲取股票收益率資料
start_date = '2020-01-01'
end_date = '2020-12-31'
return_data = pd.DataFrame()
for code in stock_list:
    try:
        stock_return = ts.pro_bar(ts_code=code, start_date=start_date, end_date=end_date, adj='qfq', 
                                   factors=['tor']).set_index('trade_date')['pct_chg']
        return_data[code] = stock_return
    except:
        pass

# 計算市場指數收益率
market_return = ts.pro_bar(ts_code='000001.SH', start_date=start_date, end_date=end_date, 
                           adj='qfq', factors=['tor']).set_index('trade_date')['pct_chg']
market_return.name = 'market_return'

# 將股票收益率和市場指數收益率合併到一個DataFrame中
data = pd.concat([return_data, market_return], axis=1)
data = data.dropna()

# 計算超額收益率
data = data.sub(data['market_return'], axis=0)

# 將市值和賬面市值比資料合併到一個DataFrame中
data = pd.concat([data, market_cap, book_to_market], axis=1)
data.columns = stock_list + ['market_return', 'market_cap', 'book_to_market']

# 計算因子收益率
factor_data = pd.DataFrame()
factor_data['market_factor'] = market_return - ts.pro_bar(ts_code='000016.SH', start_date=start_date, 
                                                          end_date=end_date, adj='qfq', 
                                                          factors=['tor']).set_index('trade_date')['pct_chg']
factor_data['size_factor'] = sm.OLS(np.array(data.mean()), sm.add_constant(np.log(data['market_cap']))).fit().resid
factor_data['value_factor'] = sm.OLS(np.array(data.mean()), sm.add_constant(np.log(data['book_to_market']))).fit().resid

# 計算因子迴歸的R2值
y = data.mean()
X = sm.add_constant(factor_data)
model = sm.OLS(y, X).fit()
rsquared = model.rsquared

# 列印檢驗結果
print(rsquared)

以上程式碼中,我們首先計算了每個因子的收益率,然後使用OLS迴歸分析計算了每個因子的殘差,即每個因子收益率中的alpha值。接著,我們將股票超額收益率和因子收益率合併到一個DataFrame中,然後計算因子迴歸的 $R^2$ 值。最後,我們輸出了檢驗結果。

3 因子相關性分析

除了單獨檢驗每個因子的有效性之外,我們還可以使用因子相關性分析來評估每個因子對投資組合表現的貢獻程度。這裡我們使用熱圖來顯示每個因子之間的相關性。以下是一個簡單的示例:

import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt

# 獲取股票收益率資料
start_date = '2020-01-01'
end_date = '2020-12-31'
return_data = pd.DataFrame()
for code in stock_list:
    try:
        stock_return = ts.pro_bar(ts_code=code, start_date=start_date, end_date=end_date, adj='qfq', 
                                   factors=['tor']).set_index('trade_date')['pct_chg']
        return_data[code] = stock_return
    except:
        pass

# 計算市場指數收益率
market_return = ts.pro_bar(ts_code='000001.SH', start_date=start_date, end_date=end_date, 
                           adj='qfq', factors=['tor']).set_index('trade_date')['pct_chg']
market_return.name = 'market_return'

# 將股票收益率和市場指數收益率合併到一個DataFrame中
data = pd.concat([return_data, market_return], axis=1)
data = data.dropna()

# 計算超額收益率
data = data.sub(data['market_return'], axis=0)

# 將市值和賬面市值比資料合併到一個DataFrame中
data = pd.concat([data, market_cap, book_to_market], axis=1)
data.columns = stock_list + ['market_return', 'market_cap', 'book_to_market']

# 計算因子收益率
factor_data = pd.DataFrame()
factor_data['market_factor'] = market_return - ts.pro_bar(ts_code='000016.SH', start_date=start_date, 
                                                          end_date=end_date, adj='qfq', 
                                                          factors=['tor']).set_index('trade_date')['pct_chg']
factor_data['size_factor'] = sm.OLS(np.array(data.mean()), sm.add_constant(np.log(data['market_cap']))).fit().resid
factor_data['value_factor'] = sm.OLS(np.array(data.mean()), sm.add_constant(np.log(data['book_to_market']))).fit().resid

# 計算因子相關性
corr_matrix = factor_data.corr()

# 繪製熱圖
sns.heatmap(corr_matrix, annot=True, cmap='RdYlBu')
plt.show()

  

以上程式碼中,我們首先計算了每個因子的收益率和超額收益率,然後將這些資料合併到一個DataFrame中。接著,我們使用OLS迴歸分析計算了每個因子的殘差,即每個因子收益率中的alpha值。然後,我們計算了每個因子之間的相關性,並使用熱圖視覺化了結果。

法瑪三因子模型的優缺點

法瑪三因子模型的優點:

  1. 解釋力強:相較於CAPM模型,法瑪三因子模型的解釋力更強,能夠更好地解釋股票收益率的變化。

  2. 考慮了多個因素:相較於單因子模型,法瑪三因子模型考慮了市場因子、規模因子和價值因子,更全面地考慮了股票收益率的影響因素。

  3. 可解釋性強:法瑪三因子模型中的三個因子,即市場因子、規模因子和價值因子,都是經濟學上有實際意義的因素,因此其結果更容易被解釋。

法瑪三因子模型的缺點:

  1. 忽略了其他因素:法瑪三因子模型只考慮了市場因子、規模因子和價值因子,忽略了其他可能對股票收益率有影響的因素,如流動性、動量等。

  2. 樣本限制:法瑪三因子模型的樣本通常是歷史股票資料,而歷史表現並不能保證未來表現,因此其預測能力有限。

  3. 可能存在共線性問題:法瑪三因子模型中的因子可能存在共線性問題,導致其解釋能力下降。

  4. 不適用於所有市場:法瑪三因子模型的適用範圍有限,可能無法適用於所有市場。例如,一些新興市場可能存在不同的因子影響股票收益率,無法使用法瑪三因子模型來解釋其表現。

綜上所述,法瑪三因子模型雖然具有一定的侷限性,但在投資組合管理和股票選擇方面仍然具有一定的實用性和可靠性。

如何改進提升

雖然法瑪三因子模型具有較好的解釋股票收益率的能力,但是其仍然存在一些缺點和侷限性。下面是一些改進和提升三因子模型的方法:

1 新增其他因子:法瑪三因子模型只考慮了市場因子、規模因子和價值因子,可以新增其他因子,如動量、流動性等,來提升模型的解釋能力。可以使用pyfolio庫中的get_factor_returns函式獲取更多的因子資料,例如動量因子和波動率因子:

import pyfolio as pf

start_date = '2015-01-01'
end_date = '2021-12-31'
tickers = ['AAPL', 'MSFT', 'AMZN', 'GOOG', 'FB']
factor_names = ['market_beta', 'size_factor', 'value_factor', 'momentum_factor', 'volatility_factor']

factor_data = pf.utils.get_factor_returns(factor_names, start_date=start_date, end_date=end_date)

2 考慮時間變化:股票市場中因子的影響可能會隨著時間變化而變化,可以建立時間變化的因子模型,或者採用滾動迴歸來考慮時間變化對因子的影響。 可以使用rolling函式進行滾動迴歸,並將時間視窗設定為1年或更長時間:

import pandas as pd
import statsmodels.api as sm

rolling_window = 252
factor_data_rolling = pd.DataFrame(index=factor_data.index)
for factor_name in factor_names:
    factor_data_rolling[factor_name] = factor_data[factor_name].rolling(window=rolling_window).apply(lambda x: sm.OLS(x, sm.add_constant(factor_data[['market_beta', 'size_factor', 'value_factor']]).loc[x.index]).fit().params)

factor_data_rolling = factor_data_rolling.dropna()

3 考慮非線性關係:股票收益率和因子之間可能存在非線性關係,可以使用非線性迴歸模型來建立因子模型,或者使用機器學習方法來建立預測模型。 可以使用scikit-learn庫中的多項式迴歸模型來建立非線性關係的因子模型:

from sklearn.linear_model import LinearRegression
from sklearn.preprocessing import PolynomialFeatures

poly_degree = 2
poly_features = PolynomialFeatures(poly_degree, include_bias=False)
X_poly = poly_features.fit_transform(factor_data[['market_beta', 'size_factor', 'value_factor']])
model = LinearRegression().fit(X_poly, factor_data['returns'])

  或者使用scikit-learn庫中的機器學習模型來建立因子模型,例如隨機森林模型:

from sklearn.ensemble import RandomForestRegressor

model = RandomForestRegressor(n_estimators=100, random_state=0)
model.fit(factor_data[['market_beta', 'size_factor', 'value_factor']], factor_data['returns'])

4 解決共線性問題:法瑪三因子模型中的因子可能存在共線性問題,可以使用主成分分析等方法來減少因子之間的共線性,提高模型的解釋能力。 可以使用scikit-learn庫中的主成分分析模型來減少因子之間的共線性:

from sklearn.decomposition import PCA

n_components = 3
pca = PCA(n_components=n_components)
X_pca = pca.fit_transform(factor_data[['market_beta', 'size_factor', 'value_factor']])

5 使用更多資料:使用更多的資料來建立因子模型,可以提高模型的預測能力和穩健性。 可以使用Quandl等資料來源獲取更多的歷史資料來建立因子模型:

import quandl

quandl.ApiConfig.api_key = 'your_api_key'
data = quandl.get_table('SHARADAR/SF1', ticker=tickers, dimension='MRY', qopts={'columns': ['ticker', 'date', 'marketcap', 'roe', 'pb']})
data = data.pivot(index='date', columns='ticker')
data.columns = [f"{col[0]}_{col[1]}" for col in data.columns]
data = data.dropna()

6 考慮國別和行業因素:股票收益率受到不同國別和行業因素的影響,可以建立考慮國別和行業因素的多因子模型,來提高模型的解釋能力。 可以使用pyfolio庫中的get_industry_returns函式獲取行業因子資料,並使用alpha_vantage等資料來源獲取國別因子資料:

import alpha_vantage
from alpha_vantage.timeseries import TimeSeries
import pyfolio as pf

# 使用alpha_vantage獲取國別因子資料
ts = TimeSeries(key='YOUR_API_KEY', output_format='pandas')
data, meta_data = ts.get_daily(symbol='SPY', outputsize='full')
data.columns = ['open', 'high', 'low', 'close', 'volume']
data = data[['close']]
data = data.pct_change().dropna()
data.columns = ['market_factor']

# 使用pyfolio獲取行業因子資料
industry_data = pf.utils.get_industry_returns('morningstar', 'usa')
industry_data.columns = ['industry_factor']

# 合併國別和行業因子資料
factor_data = pd.concat([data, industry_data], axis=1).dropna()
factor_data = factor_data.resample('M').last()

# 運用法瑪三因子模型進行分析
...

7 考慮投資組合構建方法:可以使用最佳化模型來構建投資組合,例如使用CVXPY庫中的最佳化模型:

pythonCopy codeimport cvxpy as cp

weights = cp.Variable(3)
constraints = [cp.sum(weights) == 1, weights >= 0]
expected_return = factor_data_rolling['returns'].mean()
cov_matrix = factor_data_rolling[['market_beta', 'size_factor', 'value_factor']].cov()
risk = cp.quad_form(weights, cov_matrix)
objective = cp.Minimize(risk - 0.5 * gamma * expected_return * cp.quad_form(weights, cov_matrix))
problem = cp.Problem(objective, constraints)
problem.solve()

 

相關文章