動く理由は1つではない
CAPMの要因は市場の1つだけです。1992年にファーマとフレンチは、株式リターンの差の多くを、さらに2つの要因が説明することを示しました。規模(小さな企業は大きな企業より高いリターン、SMB=small minus big)とバリュー(簿価時価比で割安な株は割高な株より高いリターン、HML=high minus low)です。のちのモデルはモメンタム、収益性、投資を加えました。各ファクターはそれ自体がリターン系列で、資産のその系列への感応度(ローディング)は、どれだけ連動するかを表します。
import numpy as np
rng = np.random.default_rng(1)
n = 250
market = rng.normal(0.0004, 0.010, size=n) # excess returns of three factors
smb = rng.normal(0.0001, 0.005, size=n)
hml = rng.normal(0.0001, 0.005, size=n)
factors = np.column_stack([market, smb, hml]) # shape (n, k) = (250, 3)
asset = 0.0002 + factors @ np.array([1.1, 0.5, -0.3]) + rng.normal(0, 0.004, size=n)
X = np.column_stack([np.ones(n), factors]) # design matrix: a column of ones for alpha
print(X.shape) # (250, 4)# The design matrix of three days and two factors
import numpy as np
factors = np.array([[0.010, -0.002], [-0.004, 0.001], [0.006, 0.003]])
X = np.column_stack([np.ones(len(factors)), factors])
print(X) # a column of ones, then market and SMB行列で書く最小二乗法
ファクターが複数あると、回帰は asset = alpha + b1 * f1 + ... + bk * fk + 誤差 です。ファクターを1の列と並べて行列 X にすると、最小二乗の係数は正規方程式 (X'X) c = X'y を解いて得られます。NumPy では np.linalg.solve(X.T @ X, X.T @ y) です。以前の単一資産の回帰と同じ仕組みで、未知数が k + 1 個になっただけです。最初の係数がアルファ、残りがローディングです。
import numpy as np
rng = np.random.default_rng(1)
n = 250
factors = np.column_stack([rng.normal(0.0004, 0.010, size=n), rng.normal(0.0001, 0.005, size=n), rng.normal(0.0001, 0.005, size=n)])
asset = 0.0002 + factors @ np.array([1.1, 0.5, -0.3]) + rng.normal(0, 0.004, size=n)
X = np.column_stack([np.ones(n), factors])
coef = np.linalg.solve(X.T @ X, X.T @ asset)
print(np.round(coef, 4)) # [alpha, market, SMB, HML]: the loadings come out near [1.1, 0.5, -0.3]
residual = asset - X @ coef
r2 = 1 - np.sum(residual ** 2) / np.sum((asset - asset.mean()) ** 2)
print(round(float(r2), 4))ファクターはどれだけ説明するか
それに答えるのが決定係数 R²で、ファクターが説明する資産の変動の割合 1 - SSres / SStot です。0(何も説明しない)から1(すべて説明)の値を取ります。分散した株式ポートフォリオは、ファーマ=フレンチのファクターでR²が0.9を超えることが多く、リスクのほぼすべてがファクターリスクです。個別株はそれより低く、残りの残差の変動は企業固有です。ファクターを足すと標本内のR²は決して下がらないので、数が大きいだけではそのファクターが重要だとは言えません。
# What the model implies: the expected return from the loadings and the factor premia
import numpy as np
betas = np.array([1.1, 0.5, -0.3]) # market, SMB, HML loadings
premia = np.array([0.0006, 0.0002, 0.0002]) # expected daily factor returns
rf = 0.0001
expected = rf + np.sum(betas * premia)
print(round(float(expected), 6)) # 0.0008 per day, about 20% a year on 250 days- ファクターモデルは超過リターンを複数のファクターに回帰する:
X = [1の列 | ファクター]、係数は[alpha, beta_1..beta_k] - 正規方程式
np.linalg.solve(X.T @ X, X.T @ y)を解く。R²は1 - SSres / SStotで、ファクターを足しても下がらない - モデルは
rf + sum(betas * premia)を意味する。ローディングもプレミアムも推定値で、ファクタープレミアムは消えうる
次は情報係数です。シグナルがそもそもリターンを予測しているかを測ります。
ラボ:採点される内容
design_matrixで回帰の準備をする25点fit_factor_modelでファクター感応度を推定する25点r_squaredで当てはまりを評価する25点factor_expected_returnで感応度に値を付ける25点