Black-Litterman Prior

PortfolioOptimisers.BlackLittermanPriorType
struct BlackLittermanPrior{__T_pe, __T_mp, __T_views, __T_sets, __T_views_conf, __T_rf, __T_tau} <: AbstractLowOrderPriorEstimator_AF

Black-Litterman prior estimator for asset returns.

BlackLittermanPrior is a low order prior estimator that computes the mean and covariance of asset returns using the Black-Litterman model. It combines a prior estimator, matrix post-processing, user or algorithmic views, asset sets, view confidences, risk-free rate, and a blending parameter tau. The estimator supports both direct and constraint-based views, and allows for flexible confidence specification and matrix processing.

Fields

  • pe: Prior estimator.
  • mp: Matrix processing estimator.
  • views: Views estimator or result.
  • sets: Sets used to map estimator values to assets.
  • views_conf: Views confidence estimator or result.
  • rf: Risk-free rate. The Black-Litterman update blends the prior mean against the view returns, so it runs on the total-return scale those are written on. A mean taken from a wrapped prior estimator is on that scale already; an equilibrium mean is a bare risk premium, and the rate converts it before the update. A member with no equilibrium branch has nothing to convert and adds the rate to the posterior asset expected returns instead. It is added exactly once either way, and the wrapped prior estimators are left alone, so a risk-free rate one of them applied internally stays where it is.
  • tau: Blending parameter controlling the weight given to the prior relative to the views.

Constructors

BlackLittermanPrior(;    pe::Onl{<:AbstractLowOrderPriorEstimator_A_F_AF} = EmpiricalPrior(;        me = EquilibriumExpectedReturns()    ),    mp::AbstractMatrixProcessingEstimator = MatrixProcessing(),    views::Lc_BLV,    sets::Option{<:UniverseSets} = nothing,    views_conf::Option{<:Num_VecNum} = nothing,    rf::Number = 0.0,    tau::Option{<:Number} = nothing) -> BlackLittermanPrior

Keywords correspond to the struct's fields.

Composition: what this estimator forwards

The views are applied to the assets. The wrapped prior is forwarded whole and only the deviations are spelled out: mu and sigma become the posterior, and chol is dropped because the posterior covariance supersedes the one it factorises. Everything else forwards — Black-Litterman leaves the observation axis untouched, so w, ens, kld, ow and Z all still describe the axis they were computed over, and rr and the factor block fpr are structural, over data the views do not modify.

Warning

The returned mu and sigma are the Black-Litterman posterior, but w is the wrapped prior's observation weighting, forwarded unchanged. Black-Litterman produces no observation-level posterior, so there is no Black-Litterman-consistent alternative to forward — and dropping w would substitute the unweighted empirical distribution, which is further from the caller's intent than the weights they computed. A caller reading pr.w, pr.ens, pr.kld or pr.ow is therefore reading a property of the prior, not of the posterior.

Warning

When the wrapped prior carries a factor block, pr.fpr describes the prior factor distribution while pr.mu is a posterior asset mean, so pr.mu != pr.rr.M * pr.fpr.mu + pr.rr.b. The block stays structurally true — the regression is over data Black-Litterman does not modify — while becoming distributionally inconsistent with the asset block. There is nothing better to report: the views land on the assets, so this estimator never computes a posterior factor distribution at all.

Its siblings differ, and the difference is worth knowing. FactorBlackLittermanPrior and BayesianBlackLittermanPrior apply their views to the factors and report the resulting posterior block, so both satisfy mu == rr.M * fpr.mu + rr.b exactly. AugmentedBlackLittermanPrior reports a posterior factor block too, but stays inconsistent for a different reason — see its own warning.

Validation

Propagated parameters

When factory is called on this type, the following @fprop-tagged fields are automatically propagated:

  • pe: Recursively updated via factory.

View parameters

When port_opt_view is called on this type, the following @vprop-tagged fields are automatically subset to the selected indices:

Examples

