Black-Litterman 模型

Black-Litterman(BL)模型 [1] 采用贝叶斯方法进行资产配置。 具体来说,它将收益的先验估计(例如,市场暗示的收益)与对某些资产的看法相结合,产生期望收益的后验估计。这样做的好处是:

  • 可以只提供对一个资产子集的看法,BL 将有意义地传播它,考虑到与其他资产的协方差。

  • 可以对你的观点提供信心。

  • 使用 Black-Litterman 后验收益的结果比使用历史平均收益要稳定得多。

从本质上讲,Black-Litterman 将期望收益向量本身视为一个需要估计的数量。Black-Litterman 公式如下:

\[E(R) = [(\tau \Sigma)^{-1} + P^T \Omega^{-1} P]^{-1}[(\tau \Sigma)^{-1} \Pi + P^T \Omega^{-1} Q]\]
  • \(E(R)\) 是一个期望收益的 Nx1 向量,其中 N 是资产的数量。

  • \(Q\) 是一个 Kx1 的观点向量。

  • \(P\) 是 KxN 选取矩阵,它将观点映射到资产领域。基本上,它告诉模型哪种观点对应于哪种资产。

  • \(\Omega\) 是观点的 KxK 不确定性矩阵。

  • \(\Pi\) 是先验期望收益的 Nx1 向量。

  • \(\Sigma\) 是资产收益的 NxN 协方差矩阵(同以往一样)。

  • \(\tau\) 是一个标量调谐常数。

虽然这个公式看起来很麻烦,但事实证明,这个公式只是代表了对收益的事先估计和观点之间的加权平均,其中的加权是由对观点的信心和参数决定的。

同样地,我们可以计算协方差矩阵的后验估计:

\[\hat{\Sigma} = \Sigma + [(\tau \Sigma)^{-1} + P^T \Omega^{-1} P]^{-1}\]

虽然算法相对简单,但从软件工程的角度来看,BL 被证明是一个挑战,因为它不太清楚如何最好地将其纳入 PyPortfolioOpt 的 API。 完整的讨论可以在 Github Issue 中找到,但我最终决定,虽然 BL 在技术上不是一个优化器,但把它的方法分割到 expected_returnsrisk_models 中并没有意义。 因此,我把它变成了一个独立的模块,并且由于理论比较广泛,我给了它一个专门的文档页面。我想感谢 Felipe Schneider 对 Black-Litterman 实现的多种贡献。 关于其使用的完整例子,包括免费获取市值数据,请参考 cookbook

小技巧

Thomas Kirschenmann 在 PyPortfolioOpt 之上建立了一个整洁的交互式 Black-Litterman 工具 ,它允许你可视化 BL 输出并比较优化目标。

先验

可以把先验看作是在没有任何信息的情况下的默认估计。Black and Litterman (1991) [2] 提供了这样的见解:这种先验的自然选择是市场对收益的估计,它被嵌入到资产的市值中。

市场投资组合中的每一项资产都会给投资组合带来一定的风险。标准理论认为,投资者会为他们所承担的风险得到补偿,因此我们可以为每项资产赋予预期补偿(即对收益的先验估计)。 这可以通过市场隐含的风险溢价来量化,即市场的超额收益除以其方差:

\[\delta = \frac{R-R_f}{\sigma^2}\]

为了计算市场隐含的收益,我们使用以下公式:

\[\Pi = \delta \Sigma w_{mkt}\]

此处, \(w_{mkt}\) 表示市值权重。这个公式是计算一项资产贡献的风险总量,并与风险的市场价格相乘,得出市场隐含的收益向量 \(\Pi\) 。 我们可以用 PyPortfolioOpt 来计算,如下:

from pypfopt import black_litterman, risk_models

"""
cov_matrix is a NxN sample covariance matrix
mcaps is a dict of market caps
market_prices is a series of S&P500 prices
"""
delta = black_litterman.market_implied_risk_aversion(market_prices)
prior = black_litterman.market_implied_prior_returns(mcaps, delta, cov_matrix)

没有什么可以阻止你使用任何你认为合适的先验(但别超出三维世界)。 如果你认为历史平均收益率是一个很好的先验,你可以用这个。但大量的研究表明,历史平均收益率是一个完全没有参考价值的先验。

备注

从技术上讲,你不需要向 Black-Litterman 模型提供先验估计。 如果你的观点(和置信度)是由一些专有模型产生的,这一点特别有用,在这种情况下,BL 本质上是一种混合你的观点的巧妙方式。

