An investor view is a forecast about future asset or portfolio performance used to adjust equilibrium expected returns in the Black–Litterman model. Rather than replacing the market-implied starting point, a view is combined with it through a Bayesian update to produce investor-specific expected returns. section 12.1.2 section 12.3
Views are probabilistic statements, not certainties. They specify both a forecast and its uncertainty: returns are modeled as random, estimates of expected returns are uncertain, and each view carries uncertainty through the covariance matrix Ω. section 12.3.1 section 12.3.2
Express a set of views with three objects:
- A: one row per view and one column per asset; each row encodes the portfolio or asset combination being forecast. section 12.3.2
- b: the forecast value for each view, such as an expected return or relative-performance spread. section 12.3.2
- Ω: the uncertainty and correlation of the views; larger uncertainty gives a view less influence in the update. section 12.3.2 section 12.3.3
A relative view compares two portfolios: (x − y)ᵀR = β states that portfolio x is expected to outperform portfolio y by β. In aggregate, the model writes AR as normally distributed with mean b and covariance Ω. The posterior expected-return mean combines the prior equilibrium estimate, its uncertainty C, and the views’ forecasts and uncertainty. section 12.3.2 section 12.3.3
Defensive sectors versus cyclical sectors
A downturn forecast can be encoded as a 1% expected outperformance of a market-weighted defensive portfolio—consumer staples, health care, and utilities—over a market-weighted cyclical portfolio. A separate absolute view can set real estate’s expected return to 2%. These become rows of A and values in b; using τ = 0.05 and Ω = diag(A(τΣ)Aᵀ), they yield posterior inputs for a constrained sector optimization. section 12.4.1 section 12.3.4
Listing 12.3 Constructing views
defensive = ['VDC', 'VHT', 'VPU'] cyclical = ['VCR', 'VDE', 'VFH', 'VIS', 'VGT', 'VAW'] defensive_port = weights[defensive] / weights[defensive].sum() cyclical_port = weights[cyclical] / weights[cyclical].sum() spread_port = pd.concat((defensive_port, -cyclical_port)) real_estate_port = pd.Series([1], index = ['VNQ']) A = pd.DataFrame([spread_port, real_estate_port]) A = A.T.reindex(tickers).fillna(0).T b = pd.Series([.01, .02])
Inspect a direct Python encoding of a relative defensive-versus-cyclical view and an absolute real-estate view into A and b. section 12.4.1
Listing 12.1 Posterior mean and covariance
import numpy as np
import pandas as pd
def compute_posterior_mean(mu, C, A, b, omega):
c_inv = np.linalg.inv(C)
a_t_oinv = A.T @ np.linalg.inv(omega) #1
er = np.linalg.inv(c_inv + a_t_oinv @ A) @ (c_inv @ mu + a_t_oinv @ b)
return pd.Series(er, index=mu.index)
def compute_posterior_cov(sigma, C, A, omega):
return sigma + np.linalg.inv((np.linalg.inv(C) + A.T @ omega @ A))
Use the posterior mean and covariance functions to turn equilibrium inputs and uncertain views into optimization inputs. section 12.3.3