julia> BlackLittermanPrior(;                           sets = UniverseSets(; xkey = "nx",                                               dict = Dict("nx" => ["A", "B", "C"])),                           views = LinearConstraintEstimator(;                                                             val = ["A == 0.03", "B + C == 0.04"]))BlackLittermanPrior          pe ┼ EmpiricalPrior             │           ce ┼ PortfolioOptimisersCovariance             │              │   ce ┼ Covariance             │              │      │    me ┼ SimpleExpectedReturns             │              │      │       │   w ┴ nothing             │              │      │    ce ┼ GeneralCovariance             │              │      │       │   ce ┼ StatsBase.SimpleCovariance: StatsBase.SimpleCovariance(true)             │              │      │       │    w ┴ nothing             │              │      │   alg ┼ FullMoment()             │              │      │     w ┴ nothing             │              │   mp ┼ MatrixProcessing             │              │      │     pdm ┼ Posdef             │              │      │         │      alg ┼ UnionAll: NearestCorrelationMatrix.Newton             │              │      │         │   kwargs ┴ @NamedTuple{}: NamedTuple()             │              │      │      dn ┼ nothing             │              │      │      dt ┼ nothing             │              │      │     alg ┼ nothing             │              │      │   order ┴ NTuple{4, Symbol}: (:pdm, :dn, :dt, :alg)             │           me ┼ EquilibriumExpectedReturns             │              │   ce ┼ PortfolioOptimisersCovariance             │              │      │   ce ┼ Covariance             │              │      │      │    me ┼ SimpleExpectedReturns             │              │      │      │       │   w ┴ nothing             │              │      │      │    ce ┼ GeneralCovariance             │              │      │      │       │   ce ┼ StatsBase.SimpleCovariance: StatsBase.SimpleCovariance(true)             │              │      │      │       │    w ┴ nothing             │              │      │      │   alg ┼ FullMoment()             │              │      │      │     w ┴ nothing             │              │      │   mp ┼ MatrixProcessing             │              │      │      │     pdm ┼ Posdef             │              │      │      │         │      alg ┼ UnionAll: NearestCorrelationMatrix.Newton             │              │      │      │         │   kwargs ┴ @NamedTuple{}: NamedTuple()             │              │      │      │      dn ┼ nothing             │              │      │      │      dt ┼ nothing             │              │      │      │     alg ┼ nothing             │              │      │      │   order ┴ NTuple{4, Symbol}: (:pdm, :dn, :dt, :alg)             │              │    w ┼ nothing             │              │    l ┴ Int64: 1             │      horizon ┼ nothing             │   fill_limit ┴ nothing          mp ┼ MatrixProcessing             │     pdm ┼ Posdef             │         │      alg ┼ UnionAll: NearestCorrelationMatrix.Newton             │         │   kwargs ┴ @NamedTuple{}: NamedTuple()             │      dn ┼ nothing             │      dt ┼ nothing             │     alg ┼ nothing             │   order ┴ NTuple{4, Symbol}: (:pdm, :dn, :dt, :alg)       views ┼ LinearConstraintEstimator             │   val ┼ Vector{String}: ["A == 0.03", "B + C == 0.04"]             │   key ┴ nothing        sets ┼ UniverseSets             │     xkey ┼ String: "nx"             │    uxkey ┼ String: "ux"             │    tfkey ┼ String: "nf"             │   utfkey ┼ String: "uf"             │    cfkey ┼ String: "ncf"             │   ucfkey ┼ String: "ucf"             │    nikey ┼ String: "ni"             │     dict ┴ Dict{String, Vector{String}}: Dict("nx" => ["A", "B", "C"])  views_conf ┼ nothing          rf ┼ Float64: 0.0         tau ┴ nothing

Related

References

  • [29] F. Black and R. Litterman. Global portfolio optimization. Financial Analysts Journal 48, 28–43 (1992).
  • [5] D. Cajas. Advanced Portfolio Optimization: A Cutting-edge Quantitative Approach (Springer Nature Switzerland, 2025). Section 5.1, Equations 5.13 to 5.15.
  • [74] J. Walters. The Black-Litterman model in detail. SSRN Electronic Journal (2011).
  • [75] T. Idzorek. A step-by-step guide to the Black-Litterman model: incorporating user-specified confidence levels. In: Forecasting Expected Returns in the Financial Markets (Academic Press, 2007); pp. 17–38. For the views_conf branch of calc_omega.
source
PortfolioOptimisers.priorFunction
prior(pe::BlackLittermanPrior, X::MatNum, F::Option{<:MatNum} = nothing,
      pnl::Option{<:AssetPanel} = nothing;
      dims::Int = 1, strict::Bool = false, kwargs...)

Compute the Black-Litterman prior moments for asset returns.

prior estimates the mean and covariance of asset returns using the Black-Litterman model, combining a prior estimator, matrix post-processing, user or algorithmic views, asset sets, view confidences, risk-free rate, and blending parameter tau. The method supports both direct and constraint-based views, flexible confidence specification, and matrix processing.

