Otama's Playground

AIで遊んだ結果などをつらつら載せていきます。

Fama-French 3ファクターモデルの基本とPython実装

以前に平均分散最適化、リスクパリティポートフォリオなど紹介しましたが、それ以外にもリスクとリターンのバランスを考えるための理論やモデルが数多く存在します。中でも、Fama-French 3ファクターモデルはそのシンプルさと実用性から、多くの投資家やアナリストに支持されています。本記事では、Fama-French 3ファクターモデルの概要、メリットとデメリット、計算方法について詳しく解説します。Pythonのコードと実際に計算した結果を載せているので、余裕がある方は実際に動かして試してみてください。

1. Fama-French 3ファクターモデルの概要

Fama-French 3ファクターモデルは、株式リターンを説明するための多因子モデルです。このモデルは、市場リスクに加えて、企業の規模(サイズ)と価値(バリュー)の要因を考慮することで、リターンの変動をより詳細に分析します。具体的には、以下の3つの要因に基づいています

  • 市場リスクプレミアム
    • 市場全体のリスクに対するリターン。
  • サイズファクター(SMB: Small Minus Big)
    • 小型株のリターンが大型株を上回る傾向。
  • バリューファクター(HML: High Minus Low)
    • 低価格株(バリュー株)が高価格株(グロース株)を上回る傾向。

このモデルの目的は、株式のリターンをこれらの要因で説明し、リスクをより精確に評価することです。結果として、投資家はリスクとリターンのバランスを取ったポートフォリオを構築することができます。

以下がこのモデルを使用するメリットとデメリットです。

メリット

  • 多因子アプローチ
    • 市場リスクだけでなく、サイズとバリューの要因も考慮することで、リターンの変動要因をより詳細に説明できる。
  • 実証研究での支持
    • 多くの実証研究で、その説明力と有効性が確認されている。
  • ポートフォリオ管理の向上
    • ポートフォリオのリスクとリターンをより精確に分析し、管理できる。

デメリット

  • 複雑性の増加
    • CAPM(資本資産価格モデル)に比べ、因子が増えるため理解と計算がやや複雑。
  • 時代や市場による変動
    • 因子の影響度は時代や市場状況によって変わるため、定期的な再評価が必要。
  • 他の因子の無視
    • モメンタムなどの他の重要な因子を考慮していないため、リターンの全てを説明できない場合がある。

2. Fama-French 3ファクターモデルの計算方法

Fama-French 3ファクターモデルは、以下の数式で表されます。2項目以降はファクター毎の計算に分かれており、それぞれにベータ(どれくらいそのファクターがリターンに影響するか)をかけて足し合わせる形になっています。

あと見ていただければわかると思いますが、2項目はCAPMの式と同等です。ベースの考え方としてはCAPMで、サイズファクターとバリューファクターから考えられるリターンを同様の考え方で式に追加したのがFama-Frenchの3ファクターモデルといえます。

 
R_t - R_f = \alpha + \beta_{i}^{MKT} (R_m - R_f) + \beta_i^{SMB} \text{SMB} + \beta_i^{HML} \text{HML} + \epsilon_t


 
\begin{align*}
R_t &: \text{個別株またはポートフォリオのリターン} \\
R_f &: \text{無リスク金利} \\
\alpha &: \text{アルファ} \\
\beta_{i}^{MKT} &: \text{市場ポートフォリオのリスクプレミアムに対するベータ} \\
\beta_{i}^{SMB} &: \text{サイズファクターに対するベータ} \\
\beta_{i}^{MKT} &: \text{バリューファクターに対するベータ} \\
R_m &: \text{マーケットポートフォリオのリターン} \\
\text{SMB} &: \text{サイズファクター(小型株のリターンが大型株を上回るリターン)} \\
\text{HML} &: \text{バリューファクター(バリュー株のリターンがグロース株を上回るリターン)} \\
\epsilon_t &: \text{残差項}
\end{align*}

SMBとHMLの計算方法

下のノートがわかりやすかったので貼っておきます。わかっても計算する気力は出ないですが...

glossary.mizuho-sc.com

SMBとHMLはマーケット全体で同じ値を用いることになります。なのでウェブ上で探せば落ちてたりします。

有名なのは下記のリンク先です。各ファクターのリターンを取得することができます。 https://mba.tuck.dartmouth.edu/pages/faculty/ken.french/data_library.html

ベータの計算方法

ベータは過去のリターンデータと取得したSMB、HMBを用いて回帰問題として解くことになります。株やポートフォリオ毎で固有なので、異なる資産で同じ値を使用しないように注意してください。

