首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >资产配置Black Litterman模型从入门到实践

资产配置Black Litterman模型从入门到实践

作者头像
赛博解生
发布2026-04-09 12:59:18
发布2026-04-09 12:59:18
2960
举报

Black-Litterman模型全解析:从数学推导到代码实现,量化投资的"均衡与主观"融合艺术

大家好,我是赛博解生酱,一个在生物医药和计算机交叉领域摸爬滚打多年的博士。最近抽空学习了下量化投资和资产配置,了解了Black-Litterman(BL)模型,准备在此做个研究记录。该模型的框架非常漂亮,既解决了传统均值-方差模型的"参数敏感陷阱",又巧妙平衡了市场客观规律与投资者主观判断,这种"理性与感性"的融合设计,着实符合真实的主动投资观念和贝叶斯的思想。今天就带着大家从头到尾了解BL模型,从数学本质到代码落地,再到实际应用中的思考,希望能给相关领域和感兴趣的朋友带来启发。

模型概述:为什么BL模型能成为量化配置的"常青树"?

要理解BL模型的价值,首先得从它要解决的核心问题——传统均值-方差模型的缺陷说起。

先搞懂:均值-方差模型到底是什么?

均值-方差模型是马科维茨于1952年提出的现代资产配置基石,核心逻辑特别直白:以资产预期收益率(均值μ)收益率波动的协方差矩阵(Σ) 为核心输入,通过量化"收益-风险"的权衡关系,求解最优资产权重w。它的目标很明确:要么在给定风险(方差)下最大化收益,要么在给定收益下最小化风险,本质是一个带约束的优化问题,核心公式可简化为:

(其中是目标收益,约束条件确保权重总和为1)

再看坑:经典模型的"参数敏感绝症"