When pe.tau is nothing the blending parameter is 1/T, where T is the number of observations of the oriented X. pe.rf reaches the answer once, on the posterior asset expected returns; apply_rf owns that contract.

Mathematical definition

The Black-Litterman posterior distribution combines the prior $(\boldsymbol{\Pi}, \tau \mathbf{\Sigma})$ with investor views $(\mathbf{P}, \boldsymbol{q}, \mathbf{\Omega})$. vanilla_posteriors computes the algebraically equivalent inverse-free form:

\[\begin{align} \hat{\boldsymbol{\mu}}_{BL} &= \left[(\tau\mathbf{\Sigma})^{-1} + \mathbf{P}^\intercal \mathbf{\Omega}^{-1} \mathbf{P}\right]^{-1} \left[(\tau\mathbf{\Sigma})^{-1} \boldsymbol{\Pi} + \mathbf{P}^\intercal \mathbf{\Omega}^{-1} \boldsymbol{q}\right]\,. \end{align}\]

\[\begin{align} \hat{\mathbf{\Sigma}}_{BL} &= \mathbf{\Sigma} + \left[(\tau\mathbf{\Sigma})^{-1} + \mathbf{P}^\intercal \mathbf{\Omega}^{-1} \mathbf{P}\right]^{-1}\,. \end{align}\]

Where:

  • $\boldsymbol{\Pi}$: N × 1 prior (equilibrium) expected returns.
  • $\mathbf{\Sigma}$: N × N prior covariance matrix.
  • $\tau$: Scaling parameter for the uncertainty in the prior.
  • $\mathbf{P}$: K × N views matrix (each row is one view).
  • $\boldsymbol{q}$: K × 1 views vector.
  • $\mathbf{\Omega}$: K × K views uncertainty matrix.

Algorithm

  1. Orient X and F with dims_oriented, to observations × assets and observations × factors.
  2. When pe.views resolves names, check that the asset universe is as long as X is wide. A precomputed BlackLittermanViews resolves no name, so it is not checked here; step 4 checks its width instead.
  3. Fit the wrapped prior pe.pe on (X, F), giving prior_model.
  4. Derive the Investable Mask and the reduced view universe with investable_views, and refuse a precomputed view matrix over a gapped universe with assert_bl_precomputed_universe.
  5. View the fitted prior at the mask with investable_prior, and read posterior_X, prior_mu and prior_sigma off that.
  6. Assemble the views and their uncertainty with bl_preroll, over the reduced prior_sigma and size(X, 1) observations, giving P, Q, tau and omega, or nothing when a departure took the last view. The axis is left at its default, :xkey, because these views land on the assets.
  7. Run the master equations with bl_posteriors, giving posterior_mu and posterior_sigma over the investable assets.
  8. Add pe.rf to posterior_mu with apply_rf. This is the one site that adds it.
  9. Process posterior_sigma in place with matrix_processing!, under pe.mp and posterior_X, while it is still the reduced block a factorisation exists for.
  10. Announce the departures once with announce_bl_departures.
  11. Write both posteriors back onto the full asset universe with expand_moment, so a non-investable asset carries NaN in mu and on the diagonal of sigma.
  12. Forward the whole of prior_model with forward_prior, replacing mu and sigma by the expanded pair and dropping chol.

Arguments

  • pe: Black-Litterman prior estimator.
  • X: Asset returns matrix (observations × assets).
  • F: Optional factor matrix.
  • pnl: Optional AssetPanel, the panel the carrier held. A wrapping prior forwards it unchanged, so that it can compose an estimator that is fitted on a panel. An estimator that reads no panel ignores it.
  • dims: Dimension along which to perform the computation.
  • strict: If true, enforce strict validation of views and sets. Default is false.
  • kwargs...: Additional keyword arguments passed to underlying estimators and matrix processing.

Validation

Returns

  • pr::LowOrderPrior: Result object containing asset returns, posterior mean vector, and posterior covariance matrix.

Related

source

References

[5]
D. Cajas. Advanced Portfolio Optimization: A Cutting-edge Quantitative Approach (Springer Nature Switzerland, 2025).
[29]
F. Black and R. Litterman. Global portfolio optimization. Financial Analysts Journal 48, 28–43 (1992).
[74]
J. Walters. The Black-Litterman model in detail. SSRN Electronic Journal (2011).
[75]
T. Idzorek. A step-by-step guide to the Black-Litterman model: incorporating user-specified confidence levels. In: Forecasting Expected Returns in the Financial Markets (Academic Press, 2007); pp. 17–38.