3. Pythonで計算する

コードを書いてみます。(月次のデータで計算)

コード

>コード(折り畳み)

# 参考: https://sec-api.io/resources/fama-french-factor-model
import pandas as pd
import matplotlib.pyplot as plt
import matplotlib.style as style
import seaborn as sns
import yfinance as yf
import zipfile
import urllib.request
import statsmodels.api as sm

######################
# prepare matplotlib #
######################
style.use("default")

params = {
    "axes.labelsize": 8, "font.size": 8, "legend.fontsize": 8,
    "xtick.labelsize": 8, "ytick.labelsize": 8, "text.usetex": False,
    "font.family": "sans-serif", "axes.spines.top": False, "axes.spines.right": False,
    "grid.color": "grey", "axes.grid": True,  "grid.alpha": 0.5, "grid.linestyle": ":",
}

plt.rcParams.update(params)

#################################################
# Collect Monthly Returns of Portfolio or Asset #
#################################################
ticker = 'QQQ'
start_date = '2018-01-01'
end_date = '2023-01-01'

data = yf.download(ticker, start=start_date, end=end_date)
data['Adj Close'].plot(title="Daily Adjusted Close", figsize=(5, 3))
plt.savefig("test.png")

#############################
# calculate monthly returns #
#############################
data_resampled = data.resample("M").ffill()
data_resampled.index = data_resampled.index.to_period("M")
data_resampled["Return"] = data_resampled['Adj Close'].pct_change() * 100
data_resampled.dropna(inplace=True)

##################################
# Load Fama-French 3-factor data #
##################################
ff3_url = 'https://mba.tuck.dartmouth.edu/pages/faculty/ken.french/ftp/F-F_Research_Data_Factors_CSV.zip'
urllib.request.urlretrieve(ff3_url,'ff3.zip')
with zipfile.ZipFile('ff3.zip', 'r') as z:
    ff_csv = z.extract('F-F_Research_Data_Factors.CSV')

ff3_data = pd.read_csv(ff_csv, index_col=0, skiprows = 3)
ff3_data = ff3_data[ff3_data.index.str.count(r'\d') == 6]
ff3_data = ff3_data.astype('float64')

ff3_data.index.names = ["Date"]
ff3_data.index = pd.to_datetime(ff3_data.index, format="%Y%m")
ff3_data.index = ff3_data.index.to_period("M")

##################################################
# Calculate Excess Returns of Portfolio or Asset #
##################################################
ff3_factors_subset = ff3_data[
    ff3_data.index.isin(data_resampled.index)
].copy()

ff3_factors_subset["Excess_Return"] = data_resampled["Return"] - ff3_factors_subset["RF"]

############################
# Run the Regression Model #
############################
# Prepare the independent variables (add a constant to the model)
X = sm.add_constant(ff3_factors_subset[["Mkt-RF", "SMB", "HML"]])
# The dependent variable
y = ff3_factors_subset["Excess_Return"]
# Run the regression
model = sm.OLS(y, X).fit()
# Display the summary of the regression
print(model.summary())

########################################################
# Plot the coefficients and their confidence intervals #
########################################################
factors = model.params.index[1:]  # ['Mkt_Rf', 'SMB', 'HML']
coefficients = model.params.values[1:]
confidence_intervals = model.conf_int().diff(axis=1).iloc[1]

# Create a DataFrame
ols_data = pd.DataFrame(
    {
        "Factor": factors,
        "Coefficient": coefficients,
        "Confidence_Lower": confidence_intervals[0],
        "Confidence_Upper": confidence_intervals[1],
    }
)

# Plotting
plt.figure(figsize=(3, 3))
sns.barplot(x="Factor", y="Coefficient", data=ols_data, capsize=0.2, palette="coolwarm")

# Add the p-value for each factor to the plot
for i, row in ols_data.iterrows():
    plt.text(
        i,
        0.2,
        f"p-value:{model.pvalues[row['Factor']]:.4f}",
        ha="center",
        va="bottom",
        fontsize=6,
    )

plt.title("Impact of Fama-French Factors on Monthly Returns")
plt.xlabel("Factor"); plt.ylabel("Coefficient Value")
plt.axhline(0, color="black", linewidth=0.8, linestyle="--")
plt.savefig("test2.png")

参考: Fama-French Factor Model in Python

実行結果

各ファクターのベータ

                            OLS Regression Results
