1. 这节究竟解决什么实际问题?¶
5.3 CAPM 与套利定价理论 (CAPM & APT) 的具体任务是:用 Beta 和因子暴露解释收益来源,并区分理论关系与样本估计。 本节将明确输入数据、计算过程、输出及其在后续研究中的用途。
2. 前置知识¶
理解收益率、协方差、波动率和矩阵的基本含义。 对公式的理解应包括变量定义、单位、时间索引和适用条件。
3. 数据说明¶
固定多资产价格样本用于演示;不把少量资产、单一时期的最优权重当作配置建议。 运行前先检查 shape、日期范围、列名与缺失值。数据看起来能画图,不代表它适合回答研究问题。
4. 学习目标与完成标准¶
完成本节后,你应能用自己的话解释 5.3 CAPM 与套利定价理论 (CAPM & APT) 的输入、计算步骤、输出和局限;尤其能说明:用 Beta 和因子暴露解释收益来源,并区分理论关系与样本估计。 你还应能独立改动一个参数并解释结果为什么变化,指出至少一个会让结论失真的假设。
5. 核心概念与公式¶
组合配置同时考虑单项资产风险和资产之间的协方差,目标是控制整体风险暴露。
组合收益是权重加权和;风险由协方差矩阵决定,权重之和、杠杆和做空限制必须写清楚。 看到公式时依次确认:每个符号的单位是什么?它属于哪个时点?分母会不会为零?它是描述性统计量、预测量,还是交易规则?
6. 从零实现¶
原始代码按照“准备输入 → 计算中间量 → 得到结果 → 解释输出”的顺序组织。每个单元应检查变量类型、索引、维度和代表性数值。中间变量用于核对公式实现和定位计算错误。
7. 成熟库实现¶
优化器可以求满足约束的数值解;它不会告诉你输入的预期收益是否可信。 使用库函数前先阅读参数含义、默认值和返回对象;库输出一个数字,并不说明这个数字适合你的交易假设。
8. 两种实现的差异¶
从零实现便于核对公式、调试和识别隐含假设;成熟库适合在计算逻辑已验证后复用。两种实现依赖相同的数据口径与时点假设,使用库函数不会改变输入数据的质量。
9. 预期结果与检查范围¶
你应看到维度正确的 Series/DataFrame、日期单调递增的索引,以及能支持“用 Beta 和因子暴露解释收益来源,并区分理论关系与样本估计。”这一任务的表格或图形。金融日收益率均值通常接近 0;若出现极大的连续收益、空表、全 NaN 或索引错位,应先排查数据和对齐逻辑,而不是立即相信结果。
10. 常见错误与诊断¶
协方差矩阵不可逆、权重未归一化、用样本内最优权重宣称可投资、忽略再平衡成本。
诊断顺序:先打印类型与 shape,再看 head()/tail(),接着检查日期和 NaN,最后才比较数值。这样能避免在错误的对象上反复改参数。
11. 研究偏差检查¶
均值估计噪声极大;需要样本外比较、参数敏感性和等权等简单基线。 结果至少应与一个简单基线比较,并明确哪些部分只是教学演示。
12. 三层练习与答案¶
- 基础练习:验证一个中间变量、补全一段小计算,并用一句话解释输出。
- 研究练习:改变窗口、样本区间、约束或标的,记录哪些结论仍成立。
- 开放项目:把本节方法嵌入模块项目,报告基线、失败结果和限制。
题目在 ../exercises/05_portfolio/03_capm_apt_exercises.ipynb;完整推导、可运行参考实现与结果检查在 ../solutions/05_portfolio/03_capm_apt_solutions.ipynb。请先独立作答,再打开答案。
13. 延伸阅读¶
延伸材料:Portfolio Selection(Markowitz, 1952)。阅读记录应包括研究假设、数据频率、样本范围和结论适用条件。
14. 下一节¶
下一节是 04_factor_analysis。本节的概念会成为理解它的输入,而不是孤立的名词。
import numpy as np
import pandas as pd
import statsmodels.api as sm
import matplotlib.pyplot as plt
np.random.seed(42)
print('Libraries loaded')
Libraries loaded
import matplotlib.pyplot as plt
# 1. 设置系统自带的中文字体(这里使用黑体 SimHei)
plt.rcParams['font.sans-serif'] = ['SimHei'] # 如果你想用微软雅黑,可以改成 ['Microsoft YaHei']
# 2. 解决更换字体后,负号(-)显示为方块的问题
plt.rcParams['axes.unicode_minus'] = False
1. CAPM 核心公式¶
资本资产定价模型 (CAPM) 假设市场是均衡的,每只股票的预期收益由一个简单关系决定:
$$E[R_i] = R_f + \beta_i \cdot (E[R_m] - R_f)$$
- $R_f$:无风险利率(国债收益率)
- $E[R_m] - R_f$:市场风险溢价(Market Risk Premium,约 4%~7%)
- $\beta_i$:股票对市场的系统性风险暴露
- $E[R_i]$:股票的理论预期收益率
证券市场线 (Security Market Line, SML) 是所有均衡资产应该落在的直线。 偏离 SML 的距离就是 Alpha——超越市场理论预期的超额收益。
# 绘制证券市场线 (SML)
np.random.seed(42)
risk_free = 0.03 # 无风险利率 3%
market_premium = 0.06 # 市场溢价 6%
# 模拟 50 只股票的 Beta 和实际年化收益率
betas = np.random.uniform(0.3, 2.0, 50)
alphas = np.random.normal(0, 0.02, 50) # 各股真实 Alpha(正负随机)
actual_returns = risk_free + betas * market_premium + alphas
# SML
beta_range = np.linspace(0, 2.5, 100)
sml_returns = risk_free + beta_range * market_premium
plt.figure(figsize=(10, 7))
plt.plot(beta_range, sml_returns * 100, 'k-', linewidth=2, label='证券市场线 (SML)')
# 绘制各股票,颜色区分 Alpha 正负
positive_alpha = actual_returns > (risk_free + betas * market_premium)
plt.scatter(betas[positive_alpha], actual_returns[positive_alpha] * 100,
color='green', s=80, alpha=0.7, label='正 Alpha 股票(SML 上方)', zorder=5)
plt.scatter(betas[~positive_alpha], actual_returns[~positive_alpha] * 100,
color='red', s=80, alpha=0.7, label='负 Alpha 股票(SML 下方)', zorder=5)
# 标注市场组合 (Beta=1)
plt.scatter([1.0], [(risk_free + market_premium) * 100], marker='*', s=400,
color='gold', zorder=6, label='市场组合 (Beta=1)')
plt.xlabel('Beta (系统性风险)', fontsize=12)
plt.ylabel('预期年化收益率 (%)', fontsize=12)
plt.title('证券市场线 (SML) & Alpha 可视化', fontsize=14)
plt.legend(fontsize=10)
plt.grid(alpha=0.3)
plt.tight_layout()
plt.show()
print(f'正 Alpha 股票: {positive_alpha.sum()} 只')
print(f'负 Alpha 股票: {(~positive_alpha).sum()} 只')
正 Alpha 股票: 25 只 负 Alpha 股票: 25 只
2. 用 OLS 估计单只股票的 Alpha 和 Beta¶
n = 252 # 一年
market_returns = np.random.normal(0.06/252, 0.18/np.sqrt(252), n)
stock_returns = 0.0003 + 1.25 * market_returns + np.random.normal(0, 0.008, n)
X = sm.add_constant(market_returns)
result = sm.OLS(stock_returns, X).fit()
print(f'估计 Alpha(日): {result.params[0]:.6f} (年化: {result.params[0]*252:.4f})')
print(f'估计 Beta: {result.params[1]:.4f}')
print(f'R-squared: {result.rsquared:.4f}')
print(f'Alpha p-value: {result.pvalues[0]:.4f} ← 是否显著不为零?')
估计 Alpha(日): -0.000032 (年化: -0.0081) 估计 Beta: 1.2081 R-squared: 0.7345 Alpha p-value: 0.9492 ← 是否显著不为零?
3. 多因子 APT¶
APT(套利定价理论)将 CAPM 中单一的市场因子扩展为多因子模型:
$$E[R_i] = R_f + \beta_{i,1} f_1 + \beta_{i,2} f_2 + ... + \beta_{i,K} f_K$$
常见因子(Fama-French 模型):
- f1: 市场因子(CAPM 的 Rm-Rf)
- f2: SMB(小市值 - 大市值)
- f3: HML(高账面市值比 - 低账面市值比)
# Fama-French 三因子简化演示
Mkt_Rf = np.random.normal(0.0002, 0.01, n) # 市场超额收益率
SMB = np.random.normal(0.0001, 0.005, n) # 规模因子
HML = np.random.normal(0.0001, 0.005, n) # 价值因子
X_ff3 = sm.add_constant(np.column_stack([Mkt_Rf, SMB, HML]))
result_ff3 = sm.OLS(stock_returns - 0.03/252, X_ff3).fit()
print('Fama-French 三因子模型回归结果:')
print(f'Alpha: {result_ff3.params[0]:.6f} (年化: {result_ff3.params[0]*252:.4f})')
print(f'Beta_Mkt: {result_ff3.params[1]:.4f}')
print(f'Beta_SMB: {result_ff3.params[2]:.4f} (正值=偏小市值)')
print(f'Beta_HML: {result_ff3.params[3]:.4f} (正值=偏价值股)')
print(f'R-squared: {result_ff3.rsquared:.4f} (更高意味着三因子解释力更强)')
Fama-French 三因子模型回归结果: Alpha: 0.000700 (年化: 0.1765) Beta_Mkt: 0.1260 Beta_SMB: 0.0706 (正值=偏小市值) Beta_HML: 0.1019 (正值=偏价值股) R-squared: 0.0088 (更高意味着三因子解释力更强)
练习¶
- 用 SPY,QQQ,GLD 三个不同 Beta 的 ETF,在同一个 SML 上标注它们的实际历史年化收益率,看谁真正有 Alpha。
- 对比单因子 CAPM 和 Fama-French 三因子上同一只股票的 R-squared,三因子是否解释了更多方差?
- 修改代码模拟一只高 Alpha(年化 +5%)且低 Beta(0.3)的股票,在 SML 图上它会在什么位置?
下一节 → 04_factor_analysis.ipynb