观点

在 Black-Litterman 模型中,用户可以提供绝对或相对的观点。绝对观点是指像这样的陈述:”AAPL 将猛涨 10%”或”XOM 将狂跌 40%”。 相对观点像是”GOOG 将相比 FB 涨 3%”这样的陈述。

这些观点必须在向量 \(Q\) 中指定,并通过选取矩阵 \(P\) 映射到资产领域。 下面是一个简单的例子, Idzorek 给出了一个全面的指南。 比方说,我们的资产池是由有序列表定义的:SBUX, GOOG, FB, AAPL, BAC, JPM, T, GE, MSFT, XOM。我们想对这 10 种资产表达四种观点,包括两种绝对观点和两种相对观点:

  1. SBUX 将下跌 20%(绝对值)

  2. MSFT 将上涨 5%(绝对值)

  3. GOOG 的表现比 FB 好 10%。

  4. BAC 和 JPM 的表现将超过 T 和 GE 的 15%。

相应的意见向量是由上述数字组成的,我们将其放入一列:

Q = np.array([-0.20, 0.05, 0.10, 0.15]).reshape(-1, 1)

挑选矩阵更有趣。请记住,它的作用是将观点(提到 8 种资产)与 10 种资产的资产池联系起来。 可以说,这是该模型最重要的部分,因为它使我们能够将我们的预期(以及对预期的信心)传播到模型中:

P = np.array(
    [
        [1, 0, 0, 0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0, 0, 1, 0],
        [0, 1, -1, 0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0.5, 0.5, -0.5, -0.5, 0, 0],
    ]
)

对上述情况的简要解释:

  • 每个观点在挑选矩阵中都有一个对应的行(顺序很重要)

  • 绝对观点在与股票在资产池的顺序相对应的列中有一个单一的 1。

  • 相对观点在名义上表现出色的资产列中有一个正数,在名义上表现不佳的资产列中有一个负数。每一行的数字之和应该为 0。

PyPortfolioOpt 提供了一个辅助方法,用于以 dictpd.Series 的形式输入绝对观点。如果你有相对观点,你必须手动建立你的选取矩阵:

from pypfopt.black_litterman import BlackLittermanModel

viewdict = {"AAPL": 0.20, "BBY": -0.30, "BAC": 0, "SBUX": -0.2, "T": 0.15}
bl = BlackLittermanModel(cov_matrix, absolute_views=viewdict)

置信矩阵和 \(\tau\)

置信矩阵是一个对角线协方差矩阵,包含每个观点的方差。计算 \(\Omega\) 的一种启发式方法是说它与先验方差成正比。 这是合理的,经常变动的数量更难预测!因此,PyPortfolioOpt 并没有对其进行预测!因此,PyPortfolioOpt 并不要求你输入置信矩阵,而是默认为:

\[\Omega = \tau * P \Sigma P^T\]

另外,我们还提供了 Idzorek method [1] 的实现。这允许你将你的观点不确定性指定为百分比置信度。 要使用这个方法,请选择 omega="idzorek" 并在 view_confidences 参数中传递一个置信度列表(从0到1)。

当然,我们欢迎你提供你自己的估计。如果你的观点是一些统计模型的输出,这一点尤其适用,因为统计模型也可能提供观点的不确定性。

另一个控制先验观点的相对权重的参数是 \(\tau\) 。关于这个参数的调整有很多说法,有很多相互矛盾的经验法则。 事实上,已经有一整篇论文是关于它的 [3] 。我们选择合理的默认值 \(\tau = 0.05\)

备注

如果你使用 \(\Omega\) 默认的估计或 omega="idzorek" ,结果发现 \(\tau\) 的值并不重要。 这是数学上的一个结果: \(\tau\) 在矩阵乘法中被抵消了。

BL 模型的输出

BL 模型会输出收益和协方差矩阵的后验估计值。 文献中的默认建议是将这些数据输入优化器(见 通用有效前沿 )。 一个快速的替代方法是计算收益向量 [4] 所隐含的权重,这对于调试来说非常有用。 这实际上与我们用来计算市场权重所隐含的收益的程序相反。

\[w = (\delta \Sigma)^{-1} E(R)\]

在 PyPortfolioOpt 中, BlackLittermanModel.bl_weights() 可以实现这个功能。 因为 BlackLittermanModel 类继承自 BaseOptimizer ,所以它遵循与 EfficientFrontier 对象相同的 API:

