Black-Litterman 模型
Black-Litterman(BL)模型 [1] 采用贝叶斯方法进行资产配置。 具体来说,它将收益的先验估计(例如,市场暗示的收益)与对某些资产的看法相结合,产生期望收益的后验估计。这样做的好处是:
可以只提供对一个资产子集的看法,BL 将有意义地传播它,考虑到与其他资产的协方差。
可以对你的观点提供信心。
使用 Black-Litterman 后验收益的结果比使用历史平均收益要稳定得多。
从本质上讲,Black-Litterman 将期望收益向量本身视为一个需要估计的数量。Black-Litterman 公式如下:
\(E(R)\) 是一个期望收益的 Nx1 向量,其中 N 是资产的数量。
\(Q\) 是一个 Kx1 的观点向量。
\(P\) 是 KxN 选取矩阵,它将观点映射到资产领域。基本上,它告诉模型哪种观点对应于哪种资产。
\(\Omega\) 是观点的 KxK 不确定性矩阵。
\(\Pi\) 是先验期望收益的 Nx1 向量。
\(\Sigma\) 是资产收益的 NxN 协方差矩阵(同以往一样)。
\(\tau\) 是一个标量调谐常数。
虽然这个公式看起来很麻烦,但事实证明,这个公式只是代表了对收益的事先估计和观点之间的加权平均,其中的加权是由对观点的信心和参数决定的。
同样地,我们可以计算协方差矩阵的后验估计:
虽然算法相对简单,但从软件工程的角度来看,BL 被证明是一个挑战,因为它不太清楚如何最好地将其纳入 PyPortfolioOpt 的 API。 完整的讨论可以在 Github Issue 中找到,但我最终决定,虽然 BL 在技术上不是一个优化器,但把它的方法分割到 expected_returns 或 risk_models 中并没有意义。 因此,我把它变成了一个独立的模块,并且由于理论比较广泛,我给了它一个专门的文档页面。我想感谢 Felipe Schneider 对 Black-Litterman 实现的多种贡献。 关于其使用的完整例子,包括免费获取市值数据,请参考 cookbook 。
小技巧
Thomas Kirschenmann 在 PyPortfolioOpt 之上建立了一个整洁的交互式 Black-Litterman 工具 ,它允许你可视化 BL 输出并比较优化目标。
先验
可以把先验看作是在没有任何信息的情况下的默认估计。Black and Litterman (1991) [2] 提供了这样的见解:这种先验的自然选择是市场对收益的估计,它被嵌入到资产的市值中。
市场投资组合中的每一项资产都会给投资组合带来一定的风险。标准理论认为,投资者会为他们所承担的风险得到补偿,因此我们可以为每项资产赋予预期补偿(即对收益的先验估计)。 这可以通过市场隐含的风险溢价来量化,即市场的超额收益除以其方差:
为了计算市场隐含的收益,我们使用以下公式:
此处, \(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 种资产表达四种观点,包括两种绝对观点和两种相对观点:
SBUX 将下跌 20%(绝对值)
MSFT 将上涨 5%(绝对值)
GOOG 的表现比 FB 好 10%。
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 提供了一个辅助方法,用于以 dict
或 pd.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 并不要求你输入置信矩阵,而是默认为:
另外,我们还提供了 Idzorek method [1] 的实现。这允许你将你的观点不确定性指定为百分比置信度。
要使用这个方法,请选择 omega="idzorek"
并在 view_confidences
参数中传递一个置信度列表(从0到1)。
当然,我们欢迎你提供你自己的估计。如果你的观点是一些统计模型的输出,这一点尤其适用,因为统计模型也可能提供观点的不确定性。
另一个控制先验观点的相对权重的参数是 \(\tau\) 。关于这个参数的调整有很多说法,有很多相互矛盾的经验法则。 事实上,已经有一整篇论文是关于它的 [3] 。我们选择合理的默认值 \(\tau = 0.05\) 。
备注
如果你使用 \(\Omega\) 默认的估计或 omega="idzorek"
,结果发现 \(\tau\) 的值并不重要。
这是数学上的一个结果: \(\tau\) 在矩阵乘法中被抵消了。
BL 模型的输出
BL 模型会输出收益和协方差矩阵的后验估计值。 文献中的默认建议是将这些数据输入优化器(见 通用有效前沿 )。 一个快速的替代方法是计算收益向量 [4] 所隐含的权重,这对于调试来说非常有用。 这实际上与我们用来计算市场权重所隐含的收益的程序相反。
在 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.ndarrayn_assets
- inttickers
- str listQ
- np.ndarrayP
- np.ndarraypi
- np.ndarrayomega
- np.ndarraytau
- float
输出:
posterior_rets
- pd.Seriesposterior_cov
- pd.DataFrameweights
- 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_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
- 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