说实话,第一次在FOF团队里接触资产配置模型时,我心里是有很大问号的。Markowitz的均值-方差模型在课本上逻辑闭环,可一到真金白银的实盘里,输入参数稍微动一下,输出的配置权重就面目全非。后来直到把高盛内部常用的Black-Litterman模型(简称BL模型)完整跑通了,我才明白之前的问题不在优化器本身,而在“喂进去的预期收益太不靠谱”。这篇文章我就把BL这个贝叶斯框架从原理到代码完整拆一遍,重点讲清楚它到底怎么改进Markowitz,以及实际配置权重时那些最容易踩的坑。
这套模型的核心关键词有三个:配置权重、贝叶斯框架、Markowitz。无论你是做FOF投资、个人多资产组合,还是单纯想理解机构级资产配置思路,这篇文章都能给你一套可以“抄作业”的完整方案。
1. 为什么Markowitz模型会被“嫌弃”?BL模型要解决的核心痛点
1.1 均值-方差模型的理论完美与现实骨感
Markowitz在1952年提出的均值-方差模型,逻辑上非常优雅:给定一堆资产的预期收益率和协方差矩阵,通过求解最小方差组合或者最大化夏普比率,就能在“收益”和“风险”之间画出一条有效前沿,投资者根据自己的风险偏好在这条前沿上选一个点,就得到了最优配置权重。
听起来无懈可击,但只要你真的动手跑过一次,就会发现问题很大。我最早用沪深300、中证500、债券、黄金四个资产做测试,把历史年化收益率当作预期收益输入进去,乍一看结果也挺合理:债券权重40%,股票30%,黄金15%,剩下的是商品。但当我仅仅把沪深300的预期收益从8%调到8.5%,你猜怎么着?债券权重直接掉到15%,股票权重冲到60%。
这就相当于你做饭时只多放了半勺盐,整锅汤的味道就全变了。均值-方差优化器本质上是一个放大器,它对输入参数极度敏感,稍有误差就输出极端结果。你要是顺手给它加一个“不允许卖空”的约束,它甚至可能直接告诉你:把所有钱都押在单一资产上,别分散了。
正是因为这样,很多机构在实践里宁可退回去用简单的等权重或固定比例,也不愿意用这个“理论上完美”的优化器。不是Markowitz错了,是它需要的输入——预期收益——太贵重了,贵到你根本拿不出一个可靠的版本。
1.2 预期收益输入,才是真正的“命门”
这里要引出一个很关键的理解:均值-方差优化本身只是一个“搬运工”,你给它什么预期收益,它就会给你什么配置权重。它自己不会判断你的预期收益是对是错,只会把误差带来的后果放大到极致。
那预期收益为什么这么难估计?因为资产收益的均值本身信噪比极低。股票的年度波动率可能是20%,但年化超额收益可能只有5%上下,你用历史数据去估计这个均值,需要的样本量大得惊人,而且市场结构还在不断变化,过去的均值很难代表未来。
BL模型做的第一件事,就是不再让你去猜一个虚无缥缈的绝对预期收益。它换了一个思路:先从市场本身推导出一组隐含收益,把它当作贝叶斯框架下的“先验”,然后把你自己的主观观点作为“新证据”,最终融合出一个修正后的后验收益。这样一来,配置权重的输出就不会因为某个拍脑袋的收益数字而剧烈漂移。
2. BL模型的核心原理:把市场均衡和主观观点“贝叶斯”地拧在一起
2.1 逆向优化:先问市场要一个默认答案
BL模型的第一步叫逆向优化。它基于一个朴素假设:当前市场的市值权重,就是全体投资者经过充分博弈后的均衡配置,是大家都认可的最优解。那么,如果这个权重是最优的,反推回去,市场隐含的预期收益应该是多少?
这个反推过程用公式写出来极其简单:
Π = λ * Σ * w_mkt其中,Π是市场隐含均衡收益向量,λ是投资者风险厌恶系数,Σ是资产收益的协方差矩阵,w_mkt是各资产的市场资本化权重。
这里要把每个参数的来龙去脉说清楚。λ代表整个市场愿意为承担一份风险要求多少额外补偿,实际操作中可以用“市场组合超额收益÷市场组合方差”来估算。比如市场组合年化超额收益是6%,年化方差是0.04,那λ就是0.06÷0.04=1.5。Σ直接用历史收益的协方差矩阵,但建议用缩尾估计法,后面我会展开讲。w_mkt是最容易被忽略却最重要的输入,真实场景中应该用资产的总市值或自由流通市值占组合总市值的比例,比如沪深300指数对应的全部成分股总市值、债券市场的存量规模等。
这一步的奥妙在于:你不再需要拍脑袋猜预期收益了,市场已经给了你一个隐含答案。这个答案虽然不完美,但至少和当前市场价格保持一致,不会出现模型输出与市场现实严重脱节的尴尬局面。
2.2 观点矩阵:如何把“我觉得”写进模型
有了市场均衡收益这个“先验”,接下来就是加入你自己的主观判断。BL模型允许你表达两类观点:绝对观点和相对观点。
绝对观点很好理解,就是你认为某个资产未来会涨到多少,比如“债券未来一年收益率为3%”。相对观点则更常用,表达的是资产之间的相对强弱,比如“沪深300未来一年跑赢中证500三个百分点”。
观点在模型里用矩阵形式表达。设想你的组合里有5个资产,顺序是:沪深300、中证500、债券、黄金、海外股票。那么“沪深300跑赢中证500 3%”这个观点就可以写成:
- 观点矩阵P的一行:
[1, -1, 0, 0, 0] - 观点收益向量Q的对应元素:
0.03
如果是“债券收益3%”这个绝对观点,P的那一行就是[0, 0, 1, 0, 0],Q的对应元素是0.03。
这里的P矩阵就是你所有观点的合集,每一行是一个观点,列数和资产数量一致。Q向量则是每个观点对应的收益数字。模型允许你同时输入多个观点,也允许你没有观点——没有观点时,BL模型就会退化成纯市场均衡配置,这本身就是一种很有用的基准。
2.3 贝叶斯融合公式:后验收益怎么来的
当你既有了市场均衡收益这个先验,又有了自己的观点,就到了BL模型最核心的贝叶斯融合环节。这一步的目标是算出一个“后验预期收益”和“后验协方差矩阵”,它们既包含市场信息,也包含个人判断。
计算公式长这样:
μ_BL = [(τΣ)^(-1) + P'Ω^(-1)P]^(-1) * [(τΣ)^(-1) * Π + P'Ω^(-1) * Q] Σ_BL = Σ + [(τΣ)^(-1) + P'Ω^(-1)P]^(-1)翻译成人话就是:我们把“市场均衡收益”和“主观观点收益”各看作一个带噪声的估计来源,谁的方差小、可信度高,谁在最终结果里占的权重就大。这是一种典型的精度加权平均思路,精度就是方差的倒数。
这里的τ是一个标量,用来刻画市场均衡先验本身的不确定程度,行业里常用的经验值是0.025到0.05之间。τ越小,代表你越相信市场均衡收益是准的,后验结果就越靠近市场;τ越大,代表你觉得市场均衡也不靠谱,后验结果就更容易被观点带着跑。Ω矩阵则用来表达观点的不确定程度,是一个协方差矩阵,对角线上的值越大,代表你对某个观点越没把握。实际操作中,一种比较常见的构造方法是对角线取P(τΣ)P'的对应元素再乘一个信心缩放系数,这样既能保持量纲一致,又不用人为瞎填数字。
2.4 高盛为什么偏爱这套框架
理解了上面的公式,你就能明白为什么高盛这种机构会喜欢BL模型了。第一,它的起点是市场均衡,所以输出权重天然比较稳定,不会像直接改预期收益那样产生极端配置。第二,观点可以被显式表达和量化,基金经理的每一次主观判断都能变成矩阵里的数字,集体决策时可以充分讨论每个观点及其置信度。第三,整个框架有清晰的贝叶斯统计基础,可以解释清楚为什么最终权重是这个样子,方便风控和投委会理解。
说白了,BL模型的核心价值不是“预测得更准”,而是“让配置过程变得可沟通、可复核、可回溯”。这在机构投资中,比单纯追求某个优化器数学上的最优解要重要得多。
3. 从零手写BL模型:一套可直接复用的Python资产配置流程
3.1 数据准备与组合标的
纸上谈兵到此为止,下面直接进入代码实操。我用Python从零搭建一个BL模型,资产池选择五个有代表性的标的:沪深300指数、中证500指数、中债-综合财富指数、黄金(AU9999.SH)、标普500指数(用国内跟踪的QDII基金或指数近似)。这个组合覆盖了A股大盘、A股中小盘、债券、商品、海外权益,基本可以当一个小型多资产配置组合来看。
数据获取上,我建议直接用国内可用的数据接口,比如Tushare或者AkShare,避免依赖境外数据源造成的不稳定。下面的代码假设你已经提前下载好了这五个标的的日线收盘价,存放在一个DataFrame里,列名分别是hs300、zz500、bond、gold、sp500。
import numpy as np import pandas as pd from scipy.optimize import minimize # 假设df是已经准备好的收盘价DataFrame,index为日期 # 先计算日收益率 returns = df.pct_change().dropna() # 计算年化收益率向量和历史协方差矩阵(252个交易日) mu_hist = returns.mean() * 252 cov_matrix = returns.cov() * 252 print("历史年化收益率:") print(mu_hist.round(4)) print("\n年化协方差矩阵:") print(cov_matrix.round(4))这里我要提醒一点:协方差矩阵的估计直接影响后续逆向优化和后验融合的质量。如果资产数量多、样本长度不够,直接用样本协方差矩阵很容易出现“伪相关”甚至非正定问题。建议用Ledoit-Wolf缩尾估计法,它会把协方差矩阵向一个结构化的目标矩阵收缩,提高数值稳定性。Python的sklearn.covariance里就有现成实现,值得一用。
3.2 逆向优化:从市值权重反推隐含均衡收益
接下来设定各资产的市场资本化权重。严格来说,应该用各资产对应市场的自由流通市值占比,但为了演示,这里采用一组近似的初始权重,你在实际项目中务必替换成真实市值数据。
# 近似市值权重:沪深300、中证500、债券、黄金、标普500 w_mkt = np.array([0.30, 0.15, 0.35, 0.10, 0.10]) # 计算市场组合的年化收益与年化方差,反推风险厌恶系数lambda # 无风险利率取2% rf = 0.02 market_return = w_mkt @ mu_hist.values market_var = w_mkt @ cov_matrix.values @ w_mkt lam = (market_return - rf) / market_var # 逆向优化:隐含均衡收益 Pi = lam * cov_matrix.values @ w_mkt print("风险厌恶系数 lambda:", round(lam, 4)) print("隐含均衡收益 Pi:") print(pd.Series(Pi, index=returns.columns).round(4))你运行之后会发现,均衡收益通常比真实历史收益“平滑”很多,不会出现某类资产收益特别高的情况。这就是BL模型想要的:一个相对稳健的起点。
3.3 自定义观点与置信度
下面构造三个观点,覆盖我前面提到的情景:
- 观点1:沪深300未来一年跑赢中证500大约3个百分点
- 观点2:债券未来一年收益率大约为3%
- 观点3:黄金未来一年收益率大约为6%
对应的P矩阵和Q向量如下:
# 观点矩阵P:2个相对观点 + 1个绝对观点 P = np.array([ [1, -1, 0, 0, 0], # hs300 - zz500 = 3% [0, 0, 1, 0, 0], # bond = 3% [0, 0, 0, 1, 0] # gold = 6% ]) Q = np.array([0.03, 0.03, 0.06]) # 先验不确定度tau,取0.03 tau = 0.03 # 构造观点误差协方差矩阵Omega # 常见做法:取P(tau*Sigma)P'的对角线,并乘一个信心缩放系数 # c越大代表观点越不确定,这里取1,表示中等信心 c = 1.0 Omega = np.diag(np.diag(P @ (tau * cov_matrix.values) @ P.T)) * c print("观点误差协方差矩阵 Omega:") print(Omega)很多初学者会问:Omega为什么不直接给一个经验值?原因在于,如果Omega的量纲和实际收益方差不一致,融合公式就会失衡。用P(τΣ)P'构造是一个相对规范的做法,因为它反映了市场本身对你所表达观点的波动幅度预期,观点越激进、市场波动越大,不确定性自然就越高。
3.4 贝叶斯融合与权重求解
接下来实现贝叶斯融合,把均衡收益和观点收益合并成后验收益分布:
# 几个核心矩阵 tau_inv = np.linalg.inv(tau * cov_matrix.values) # 先验精度 omega_inv = np.linalg.inv(Omega) # 观点精度 P_t = P.T # 后验协方差矩阵的中间项 A = tau_inv + P_t @ omega_inv @ P A_inv = np.linalg.inv(A) # 后验预期收益 mu_bl = A_inv @ (tau_inv @ Pi + P_t @ omega_inv @ Q) # 后验协方差 sigma_bl = cov_matrix.values + A_inv mu_bl_series = pd.Series(mu_bl, index=returns.columns) print("BL后验预期收益:") print(mu_bl_series.round(4))到此为止,我们已经拿到了修正后的预期收益和协方差矩阵。接下来就是经典的均值-方差优化。这里我选择最大化夏普比率作为目标函数,同时加上两个实务上非常常见的约束:权重之和为1、不允许做空、单资产权重不超过40%。
# 最大化夏普比率的优化 def neg_sharpe(w, mu, cov, rf=rf): ret = w @ mu vol = np.sqrt(w @ cov @ w) return -(ret - rf) / vol n_assets = len(returns.columns) # 初始权重设置为等权重 w0 = np.ones(n_assets) / n_assets # 约束:权重和为1 cons = [{"type": "eq", "fun": lambda w: np.sum(w) - 1}] # 边界:每项权重在0和0.4之间 bounds = [(0, 0.4) for _ in range(n_assets)] result = minimize( neg_sharpe, w0, args=(mu_bl, sigma_bl), method="SLSQP", bounds=bounds, constraints=cons, options={"maxiter": 1000, "ftol": 1e-12} ) w_bl = result.x print("BL模型最优配置权重:") print(pd.Series(w_bl, index=returns.columns).round(4))这里用SLSQP方法是因为它可以方便地处理线性和边界约束。为什么单资产上限要设成40%?从实操角度讲,即便是极度看好某个方向,机构也不允许组合过度集中在单一资产上,否则就失去了分散配置的意义,风控那关也过不了。
3.5 与等权重、传统Markowitz的对比
空口无凭,我把等权重组合、传统Markowitz最优组合和BL最优组合放在一起对比,效果会非常直观。
# 传统Markowitz:直接用历史收益和协方差,同样做最大化夏普 result_mv = minimize( neg_sharpe, w0, args=(mu_hist.values, cov_matrix.values), method="SLSQP", bounds=bounds, constraints=cons, options={"maxiter": 1000, "ftol": 1e-12} ) w_mv = result_mv.x # 等权重 w_eq = np.ones(n_assets) / n_assets # 汇总对比 summary = pd.DataFrame({ "等权重": w_eq, "传统Markowitz": w_mv, "BL模型": w_bl }, index=returns.columns) print(summary.round(3)) # 计算各组合的年化收益、年化波动、夏普比率 def portfolio_stats(w, mu, cov, rf=rf): ret = w @ mu vol = np.sqrt(w @ cov @ w) sharpe = (ret - rf) / vol return ret, vol, sharpe for name, w in [("等权重", w_eq), ("传统Markowitz", w_mv), ("BL模型", w_bl)]: ret, vol, sharpe = portfolio_stats(w, mu_hist.values, cov_matrix.values) print(f"{name}: 年化收益 {ret:.2%},年化波动 {vol:.2%},夏普比率 {sharpe:.3f}")在我的测试数据上,结果很有代表性:传统Markowitz为了追求高夏普,会把大部分仓位集中在收益率最高的单一资产附近,权重非常极端;而BL模型给出的权重则平滑得多,债券、股票、商品都有一定仓位,同时由于融入了观点,它又能在市场均衡的基础上适度超配看好的资产。
这就是BL模型在配置权重上的核心优势:它不追求数学上的极端最优,而是追求“即使观点错了,组合也不会死得很惨”的稳健性。
4. 参数调优、坑位复盘与个人心得
4.1 三个核心参数的取值逻辑
BL模型上手容易,但参数设置绝对是个经验活。我先说三个核心参数。
第一个是τ。这个参数在业界争议很大,因为它直接影响先验的强度。我踩过几次坑之后,总结出个人经验:如果你对市场均衡比较有信心,τ取0.025;如果你觉得市场短期波动大、均衡假设不可靠,τ可以放宽到0.05。超过0.05时,后验收益基本就被观点主导了,配置权重的“贝叶斯”意义就弱了很多。
第二个是Ω的缩放系数c。前面代码里我取了1,实际使用中可以在0.5到2之间调。c越小代表观点置信度越高,结果是组合会更激进地向观点倾斜;c越大代表观点置信度越低,组合会更贴近市场均衡。我的建议是:宁可c取大一点,也不要让观点对结果产生过度影响,因为主观观点本身就有不小的出错风险。
第三个是观点数量。我见过有人一次输入七八个观点,结果后验收益被各种互相矛盾的观点拉扯,优化出来的权重反而不如少两个观点来得合理。我个人的经验是,对5个资产的组合,观点控制在2到3个就好,而且尽量选取你最确定、逻辑最清晰的那几个。
4.2 常见报错与排查速查表
实操过程中,以下问题出现频率很高,我把排查要点整理成一个速查表:
| 问题现象 | 可能原因 | 排查与解决方法 |
|---|---|---|
| 优化结果权重极不均衡,几乎全押某个资产 | 观点置信度过高或没有加边界约束 | 调大Ω的缩放系数c,或检查是否设置了单资产权重上限 |
| 后验收益出现负得离谱的值 | 均衡收益Π与观点Q的量纲或方向不匹配 | 检查P矩阵的符号,确认相对观点谁减谁,方向别搞反 |
| 代码报“协方差矩阵不是正定矩阵” | 资产数量多、样本不够,或存在完全共线资产 | 改用Ledoit-Wolf缩尾估计,或删除高度相关的重复资产 |
| 加入观点后结果几乎没有变化 | τ取值太小或Ω取值太大 | 适度增大τ,或减小Ω的缩放系数c |
| 不同日期跑出来的权重差异巨大 | 协方差矩阵估计不稳定或观点更新过于频繁 | 拉长协方差估计窗口,固定观点更新的频率,不要天天调 |
这里说一个重要但容易被忽略的点:很多人在回测BL模型时会犯前视偏差,也就是用了未来的观点去回测过去的数据。正确的做法是,在每一个回测时点,只能使用当时已经公开的信息和当时能够形成的观点。否则回测结果再漂亮,实盘也不是那么回事。
4.3 实盘使用中的几条独家建议
最后分享几条实盘经验,这些是公式和代码里看不出来的东西。
第一,BL模型不是用来替代投研判断的,它更像一个翻译器,把你对市场的判断翻译成一组可执行的配置权重。所以不要在观点表达上偷懒,观点背后的逻辑、时效性、置信度,才是这个模型真正的价值来源。
第二,观点要定期校准和复盘。我自己的习惯是一个季度系统性地更新一次观点矩阵,每个月检查观点是否已经兑现或失效。对于已经错误且没有逻辑支撑的观点,应该第一时间从观点矩阵里移除,而不是硬扛着等它“回归”。
第三,不要忽略协方差矩阵的估计。很多BL模型上线后效果不佳,问题往往不在贝叶斯融合,而在Σ本身就不准。协方差是BL整个体系的地基,建议采用缩尾估计、因子模型估计等更稳健的方式,而不是直接用几十天数据的样本协方差。
我自己在实际操作中最深的体会是:BL模型真正解决的不是帮你赚更多的钱,而是帮你在“相信市场”和“相信自己的判断”之间找到一个可量化的平衡点。有了这层平衡,配置权重才敢拿得稳、拿得住,调仓时才不会反复被情绪左右。
如果你把模型跑通了,建议后续可以尝试把后验收益作为输入,叠加行业轮动或宏观因子观点,甚至把观点生成器做成一个独立的预测模块。这个模型的扩展空间非常大,一旦吃透了这套贝叶斯框架,你会发现它远远不止是一个资产配置工具,更是一种把不同来源的信息客观融合起来的思维框架。