from pypfopt import black_litterman
from pypfopt.black_litterman import BlackLittermanModel
from pypfopt.efficient_frontier import EfficientFrontier

viewdict = {"AAPL": 0.20, "BBY": -0.30, "BAC": 0, "SBUX": -0.2, "T": 0.15}
bl = BlackLittermanModel(cov_matrix, absolute_views=viewdict)

rets = bl.bl_returns()
ef = EfficientFrontier(rets, cov_matrix)

# OR use return-implied weights
delta = black_litterman.market_implied_risk_aversion(market_prices)
bl.bl_weights(delta)
weights = bl.clean_weights()

文档参考

black_litterman 模块包含 BlackLittermanModel 类,它在给定先验估计值和用户提供的观点的情况下,生成期望收益的后验估计值。 此外,还定义了两个效用函数,它们计算:

  • 市场隐含的收益的先验估计

  • 市场隐含的风险规避参数

class pypfopt.black_litterman.BlackLittermanModel(cov_matrix, pi=None, absolute_views=None, Q=None, P=None, omega=None, view_confidences=None, tau=0.05, risk_aversion=1, **kwargs)[源代码]

BlackLittermanModel 对象(继承自 BaseOptimizer)包含需要一个特定的输入格式, 指定先验、观点、观点中的不确定性,以及将观点映射到资产范围的拾取矩阵。 然后我们可以计算收益和协方差的后验估计值。在可能的情况下,我们提供了帮助方法来提供默认值。

可用变量:

  • 输入:

    • cov_matrix - np.ndarray

    • n_assets - int

    • tickers - str list

    • Q - np.ndarray

    • P - np.ndarray

    • pi - np.ndarray

    • omega - np.ndarray

    • tau - float

  • 输出:

    • posterior_rets - pd.Series

    • posterior_cov - pd.DataFrame

    • weights - np.ndarray

公共方法:

  • default_omega() 观点不确定性与资产方差成比例

  • idzorek_method() 将指定为百分比的观点转换为 BL 不确定度

  • bl_returns() 收益的后验估计值

  • bl_cov() 协方差的后验估计值

  • bl_weights() 后验收益所隐含的权重

  • portfolio_performance() 计算投资组合的期望收益率、波动率和夏普比率。

  • set_weights() 从权重数据中创建 self.weights (np.ndarray)。

  • clean_weights() 对权重进行四舍五入,并剪掉接近零的部分。

  • save_weights_to_file() 将权重保存为 csv、json 或 txt。

__init__(cov_matrix, pi=None, absolute_views=None, Q=None, P=None, omega=None, view_confidences=None, tau=0.05, risk_aversion=1, **kwargs)[源代码]
参数:
  • cov_matrix (pd.DataFrame or np.ndarray) – NxN covariance matrix of returns

  • pi (np.ndarray, pd.Series, optional) – Nx1 prior estimate of returns, defaults to None. If pi=”market”, calculate a market-implied prior (requires market_caps to be passed). If pi=”equal”, use an equal-weighted prior.

  • absolute_views (pd.Series or dict, optional) – a collection of K absolute views on a subset of assets, defaults to None. If this is provided, we do not need P, Q.

  • Q (np.ndarray or pd.DataFrame, optional) – Kx1 views vector, defaults to None

  • P (np.ndarray or pd.DataFrame, optional) – KxN picking matrix, defaults to None

  • omega (np.ndarray or Pd.DataFrame, or string, optional) – KxK view uncertainty matrix (diagonal), defaults to None Can instead pass “idzorek” to use Idzorek’s method (requires you to pass view_confidences). If omega=”default” or None, we set the uncertainty proportional to the variance.

  • view_confidences (np.ndarray, pd.Series, list, optional) – Kx1 vector of percentage view confidences (between 0 and 1), required to compute omega via Idzorek’s method.

  • tau (float, optional) – the weight-on-views scalar (default is 0.05)

  • risk_aversion (positive float, optional) – risk aversion parameter, defaults to 1

  • market_caps (np.ndarray, pd.Series, optional) – (kwarg) market caps for the assets, required if pi=”market”

  • risk_free_rate (float, defaults to 0.02) – (kwarg) risk_free_rate is needed in some methods

小心

你必须指定协方差矩阵,和绝对观点或同时指定 Q 和 P,除非在特殊情况下,你为每个资产正好提供一个视图,在这种情况下,P 能被推断出来。

