Black-Litterman Views Generation: private API
PortfolioOptimisers.Lc_BLV — Type
const Lc_BLV = Union{<:LinearConstraintEstimator, <:BlackLittermanViews}Alias for a union of linear constraint estimator and Black-Litterman views types.
Related
PortfolioOptimisers.get_black_litterman_views — Function
get_black_litterman_views(lcs::PR_VecPR, sets::UniverseSets,
key::Option{<:AbstractString} = nothing;
datatype::DataType = Float64, strict::Bool = false)Convert parsed Black-Litterman view equations into a BlackLittermanViews object.
get_black_litterman_views takes one or more ParsingResult objects (as produced by parse_equation), expands variable names using the provided UniverseSets, and assembles the canonical views matrix P and expected returns vector Q for Black-Litterman prior construction. The result is a BlackLittermanViews object suitable for use in portfolio optimisation routines.
key selects which universe the view names resolve against, exactly as it does for get_linear_constraints; nothing means sets.xkey. A view is never re-based — the estimator that owns it decides which distribution it lands on, and passes the matching key — so the assembled P is one row per view over length(sets.dict[key]) columns, and the message an unresolved name produces names the axis via universe_axis.
A view that is dropped is dropped, not refused. Its index joins excl, the remaining rows keep their order, and remove_excl_views drops the matching entry of a per-view confidence vector. When every view is dropped there is no row left and the return is nothing; bl_preroll is the caller that decides what that means.
A row is the unit of a drop. A view is a joint statement over several names with one right-hand side, so a name this function cannot resolve takes the whole row with it rather than only its own term: a + c == 0.05 assembled without c would fit a == 0.05, a different and stronger claim than the caller wrote, and one the update would blend the prior against. What the name's failure was decides only whether the drop is reported. A name on the counterpart axis — read with counterpart_axis_names, and in practice the Non-Investable Axis a door minted with investable_views — is dropped in silence under both settings of strict, because it was a correct name over the universe the caller was handed and the data moved it; the departure is announced once, by the door, through announce_non_investable. A name on neither axis is a typo, and is reported exactly as before.
Algorithm
- When
lcsis a vector, check that it is not empty. - Resolve the universe key
k, which iskeywhen it is given andsets.xkeyotherwise. Read the universenx = sets.dict[k], read its axis withuniverse_axis, givingaxisfor the diagnostic messages, and read the counterpart axis withcounterpart_axis_names. - For each parsed view
lc, in the order the caller wrote them, run steps 4 to 7 over the row accumulatorAt, which holdslength(nx)coefficients and starts at zero, and start that view not dropped. - For each variable-coefficient pair
(v, c)oflc, build the indicatorAi = (nx .== v). WhenAiselects no entry, mark the row dropped; report the unresolved name throughstrict_diagnosticunless it names the counterpart axis, and record the row intoledgerwithrecord_non_investable_drop!when it does. Otherwise addAi * ctoAt. Every name of the row is still visited, so a row carrying two typos names both. - When the row was marked dropped, push the view's index
iontoexcland go on to the next view. - When
Atis still all zeros, every name of this view resolved and the row still summed to zero. Report it throughstrict_diagnostic, pushiontoexcl, and go on to the next view without writing a row. - Append
AttoPandlc.rhstoQ. - When
Pholds at least one row, reshape it tolength(nx)rows and transpose it, soPis one view per row. Return aBlackLittermanViewsoverP,Qandexcl, where an emptyexclis passed asnothing. WhenPis empty, returnnothing.
Arguments
lcs: A singleParsingResultor a vector of such objects, representing parsed Black-Litterman view equations.sets: AUniverseSetsobject specifying the universes and groupings.key: Key to specify the universe insets.dictthat names resolve against. Ifnothing, the key is taken fromsets.xkey— or, where the caller is written against another declared axis, from that axis' key.datatype: Numeric type for coefficients and expected returns.strict: Iftrue, throws an error if a variable or group is not found insets; iffalse, issues a warning.ledger: The door's ledger of departure casualties, ornothingwhen nobody is collecting. A row dropped for a name on the counterpart axis is recorded into it throughrecord_non_investable_drop!.
Validation
- When
lcsis a vector,!isempty(lcs). - A name that matches no entry of
sets.dict[key]and none of the counterpart axis raises throughstrict_diagnosticwhenstrictistrue, and warns otherwise. The message names the axis. The row is dropped either way. - A name on the counterpart axis drops its row in silence, under both settings of
strict. - A view whose names all resolve and whose coefficients cancel raises the same way, and is dropped when
strictisfalse. - The assembled pair passes the
BlackLittermanViewsconstructor's own checks.
Returns
blv::Option{<:BlackLittermanViews}: The assembled views matrixP, one row per view overlength(sets.dict[key])columns, the expected returns vectorQ, and the indicesexclof the views that resolved no name.nothingwhen no view resolved.
Examples
julia> sets = UniverseSets(; xkey = "nx", dict = Dict("nx" => ["A", "B", "C"]));julia> lcs = parse_equation(["A + B == 0.05", "C == 0.02"]);julia> PortfolioOptimisers.get_black_litterman_views(lcs, sets)BlackLittermanViews P ┼ 2×3 LinearAlgebra.Transpose{Float64, Matrix{Float64}} Q ┼ Vector{Float64}: [0.05, 0.02] excl ┴ nothingRelated
BlackLittermanViewsparse_equationUniverseSetsstrict_diagnosticDecides whether an unresolved name raises or warns.universe_axiscounterpart_axis_namesrecord_non_investable_drop!investable_viewsremove_excl_viewsDrops the confidences of the views this function excluded.bl_prerollThe caller that decides what anothinganswer means.
PortfolioOptimisers.assert_bl_views_conf — Function
assert_bl_views_conf(::Nothing, args...)
assert_bl_views_conf(views_conf::Number, ::EqnType)
assert_bl_views_conf(views_conf::VecNum, val::EqnType)
assert_bl_views_conf(views_conf::Num_VecNum, views::LinearConstraintEstimator)
assert_bl_views_conf(views_conf::Num_VecNum, views::BlackLittermanViews)Validate Black-Litterman view confidence specification.
assert_bl_views_conf checks that the view confidence parameter(s) provided for Black-Litterman prior construction are valid. It supports scalar and vector confidence values, and works with views specified as equations, constraint estimators, or canonical views objects. The function enforces that confidence values are strictly between 0 and 1, and that the number of confidence values matches the number of views when a vector is given.
A scalar confidence is one confidence for every view, whatever the number of views. Only a vector states one confidence per view, so only a vector is counted against the views. Both shapes reach the same calc_omega answer: a scalar $v$ and the constant vector of $v$ give the same $\mathbf{\Omega}$.
The unit-interval bound is load-bearing rather than cosmetic. calc_omega maps a confidence $v$ to the scale $1/v - 1$, which is negative for every $v > 1$ and for every $v < 0$. A view uncertainty matrix with a negative diagonal entry is not a covariance, and the estimator returns an answer built from it rather than raising.
Arguments
views_conf: Scalar or vector of confidence values.views: Black-Litterman views, which may be equations.val: The equations of aLinearConstraintEstimator, unwrapped.
Validation
Each method selects one shape of views_conf and one shape of views, and refuses what that pair does not admit.
(::Nothing, args...): no confidence was given, so nothing is checked.(::Number, ::EqnType):0 < views_conf < 1, throughassert_unit_interval. The count is not checked, because one scalar covers any number of equations.(::VecNum, ::EqnType): whenvalis a vector of equations,length(val) == length(views_conf); whenvalis one equation,length(views_conf) == 1. Thenall(x -> 0 < x < 1, views_conf).(::Num_VecNum, ::LinearConstraintEstimator): selects nothing itself, and forwardsviews.valto the two methods above.(::Num_VecNum, ::BlackLittermanViews): whenviews_confis a vector,length(views_conf) == length(views.Q). Thenall(x -> 0 < x < 1, views_conf). This is the only site that sees the confidences of a precomputed pair, because such a pair resolves no name.
Returns
nothing.
Related
PortfolioOptimisers.assert_bl — Function
assert_bl(
views::Union{BlackLittermanViews, LinearConstraintEstimator},
sets::Union{Nothing, UniverseSets},
views_conf::Union{Nothing, Number, AbstractVector{<:Union{var"#s136", var"#s53"} where {var"#s136"<:Number, var"#s53"<:AbstractJuMPScalar}}},
tau::Union{Nothing, Number}
)
Assert that the Black-Litterman prior's views, sets, view confidences, and blending parameter are valid.
This is the one guard every Black-Litterman constructor calls, so the four families refuse the same input. It does not look at the returns matrix, which the constructor never sees; prior checks the universe against X and bl_preroll checks the width of P against the covariance.
Validation
- When
viewsis aLinearConstraintEstimator,!isnothing(sets), because the names of such a view resolve against a universe. views_confis checked byassert_bl_views_conf, against the shape ofviews.- When
tauis given,tau > 0. Anothingis admitted, andbl_prerollresolves it to1/T.
Related