quantlab.portfolio.predefined.black_litterman.BlackLittermanOptimizer#
- class quantlab.portfolio.predefined.black_litterman.BlackLittermanOptimizer(config)[source]#
Bases:
MeanVarianceOptimizerMean-variance weights on a Black-Litterman posterior.
See the module docstring for the model. On each rebalance bar the candidates are every selectable symbol (tradable, not locked, covered by the forecast, with every bounded exposure): a symbol without a view gets its equilibrium return plus what the views on correlated symbols move it by. The symbol views are the
expected_returnmodel’smuon the predicted candidates over the span, each a total return or, withviews_relative_to_prior, an alpha added to the symbol’s equilibrium return;omegasets their uncertainty. The user’s views arefactor_views(FactorReturnView,RelativeFactorReturnView) andmarket_views(MarketReturnView). Withoutexpected_returnthe rule reads no prediction and its span isspan. The market’s weights aremarket’s outputmarket_weightat the bar on the symbols the forecast covers (NaN counts as 0), renormalised to sum to one.candidate_top_kranks the posterior expected return. Every other field works as inMeanVarianceOptimizer;risk_aversionis both the optimiser’s and the equilibrium’sdelta.- Parameters:
- configBlackLittermanConfig
MeanVarianceConfig’s fields, themarketfactor, itsmarket_weightoutput,tau,omega(withconfidence),views_relative_to_prior,spanand the user’s views.
- Raises:
- ValueError
If there are no views (no
expected_returnand no user views),expected_returnandexpected_return_labelare not set together,spanis missing withoutexpected_returnor not a positive integer, afactor_viewsentry is not aFactorReturnVieworRelativeFactorReturnViewor amarket_viewsone not aMarketReturnView, a factor view names a factor the risk model lacks,bindfinds aspanother than the label’s,covarianceis not aFactorRiskStoreEstimator,marketis missing or lacksmarket_weight,tauis not positive,omegais unknown,confidenceis missing or outside(0, 1]with"idzorek"or set with anotheromega, or the rule measures risk against a reference book (reference_weights); and asMeanVarianceOptimizer.
- Warns:
- UserWarning
If a z-score expected return model (
GrinoldExpectedReturn,ZScoreExpectedReturn,UnscaledZScoreExpectedReturn) gives total-return views: itsmuis a cross-sectionally centred alpha, so as a total return it pulls every viewed name towards zero; setviews_relative_to_prior=True.
Examples
forecastis aFactorRiskForecastoverAAA..``FFF``,riskaFactorRiskStoreEstimatorreturning it,cap_weighta factor whose outputbenchmark_weightis 0.3, 0.25, 0.2, 0.15, 0.07 and 0.03, andcontexta bar whose predictions are all NaN. With no views the book is the market:>>> from quantlab.portfolio.config import BlackLittermanConfig, RawExpectedReturnConfig >>> from quantlab.portfolio.predefined.expected_return import RawExpectedReturn >>> rule = BlackLittermanOptimizer(BlackLittermanConfig( ... expected_return_label="ret_5", covariance=risk, ... expected_return=RawExpectedReturn(RawExpectedReturnConfig()), ... risk_aversion=3.0, market=cap_weight, tau=0.05, ... )) >>> rule.bind(specs) >>> rule.construct(context).values.round(4) array([0.3 , 0.25, 0.2 , 0.15, 0.07, 0.03])
- axis_factors()[source]#
The market’s weights: its symbols without a prediction are in the equilibrium portfolio.
Examples
>>> rule.axis_factors() == (rule.config.market_weight,) True
- bind(labels)[source]#
Read the span: the expected-return label’s, or
spanwithout symbol views.- Parameters:
- labelsSequence[LabelSpec]
The specs of the predicted labels; not read without
expected_return.
- Raises:
- ValueError
If
spanis set and differs from the label’s span, and asMeanVarianceOptimizer.bind.
Examples
>>> rule.bind([LabelSpec(name="ret_5", scale="raw", delay=1, span=5)]) >>> rule.span 5
- config_cls#
alias of
BlackLittermanConfig
- declared_inputs()[source]#
The optimiser’s declaration, and the market among the factors.
Examples
>>> rule.declared_inputs().factors[-1] is rule.config.market True
- market_exposures(context, forecast, weights)[source]#
Return the market’s factor exposures
x:B' won the forecast’s symbols.A market wider than the roster (the whole market, say) overrides it together with
market_weights, the market’s weights on the roster, so thatxand the weights describe one portfolio.- Parameters:
- contextPortfolioContext
The bar’s context.
- forecastFactorRiskForecast
The bar’s forecast.
- weightsnp.ndarray
The market’s weights on
forecast.symbols(market_weights).
- Returns:
- np.ndarray
One exposure per factor of the forecast.
Examples
>>> rule.market_exposures(context, forecast, weights).shape (2,)
- market_weights(context, forecast)[source]#
Return the market’s weights on the forecast’s symbols, summing to one.
- Parameters:
- contextPortfolioContext
The bar’s context;
context.factorsholdsmarket_weight.- forecastFactorRiskForecast
The bar’s forecast.
- Returns:
- np.ndarray
One weight per
forecast.symbols.
- Raises:
- PortfolioConstructionError
If the market has no weight on the covered symbols.
Examples
>>> rule.market_weights(context, forecast).sum() 1.0
- moments(timestamp, factor_names, factor_form, market, rows, variance, views)[source]#
Return the posterior on one bar’s arrays: the mean, the factor covariance and the diagonal parts of
M.The model’s arithmetic (see the module docstring) on the bar’s forecast in factor form, shared by
posteriorand the approximate backtest’s implementation of the rule, which hands it each bar’s arrays; the user’s views known attimestampare read here.- Parameters:
- timestamppd.Timestamp or np.datetime64
The bar, at which the user’s views are read.
- factor_namesSequence[str]
The forecast’s factors.
- factor_formtuple of np.ndarray
(B, F, D)over the forecast’snsymbols on the span:[n, k]exposures,[k, k]factor covariance,[n]specific variances.- markettuple of np.ndarray
(w, x): the market’s weights on thensymbols, summing to one, and its factor exposures.- rowsnp.ndarray
The viewed symbols’ positions among the
n.- variancenp.ndarray or None
Sigma_jjof each viewed symbol; not read (None) withomega="specific".- viewsnp.ndarray
The expected return model’s
muof each viewed symbol.
- Returns:
- meannp.ndarray
[n]: the posterior expected return.- factor_posteriornp.ndarray
[k, k]: the posterior covariance off,F_post.- keepnp.ndarray
[n]: how much of each symbol’s specific prior variance a view keeps, soM = U F_post U' + diag(specific),U = diag(keep) B.- specificnp.ndarray
[n]: the diagonal ofM.
- Raises:
- PortfolioConstructionError
If the user’s views cannot be blended (two fully confident views on one return, or a confident view on a factor without variance).
Examples
With no views the mean is the equilibrium
delta (1 + tau) Sigma w:>>> mean, _, keep, _ = rule.moments( ... day, forecast.factor_names, forecast.factor_form(), (w, forecast.exposures.T @ w), ... np.array([], dtype=int), np.array([]), np.array([]), ... ) >>> np.allclose(mean, 3.0 * 1.05 * forecast.covariance @ w), bool((keep == 1).all()) (True, True)
- posterior(context, symbols, expected_return, estimate, candidates)[source]#
Return the posterior expected return, the posterior covariance and every selectable symbol.
See the module docstring for the model; the views are the finite entries of
expected_return, total returns or alphas (views_relative_to_prior), withomega’s uncertainty.- Parameters:
- contextPortfolioContext
The bar’s context;
context.factorsholdsmarket_weight.- symbolsnp.ndarray
The selectable symbols.
- expected_returnnp.ndarray
The
expected_returnmodel’smuper selectable symbol on the span, NaN without a prediction: the symbol views.- estimateFactorRiskForecast
The bar’s forecast on the span.
- candidatesnp.ndarray
Ignored: every selectable symbol is a candidate.
- Returns:
- expected_returnnp.ndarray
The posterior mean per selectable symbol.
- estimateFactorRiskForecast
The posterior covariance
Sigma + Moverestimate’s symbols, its factors the forecast’s and their posterior copies.- candidatesnp.ndarray
All
True.
Examples
With no views, the posterior mean is the equilibrium and the covariance
(1 + tau) Sigma:>>> mu, posterior, chosen = rule.posterior( ... context, symbols, np.full(len(symbols), np.nan), estimate, candidates, ... ) >>> np.allclose(posterior.covariance, (1 + rule.config.tau) * estimate.covariance) True >>> bool(chosen.all()) True