第 2 章 · 03 CAPM 模型 本节摘要:本节讲 提供的第三种、也是理论根基最稳的收益估计—— 。资本资产定价模型(CAPM)由 Sharpe 等人 1960 年代提出,核心公式 :资产的期望收益等于无风险利率加上其 beta 乘以市场风险溢价。本节拆解 PyPortfolioOpt 的实现:如何把「市场组合」当成基准(无显式输入时用资产等权均值代理)、如何用协方差回归估 beta、如何把日度市场均值年化,以及它相对前两种历史估计的优势——把「外推个股历史」换成「外推市场 + 个股相对风险」,因而更稳。最后讲清它的理论假设与实务局限。 内容来源:原项目源码 (函数 )、文档 ,汉化并套用体系化模板。 学习目标 阅读完本节,你应当能够: 写出 CAPM 公式,说清每项含义。
本节摘要:本节讲
expected_returns.py提供的第三种、也是理论根基最稳的收益估计——capm_return。资本资产定价模型(CAPM)由 Sharpe 等人 1960 年代提出,核心公式R_i = R_f + β_i (E(R_m) - R_f):资产的期望收益等于无风险利率加上其 beta 乘以市场风险溢价。本节拆解 PyPortfolioOpt 的实现:如何把「市场组合」当成基准(无显式输入时用资产等权均值代理)、如何用协方差回归估 beta、如何把日度市场均值年化,以及它相对前两种历史估计的优势——把「外推个股历史」换成「外推市场 + 个股相对风险」,因而更稳。最后讲清它的理论假设与实务局限。
内容来源:原项目源码
pypfopt/expected_returns.py(函数capm_return)、文档docs/ExpectedReturns.rst,汉化并套用体系化模板。
阅读完本节,你应当能够:
capm_return 算期望收益,知道默认市场代理是什么。CAPM(Capital Asset Pricing Model)由 William Sharpe 等人于 1964 年前后提出,是现代金融理论的基石之一。它给出了资产期望收益的「应该值」:
R_i = R_f + β_i × (E(R_m) - R_f)
各项含义:
| 符号 | 含义 | 直觉 |
|---|---|---|
R_i |
资产 i 的期望收益 | 我们要估的 μ_i |
R_f |
无风险利率 | 国债收益,「时间价值」 |
β_i |
资产 i 的 beta | 相对市场的系统性风险 |
E(R_m) |
市场组合的期望收益 | 整体市场的回报 |
E(R_m) - R_f |
市场风险溢价 | 承担市场风险的额外补偿 |
💡 核心心法:CAPM 把个股的收益拆成两部分——时间价值(R_f,谁都能拿)+ 承担市场风险的补偿(β × 市场溢价)。一只股票期望收益高,不是因为「它过去涨得多」,而是因为它对市场更敏感(beta 大),需要更多补偿。这把「外推个股」换成了「外推市场 + 估个股相对风险」,后者统计上更稳。
beta 衡量资产收益对市场收益的敏感度:
β_i = Cov(R_i, R_m) / Var(R_m)
β = 1:与市场同步(如 SPY 指数基金)。β > 1:比市场更波动(成长股、科技股常见,牛市涨得快、熊市跌得狠)。β < 1:比市场稳(公用事业、消费必需品)。β < 0:与市场反向(罕见,如黄金、某些对冲基金)。打开 pypfopt/expected_returns.py,定位 capm_return(源码 226-315 行)。函数签名:
def capm_return( prices, market_prices=None, returns_data=False, risk_free_rate=0.0, compounding=True, frequency=252, log_returns=False, ):
核心实现(节选):
# 1. 准备市场收益 market_returns = None # ... 解析 market_prices ... if market_returns is None: # 用资产等权均值作为市场代理 returns["mkt"] = returns.mean(axis=1) else: market_returns.columns = ["mkt"] returns = returns.join(market_returns, how="left") # 2. 算协方差,提取 beta cov = returns.cov() betas = cov["mkt"] / cov.loc["mkt", "mkt"] betas = betas.drop("mkt") # 3. 算市场期望收益(年化) if compounding: mkt_mean_ret = (1 + returns["mkt"]).prod() ** ( frequency / returns["mkt"].count() ) - 1 else: mkt_mean_ret = returns["mkt"].mean() * frequency # 4. 套 CAPM 公式 return risk_free_rate + betas * (mkt_mean_ret - risk_free_rate)
关键设计:
market_prices,函数把「所有资产每日等权平均收益」当成市场收益 mkt 列。这是个粗糙但可用的代理——若你的资产池本身就是大盘成分,近似合理;若不是(比如全是科技股),会严重偏离真实市场。生产环境建议传一个真实指数(如 S&P 500 ETF SPY)的价格序列。returns.cov() 的完整协方差矩阵里,cov["mkt"] 这一列就是每只资产与市场的协方差,除以 cov.loc["mkt", "mkt"](市场方差)即得 beta。最后 drop("mkt") 把市场自身那行去掉。compounding=True 用 CAGR 公式,False 用算术均值 × frequency,与 mean_historical_return 一致。risk_free_rate + betas * (mkt_mean_ret - risk_free_rate),严格对应 CAPM。⚠️ 无风险利率的频率:
risk_free_rate的周期必须与frequency对应——日度数据(frequency=252)用日度无风险利率(如年化 2% 对应日度约0.02/252);默认0.0是为了简化,实务中应当填一个真实值。新手最常犯的错是把年化无风险利率直接喂给日度频率。
| 参数 | 默认 | 含义 | 注意 |
|---|---|---|---|
prices |
— | 价格 DataFrame | 必填 |
market_prices |
None |
基准(市场)价格;None 则用资产等权均值代理 | 生产建议传 SPY 等 |
returns_data |
False |
True 表示输入已是收益 | 不应是对数收益 |
risk_free_rate |
0.0 |
无风险利率 | 周期须匹配 frequency |
compounding |
True |
市场均值的几何/算术模式 | 见第三节 |
frequency |
252 |
一年交易日数 | 月度改 12 |
log_returns |
False |
是否用对数收益 |
最简调用(用等权均值代理市场):
from pypfopt import expected_returns mu = expected_returns.capm_return(df)
传入真实市场基准(推荐):
import yfinance as yf spy = yf.download("SPY", period="max")["Close"] mu = expected_returns.capm_return(df, market_prices=spy)
带无风险利率(年化 2%,需转日度):
mu = expected_returns.capm_return( df, market_prices=spy, risk_free_rate=0.02 / 252, # 年化转日度 )
cookbook 2 里官方推荐 CAPM 作为「比均值历史收益更稳」的默认选择:
mu = expected_returns.capm_return(prices)
相对前两种历史估计,CAPM 在实务上更稳,原因有三:
💡 官方推荐:cookbook 第 2 节明确写道「CAPM 收益旨在比默认的均值历史收益稍微稳定一些」。这也是
docs/ExpectedReturns.rst的隐藏建议——若没有自研收益模型,优先用 CAPM。
CAPM 是个理想化模型,建立在几个强假设上:
实证上,CAPM 的预测也有偏差:
⚠️ 仍是被优化器放大的输入:无论 CAPM 多稳,它仍是 μ 的估计,仍会被 max_sharpe 最大化成权重。最稳健的做法仍是放弃 μ 只跑
min_volatility,或第 7 章用 Black-Litterman 把 CAPM 当先验再融合观点。
R_i = R_f + β_i (E(R_m) - R_f),收益 = 无风险 + β × 市场溢价。β_i = cov(R_i, R_m) / var(R_m),衡量相对市场的系统性风险。market_prices=None 时用资产等权均值代理;生产建议传 SPY 等真实指数。至此,收益模型三层讲完。下一章,我们进入风险模型——从样本协方差开始,看协方差矩阵如何估、为何易失真。