第 7 章 · 01 市场隐含收益 本节摘要:均值-方差优化的输入是「期望收益向量 μ」,而历史均值估计噪声极大、对样本极度敏感,稍有调整权重就剧烈变化。Black-Litterman 模型的第一块基石是「先验(prior)」:用市场当前市值反推出「市场自身隐含的收益预期」——市场是个庞大的加权 consensus,它的定价本身就包含了对未来收益的最佳猜测。本节讲清 PyPortfolioOpt 里的两个工具函数: (从市值 + 协方差 + 风险厌恶推隐含收益 Π = δΣw)和 (从市场指数价推 δ)。读完本节,你能从一组市值和协方差算出市场隐含的先验收益。 内容来源:原项目源码 、文档 ,汉化并套用体系化模板。 学习目标 阅读完本节,你应当能够: 说清为什么历史均值收益不是好的先验。
本节摘要:均值-方差优化的输入是「期望收益向量 μ」,而历史均值估计噪声极大、对样本极度敏感,稍有调整权重就剧烈变化。Black-Litterman 模型的第一块基石是「先验(prior)」:用市场当前市值反推出「市场自身隐含的收益预期」——市场是个庞大的加权 consensus,它的定价本身就包含了对未来收益的最佳猜测。本节讲清 PyPortfolioOpt 里的两个工具函数:
market_implied_prior_returns(从市值 + 协方差 + 风险厌恶推隐含收益 Π = δΣw)和market_implied_risk_aversion(从市场指数价推 δ)。读完本节,你能从一组市值和协方差算出市场隐含的先验收益。
内容来源:原项目源码
pypfopt/black_litterman.py、文档docs/BlackLitterman.rst,汉化并套用体系化模板。
阅读完本节,你应当能够:
均值-方差优化的痛点集中在「期望收益」这一输入:
Black-Litterman 的洞见是:与其从噪声历史猜,不如从市场当前定价反推。市场本身就是无数投资者交易出的加权 consensus,市值大的资产就是市场集体认为「该重仓」的资产。把这个共识解析出来,就是先验。
💡 核心心法:市场是个大算盘——成千上万投资者用真金白银定价出来的市值权重,本身就隐含了对未来收益的预期。把这个预期反解出来,比硬算历史均值靠谱得多。
要算隐含收益,先估「市场风险厌恶系数 δ」。源码定义:
δ = (R_market - R_f) / σ²_market
即市场超额收益除以市场方差。源码 market_implied_risk_aversion:
def market_implied_risk_aversion(market_prices, frequency=252, risk_free_rate=0.0): if not isinstance(market_prices, (pd.Series, pd.DataFrame)): raise TypeError("Please format market_prices as a pd.Series") market_prices = market_prices.squeeze() rets = market_prices.pct_change().dropna() r = rets.mean() * frequency # 年化收益 var = rets.var() * frequency # 年化方差 return (r - risk_free_rate) / var
用法很直接,通常用 SPY(标普 500 ETF)作为市场代理:
from pypfopt import black_litterman market_prices = ... # pd.Series,如 yfinance 拉 SPY 收盘价 delta = black_litterman.market_implied_risk_aversion(market_prices) # 典型值 2 ~ 4,比如 2.5
⚠️ 频率对齐:
frequency=252默认按日线年化。如果你的market_prices是月线,要改frequency=12,否则 δ 会差一个数量级。
δ 的金融含义是「投资者每承受一单位方差,要求多少超额收益补偿」。比如 δ=2.5,意味着「方差增加 1%,要求超额收益增加 2.5%」。这就是市场的「风险价格」。
给定 δ、协方差 Σ、市值 mcaps,可以反推每个资产的隐含收益。核心公式:
Π = δΣw_mkt
其中 w_mkt 是市值归一化的权重。源码:
def market_implied_prior_returns(market_caps, risk_aversion, cov_matrix, risk_free_rate=0.0): mcaps = pd.Series(market_caps) mkt_weights = mcaps / mcaps.sum() # 归一化市值权重 return risk_aversion * cov_matrix.dot(mkt_weights) + risk_free_rate
逻辑解读:
💡 公式背后的直觉:如果 AAPL 市值占市场 5%,它在市场组合里贡献的风险是
Σ·w_aapl;按市场风险价格 δ 定价,这些风险「应该」换来δ·Σ·w_aapl的超额收益。这就是「从市场反推」的全部魔法。
import pandas as pd from pypfopt import black_litterman, risk_models # 假设已有: # prices: 资产价格 DataFrame,列是 ticker # market_prices: 市场指数(SP500/SPY)价格 Series # mcaps: 市值字典 {"AAPL": 2.7e12, "MSFT": 2.5e12, ...} S = risk_models.CovarianceShrinkage(prices).ledoit_wolf() delta = black_litterman.market_implied_risk_aversion(market_prices) prior = black_litterman.market_implied_prior_returns(mcaps, delta, S) print(f"风险厌恶 δ = {delta:.2f}") print(prior) # AAPL 0.10 # MSFT 0.12 # XOM 0.05 # ...
⚠️ 协方差必须对齐:
cov_matrix的列/索引要与mcaps的键一致。如果cov_matrix不是 DataFrame,源码会发警告:If cov_matrix is not a dataframe, market cap index must be aligned to cov_matrix。建议始终用 DataFrame。
mcaps 是个 {ticker: 市值} 字典。常见来源:
yf.Ticker(t).info["marketCap"](cookbook 4 的做法)。cookbook 4 的写法:
mcaps = {} for t in tickers: mcaps[t] = yf.Ticker(t).info["marketCap"]
⚠️ 市值要同步:市值是「当下快照」,不同日期拉的市值可能差异巨大(尤其经过拆股、并购)。批量拉取时尽量用同一天的快照,避免不一致污染 w_mkt。
| 输入来源 | 优点 | 缺点 |
|---|---|---|
| 历史均值 | 简单、纯数据驱动 | 噪声大、对样本敏感、忽略 consensus |
| 市场隐含(先验) | 稳定、含市场共识、直觉清晰 | 依赖市值的准确性、依赖 δ 估计 |
历史均值不是不能用,但 Black-Litterman 把它降级为「可选的先验之一」——你可以 pi= 喂历史均值或等权先验,但市场隐含是默认推荐。
# 等权先验(每个资产相同) bl = BlackLittermanModel(S, pi="equal", absolute_views=viewdict) # 历史均值作先验 bl = BlackLittermanModel(S, pi=mu_historical, absolute_views=viewdict) # 市场隐含(默认推荐) bl = BlackLittermanModel(S, pi="market", market_caps=mcaps, absolute_views=viewdict)
δ=(R-R_f)/σ² 从市场指数价算风险厌恶,典型值 2~4,默认 frequency=252。Π=δΣw_mkt+R_f 从市值反推隐含收益,w_mkt 是市值归一化。pi= 可以是 array / Series / "market" / "equal" / None,市场隐含是默认推荐。下一节,我们引入主观观点——把「你对 AAPL 涨 20% 的判断」融合进先验,得到贝叶斯后验。