但这个看似完美的经典模型,在实际应用中却举步维艰,核心问题就是对输入参数的极端敏感性——也就是常说的"垃圾进,垃圾出":

  • 哪怕对某只资产的预期收益率做0.5%的微小调整(比如从8%调到8.5%),输出的最优权重可能从10%飙升至60%,或直接出现负权重(做空);
  • 极端市场环境下,模型还会给出高度集中的持仓(比如单资产权重超70%),完全违背"分散风险"的投资常识;
  • 更现实的问题是,资产预期收益率μ根本无法精准预测(哪怕用复杂模型,预测误差也常超过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模型的精妙之处在于,它能把这些定性判断转化为严谨的数学表达:

这里的三个核心矩阵需要重点理解,结合我的经验分享:

  • 观点矩阵:维度是"观点数量×资产数量",用来定义每个观点涉及的资产。比如对5只资产,表达"资产1比资产2收益率高3%"的观点时,的对应行就是[1, -1, 0, 0, 0],直观体现了"长资产1、短资产2"的相对关系。
  • 观点收益向量:对应每个观点的预期收益,比如上面的相对观点,的对应元素就是0.03。
  • 信心矩阵:对角矩阵,对角线元素越小,说明对该观点的信心越强。我通常会根据研究深度设定——如果是基于高频数据和多因子模型验证的观点,取0.001左右;如果是偏宏观逻辑的定性判断,会放大到0.01以上,避免过度自信导致的模型偏差。

对应的条件概率分布,本质上是衡量"基于真实收益率,我们的主观观点实现的概率"。

第三步:贝叶斯定理应用——融合先验与观点,得到后验分布

贝叶斯定理的核心是"新信息更新旧认知",在这里就是用主观观点(新信息)更新市场均衡(旧认知),得到更可靠的后验预期收益。

根据贝叶斯公式:

由于分母是与无关的常数(归一化因子),我们只需要关注分子部分,即"先验概率×似然概率":

把前两步的概率密度函数代入,就能得到:

这里的关键洞察是:两个正态分布的乘积依然是正态分布(对数后是二次函数),所以后验分布必然也是正态分布,我们只需要求出它的均值和协方差即可。

第四步:推导后验分布——化简公式,得到核心结论

接下来的工作就是合并指数项中的二次函数,通过配方法化简。先展开指数内的表达式:

令,,通过配方法可以将上式转化为:

常数项

由于常数项不影响分布的形状,最终得到后验分布:

其中后验均值和后验协方差是BL模型的核心输出,也是我们进行资产配置的关键依据:

我对的理解是"加权平均":第一项是市场均衡收益的加权项,权重是先验信息的置信度;第二项是主观观点的加权项,权重是观点的置信度;而整体的权重矩阵是两者的和的逆,本质上是"谁更可靠,谁的权重就更大"。

第五步:后验收益率分布——考虑模型不确定性,完善风险度量

最后需要说明的是,资产实际收益率的后验分布,不仅要考虑的不确定性(),还要考虑资产本身的收益波动(),因此最终的后验协方差矩阵是两者之和:

其中

这个细节在实际应用中非常重要——如果忽略,会低估组合的真实风险,加入后,风险度量才更贴合实际。在得到后验收益率分布后,即可调整资产配置,将后验收益率均值和协方差矩阵输入均值-方差优化框架中。通过求解最优权重公式(如无约束条件下使用 ,得到新的资产权重。这些权重综合了市场均衡信息和主观观点,从而指导增持预期收益高的资产、减持预期收益低的资产,实现风险调整后的配置优化。

完整代码实现:从理论到实践,可直接落地的量化工具

基于上面的数学推导,以下是完整的BL模型实现代码(采用合成数据),均衡收益计算、观点表达、后验推导、组合优化和结果分析等全流程,大家可以结合上述推导过程进行参考。

代码语言:javascript
复制
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模型的效果很大程度上依赖参数校准,下面结合相关研究给出参数设置经验:

  1. τ参数(先验权重调节)
    • 取值范围:0.01-0.05
    • 适用场景:有效市场(如美股)取偏小值(0.02-0.03),因为市场定价更合理,主观观点的边际价值较低;弱有效市场(如A股、新兴市场)取偏大值(0.03-0.05),因为市场存在更多定价偏差,主观研究能创造更多价值。
    • 校准技巧:可以通过回测验证——如果τ太小,组合权重接近市值加权,夏普比率提升有限;如果τ太大,组合会过度依赖主观观点,导致回撤放大。
  2. δ参数(风险厌恶系数)
    • 取值范围:2.5-3.5
    • 适用场景:保守型投资者(如社保基金)取3.0-3.5,追求稳健收益;进取型投资者(如量化私募)取2.5-3.0,愿意承担更多风险换取超额收益。
    • 我的习惯:A股市场默认取2.8,美股取3.0,这个参数对组合权重的影响不如τ和Ω敏感,无需频繁调整。
  3. Ω矩阵(观点信心)
    • 设定逻辑:对角线元素越小,信心越强,通常取0.001-0.01。
    • 实战技巧:相对观点的通常应比绝对观点小。因为预测单资产的涨跌(绝对观点)受市场整体波动干扰大,难度极高;而预测资产间的强弱关系(相对观点,如配对交易)对冲了系统性风险,确定性往往更高。例如,基于基本面逻辑判断“行业A比行业B高3%”时,我们更有把握,可取较小值(如0.001);而单纯看多某资产时,应保留更多不确定性,适当放大(如0.01)。

模型优势总结:

结合相关的研究报导,BL模型的核心优势的可以总结为四点,每一点都戳中了实际投资的痛点:

  1. 稳定性强,避免极端配置:以市场均衡为起点,主观观点只是"微调"而非"颠覆",不会出现传统均值-方差模型的极端权重问题。有研究发现即使主观观点出现小幅偏差,组合最大回撤也能控制在8%以内,而传统模型的回撤常超过15%。
  2. 灵活性高,适配多种观点:支持绝对观点、相对观点,既可以对单只资产表达判断,也可以对行业、板块进行比较,完美契合实际投资中的研究场景(比如A股中"消费 vs 科技"的相对收益)。
  3. 理论严谨,逻辑自洽:基于贝叶斯定理和CAPM理论,既有坚实的数学基础,又符合"市场定价+主观研究"的投资逻辑,不是纯数据驱动的"黑箱模型",便于解释和落地。
  4. 实用性强,门槛友好:不需要预测所有资产的收益率,只需要对少数熟悉的资产表达观点,降低了研究难度。

当然,BL模型也有局限性——比如对协方差矩阵的估计敏感、主观观点的量化存在偏差等。但在我看来,这些问题可以通过技术手段缓解(比如用LSTM模型预测协方差矩阵、用多因子模型验证主观观点)。未来我也计划将大模型引入BL模型的观点生成环节,通过自然语言处理解析研报文本,自动量化主观观点,进一步提升模型的效率和准确性。

写在最后

Black-Litterman模型不仅是一个量化工具,更一种投资哲学:既要敬畏市场的集体智慧(以均衡为锚),也要相信自己的研究价值(主观观点)。在这个市场波动加剧、信息过载的时代,这种"理性均衡+适度灵活"的配置思路,或许是穿越牛熊的关键。

以上就是我对BL模型的研究记录,从数学推导到代码实现,希望能给大家带来启发。如果你在使用BL模型时遇到了参数校准、观点表达、代码优化等问题,或者有更好的改进思路,欢迎在评论区交流分享。未来我还会分享更多量化投资与深度学习交叉的内容,例如采用真实数据的策略回测分析等。关注我,一起在AI和量化投资的道路上稳步前行~

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2025-11-22,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 赛博解生 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • Black-Litterman模型全解析:从数学推导到代码实现,量化投资的"均衡与主观"融合艺术
    • 模型概述:为什么BL模型能成为量化配置的"常青树"?
      • 先搞懂:均值-方差模型到底是什么?
      • 再看坑:经典模型的"参数敏感绝症"
    • 核心数学推导详解:拨开公式迷雾,看懂贝叶斯融合本质
      • 第一步:建立先验分布——以市场均衡为锚,筑牢基准防线
      • 第二步:表达主观观点——把"模糊判断"转化为"量化语言"
      • 第三步:贝叶斯定理应用——融合先验与观点,得到后验分布
      • 第四步:推导后验分布——化简公式,得到核心结论
      • 第五步:后验收益率分布——考虑模型不确定性,完善风险度量
    • 完整代码实现:从理论到实践,可直接落地的量化工具
    • 关键参数说明:
    • 模型优势总结:
    • 写在最后
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档