Black-Litterman Prior: private API
PortfolioOptimisers.calc_omega — Function
calc_omega(::Nothing, P::MatNum, sigma::MatNum) -> LinearAlgebra.Diagonal
calc_omega(views_conf::Number, P::MatNum, sigma::MatNum) -> LinearAlgebra.Diagonal
calc_omega(views_conf::VecNum, P::MatNum, sigma::MatNum) -> LinearAlgebra.DiagonalCompute the Black-Litterman view uncertainty matrix Ω.
Each method selects one shape of views_conf and computes the branch of the closed form below that the shape names: ::Nothing the unscaled diagonal, ::Number the same diagonal under one shared scale, and ::VecNum the same diagonal under one scale per view.
Mathematical definition
Let $\mathbf{P}$ be the $K \times N$ view matrix and $\mathbf{\Sigma}$ the $N \times N$ prior covariance matrix. The view uncertainty matrix $\mathbf{\Omega}$ for each views_conf variant is:
\[\begin{align} \mathbf{\Omega} &= \mathrm{Diag}(\mathbf{P} \mathbf{\Sigma} \mathbf{P}^\intercal) \quad (\text{no confidence})\,. \end{align}\]
\[\begin{align} \mathbf{\Omega} &= \left(\frac{1}{v} - 1\right) \mathrm{Diag}(\mathbf{P} \mathbf{\Sigma} \mathbf{P}^\intercal) \quad (\text{scalar confidence } v)\,. \end{align}\]
\[\begin{align} \mathbf{\Omega} &= \mathrm{Diag}\!\left(\left(\frac{1}{\boldsymbol{v}} - \boldsymbol{1}\right) \odot \mathrm{diag}(\mathbf{P} \mathbf{\Sigma} \mathbf{P}^\intercal)\right) \quad (\text{vector confidence } \boldsymbol{v})\,. \end{align}\]
Where:
- $\mathbf{\Omega}$: $K \times K$ diagonal view uncertainty matrix.
- $\mathbf{P}$: $K \times N$ views matrix.
- $\mathbf{\Sigma}$: $N \times N$ prior covariance matrix.
- $v$: Scalar view confidence level.
- $\boldsymbol{v}$: $K \times 1$ vector of view confidence levels.
- $\odot$: Element-wise multiplication.
The no-confidence branch is the diagonal uncertainty of the view creation model. bl_preroll scales the result by $\tau$, so the pair returns $\mathrm{Diag}(\mathbf{P}(\tau\mathbf{\Sigma})\mathbf{P}^\intercal)$.
A confidence $v$ rescales that diagonal by $1/v - 1$, which is Idzorek's method in Walters' closed form. A high confidence therefore shrinks the view uncertainty and a low one widens it. The scale is negative for every $v$ outside $(0, 1)$, which is why assert_bl_views_conf refuses such a value.
The scalar branch and the vector branch agree where they overlap: a scalar $v$ gives the same $\mathbf{\Omega}$ as the constant vector of $v$, to the last bit. So the two shapes are two ways of writing one input, and assert_bl_views_conf counts only the vector against the views.
Both endpoints are refused too, and the bound is strict on purpose. $v = 1$ gives $\mathbf{\Omega} = \mathbf{0}$, a view held with no uncertainty at all, which makes $\mathbf{P}\tau\mathbf{\Sigma}\mathbf{P}^\intercal + \mathbf{\Omega}$ singular whenever $\mathbf{P}$ is rank-deficient. Two identical views over a three-asset sample give a rank-one $\mathbf{P}$; at $v = 1$ the sum is the constant matrix 1.0021e-6, whose determinant is 0.0 and whose rank is 1, and the solve raises SingularException. Just inside the bound it is merely ill-conditioned: at $v = 1 - 10^{-8}$ the condition number is 2.0e8, and at $v = 0.99$ it is 199. $v = 0$ gives an infinite uncertainty, which is the same thing as omitting the view.
Arguments
views_conf:::Nothing: No confidence specified;Ω = Diag(P * sigma * P').::Number: Scalar confidencev;Ω = (1/v - 1) * Diag(P * sigma * P').::VecNum: Per-view confidencesv;Ω = Diag((1 ./ v .- 1) .* diag(P * sigma * P')).
P: Views loading matrixviews × assets.sigma: Covariance matrixassets × assets.
Returns
omega::LinearAlgebra.Diagonal: Diagonal view uncertainty matrixviews × views.
Related
PortfolioOptimisers.bl_preroll — Function
bl_preroll(
views,
sets,
views_conf,
prior_sigma,
pe_tau,
T,
datatype,
strict;
...
) -> Union{Nothing, NamedTuple{(:P, :Q, :tau, :omega), <:NTuple{4, Any}}}
bl_preroll(
views,
sets,
views_conf,
prior_sigma,
pe_tau,
T,
datatype,
strict,
axis::Symbol;
ledger
) -> Union{Nothing, NamedTuple{(:P, :Q, :tau, :omega), <:NTuple{4, Any}}}
Pre-compute shared Black-Litterman inputs from views, prior covariance, and blending parameters.
Extracts the view matrix P, view returns vector Q, and excluded indices from views and sets via black_litterman_views, resolves tau, filters excluded rows from views_conf via remove_excl_views, and computes the scaled uncertainty matrix omega = tau * Ω via calc_omega.
axis names the declared axis of the distribution the views land on, and every caller knows it from its own type rather than from the views: BlackLittermanPrior takes the default :xkey (the asset axis), while a member whose views update the factor distribution passes :tfkey or :cfkey. It is the last argument because it is the only one an asset-space caller never supplies.
UniverseSets declares two factor axes, so the accepted set is three symbols wide. Every member in the library today updates a factor distribution whose columns are the columns of F, so every one of them passes :tfkey; :cfkey is accepted because the axis exists and a view can land on it, not because a member reaches it yet.
The selector is a field of UniverseSets rather than a key resolved from one, so a caller states its axis and nothing else. Resolving the key is this function's work, and it happens only when there is a sets to read it from — reading sets.tfkey to describe a universe that does not exist is the same error as reading the universe itself. Views supplied as a BlackLittermanViews result are the one shape that arrives with no sets at all: they resolve no names and ignore both the sets and the axis.
This is also where P meets the distribution it updates, so it is where their widths are reconciled. A P assembled from names is the right width by construction; a precomputed BlackLittermanViews resolves no names and is checked nowhere else.
No view left answers nothing, whatever emptied the set. A row a builder cannot assemble is dropped whole rather than fitted without its term, so a view set can end with no row for several reasons: a departure took the last one, the universe this fit was handed is a sub-universe — a cluster of a nested optimisation, a subset of a resampling — in which the names sit outside, or the caller mistyped. None of them changes what is left to condition on, so none of them changes the answer: the fit carries on as though no view had been stated, and each estimator spells what that means for it through bl_posteriors or bl_view_block.
The unresolved name is still reported where it is met, by strict_diagnostic — an ArgumentError under strict = true, a warning otherwise, naming the name and the universe it failed against. That report names the cause; a refusal here could only name the symptom, and could not tell a typo from a name that is legitimately outside a sub-universe. The ledger still separates the two for the announcement: only a departed name writes into it, and announce_bl_departures reads it to say which of the two happened.
The returned omega already carries $\tau$, so a caller passes it to vanilla_posteriors as it stands. That scaling has a consequence worth knowing: calc_omega is homogeneous of degree one in the covariance it reads, so $\tau$ multiplies both $\mathbf{P}\tau\mathbf{\Sigma}\mathbf{P}^\intercal$ and $\mathbf{\Omega}$, and cancels out of the posterior mean on every confidence branch. It does not cancel out of the posterior covariance. vanilla_posteriors states the measurement.
Algorithm
- Check that
axisnames a declared axis a view can land on. - Resolve
axisto a universe key. Whensetsisnothingthe key isnothingtoo, because a precomputed views object resolves no name and needs none. - Assemble the views with
black_litterman_viewsunder that key, givingblv. - When no view survived, answer
nothing. The caller decides what a view-free fit is. - Read
P,Qandexcloffblv. - Check that
Pis as wide asprior_sigmais tall. - Resolve
tau, which ispe_tauwhen the estimator carries one and1/Totherwise. - Drop the confidences of the views that step 3 excluded, with
remove_excl_views. - Build the view uncertainty matrix from the surviving confidences with
calc_omega, scale it bytau, and return it asomegaalongsideP,Qandtau.
Validation
axis in (:xkey, :tfkey, :cfkey).size(P, 2) == size(prior_sigma, 1), when a view survived.
Arguments
views: Views estimator or result.sets: Sets used to map estimator values to assets.views_conf: Views confidence estimator or result.prior_sigma::MatNum: Prior covariance matrix of the distribution the views update,n × nover that axis.pe_tau::Option{<:Number}: Optional user-specified blending parameter. Ifnothing, defaults to1/T.T::Integer: Number of observations used to compute the defaulttau = 1/T.datatype: Data type to use for the result in casevalisnothing.strict: Whether to throw an error ifsetsdoes not contain the desired value insets.dict[key].axis: Field ofsetsnaming the declared axis the views resolve against::xkeyfor the asset axis,:tfkeyfor the time-series factor axis,:cfkeyfor the cross-sectional one. The key itself is read fromsetshere, and only whensetsis notnothing.ledger: The door's ledger of departure casualties, ornothingwhen nobody is collecting. It is threaded intoblack_litterman_views, and it is what tells a view set emptied by a departure from one emptied any other way — a distinction the announcement makes, not the answer.
Returns
(; P, Q, tau, omega): Named tuple where:P::MatNum: View matrixviews × assets.Q::VecNum: View returns vectorviews × 1.tau::Number: Resolved blending parameter.omega::LinearAlgebra.Diagonal: Scaled view uncertainty matrixtau * Ω.
nothing: Every view was dropped, so the fit proceeds view-free.
Related
PortfolioOptimisers.announce_bl_departures — Function
announce_bl_departures(ni::VecStr, ledger::VecStr, viewless::Bool) -> NothingReport, once per Black-Litterman fit, who left the investable universe and what their leaving cost the view set.
This is the family's call of announce_non_investable, written once so the four prior methods each spend one line on it and none of them can word it differently. It names the process a Black-Litterman fit, because the message is otherwise the optimisation door's and would tell a standalone prior(pe, X) call that it is inside an optimisation it is not.
The sentence is one sentence for all four members, and it is exactly true of each. Two of them write their views on the factor axis, where no asset name ever appears, so the view clause simply does not bite for them; what does bite for every member is the first clause, because all four estimate their posterior over the assets that remain.
viewless says the fit ended with no view at all, and it is reported two ways because it happens two ways. When the ledger is non-empty a departure took the last surviving view: the message is raised to a warning and says so in the departure's own words. When the ledger is empty nobody departed, so announce_non_investable is silent — it has no name to report — and the view-free fit gets a warning of its own instead. That is the sub-universe case: a cluster of a nested optimisation or a subset of a resampling holds none of the names the caller wrote, every row is dropped whole, and the caller is handed an unconditioned answer with nothing else to tell them. Every drop that only trims the view set stays @info.
Arguments
ni: The names the Investable Mask left out, frominvestable_views.ledger: What the departures cost, as the view builder recorded it. Non-empty is what makes a view-free fit a departure's doing.viewless: Whether the fit ended up with no view at all.
Returns
nothing.
Related
PortfolioOptimisers.bl_posteriors — Function
bl_posteriors(::Nothing, prior_mu::VecNum, prior_sigma::MatNum) -> Tuple
bl_posteriors(blp::NamedTuple, prior_mu::VecNum, prior_sigma::MatNum) -> TupleRun the master equations, or hand back the prior pair when nothing is left to run them with.
bl_preroll answers nothing whenever no view row survived — a departure took the last one, the universe this fit was handed holds none of the names, or the caller mistyped. The fit proceeds rather than refusing, and a Black-Litterman posterior with no view is the distribution it was going to update — so that is what this returns.
It is the prior pair itself and not the empty-view algebra, and the difference is not rounding. vanilla_posteriors adds the estimation-error term $[(\tau\mathbf{\Sigma})^{-1}]^{-1} = \tau\mathbf{\Sigma}$ to the covariance, so an empty $\mathbf{P}$ would answer $(1 + \tau)\mathbf{\Sigma}$ — a wider covariance than the prior, produced by views that are not there. Forwarding the prior pair is what makes the missing view cost the caller the view and nothing else.
BayesianBlackLittermanPrior is the member this verb does not serve, and the reason is worth stating: its update is a precision sum, $\hat{\mathbf{\Sigma}}^{-1} = \mathbf{\Sigma}_f^{-1} + \mathbf{P}^\intercal\mathbf{\Omega}^{-1}\mathbf{P}$, so an empty $\mathbf{P}$ contributes exactly zero and carries no $\tau\mathbf{\Sigma}$ term to inflate anything. Its no-view answer is therefore the empty-block algebra of bl_view_block — which is not its wrapped prior: it collapses by Woodbury to $\mathbf{\Sigma}_a + \mathbf{M}\mathbf{\Sigma}_f\mathbf{M}^\intercal$ and $\mathbf{M}\boldsymbol{\mu}_f + \mathbf{b}$, the moments the factor model implies. That member has no unadjusted prior to hand back, because it transforms its wrapped prior whether or not a view is stated.
The covariance is copied, because every caller of this passes what it gets to matrix_processing!, which writes in place, and the prior result must not be mutated under a caller still holding it.
The split is dispatch: bl_preroll answers a concrete NamedTuple or a literal nothing at each call site, so the method pair is resolved statically.
Arguments
blp: Whatbl_prerollanswered, ornothing.prior_mu: The prior mean of the distribution the views update.prior_sigma: The prior covariance of that distribution.
Returns
(posterior_mu, posterior_sigma)::Tuple{VecNum, MatNum}: The posterior pair, or the prior pair when there is no view.
Related
PortfolioOptimisers.bl_view_block — Function
bl_view_block(::Nothing, n::Integer, datatype::DataType) -> Tuple
bl_view_block(blp::NamedTuple, ::Integer, ::DataType) -> TupleRead the P, Q and omega of a view system, as a block with no row when it has no view left.
Two members need this rather than bl_posteriors, and for two different reasons.
AugmentedBlackLittermanPrior stacks an asset-side view block above a factor-side one, and either half can empty on its own. An empty half contributes no row rather than collapsing the stack to the prior, because the joint posterior is still conditioned by whatever the other half kept: handing back the prior stack there would throw away views the caller stated and that still resolve. Only when both halves empty is there nothing left, and that member reads the pair itself to take the prior stack. The block is 0 × n rather than a zero row, so the stack carries no phantom view and aug_Q is one entry shorter rather than one entry of zero.
BayesianBlackLittermanPrior has one view system and no stack, and takes an empty block because for it the empty-block algebra is the no-view answer. Its update is a precision sum, $\hat{\mathbf{\Sigma}}^{-1} = \mathbf{\Sigma}_f^{-1} + \mathbf{P}^\intercal\mathbf{\Omega}^{-1}\mathbf{P}$, so a 0 × n $\mathbf{P}$ adds exactly zero and there is no $\tau\mathbf{\Sigma}$ estimation-error term for it to inflate — which is precisely what stops bl_posteriors from serving the members that run vanilla_posteriors. What comes out is not that member's wrapped prior but $\mathbf{\Sigma}_a + \mathbf{M}\mathbf{\Sigma}_f\mathbf{M}^\intercal$, the moments its factor model implies, and it has no unadjusted prior to offer instead.
Arguments
blp: Whatbl_prerollanswered for this system, ornothing.n: The width of this system's axis, for the emptyP.datatype: The numeric type of the block.
Returns
(P, Q, omega)::Tuple: The view matrix, view returns and uncertainty, with no row when the system was emptied.
Related
PortfolioOptimisers.assert_bl_precomputed_universe — Function
assert_bl_precomputed_universe(sets::UniverseSets, pr::AbstractPriorResult) -> Nothing
assert_bl_precomputed_universe(sets::Nothing, pr::AbstractPriorResult) -> NothingRefuse the one configuration in which an asset-side Black-Litterman view cannot be reduced: a precomputed view matrix over a universe an asset has left.
The reduction works by resolving the caller's view names against the universe that survives, which is what lets a row naming a departed asset be dropped whole and every other row be rebuilt over the investable columns. A BlackLittermanViews passed in ready-made resolves no name — black_litterman_views hands it straight back, and its docstring says why: it was assembled against whatever universe the caller held, and nothing downstream can re-check it. So its P cannot be reduced, and there is nothing to reduce it by: an estimator whose views are precomputed is permitted to carry no sets at all.
Left alone, this is the defect the whole ticket is about, unfixed for one configuration and silent: calc_omega forms P * sigma * transpose(P) over the full universe, 0 * NaN is NaN, and the posterior comes back all NaN. Under the default mp that surfaces one layer later as ArgumentError: matrix contains Infs or NaNs from posdef!, which names the wrong cause; under a matrix processing estimator that does nothing, the fit succeeds and hands the caller an empty universe. This is the map's rule applied where it has to be: handle it, or refuse by name.
The split is dispatch on the type of sets, which is a field of a @concrete estimator and so a type fact. The UniverseSets method is the whole of the ordinary path and costs a dispatch; only the sets-less path derives the mask, and only to refuse.
Arguments
sets: Sets used to map estimator values to assets.pr: Prior result.
Validation
- When
setsisnothing, every asset ofprmust be investable.
Returns
nothing.
Related
PortfolioOptimisers.vanilla_posteriors — Function
vanilla_posteriors(tau::Number, prior_mu::VecNum, prior_sigma::MatNum,
omega::MatNum, P::MatNum, Q::VecNum)Compute the Black-Litterman posterior mean and covariance for asset returns.
vanilla_posteriors implements the standard Black-Litterman update equations, combining the prior mean and covariance with user or algorithmic views. The function returns the posterior mean and covariance matrix, incorporating the blending parameter tau, view uncertainty matrix omega, view matrix P, and view returns vector Q.
The kernel carries no risk-free rate. Each Black-Litterman prior estimator adds its own rf once, through apply_rf, which owns the site each member adds it at.
The two equations below are the inverse-free form of the master equations. They are algebraically the same object as the form stated on prior: the covariance term is the Woodbury expansion of $\left[(\tau\mathbf{\Sigma})^{-1} + \mathbf{P}^\intercal\mathbf{\Omega}^{-1}\mathbf{P}\right]^{-1}$. This form is used because it inverts one $K \times K$ matrix rather than three $N \times N$ ones.
Mathematical definition
Let $\boldsymbol{\Pi}$ be the prior mean, $\mathbf{\Sigma}$ the prior covariance, $\tau$ the scaling parameter, $\mathbf{P}$ the view matrix, $\boldsymbol{q}$ the view vector, and $\mathbf{\Omega}$ the view uncertainty matrix:
\[\begin{align} \hat{\boldsymbol{\mu}}_{BL} &= \boldsymbol{\Pi} + \tau\mathbf{\Sigma}\mathbf{P}^\intercal \left(\mathbf{P}\tau\mathbf{\Sigma}\mathbf{P}^\intercal + \mathbf{\Omega}\right)^{-1} (\boldsymbol{q} - \mathbf{P}\boldsymbol{\Pi})\,. \end{align}\]
\[\begin{align} \hat{\mathbf{\Sigma}}_{BL} &= \mathbf{\Sigma} + \tau\mathbf{\Sigma} - \tau\mathbf{\Sigma}\mathbf{P}^\intercal \left(\mathbf{P}\tau\mathbf{\Sigma}\mathbf{P}^\intercal + \mathbf{\Omega}\right)^{-1} \mathbf{P}\tau\mathbf{\Sigma}\,. \end{align}\]
Where:
- $\hat{\boldsymbol{\mu}}_{BL}$: Black-Litterman posterior mean vector.
- $\hat{\mathbf{\Sigma}}_{BL}$: Black-Litterman posterior covariance matrix.
- $\boldsymbol{\Pi}$: $N \times 1$ prior (equilibrium) expected returns.
- $\mathbf{\Sigma}$: $N \times N$ prior covariance matrix.
- $\tau$: Scaling parameter for the uncertainty in the prior.
- $\mathbf{P}$: $K \times N$ views matrix.
- $\boldsymbol{q}$: $K \times 1$ views vector.
- $\mathbf{\Omega}$: $K \times K$ view uncertainty matrix.
$\tau$ stands in both equations, but it does not move the posterior mean when $\mathbf{\Omega}$ comes from the bl_preroll pair. calc_omega is homogeneous of degree one in the covariance it reads, and bl_preroll scales its answer by $\tau$, so the gain $\tau\mathbf{\Sigma}\mathbf{P}^\intercal(\mathbf{P}\tau\mathbf{\Sigma}\mathbf{P}^\intercal + \mathbf{\Omega})^{-1}$ has one $\tau$ above and one below, and they cancel. Over $\tau \in \{1/200, 0.05, 0.5\}$ on a $200 \times 3$ sample with two views the posterior mean moves by at most 3.3e-19 on every confidence branch — no confidence, the scalar 0.4, and the vector [0.25, 0.75]. The posterior covariance does move, because $\mathbf{\Sigma} + \tau\mathbf{\Sigma} - \ldots$ carries a bare $\tau$: its excess over $\mathbf{\Sigma}$ has trace 2.013e-7, 2.013e-6 and 2.013e-5 at $\tau = 0.001$, 0.01 and 0.1, which is linear in $\tau$ to three figures.
A view that repeats the prior is a null update. With $\boldsymbol{q} = \mathbf{P}\boldsymbol{\Pi}$ the residual $\boldsymbol{q} - \mathbf{P}\boldsymbol{\Pi}$ is zero, so the posterior mean equals the prior mean exactly — measured at 0.0 on the same sample.
Algorithm
- Scale the prior covariance by
tauand carry it through the views, givingv1, the $N \times K$ matrix $\tau\mathbf{\Sigma}\mathbf{P}^\intercal$. - Close
v1under the views and add the view uncertainty, givingv2, the $K \times K$ matrix $\mathbf{P}\tau\mathbf{\Sigma}\mathbf{P}^\intercal + \mathbf{\Omega}$. This is the only matrix the body inverts. - Take the view residual
v3, which is $\boldsymbol{q}$ less the prior's own answer to the views. - Solve
v2againstv3, carry the solution throughv1, and add it toprior_mu, givingposterior_mu. - Solve
v2against the transpose ofv1, carry that throughv1, and subtract it fromprior_sigma + tau * prior_sigma, givingposterior_sigma.
Arguments
tau: Scalar blending parameter for prior and views.prior_mu: Prior mean vector of asset returns.prior_sigma: Prior covariance matrix of asset returns.omega: View uncertainty matrix.P: View matrix (views × assets).Q: Vector of view returns (views).
Returns
posterior_mu::VecNum: Posterior mean vector of asset returns.posterior_sigma::Matrix{<:Number}: Posterior covariance matrix of asset returns.
Related
PortfolioOptimisers.apply_rf — Function
apply_rf(rf::Number, mu::VecNum)Shift a Black-Litterman mean by the risk-free rate.
apply_rf is the single site that reads the rf field of a Black-Litterman prior estimator. The four families – BlackLittermanPrior, BayesianBlackLittermanPrior, FactorBlackLittermanPrior and AugmentedBlackLittermanPrior – each call it once, and nowhere else. Nothing subtracts the rate.
Three properties follow, and all three are contracts of the family:
- The rate is added once. No body adds it twice.
- The update runs on the scale the views are written on. A Black-Litterman update blends the prior mean against the view returns in
Qby forming the residualQ - P * mu, so the prior mean must be on the scale ofQ, which is a total return. A mean taken from a wrapped prior estimator is one already and reaches the update untouched. The equilibrium mean ofequilibrium_muis a bare risk premium, so the two members that can build one add the rate to it before the update, on the axis that mean lives on. A level that is missing from the prior mean is a level the views are blended against wrongly, which is why the rate goes on first rather than last. - A prior is isolated. A wrapped prior estimator is never re-fitted and its mean is never rescaled, so a risk-free rate one of them applied internally stays where it is.
Where each member calls it, and what the field therefore does:
BlackLittermanPriorandBayesianBlackLittermanPriorhave no equilibrium branch and so have nothing to convert. They add the rate to the posterior asset mean, last, and the field is a plain shift of the answer.FactorBlackLittermanPriorandAugmentedBlackLittermanPriorcall it on the equilibrium mean, and only wherelis set. Wherelisnothingneither reads the field at all, so it does not reach the answer.
"Once" is measured, not asserted. Two BlackLittermanPrior fits over one $200 \times 3$ sample, differing only in rf, give posterior means whose difference is rf in every entry to the last bit: at rf = 0.03 the difference is [0.03, 0.03, 0.03] and max|diff - rf| is 0.0.
Algorithm
- Add
rfto every entry ofmu, and return the result. The input is not modified.
Arguments
rf: Risk-free rate.mu: Expected returns vector. It is a posterior asset mean for the two members with no equilibrium branch, and a prior equilibrium mean for the two with one.
Returns
mu::VecNum:mushifted byrf.
Related
PortfolioOptimisers.remove_excl_views — Function
remove_excl_views(
views_conf::Union{Nothing, Number},
args...
) -> Union{Nothing, Number}
Remove excluded views from views_conf.
This is the method for a confidence that is nothing or a scalar. Neither is indexed by view — a scalar is one confidence for every view — so dropping a view changes nothing, and every argument after the first is ignored.
Algorithm
- Return
views_confunchanged.
Arguments
views_conf:nothing, or one confidence shared by every view.args...: The excluded indices, ignored.
Returns
views_conf::Option{<:Number}: The input, unchanged.
Related
remove_excl_views(
views_conf::AbstractVector{<:Union{var"#s136", var"#s53"} where {var"#s136"<:Number, var"#s53"<:AbstractJuMPScalar}},
_::Nothing
) -> AbstractVector{<:Union{var"#s136", var"#s53"} where {var"#s136"<:Number, var"#s53"<:AbstractJuMPScalar}}
Remove excluded views from views_conf.
This is the method for a per-view confidence vector when no view was excluded. get_black_litterman_views passes nothing rather than an empty vector when every view resolved, so this method carries the common case.
Algorithm
- Return
views_confunchanged.
Arguments
views_conf: One confidence per view.::Nothing: No view was excluded.
Returns
views_conf::VecNum: The input, unchanged.
Related
remove_excl_views(
views_conf::AbstractVector{<:Union{var"#s136", var"#s53"} where {var"#s136"<:Number, var"#s53"<:AbstractJuMPScalar}},
excl::AbstractVector{<:Integer}
) -> Any
Remove excluded views from views_conf.
This is the method that does the work: a per-view confidence vector, and the indices of the views that get_black_litterman_views dropped. The surviving entries keep the order the caller wrote them in, so entry k of the answer still belongs to row k of the P the same call assembled. Excluding every view leaves an empty vector.
Algorithm
- Take the indices of
views_confthat are not members ofexcl, in ascending order. - Return the corresponding entries as a lazy view, with
nothing_scalar_array_view.
Arguments
views_conf: One confidence per view, over the views the caller wrote.excl: The indices of the views that resolved no name.
Returns
views_conf::VecNum: A view of the input, holding one confidence per surviving view.
Related
PortfolioOptimisers.assert_bl_views_axis — Function
assert_bl_views_axis(
pe::BlackLittermanPrior,
X::AbstractMatrix{<:Union{var"#s136", var"#s53"} where {var"#s136"<:Number, var"#s53"<:AbstractJuMPScalar}}
)
Refuses a BlackLittermanPrior whose asset universe does not describe the returns matrix.
The axis is checked only by the views that resolve names against it. A BlackLittermanViews result carries its own P and never touches sets, so demanding a universe for it would reject the legitimate precomputed-views configuration, which assert_bl deliberately permits to supply no sets at all.
Arguments
pe: Black-Litterman prior estimator.X: Asset returns,observations × assets.
Validation
length(pe.sets.dict[pe.sets.xkey]) == size(X, 2)where the views resolve names. ADimensionMismatchis thrown otherwise.
Returns
nothing.
Related
PortfolioOptimisers.bl_posterior — Function
bl_posterior(
pe::BlackLittermanPrior,
prior_model::AbstractPriorResult,
strict::Bool;
kwargs...
) -> Union{HighOrderPrior{<:AbstractPriorResult}, LowOrderPrior{var"#s185", _A, var"#s1851", <:AbstractMatrix{var"#s137"}} where {var"#s137"<:(Union{var"#s136", var"#s53"} where {var"#s136"<:Number, var"#s53"<:AbstractJuMPScalar}), var"#s185"<:AbstractMatrix{var"#s137"}, _A, var"#s137"<:(Union{var"#s136", var"#s53"} where {var"#s136"<:Number, var"#s53"<:AbstractJuMPScalar}), var"#s1851"<:AbstractVector{var"#s137"}, var"#s137"<:(Union{var"#s136", var"#s53"} where {var"#s136"<:Number, var"#s53"<:AbstractJuMPScalar})}}
Blends the views of a BlackLittermanPrior into the result its embedded prior answered.
Everything the estimator does after the embedded prior has answered, written once. The batch method reaches it with the result of a fit over the caller's matrix, and the read-out of a folded estimator reaches it with the result of the embedded prior's own fold. Nothing here reads a returns matrix that the result does not already carry — T, the axis and the matrix the processing runs over all come off prior_model — which is why the two routes are one body and cannot drift.
Algorithm
- Reduce to the investable columns with
investable_views, because a view is a dense linear form over the asset axis and one departed asset would poisonomega. - Assemble
P,Q,tauandomegawithbl_preroll, whoseTis the number of observations the result carries. - Blend with
bl_posteriors, and applype.rfonce, here, withapply_rf. - Process the reduced covariance, before the expansion, because a
NaNframe has no factorisation. - Expand both moments back onto the caller's universe with
expand_moment. - Forward everything the embedded result carried, dropping
cholalone, withforward_prior.
Arguments
pe: Black-Litterman prior estimator.prior_model: The result the embedded prior answered.strict: Whether an unresolved view raises rather than warns.kwargs...: Additional keyword arguments, forwarded to the matrix processing.
Returns
pr::AbstractPriorResult: The embedded result with its two moments replaced by the posteriors.
Related