bl_cov()[源代码]

考虑到对某些资产的看法,计算协方差矩阵的后验估计。基于 He and Litterman(2002)。 假设 omega 是对角线。如果不是这样,请手动设置 omega_inv。

返回:

posterior covariance matrix

返回类型:

pd.DataFrame

bl_returns()[源代码]

鉴于对某些资产的看法,计算收益向量的后验估计。

返回:

posterior returns vector

返回类型:

pd.Series

bl_weights(risk_aversion=None)[源代码]

考虑到风险的市场价格,计算后验收益所隐含的权重。从技术上讲,这可以适用于任何期望收益的估计,实际上是均值-方差优化的一个特例。

\[w = (\delta \Sigma)^{-1} E(R)\]
参数:

risk_aversion (positive float, optional) – risk aversion parameter, defaults to 1

返回:

asset weights implied by returns

返回类型:

OrderedDict

static default_omega(cov_matrix, P, tau)[源代码]

如果没有提供不确定性矩阵 omega,我们使用 He and Litterman(1999)的方法进行计算,这样 omega/tau 的比率与观点组合的方差成比例。

返回:

KxK diagonal uncertainty matrix

返回类型:

np.ndarray

static idzorek_method(view_confidences, cov_matrix, pi, Q, P, tau, risk_aversion=1)[源代码]

使用 Idzorek 的方法来创建给定用户指定的置信度百分比的不确定性矩阵。 我们使用 Jay Walters 在《The Black-Litterman Model in Detail》(2014)中描述的闭合式解决方案。

参数:

view_confidences (np.ndarray, pd.Series, list,, optional) – Kx1 vector of percentage view confidences (between 0 and 1), required to compute omega via Idzorek’s method.

返回:

KxK diagonal uncertainty matrix

返回类型:

np.ndarray

optimize(risk_aversion=None)[源代码]

bl_weights 的别名,以便与其他方法保持一致。

portfolio_performance(verbose=False, risk_free_rate=0.02)[源代码]

优化后,计算(并可选择打印)最优投资组合的表现。目前计算的是期望收益率、波动率和夏普比率。该方法使用 BL 后验收益和协方差矩阵。

参数:
  • verbose (bool, optional) – whether performance should be printed, defaults to False

  • risk_free_rate (float, optional) – risk-free rate of borrowing/lending, defaults to 0.02. The period of the risk-free rate should correspond to the frequency of expected returns.

抛出:

ValueError – if weights have not been calculated yet

返回:

expected return, volatility, Sharpe ratio.

返回类型:

(float, float, float)

pypfopt.black_litterman.market_implied_prior_returns(market_caps, risk_aversion, cov_matrix, risk_free_rate=0.02)[源代码]

计算市场权重所隐含的收益的先验估计。换句话说,鉴于每种资产对市场组合风险的贡献,我们期望得到多少补偿?

\[\Pi = \delta \Sigma w_{mkt}\]
参数:
  • market_caps ({ticker: cap} dict or pd.Series) – market capitalisations of all assets

  • risk_aversion (positive float) – risk aversion parameter

  • cov_matrix (pd.DataFrame) – covariance matrix of asset returns

  • risk_free_rate (float, optional) – risk-free rate of borrowing/lending, defaults to 0.02. You should use the appropriate time period, corresponding to the covariance matrix.

返回:

prior estimate of returns as implied by the market caps

返回类型:

pd.Series

pypfopt.black_litterman.market_implied_risk_aversion(market_prices, frequency=252, risk_free_rate=0.02)[源代码]

根据市场价格计算市场暗示的风险规避参数(即风险的市场价格)。 例如,如果市场每年的超额收益为 10%,方差为 5%,则风险规避参数为 2,即你必须得到 2 倍的方差补偿。

\[\delta = \frac{R - R_f}{\sigma^2}\]
参数:
  • market_prices (pd.Series with DatetimeIndex.) – the (daily) prices of the market portfolio, e.g SPY.

  • frequency (int, optional) – number of time periods in a year, defaults to 252 (the number of trading days in a year)

  • risk_free_rate (float, optional) – risk-free rate of borrowing/lending, defaults to 0.02. The period of the risk-free rate should correspond to the frequency of expected returns.

抛出:

TypeError – if market_prices cannot be parsed

返回:

market-implied risk aversion

返回类型:

float

参考文献