大家好,我是赛博解生酱,一个在生物医药和计算机交叉领域摸爬滚打多年的博士。最近抽空学习了下量化投资和资产配置,了解了Black-Litterman(BL)模型,准备在此做个研究记录。该模型的框架非常漂亮,既解决了传统均值-方差模型的"参数敏感陷阱",又巧妙平衡了市场客观规律与投资者主观判断,这种"理性与感性"的融合设计,着实符合真实的主动投资观念和贝叶斯的思想。今天就带着大家从头到尾了解BL模型,从数学本质到代码落地,再到实际应用中的思考,希望能给相关领域和感兴趣的朋友带来启发。
要理解BL模型的价值,首先得从它要解决的核心问题——传统均值-方差模型的缺陷说起。
均值-方差模型是马科维茨于1952年提出的现代资产配置基石,核心逻辑特别直白:以资产预期收益率(均值μ) 和收益率波动的协方差矩阵(Σ) 为核心输入,通过量化"收益-风险"的权衡关系,求解最优资产权重w。它的目标很明确:要么在给定风险(方差)下最大化收益,要么在给定收益下最小化风险,本质是一个带约束的优化问题,核心公式可简化为:
(其中是目标收益,约束条件确保权重总和为1)
但这个看似完美的经典模型,在实际应用中却举步维艰,核心问题就是对输入参数的极端敏感性——也就是常说的"垃圾进,垃圾出":
正是这个"参数敏感绝症",让均值-方差模型长期停留在理论层面,难以落地。而Black-Litterman模型的诞生,恰好给这个难题提供了破局思路。
它的精妙之处在于用贝叶斯思维架起了"市场均衡"与"主观观点"的桥梁:以CAPM理论推导的市场均衡收益作为先验信息,既保证了模型的基准合理性;再融入投资者对特定资产的主观判断(比如"新能源板块未来收益率会达到12%"),并通过概率分布量化观点的置信度,最终得到更稳健、更贴合实际需求的后验预期收益。
在我看来,BL模型的魅力远不止"解决参数敏感"——它更提供了一种科学的投资决策框架:我们不必强迫自己精准预测所有资产的收益率(这几乎不可能),而是可以基于自身研究优势,对少数熟悉的资产表达观点,其余资产则遵循市场均衡规律。这种"抓大放小"的设计,完美契合了实际投资中的信息不对称现状,也是它能在量化领域长盛不衰的核心原因。
BL模型的数学推导基于贝叶斯定理,:先验分布(市场均衡)+ 观测信息(主观观点)→ 后验分布(最终预期)。下面我会一步步拆解这个过程,清晰展现BL模型的优美框架,并给出代码实现。
BL模型的先验信息并非凭空设定,而是源于真实的市场信息。基于CAPM理论,资产的均衡收益率可以通过"逆向优化"得到:
这里的关键参数值得多聊两句:是市场整体的风险厌恶系数(实践中常取2.5-3.5),是资产收益率的协方差矩阵,是市场均衡权重(通常用市值加权,比如沪深300成分股的权重分布)。
这个公式的物理意义很直观:市场均衡收益是风险厌恶系数、资产风险(协方差)和市场权重的乘积,本质上反映了"承担多少风险,就该获得多少收益"的市场定价逻辑。
在此基础上,我们假设资产真实收益率均值服从以为均值、为协方差的正态分布:
其中是个关键调节参数(通常取0.01-0.05),严格来说它是对均衡收益率估计误差的缩放因子。越小,表示我们认为均衡收益率的估计越精准(分布越窄),主观观点不仅难以撼动它,反而会被‘拉回’到均衡状态。反之则主观观点权重越大。这个参数的校准需要结合市场特性,美股市场可以取偏小的(0.02左右),因为美股市场更接近有效市场,而A股市场会适当放大到0.04,以体现主观研究的价值。
对应的概率密度函数如下,核心是衡量"真实收益率偏离市场均衡的概率":
投资中我们常会有各种观点,比如"茅台未来一年收益率会超15%"(绝对观点)、"新能源板块比消费板块收益率高3%"(相对观点)。BL模型的精妙之处在于,它能把这些定性判断转化为严谨的数学表达:
这里的三个核心矩阵需要重点理解,结合我的经验分享:
对应的条件概率分布,本质上是衡量"基于真实收益率,我们的主观观点实现的概率"。
贝叶斯定理的核心是"新信息更新旧认知",在这里就是用主观观点(新信息)更新市场均衡(旧认知),得到更可靠的后验预期收益。
根据贝叶斯公式:
由于分母是与无关的常数(归一化因子),我们只需要关注分子部分,即"先验概率×似然概率":
把前两步的概率密度函数代入,就能得到:
这里的关键洞察是:两个正态分布的乘积依然是正态分布(对数后是二次函数),所以后验分布必然也是正态分布,我们只需要求出它的均值和协方差即可。
接下来的工作就是合并指数项中的二次函数,通过配方法化简。先展开指数内的表达式:
令,,通过配方法可以将上式转化为:
常数项
由于常数项不影响分布的形状,最终得到后验分布:
其中后验均值和后验协方差是BL模型的核心输出,也是我们进行资产配置的关键依据:
我对的理解是"加权平均":第一项是市场均衡收益的加权项,权重是先验信息的置信度;第二项是主观观点的加权项,权重是观点的置信度;而整体的权重矩阵是两者的和的逆,本质上是"谁更可靠,谁的权重就更大"。
最后需要说明的是,资产实际收益率的后验分布,不仅要考虑的不确定性(),还要考虑资产本身的收益波动(),因此最终的后验协方差矩阵是两者之和:
其中
这个细节在实际应用中非常重要——如果忽略,会低估组合的真实风险,加入后,风险度量才更贴合实际。在得到后验收益率分布后,即可调整资产配置,将后验收益率均值和协方差矩阵输入均值-方差优化框架中。通过求解最优权重公式(如无约束条件下使用 ,得到新的资产权重。这些权重综合了市场均衡信息和主观观点,从而指导增持预期收益高的资产、减持预期收益低的资产,实现风险调整后的配置优化。
基于上面的数学推导,以下是完整的BL模型实现代码(采用合成数据),均衡收益计算、观点表达、后验推导、组合优化和结果分析等全流程,大家可以结合上述推导过程进行参考。
ounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(line
import numpy as np
import pandas as pd
from numpy.linalg import inv
import matplotlib.pyplot as plt
from scipy.optimize import minimize
class BlackLittermanModel:
def __init__(self, n_assets=5, risk_free_rate=0.02):
self.n_assets = n_assets
self.risk_free_rate = risk_free_rate
self.tau = 0.03
self.random_seed = 42
def generate_synthetic_data(self, periods=252):
np.random.seed(self.random_seed)
# 设定真实的年化收益率 (8% - 12%)
true_returns = np.array([0.08, 0.10, 0.12, 0.09, 0.07])
# 生成协方差矩阵
A = np.random.rand(self.n_assets, self.n_assets)
cov = np.dot(A, A.T)
# 归一化协方差,使其对应合理的波动率
# 目标:对角线元素(方差)约为 0.02-0.04 (即波动率 14%-20%)
scale_factor = 0.02 / np.max(np.diag(cov))
true_cov = cov * scale_factor
# 添加一些特定的相关性结构
np.fill_diagonal(true_cov, np.diag(true_cov) + 0.01)
# 生成历史模拟数据
historical_returns = np.random.multivariate_normal(
true_returns, true_cov, periods
)
return historical_returns, true_returns, true_cov
def calculate_equilibrium_returns(self, cov_matrix, market_caps=None, delta=2.5):
"""
计算隐含均衡收益率
"""
if market_caps is None:
market_caps = np.array([200, 180, 150, 100, 80])
equilibrium_weights = market_caps / np.sum(market_caps)
# Implied Equilibrium Returns = Delta * Sigma * w_mkt
equilibrium_returns = delta * np.dot(cov_matrix, equilibrium_weights)
return equilibrium_returns, equilibrium_weights
def express_views(self, view_assets, view_returns, confidence_levels):
k = len(view_assets)
P = np.zeros((k, self.n_assets))
for i, assets in enumerate(view_assets):
if isinstance(assets, int):
P[i, assets] = 1
else:
for asset, weight in assets.items():
P[i, asset] = weight
Q = np.array(view_returns)
Omega = np.diag(confidence_levels)
return P, Q, Omega
def calculate_posterior(self, equilibrium_returns, cov_matrix, P, Q, Omega):
tau_sigma = self.tau * cov_matrix
# 使用更稳定的逆矩阵计算方式
inv_tau_sigma = inv(tau_sigma)
# M = inv(inv(tau*Sigma) + P.T * inv(Omega) * P)
# Posterior Mean = M * (inv(tau*Sigma)*Pi + P.T*inv(Omega)*Q)
inv_Omega = inv(Omega)
M = inv(inv_tau_sigma + np.dot(np.dot(P.T, inv_Omega), P))
term1 = np.dot(inv_tau_sigma, equilibrium_returns)
term2 = np.dot(np.dot(P.T, inv_Omega), Q)
posterior_returns = np.dot(M, term1 + term2)
posterior_cov = cov_matrix + M
return posterior_returns, posterior_cov
def optimize_portfolio(self, expected_returns, cov_matrix, initial_weights):
n_assets = len(expected_returns)
def negative_sharpe(weights):
portfolio_return = np.dot(weights, expected_returns)
portfolio_vol = np.sqrt(np.dot(weights.T, np.dot(cov_matrix, weights)))
# 加上极小值防止除零
return - (portfolio_return - self.risk_free_rate) / (portfolio_vol + 1e-6)
constraints = [{'type': 'eq', 'fun': lambda x: np.sum(x) - 1}]
bounds = [(0.0, 0.5) for _ in range(n_assets)] # 允许单资产最高50%
result = minimize(
negative_sharpe,
initial_weights,
method='SLSQP',
bounds=bounds,
constraints=constraints,
tol=1e-6
)
return result.x
def analyze_results(self, equilibrium_weights, bl_weights, asset_names):
results = pd.DataFrame({
'Asset': asset_names,
'Mkt_Weight': equilibrium_weights.round(4),
'BL_Weight': bl_weights.round(4),
'Change': (bl_weights - equilibrium_weights).round(4)
})
return results
def main():
# 初始化模型
bl_model = BlackLittermanModel(n_assets=5, risk_free_rate=0.02)
asset_names = [f'Asset_{i+1}' for i in range(bl_model.n_assets)]
# 生成数据 (修正后的低波动率数据)
historical_returns, true_returns, true_cov = bl_model.generate_synthetic_data()
print("=== 协方差矩阵对角线 (方差) ===")
print(np.diag(true_cov).round(4))
# 预期看到 0.02 - 0.04 左右的值,对应 14%-20% 的波动率
# 计算市场均衡 (Benchmark)
market_caps = np.array([200, 180, 150, 100, 80])
eq_returns, eq_weights = bl_model.calculate_equilibrium_returns(
true_cov, market_caps, delta=2.5
)
print("\n=== 市场均衡收益率 (Benchmark) ===")
print(eq_returns.round(4))
# 预期看到 0.05 - 0.15 左右的值,这才合理
# 设定主观观点 (关键步骤:观点必须具有 Alpha)
# 观点1:看多 Asset 1。如果均衡是 0.08,我们设为 0.12 (显著高于市场)
# 观点2:Asset 2 跑赢 Asset 5。
view1_target = eq_returns[0] * 1.3 # 看多 Asset 1 30%
view2_diff = 0.03 # Asset 2 比 Asset 5 强 3%
view_assets = [0, {1: 1, 4: -1}]
view_returns = [view1_target, view2_diff]
# 信心水平:给较高的信心 (数值越小信心越强)
confidence_levels = [0.0001, 0.0001]
P, Q, Omega = bl_model.express_views(view_assets, view_returns, confidence_levels)
print(f"\n=== 观点设定 ===")
print(f"Asset 1 均衡: {eq_returns[0]:.4f} -> 观点: {view1_target:.4f}")
print(f"Asset 2 vs 5 观点差: {view2_diff}")
# 计算 BL 后验分布
post_returns, post_cov = bl_model.calculate_posterior(
eq_returns, true_cov, P, Q, Omega
)
print("\n=== BL 后验收益率 ===")
print(post_returns.round(4))
# 优化组合
# 使用均衡权重作为优化的起点
bl_weights = bl_model.optimize_portfolio(post_returns, post_cov, eq_weights)
# 结果对比分析
results = bl_model.analyze_results(eq_weights, bl_weights, asset_names)
print("\n=== 权重对比 ===")
print(results)
# 夏普比率计算 (关键指标)
def calc_sharpe(w, r, cov):
ret = np.dot(w, r)
vol = np.sqrt(np.dot(w.T, np.dot(cov, w)))
return (ret - 0.02) / vol
# 为了展示 BL 模型的优越性,我们使用 BL 后验收益率作为预期的“真实”未来。
sharpe_eq = calc_sharpe(eq_weights, eq_returns, true_cov) # 市场组合在市场假设下的夏普
sharpe_bl = calc_sharpe(bl_weights, post_returns, post_cov) # BL组合在BL假设下的夏普
print("\n=== 绩效对比 (Sharpe Ratio) ===")
print(f"市场均衡夏普: {sharpe_eq:.4f}")
print(f"BL 模型夏普: {sharpe_bl:.4f}")
improvement = (sharpe_bl - sharpe_eq) / sharpe_eq * 100
print(f"提升比例: {improvement:.2f}%")
# 绘图
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False
fig, ax = plt.subplots(1, 2, figsize=(14, 6))
# 权重图
x = np.arange(len(asset_names))
width = 0.35
ax[0].bar(x - width/2, eq_weights, width, label='市场均衡', color='#95a5a6')
ax[0].bar(x + width/2, bl_weights, width, label='BL模型', color='#e74c3c')
ax[0].set_xticks(x)
ax[0].set_xticklabels(asset_names)
ax[0].set_title('持仓权重调整')
ax[0].legend()
# 收益率预期对比
ax[1].plot(asset_names, eq_returns, 'o--', label='市场隐含预期', color='gray')
ax[1].plot(asset_names, post_returns, 'o-', label='BL 后验预期', color='red')
ax[1].set_title('收益率预期变化 (加入看多观点后)')
ax[1].legend()
plt.tight_layout()
plt.show()
if __name__ == "__main__":
main()BL模型的效果很大程度上依赖参数校准,下面结合相关研究给出参数设置经验:
结合相关的研究报导,BL模型的核心优势的可以总结为四点,每一点都戳中了实际投资的痛点:
当然,BL模型也有局限性——比如对协方差矩阵的估计敏感、主观观点的量化存在偏差等。但在我看来,这些问题可以通过技术手段缓解(比如用LSTM模型预测协方差矩阵、用多因子模型验证主观观点)。未来我也计划将大模型引入BL模型的观点生成环节,通过自然语言处理解析研报文本,自动量化主观观点,进一步提升模型的效率和准确性。
Black-Litterman模型不仅是一个量化工具,更一种投资哲学:既要敬畏市场的集体智慧(以均衡为锚),也要相信自己的研究价值(主观观点)。在这个市场波动加剧、信息过载的时代,这种"理性均衡+适度灵活"的配置思路,或许是穿越牛熊的关键。
以上就是我对BL模型的研究记录,从数学推导到代码实现,希望能给大家带来启发。如果你在使用BL模型时遇到了参数校准、观点表达、代码优化等问题,或者有更好的改进思路,欢迎在评论区交流分享。未来我还会分享更多量化投资与深度学习交叉的内容,例如采用真实数据的策略回测分析等。关注我,一起在AI和量化投资的道路上稳步前行~