==============================================================================
Dep. Variable:          Excess_Return   R-squared:                       0.954
Model:                            OLS   Adj. R-squared:                  0.951
Method:                 Least Squares   F-statistic:                     376.5
Date:                Wed, 12 Jun 2024   Prob (F-statistic):           1.27e-36
Time:                        02:05:35   Log-Likelihood:                -101.20
No. Observations:                  59   AIC:                             210.4
Df Residuals:                      55   BIC:                             218.7
Df Model:                           3
Covariance Type:            nonrobust
==============================================================================
                 coef    std err          t      P>|t|      [0.025      0.975]
------------------------------------------------------------------------------
const          0.1852      0.183      1.013      0.316      -0.181       0.552
Mkt-RF         1.0914      0.034     31.733      0.000       1.022       1.160
SMB           -0.1214      0.070     -1.732      0.089      -0.262       0.019
HML           -0.3434      0.041     -8.351      0.000      -0.426      -0.261
==============================================================================
Omnibus:                        0.369   Durbin-Watson:                   2.542
Prob(Omnibus):                  0.831   Jarque-Bera (JB):                0.536
Skew:                           0.042   Prob(JB):                        0.765
Kurtosis:                       2.541   Cond. No.                         5.75
==============================================================================

結果の見方

1段目

  • Dep. Variable: 回帰の従属変数、今回は超過リターン(リスクフリーレートを超えるリターン)。
  • R-squared(決定係数): 独立変数(Mkt-RF, SMB, HML)が従属変数の変動をどれだけ説明しているかを示す指標。0.954は、超過リターンの95.4%がこのモデルで説明されていることを意味します。
  • Adj. R-squared(調整済み決定係数): 説明変数の数を考慮した決定係数。R-squaredとほぼ同じ値で、高い説明力を維持しています。
  • F-statistic と Prob (F-statistic): モデル全体の有意性をテストする指標。F-statisticが高く、p値が非常に低い(1.27e-36)ことから、このモデルが統計的に有意であることがわかります。

2段目

  • const( \alpha): 回帰直線の切片。この場合、0.1852ですが、有意ではありません(p値 = 0.316)。
  • Mkt-RF(市場リスクプレミアム): 係数は1.0914で、市場リスクプレミアムとの強い正の関係を示します。これは統計的に有意です(p値 = 0.000)。
  • SMB: 係数は-0.1214で、小型株とのわずかな負の関係を示します。これはやや有意です(p値 = 0.089)。
  • HML: 係数は-0.3434で、バリュー株との強い負の関係を示します。これは非常に有意です(p値 = 0.000)。
  • t-statistic と P>|t|: 各係数の有意性をテストします。t-statisticが高く、p値が低い(通常0.05未満)場合、その係数は統計的に有意です。
  • [0.025, 0.975] 信頼区間: この区間は、真の係数が95%の確率で収まる範囲を示します。

3段目

  • Omnibus, Prob(Omnibus), Jarque-Bera (JB), Prob(JB): 残差が正規分布に従うかをチェックするテスト。高いp値(0.05以上)は残差が正規分布していることを示します。
  • Durbin-Watson: 残差の自己相関をテストします。値が2に近いほど、自己相関がないことを示します。

最終的な解釈

  • モデルの適合度: モデルは超過リターンの95.4%を説明しており、非常に良い適合度を示しています。
  • 市場リスクプレミアム(Mkt-RF): 強い正の係数と高い有意性は、超過リターンが市場リスクプレミアムと高い相関を持つことを示しています。
  • サイズファクター(SMB): わずかな負の係数は小型株との若干の負の関係を示していますが、強い有意性はありません。
  • バリューファクター(HML): 強い負の係数と高い有意性は、バリュー株が超過リターンに対して有意な負の影響を持つことを示しています。
  • モデル全体の有意性: 高いF-statisticと極めて低いp値は、モデル全体が統計的に有意であることを示しています。

4. 最後に

Fama-French 3ファクターモデルは、市場リスク、サイズ、バリューの3つの要因を考慮して、株式リターンの変動をより詳細に説明する強力なツールです。この記事を通じて、モデルの基本的な仕組みや計算方法、Pythonでの具体的な実践例を学ぶことができました。Pythonのコードは(環境差異がありますが)おそらく、ライブラリをインストールすればすぐ実行できる状態だと思いますので、ぜひ活用してください。

今回は多因子モデルの中で実績のある3ファクターモデルを選びましたが、現在ではもっとファクターを増やしたモデルも存在しています。今度余裕があったら、そういったモデルでどんなファクターが用いられているのか見てみたいと思います。

ポートフォリオ構築に関連する他のモデルを知りたい方は下記のリンク集をぜひご活用ください。

otama-playground.com