不止一个波动的理由
CAPM 只有一个因子,即市场。1992 年法玛和弗伦奇表明,另外两个因子能解释股票收益差异的大部分:规模(小公司比大公司赚得多,因子 SMB,小减大)和价值(按账面市值比衡量的便宜股票比贵的赚得多,因子 HML,高减低)。后来的模型又加入了动量、盈利能力和投资。每个因子本身就是一条收益序列,资产对它的载荷表示它与这条序列联动得有多强。
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(完全解释)之间。一个分散化的股票组合对 Fama–French 因子的 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 分