Entropy Pooling: private API

PortfolioOptimisers.AbstractEntropyPoolingOptimiserType
abstract type AbstractEntropyPoolingOptimiser <: AbstractEstimator

Abstract supertype for the optimisers that solve an entropy pooling problem.

A subtype names the numerical route to the posterior probabilities: which package drives the solve, and whether it solves the dual or the primal.

Related

source
PortfolioOptimisers.AbstractEntropyPoolingAlgorithmType
abstract type AbstractEntropyPoolingAlgorithm <: AbstractAlgorithm

Abstract supertype for the algorithms that decide how the views of an entropy pooling problem reach the optimiser.

A subtype states whether every view is enforced in one optimisation or in stages, from the lower moments to the higher ones, and which probabilities each stage starts from.

Related

References

  • [78] A. Meucci. Fully flexible views: theory and practice. Risk 21, 97–102 (2008).
  • [79] A. Vorobets. Sequential entropy pooling heuristics. Available at SSRN 3936392 (2021).
source
PortfolioOptimisers.AbstractEntropyPoolingOptAlgorithmType
abstract type AbstractEntropyPoolingOptAlgorithm <: AbstractAlgorithm

Abstract supertype for the algorithms that decide how an entropy pooling optimiser evaluates its objective.

Every subtype minimises the same Kullback-Leibler divergence of the posterior probabilities from the prior ones. They differ only in the arithmetic that evaluates it, so they answer the same problem with the same posterior.

Related

References

  • [78] A. Meucci. Fully flexible views: theory and practice. Risk 21, 97–102 (2008).
source
PortfolioOptimisers.AbstractEntropyPoolingViewEstimatorType
abstract type AbstractEntropyPoolingViewEstimator <: AbstractEstimator

Abstract supertype for the estimators that carry a group of entropy pooling views together with the settings those views are read under.

A significance level is a property of a view, not of the estimator that holds it: the value at risk at 1% and at 10% are different statistics of the same series. An estimator of this family pairs a group of view equations with the settings they are read under, so one entropy pooling estimator can hold views stated at several levels.

Related

source
PortfolioOptimisers.AbstractEntropyPoolingViewFormulationType
abstract type AbstractEntropyPoolingViewFormulation <: AbstractAlgorithm

Abstract supertype for the formulations that express a tail view inside an entropy pooling problem.

A tail view constrains a quantile-based risk measure of the posterior distribution. Unlike a mean, variance or correlation view, it is not a linear function of the posterior probabilities, so each measure admits more than one way of writing it as a solvable program. The concrete subtypes name those ways.

Related

References

  • [1] D. Cajas. Entropy Pooling with CVaR and EVaR Views. Available at SSRN 7120258 (2026).
source
PortfolioOptimisers.AbstractRelativisticValueatRiskViewFormulationType
abstract type AbstractRelativisticValueatRiskViewFormulation <: AbstractEntropyPoolingViewFormulation

Abstract supertype for the formulations of a relativistic value-at-risk view.

Related

References

  • [2] D. Cajas. Entropy Pooling with Relativistic Value at Risk Views. Available at SSRN 7329718 (2026).
source
PortfolioOptimisers.AbstractEntropyPoolingTailViewEstimatorType
abstract type AbstractEntropyPoolingTailViewEstimator <: AbstractEntropyPoolingViewEstimator

Abstract supertype for the estimators that carry a group of tail views together with the settings those views are read under.

A significance level is a property of a view, not of the estimator that holds it: the conditional value at risk at 1% and at 10% are different statistics of the same series. An estimator of this family pairs a group of view equations with the level and the formulation they take, so one EntropyPoolingPrior can hold views stated at several levels.

Related

References

  • [1] D. Cajas. Entropy Pooling with CVaR and EVaR Views. Available at SSRN 7120258 (2026).
source
PortfolioOptimisers.AbstractEntropyPoolingTailViewType
abstract type AbstractEntropyPoolingTailView <: AbstractResult

Abstract supertype for the tail view constraints of an entropy pooling problem.

A tail view constraint is the parsed, resolved form of a conditional or entropic value-at-risk view. It carries the loss series, the level, the operator and the target, in the shape the formulation that produced it needs. Unlike the linear views, which reduce to rows of a matrix that multiplies the posterior probabilities, a tail view constraint needs auxiliary variables, so it is handed to the optimiser as a struct and built into the model there.

Related

References

  • [1] D. Cajas. Entropy Pooling with CVaR and EVaR Views. Available at SSRN 7120258 (2026).
source
PortfolioOptimisers.add_ep_constraint!Function
add_ep_constraint!(epc::AbstractDict, lhs::MatNum, rhs::VecNum, key::Symbol)

Add an entropy pooling view constraint to the constraint dictionary.

add_ep_constraint! normalises and adds a constraint to the entropy pooling constraint dictionary epc. If a constraint with the same key already exists, it concatenates the new constraint to the existing one. This function is used internally to build the set of linear constraints for entropy pooling optimisation.

Every view that is linear in the posterior probabilities reaches epc as a block $(\mathbf{A},\, \boldsymbol{B})$ of the system $\mathbf{A} \boldsymbol{p} = \boldsymbol{B}$ or $\mathbf{A} \boldsymbol{p} \leq \boldsymbol{B}$. The key names the sense of the block, and the optimiser reads the block back by that key.

Mathematical definition

The block is divided by the Frobenius norm of its left-hand side, which leaves the row it states unchanged:

\[\begin{align} \tilde{\mathbf{A}} &= \dfrac{\mathbf{A}}{\lVert \mathbf{A} \rVert_{F}}\,, \\ \tilde{\boldsymbol{B}} &= \dfrac{\boldsymbol{B}}{\lVert \mathbf{A} \rVert_{F}}\,. \end{align}\]

Where:

  • $\mathbf{A}$: Constraint coefficient matrix.
  • $\boldsymbol{B}$: Constraint response vector.
  • $\boldsymbol{p}$: $T \times 1$ posterior probabilities of the observations, summing to one. They are the unknown of the entropy pooling problem.
  • $\lVert \mathbf{A} \rVert_{F}$: Frobenius norm of the left-hand side block.

Algorithm

  1. Read the Frobenius norm of lhs into sc.
  2. Divide lhs and rhs by sc, giving the normalised block.
  3. Store the pair under key when epc holds no such key.
  4. Otherwise stack the normalised lhs under the block already held, and append rhs to the block's right-hand side.

Arguments

  • epc: Dictionary of entropy pooling constraints, mapping keys to (lhs, rhs) pairs.
  • lhs: Left-hand side constraint matrix.
  • rhs: Right-hand side constraint vector.
  • key: Constraint type key (:eq, :ineq, :feq, :cvar_eq).

Returns

  • nothing: The function mutates epc in-place.

Related

source
PortfolioOptimisers.announce_ep_departuresFunction
announce_ep_departures(ni::VecStr, ledger::VecStr, viewless::Bool) -> Nothing

Report, once per entropy pooling 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 ep_prior methods each spend one line on it and none of them can word it differently. It names the process an entropy pooling 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.

It is said at the end of the fit, not at the reduction. A staged algorithm interleaves its view builders with its solves, so the ledger is only complete when the last stage has stated its views; reporting earlier would report a third of the truth and reporting per stage would be three messages for one departure.

viewless raises the message to a warning: a departure that took the last surviving view leaves the fit with nothing to condition on, so the posterior is the prior probabilities and the answer is not the one the caller asked for. Every other drop trims the view set and is @info.

Arguments

  • ni: The names the Investable Mask left out, from investable_views.
  • ledger: What the departures cost, as the view builders recorded it.
  • viewless: Whether the fit ended up with no view at all because of the departures.

Returns

  • nothing.

Related

source
PortfolioOptimisers.replace_prior_viewsFunction
replace_prior_views(res::ParsingResult, pr::AbstractPriorResult, sets::UniverseSets,
                    key::Symbol, alpha::Option{<:Number} = nothing, params...;
                    strict::Bool = false)

Replace prior references in view parsing results with their corresponding prior values.

replace_prior_views scans a parsed view constraint ParsingResult for references to prior values (e.g., prior(A)), and replaces them with the actual prior value from the provided prior result object. This ensures that prior-based terms in view constraints are treated as constants and not as variables in the optimisation.

Mathematical definition

A parsed view is the row $\sum_{k} c_{k} v_{k} \lessgtr b$, and a term whose variable is prior(a) carries the constant $\pi_{a}$ rather than an unknown. Moving every such term to the right-hand side gives an equivalent row over the remaining terms:

\[\sum_{k \notin \mathcal{P}} c_{k} v_{k} \lessgtr b - \sum_{k \in \mathcal{P}} c_{k} \pi_{a_{k}}\,.\]

Where:

  • $c_{k}$, $v_{k}$: Coefficient and variable of the $k$-th term of the view.
  • $b$: Right-hand side of the view.
  • $\mathcal{P}$: Terms whose variable is a prior(...) reference.
  • $\pi_{a}$: Prior value of the statistic key for asset $a$, read by get_pr_value at the level alpha and the further parameters params....

Algorithm

  1. Match the pattern prior(<asset>) against the variable of each term in turn.
  2. When a term does not match, record that the view keeps a variable of its own, and take the next term.
  3. Find the named asset in the universe. When it is absent, report it through strict_diagnostic, record the term for removal, and take the next term.
  4. Subtract get_pr_value times the term's coefficient from rhs, and record the term for removal.
  5. Return res unchanged when step 3 and step 4 recorded no term.
  6. Drop the recorded terms from vars and coef, rebuild the equation string, and return a ParsingResult that carries the adjusted rhs.

Arguments

  • res: Parsed view constraint containing variables and coefficients.

  • pr: Prior result object containing prior values.

  • sets: Asset set mapping asset names to indices.

  • key: Moment type key (:mu, :var, :cvar, etc.).

  • alpha: Optional confidence level for VaR/CVaR views.

  • params...: Further parameters of the statistic, forwarded to get_pr_value. A tail risk view passes the observation weights the reference is read under here, and a relativistic value-at-risk view its deformation parameter.

  • strict: If true, throws error for missing assets; otherwise, issue warnings.

Validation

  • An asset a prior(...) reference names that the universe does not hold raises an ArgumentError when strict is true, and warns otherwise. The term is dropped either way.
  • At least one term of the view must keep a variable of its own. A view whose every term is a prior(...) reference is a statement about constants alone, and raises an ArgumentError.

Returns

  • res::ParsingResult: Updated parsing result with prior references replaced by their values.

Related

source
replace_prior_views(res::VecPR, args...; kwargs...)

Replace the prior references of every view constraint of a group.

replace_prior_views applies replace_prior_views to each element of a vector of parsed view constraints, replacing prior references with their corresponding prior values. parse_equation answers a group of view equations with a vector of results, so this is the shape every caller in this file meets.

The loop is a comprehension rather than a broadcast. Every parameter after res is one value for the whole group, and a broadcast reads a Tuple or a vector of observation weights as a container to walk beside res instead. That raises a DimensionMismatch whenever a group holds more than one equation, which is the shape this method exists for.

Algorithm

  1. Call the single-view method once per element of res, forwarding args... and kwargs... to each call.
  2. Return the vector of the results, one per element of res, in the order of res.

Arguments

Returns

  • res::Vector{<:ParsingResult}: Vector of updated parsing results with prior references replaced by their values.

Related

source
PortfolioOptimisers.replace_coprior_viewsFunction
replace_coprior_views(res::ParsingResult, pr::AbstractPriorResult, sets::UniverseSets, key::Symbol;
                      strict::Bool = false)

Replace correlation prior references in view parsing results with their corresponding prior values.

replace_coprior_views scans a parsed correlation view constraint (ParsingResult) for references to prior values (e.g., prior(A, B)), and replaces them with the actual prior correlation value from the provided prior result object. This ensures that prior-based terms in correlation view constraints are treated as constants and not as variables in the optimisation.

It is the pair counterpart of replace_prior_views, and it answers a RhoParsingResult rather than a ParsingResult: a pair view carries the index pair of every term, which the verb that places the view in the covariance matrix reads back.

Mathematical definition

A parsed pair view is the row $\sum_{k} c_{k} v_{k} \lessgtr b$, whose variables name asset pairs. A term whose variable is prior(a, b) carries a constant, so moving every such term to the right-hand side gives an equivalent row:

\[\sum_{k \notin \mathcal{P}} c_{k} v_{k} \lessgtr b - \sum_{k \in \mathcal{P}} c_{k} \pi_{a_{k},\, b_{k}}\,.\]

A prior(gA, gB) reference over a pair of groups carries one constant per spanned asset pair, so the subtraction broadcasts and $b$ widens to a vector of that length, one right-hand side per row the view emits.

Where:

  • $c_{k}$, $v_{k}$: Coefficient and variable of the $k$-th term of the view.
  • $b$: Right-hand side of the view.
  • $\mathcal{P}$: Terms whose variable is a prior(...) reference.
  • $\pi_{a,\,b}$: Prior value of the statistic key for the asset pair $(a, b)$, read by get_pr_value.

Algorithm

  1. Match the pattern prior(<asset1>, <asset2>) against the variable of each term in turn.
  2. When a term does not match, read its own pair (a, b) instead. Raise when the term is not of the form (a, b).
  3. Find both names in the universe, reading a bracketed name as a group and every other as one asset. When either is absent, report it through strict_diagnostic, record the term for removal, and take the next term.
  4. Record the index pair in jk_idx, and take the next term.
  5. For a term that does match, find both names the same way, subtract get_pr_value times the term's coefficient from rhs with the broadcasting operators, and record the term for removal.
  6. Return a RhoParsingResult over the untouched terms when step 3 and step 5 recorded no term.
  7. Drop the recorded terms from vars and coef, rebuild the equation string, and return a RhoParsingResult that carries the adjusted rhs and jk_idx.

Arguments

  • res: Parsed correlation view constraint containing variables and coefficients.
  • pr: Prior result object containing prior correlation values.
  • sets: Asset set mapping asset names to indices.
  • key: Symbol representing whether it's a correlation or covariance view.
  • strict: If true, throws error for missing assets; otherwise, issue warnings.

Validation

  • Every term that is not a prior(...) reference must be of the form (a, b). Any other form raises an ArgumentError.
  • Every prior(...) reference must be of the form prior(a, b). Any other form raises an ArgumentError.
  • An asset a reference names that the universe does not hold raises an ArgumentError when strict is true, and warns otherwise. The term is dropped either way.
  • At least one term of the view must keep a variable of its own. A view whose every term is a prior(...) reference raises an ArgumentError.

Returns

  • res::RhoParsingResult: Updated parsing result with prior references replaced by their values and correlation indices.

Related

source
replace_coprior_views(res::VecPR, args...; kwargs...)

Broadcast prior reference replacement across multiple view constraints.

replace_coprior_views applies replace_coprior_views to each element of a vector of parsed view constraints, replacing prior references with their corresponding prior values. parse_equation answers a group of view equations with a vector of results, so this is the shape every caller in this file meets.

Algorithm

  1. Broadcast the single-view method over res, forwarding args... and kwargs... to each call.
  2. Return the vector of the results, one RhoParsingResult per element of res, in the order of res.

Arguments

Returns

  • res::Vector{<:ParsingResult}: Vector of updated parsing results with prior references replaced by their values.

Related

source
PortfolioOptimisers.get_pr_valueFunction
get_pr_value(pr::AbstractPriorResult, i::Integer, ::Val{:mu}, args...)

Read the prior mean of asset i.

get_pr_value is the dispatch table that resolves a prior(...) reference inside a view. This method reads the statistic the tag Val(:mu) names, the i-th entry of pr.mu. It is used internally by replace_prior_views and by the ep_*_views! verbs.

Arguments

  • pr: Prior result containing asset return information.
  • i: Index of the asset.
  • ::Val{:mu}: Dispatch tag for mean extraction.
  • args...: Additional arguments (ignored).

Returns

  • mu::Number: Mean (expected return) for asset i.

Related

source
get_pr_value(pr::AbstractPriorResult, i::Integer, ::Val{:var}, alpha::Number,
             w::Option{<:ObsWeights} = nothing)

Read the prior value at risk of asset i at the level alpha.

get_pr_value is the dispatch table that resolves a prior(...) reference inside a view. This method reads the statistic the tag Val(:var) names, by applying ValueatRisk to the i-th column of pr.X. That is the $\alpha$-quantile of the loss series under w, the observation weights the initial prior result was read at. A caller who states a non-uniform w reads the reference off the distribution that caller's own prior carries.

Arguments

  • pr: Prior result containing asset return information.
  • i: Index of the asset.
  • ::Val{:var}: Dispatch tag for VaR extraction.
  • alpha: Confidence level (e.g., 0.05 for 5% VaR).
  • w: Optional observation weights vector observations × 1, or a concrete subtype of DynamicAbstractWeights. If nothing, the computation is unweighted.

Returns

  • var::Number: Value-at-Risk for asset i at level alpha.

Related

source
get_pr_value(pr::AbstractPriorResult, i::Integer, ::Val{:cvar}, alpha::Number,
             w::Option{<:ObsWeights} = nothing)

Read the prior conditional value at risk of asset i at the level alpha.

get_pr_value is the dispatch table that resolves a prior(...) reference inside a view. This method reads the statistic the tag Val(:cvar) names, by applying ConditionalValueatRisk to the i-th column of pr.X. That is the conditional value at risk of the loss series under w, the observation weights the initial prior result was read at, and it rests on no distributional assumption. It reads w on the reasoning get_pr_value gives for the value at risk.

Arguments

  • pr: Prior result containing asset return information.
  • i: Index of the asset.
  • ::Val{:cvar}: Dispatch tag for CVaR computation.
  • alpha: Confidence level.
  • w: Optional observation weights vector observations × 1, or a concrete subtype of DynamicAbstractWeights. If nothing, the computation is unweighted.

Returns

  • cvar::Number: Conditional Value-at-Risk for asset i at level alpha.

Related

source
get_pr_value(pr::AbstractPriorResult, i::Integer, ::Val{:sigma}, args...)

Read the prior variance of asset i.

get_pr_value is the dispatch table that resolves a prior(...) reference inside a view. This method reads the statistic the tag Val(:sigma) names, the i-th diagonal entry of pr.sigma. The tag is :sigma and the statistic is the variance, not the standard deviation and not the value at risk, which the tag :var names.

Arguments

  • pr: Prior result containing asset return information.
  • i: Index of the asset.
  • ::Val{:sigma}: Dispatch tag for variance extraction.
  • args...: Additional arguments (ignored).

Returns

  • sigma::Number: Variance for asset i.

Related

source
get_pr_value(pr::AbstractPriorResult, i::Integer, j::Integer, ::Val{:rho}, args...)
get_pr_value(pr::AbstractPriorResult, i::Integer, j::Integer, ::Val{:cov}, args...)

Read the prior correlation or covariance of the asset pair (i, j).

get_pr_value is the dispatch table that resolves a prior(...) reference inside a view. Val(:cov) names the entry pr.sigma[i, j], and Val(:rho) names the same entry of StatsBase.cov2cor(pr.sigma). These methods are used internally by replace_coprior_views to resolve a prior(a, b) reference.

Arguments

  • pr: Prior result containing asset return information.
  • i: Index of the first asset.
  • j: Index of the second asset.
  • ::Val{:rho}: Dispatch tag for correlation extraction.
  • ::Val{:cov}: Dispatch tag for covariance extraction.
  • args...: Additional arguments (ignored).

Returns

  • val::Number: Correlation coefficient or covariance between assets i and j.

Related

source
get_pr_value(pr::AbstractPriorResult, i::VecInt, j::VecInt, ::Val{:rho}, args...)
get_pr_value(pr::AbstractPriorResult, i::VecInt, j::VecInt, ::Val{:cov}, args...)

Read the prior correlations or covariances of the asset pairs that two groups span.

get_pr_value is the dispatch table that resolves a prior(...) reference inside a view. These methods read the same statistics their scalar siblings do, once per spanned pair, in the order of zip(i, j). A view over a pair of groups emits one constraint row per spanned pair, so a prior(gA, gB) reference inside such a view must give each row that pair's own prior value.

Arguments

  • pr: Prior result containing asset return information.
  • i: Vector of indices for the first asset group.
  • j: Vector of indices for the second asset group.
  • ::Val{:rho}: Dispatch tag for correlation extraction.
  • ::Val{:cov}: Dispatch tag for covariance extraction.
  • args...: Additional arguments (ignored).

Returns

  • val::Vector{<:Number}: Correlation or covariance of each spanned pair, one entry per element of zip(i, j).

Related

source
get_pr_value(pr::AbstractPriorResult, i::Integer, ::Val{:skew}, args...)

Read the prior skewness of asset i.

get_pr_value is the dispatch table that resolves a prior(...) reference inside a view. This method reads the statistic the tag Val(:skew) names, by applying Skewness to the i-th column of pr.X. That is the standardised third central moment of the sample, and it ignores pr.w.

Arguments

  • pr: Prior result containing asset return information.
  • i: Index of the asset.
  • ::Val{:skew}: Dispatch tag for skewness extraction.
  • args...: Additional arguments (ignored).

Returns

  • skew::Number: Skewness for asset i.

Related

source
get_pr_value(pr::AbstractPriorResult, i::Integer, ::Val{:kurtosis}, args...)

Read the prior kurtosis of asset i.

get_pr_value is the dispatch table that resolves a prior(...) reference inside a view. This method reads the statistic the tag Val(:kurtosis) names, by applying HighOrderMoment with a StandardisedHighOrderMoment of FourthMoment to the i-th column of pr.X. That is the standardised fourth central moment of the sample, and it ignores pr.w.

Arguments

  • pr: Prior result containing asset return information.
  • i: Index of the asset.
  • ::Val{:kurtosis}: Dispatch tag for kurtosis extraction.
  • args...: Additional arguments (ignored).

Returns

  • kurtosis::Number: Kurtosis for asset i.

Related

source
get_pr_value(pr::AbstractPriorResult, i::Integer, ::Val{:evar}, alpha::Number,
             w::Option{<:ObsWeights} = nothing, args::Tuple = (),
             kwargs::NamedTuple = (;), zlo_frac::Option{<:Number} = nothing)

Extract the Entropic Value-at-Risk (EVaR) for asset i from a prior result.

get_pr_value computes the EVaR at confidence level alpha for the asset indexed by i from the prior result object pr, by minimising the scalar objective of the sample EVaR formula with ep_evar. The observations carry w, the weights the initial prior result was read at. A w of nothing leaves them uniform.

Arguments

  • pr: Prior result containing asset return information. Only its returns matrix is read, under the weights w names.
  • i: Index of the asset.
  • ::Val{:evar}: Dispatch tag for EVaR extraction.
  • alpha: Confidence level (e.g. 0.05 for 5% EVaR).
  • w: Optional observation weights vector observations × 1, or a concrete subtype of DynamicAbstractWeights. If nothing, the computation is unweighted.
  • args: Additional positional arguments passed to the optimisation function.
  • kwargs: Additional keyword arguments passed to the optimisation function.
  • zlo_frac: Lower end of the bracket of the dual variable, as a fraction of the upper end, forwarded to ep_evar.

Returns

  • evar::Number: Entropic Value-at-Risk for asset i at level alpha.

Related

source
get_pr_value(pr::AbstractPriorResult, i::Integer, ::Val{:rlvar}, alpha::Number,
             kappa::Number, w::Option{<:ObsWeights} = nothing, args::Tuple = (),
             kwargs::NamedTuple = (;),
             bracket::Option{<:RelativisticValueatRiskViewBracket} = nothing)

Extract the Relativistic Value-at-Risk (RLVaR) for asset i from a prior result.

get_pr_value computes the RLVaR at confidence level alpha and deformation parameter kappa for the asset indexed by i from the prior result object pr, by minimising the primal objective of the sample RLVaR with ep_rlvar. The observations carry w, the weights the initial prior result was read at, on the reasoning the entropic value at risk method above gives.

Arguments

  • pr: Prior result containing asset return information. Only its returns matrix is read, under the weights w names.
  • i: Index of the asset.
  • ::Val{:rlvar}: Dispatch tag for RLVaR extraction.
  • alpha: Confidence level (e.g. 0.05 for 5% RLVaR).
  • kappa: Deformation parameter, in (0, 1).
  • w: Optional observation weights vector observations × 1, or a concrete subtype of DynamicAbstractWeights. If nothing, the computation is unweighted.
  • args: Additional positional arguments passed to the optimisation function.
  • kwargs: Additional keyword arguments passed to the optimisation function.
  • bracket: Spans of the searches, forwarded to ep_rlvar and ep_rlvar_shift.

Returns

  • rlvar::Number: Relativistic Value-at-Risk for asset i at level alpha and deformation kappa.

Related

source
PortfolioOptimisers.ep_mu_views!Function
ep_mu_views!(mu_views::Nothing, args...; kwargs...)

Do nothing when a problem states no mean view.

ep_mu_views! is the verb that turns a group of mean views into rows of the entropy pooling constraint dictionary. This method is the absent-view branch: it registers no row, so a higher-level routine can call the verb without special-casing mu_views = nothing.

Arguments

  • mu_views::Nothing: Indicates that no mean view constraints are specified.
  • args...: Additional positional arguments (ignored).
  • kwargs...: Additional keyword arguments (ignored).

Returns

  • nothing.

Related

source
ep_mu_views!(mu_views::LinearConstraintEstimator, epc::AbstractDict,
             pr::AbstractPriorResult, sets::UniverseSets; strict::Bool = false)

Add the mean views of a group to the entropy pooling constraint dictionary.

ep_mu_views! parses mean view equations from a LinearConstraintEstimator, replaces any prior references with their actual values, and constructs the corresponding linear constraints for entropy pooling. The constraints are then added to the entropy pooling constraint dictionary epc. The statistic is the mean of the posterior distribution, which is linear in the posterior probabilities, so the view needs no auxiliary variable and no moment is fixed on its account.

Mathematical definition

The posterior mean of an asset is the probability weighted average of its returns, and a view states a linear combination of such means:

\[\begin{align} \mathrm{E}_{\boldsymbol{p}}[x_{i}] &= \sum_{t=1}^{T} p_{t} x_{t,\,i}\,, \\ \sum_{i=1}^{N} a_{k,\,i} \mathrm{E}_{\boldsymbol{p}}[x_{i}] &\lessgtr B_{k}\,, \quad \forall\, k = 1,\ldots,K\,. \end{align}\]

The left-hand side is linear in $\boldsymbol{p}$, so the $K$ views are the block $\left(\mathbf{A} \mathbf{X}^{\intercal},\, \boldsymbol{B}\right)$.

Where:

  • $\boldsymbol{p}$: $T \times 1$ posterior probabilities of the observations, summing to one. They are the unknown of the entropy pooling problem.
  • $x_{t,\,i}$: Return of asset $i$ at observation $t$.
  • $T$: Number of observations.
  • $N$: Number of assets.
  • $\mathbf{A}$: Constraint coefficient matrix.
  • $\boldsymbol{B}$: Constraint response vector.
  • $a_{k,\,i}$: Coefficient asset $i$ takes in view $k$, the $(k, i)$ entry of $\mathbf{A}$.
  • $K$: Number of views the group states.
  • $\mathbf{X}$: $T \times N$ returns matrix of the prior.

Algorithm

  1. Parse the view equations of mu_views.val, giving one ParsingResult per view.
  2. Replace every group name by the assets it spans.
  3. Replace every prior(...) reference by the prior mean, through replace_prior_views.
  4. Turn the parsed views into the linear constraint blocks lcs, one for :ineq and one for :eq. Under strict = false every row of the group can drop, and lcs is then nothing: the group states no view, and the call returns without adding a row.
  5. For each block present, add A * transpose(X) against B under that key with add_ep_constraint!.

Arguments

  • mu_views: Mean view constraints.
  • epc: Dictionary of entropy pooling constraints, mapping keys to (lhs, rhs) pairs.
  • pr: Prior result containing asset return information.
  • sets: Asset set mapping asset names to indices.
  • strict: If true, throws error for missing assets; otherwise, issue warnings.

Returns

  • nothing: The function mutates epc in-place.

Related

source
PortfolioOptimisers.fix_mu!Function
fix_mu!(epc::AbstractDict, fixed::AbstractVector, to_fix::BitVector,
        pr::AbstractPriorResult)

Hold the mean of the named assets at the prior value.

fix_mu! identifies assets in to_fix that are not yet fixed (i.e., not present in fixed), and adds constraints to the entropy pooling constraint dictionary epc to fix their mean to the prior value. This ensures that higher moment views (e.g., variance, skewness, kurtosis, correlation) do not inadvertently alter the mean of these assets. The function updates fixed in-place to reflect the newly fixed assets.

The rows go in under the :feq key, which the optimiser relaxes with a penalised slack rather than enforcing exactly. A fixing row is a wish, not a view: it competes with the views that were asked for, and it yields where the two cannot both hold.

Mathematical definition

The posterior mean of every named asset is held at the prior mean:

\[\sum_{t=1}^{T} p_{t} x_{t,\,i} = \mu_{i}\,, \quad \forall\, i \in \mathcal{F}\,.\]

Where:

  • $\boldsymbol{p}$: $T \times 1$ posterior probabilities of the observations, summing to one. They are the unknown of the entropy pooling problem.
  • $x_{t,\,i}$: Return of asset $i$ at observation $t$.
  • $\mu_{i}$: Prior mean of asset $i$. It is a constant of the view, and a lower moment view or a fixing row holds the posterior mean at it.
  • $T$: Number of observations.
  • $\mathcal{F}$: Assets named by to_fix that fixed does not already hold.

Algorithm

  1. Read the assets that to_fix names and fixed does not already hold into fix.
  2. Return when fix names no asset.
  3. Add one :feq row per named asset, transpose(view(pr.X, :, fix)) against pr.mu[fix], with add_ep_constraint!.
  4. Mark the named assets in fixed, so a later call adds no second row for them.

Arguments

  • epc: Dictionary of entropy pooling constraints, mapping keys to (lhs, rhs) pairs.
  • fixed: Boolean vector indicating which assets have their mean fixed.
  • to_fix: Boolean vector indicating which assets should have their mean fixed.
  • pr: Prior result containing asset return information.

Returns

  • nothing: The function mutates epc and fixed in-place.

Related

source
PortfolioOptimisers.ep_var_views!Function
ep_var_views!(var_views::Nothing, args...; kwargs...)

Do nothing when a problem states no value at risk view.

ep_var_views! is the verb that turns a group of value at risk views into rows of the entropy pooling constraint dictionary. This method is the absent-view branch: it registers no row, so a higher-level routine can call the verb without special-casing var_views = nothing.

Arguments

  • var_views::Nothing: Indicates that no value at risk (VaR) view constraints are specified.
  • args...: Additional positional arguments (ignored).
  • kwargs...: Additional keyword arguments (ignored).

Returns

  • nothing.

Related

source
ep_var_views!(var_views::ValueatRiskView, epc::AbstractDict,
              pr::AbstractPriorResult, sets::UniverseSets,
              w::Option{<:ObsWeights} = nothing; strict::Bool = false)
ep_var_views!(var_views::LinearConstraintEstimator, epc::AbstractDict,
              pr::AbstractPriorResult, sets::UniverseSets, alpha::Number,
              w::Option{<:ObsWeights} = nothing; strict::Bool = false)

Add the value at risk views of a group to the entropy pooling constraint dictionary.

The first method unpacks a ValueatRiskView into its equations and its significance level, and hands both to the second. The second carries the body: it parses the view equations, replaces any prior references with their actual values, and constructs the corresponding linear constraints for entropy pooling. The statistic is the value at risk of the posterior distribution, not the variance, which the sigma_views family holds.

A value at risk view is linear in the posterior probabilities: it constrains the probability mass at or beyond the target loss, so it needs no auxiliary variable and no moment is fixed on its account.

Mathematical definition

The value at risk at level $\alpha$ is the smallest loss the posterior leaves at most $\alpha$ of its mass beyond, so a view on it is a statement about the tail mass of the sample:

\[\begin{align} \mathrm{VaR}_{\alpha}(x_{i}) \geq \bar{v} \quad &\Longleftrightarrow \quad \sum_{t \in \mathcal{T}_{i}(\bar{v})} p_{t} \geq \alpha\,, \\ \mathrm{VaR}_{\alpha}(x_{i}) = \bar{v} \quad &\Longleftrightarrow \quad \sum_{t \in \mathcal{T}_{i}(\bar{v})} p_{t} = \alpha\,, \\ \mathcal{T}_{i}(\bar{v}) &= \left\{ t : x_{t,\,i} \leq -\lvert \bar{v} \rvert \right\}\,. \end{align}\]

Both are linear in $\boldsymbol{p}$, which is why this view reaches OptimEntropyPooling as readily as JuMPEntropyPooling.

Where:

  • $\boldsymbol{p}$: $T \times 1$ posterior probabilities of the observations, summing to one. They are the unknown of the entropy pooling problem.
  • $x_{t,\,i}$: Return of asset $i$ at observation $t$.
  • $\alpha$: Significance level (left tail probability), $\alpha \in (0, 1)$.
  • $\bar{v}$: Target value at risk of the view.
  • $\mathcal{T}_{i}(\bar{v})$: Observations of asset $i$ whose loss reaches the target.
The view is met to one observation

The constraint fixes the posterior mass of $\mathcal{T}_{i}(\bar{v})$, and the posterior value at risk of that mass is a sample order statistic: ValueatRisk reads the first observation whose cumulative weight reaches $\alpha$. An entropy pooling solve meets its constraints to its own tolerance, and a mass short of $\alpha$ by as little as 1e-8 reads one observation further down the tail. The posterior value at risk then sits under $\bar{v}$, by the gap between two neighbouring losses. No tolerance on the solve removes this, because the reading is a step function of the mass. The view is met to the resolution the sample has, which is one observation. Read a posterior value at risk against the two observations that bracket the target, and read the tail mass where an exact statement is needed.

Algorithm

  1. Parse the view equations of var_views.val, accepting == and >= alone.
  2. Replace every group name by the assets it spans.
  3. Replace every prior(...) reference by the prior value at risk at alpha, read under w, through replace_prior_views.
  4. Turn the parsed views into the linear constraint blocks lcs, one for :ineq and one for :eq. Under strict = false every row of the group can drop, and lcs is then nothing: the group states no view, and the call returns without adding a row.
  5. Check the three preconditions of the section below.
  6. For each block present, and each row i of it, read the asset the row names into j, and the observations of view(X, :, j) at or below -abs(B[i]) into idx.
  7. Raise when idx names no observation.
  8. Read the sense the row takes into sign: it is one for an equality row and for a non-negative right-hand side, and minus one otherwise.
  9. Build the row Ai that carries sign at idx and zero elsewhere, and add it against sign * alpha with add_ep_constraint!.

Arguments

  • var_views: VaR view constraints.
  • epc: Dictionary of entropy pooling constraints, mapping keys to (lhs, rhs) pairs.
  • pr: Prior result containing asset return information.
  • sets: Asset set mapping asset names to indices.
  • alpha: Confidence level for VaR.
  • w: Optional observation weights vector observations × 1, or a concrete subtype of DynamicAbstractWeights. If nothing, the computation is unweighted.
  • strict: If true, throws error for missing assets; otherwise, issue warnings.

Validation

  • Every coefficient has magnitude zero or one. Any other coefficient raises an ArgumentError. The check reads the magnitude because the parser normalises a >= row to <= by negation, which carries a coefficient of one into the block as minus one.
  • Every view names one asset. A view over more than one asset raises an ArgumentError.
  • Every target is non-negative. A negative target raises a DomainError.
  • The sample must hold at least one observation whose loss reaches the target. A view more extreme than the worst realisation raises a DomainError naming the largest target the asset admits.

Returns

  • nothing: The function mutates epc in-place.

Related

source
ep_var_views!(var_views::AbstractVector{<:ValueatRiskView}, args...; kwargs...)

Add each group of value at risk views under its own significance level.

Every ValueatRiskView in the vector is added in turn, so the groups accumulate into the same constraint set and one entropy pooling solve answers all of them.

Algorithm

  1. Add each ValueatRiskView of var_views in turn, forwarding args... and kwargs... to each call.
  2. Return nothing. Each call has already written its rows into epc.

Arguments

  • var_views: Groups of VaR views.
  • args...: Additional positional arguments forwarded to ep_var_views!.
  • kwargs...: Additional keyword arguments forwarded to ep_var_views!.

Returns

  • nothing: The function mutates epc in-place.

Related

source
PortfolioOptimisers.ep_prior_probabilitiesFunction
ep_prior_probabilities(w::Option{<:StatsBase.ProbabilityWeights},
                       pr::AbstractPriorResult, Ti::Int)

Return the prior probabilities an entropy pooling fit starts from.

A prior that reweights observations works on the observation axis its nested prior answered, not on the axis it was handed. A nested prior may drop rows: a CrossSectionalFactorPrior drops the observations its Descriptors warm up over and the observations its exposure lag consumes, so its scenarios are the window the fit is defined on. So pr is fitted first, and the prior probabilities are read on its rows.

The three sources are read in order. A caller's pe.w wins, because it is the one tilt no fit can state. The nested result's own w comes next, because a nested pooling prior already tilted the scenarios it answered, and uniform is then not the prior. Uniform over the rows of pr.X is the last.

Algorithm

  1. Take T as size(pr.X, 1), the observations the nested prior answered.
  2. When w is not nothing, check its length against T and return it.
  3. When w is nothing and pr.w is not, return pr.w as StatsBase.pweights.
  4. Otherwise return the uniform 1/T as StatsBase.pweights.

Arguments

  • w: A caller's prior probabilities, the w field of the pooling estimator, or nothing.
  • pr: Prior result of the nested estimator, fitted before this call.
  • Ti: Observations the pooling estimator was handed. It is read only by the refusal message.

Validation

  • length(w) == size(pr.X, 1). A length that does not match raises a DimensionMismatch naming both counts and the count the estimator was handed.

Returns

  • w0::StatsBase.ProbabilityWeights: Prior probabilities, on the rows of pr.X.

Related

source
PortfolioOptimisers.entropy_poolingFunction
entropy_pooling(w::VecNum, epc::AbstractDict, opt::OptimEntropyPooling)

Solve the dual of the entropy pooling problem using Optim.jl.

entropy_pooling computes posterior probabilities by minimising the Kullback-Leibler divergence of the posterior weights from the prior ones, subject to moment and view constraints. The optimisation is performed using Optim.jl. This method is used internally by MeucciEntropyPoolingPrior and EntropyPoolingPrior when the optimiser is an OptimEntropyPooling.

The two optimisation algorithms minimise the same objective and reach the same posterior. They differ only in the arithmetic that evaluates it.

The dual carries one variable per row rather than one per observation, and it has no room for an auxiliary variable, so it expresses no tail view. It also has no slack variable: the fixed equality rows of the :feq key are relaxed by holding their dual variables in the box $[-s_{c2},\, s_{c2}]$, which is the dual of a penalty of weight $s_{c2}$ on the norm of the slack the primal would carry.

Warning

An infeasible view set answers without a raise. The dual of such a set is unbounded below, so the minimiser runs away rather than settling. The iterate stops moving once the exponential underflows, Optim reports x_converged or f_converged, and Optim.converged accepts it. The posterior it returns is degenerate: the probability collapses onto the observation with the largest coefficient, and the view the caller wrote is missed by any margin. Read the answer rather than the flag. The effective number of scenarios falls to a handful out of $T$ and one weight sits near one, the Kullback-Leibler divergence is large, and the posterior statistic the view named is far from its target. Views that pull one asset in two directions at once are the common way to reach it: a variance view that shrinks an asset, written beside a conditional value at risk view that fattens the same asset's tail, asks for a thin body and a fat tail at once. The same pair on two different assets is feasible and solves normally, so it is the direction and not the pairing. The gradient of this dual is $\boldsymbol{B} - \mathbf{A} \boldsymbol{y}$, the primal residual of the view set, so Optim.g_converged and Optim.g_residual do separate the two outcomes. Neither is read: Optim.g_converged also refuses a solve that is correct and merely loose, so acting on it needs a tolerance on the residual, and that tolerance is a policy this library does not set.

Mathematical definition

The primal minimises the Kullback-Leibler divergence of the posterior from the prior, over the probabilities that meet every row. Its dual carries one Lagrange multiplier per row, and is unconstrained apart from the box the sense of each row imposes:

\[\begin{align} \underset{\boldsymbol{x}}{\min} &\; \boldsymbol{x}^\intercal \boldsymbol{B} + \sum_{t=1}^{T} q_{t} \exp\!\left(-\boldsymbol{x}^\intercal \mathbf{A}_{\cdot t} - 1\right)\,. \end{align}\]

The optimal posterior probabilities recover from the minimiser as:

\[\begin{align} p_{t}^{*} &= q_{t} \exp\!\left(-\boldsymbol{x}^{*\intercal} \mathbf{A}_{\cdot t} - 1\right)\,. \end{align}\]

Where:

  • $\boldsymbol{q}$: $T \times 1$ prior probabilities of the observations, summing to one.
  • $\boldsymbol{p}$: $T \times 1$ posterior probabilities of the observations, summing to one. They are the unknown of the entropy pooling problem.
  • $\mathbf{A}$: Constraint coefficient matrix.
  • $\boldsymbol{B}$: Constraint response vector.
  • $T$: Number of observations.
  • $s_{c1}$: Constraint scale of the entropy pooling optimiser. It multiplies both sides of a row, so a positive value leaves the feasible set unchanged.
  • $s_{c2}$: Slack penalty of the fixed equality rows. It weights the norm of the slack in the objective, so a larger value holds those rows tighter.
  • $\boldsymbol{x}$: Lagrange multipliers of the rows, the variable of the dual.
  • $\mathbf{A}_{\cdot t}$: $t$-th column of $\mathbf{A}$, the coefficient every row gives observation $t$.
  • $q_{t}$, $p_{t}^{*}$: Prior and optimal posterior probability of observation $t$.

Algorithm

  1. Return w when epc holds no row. An empty view set states nothing, so the posterior is the prior, and it is answered exactly rather than solved for.
  2. Open A and B with the row that pins the posterior to sum to one, both sides divided by $\sqrt{T}$.
  3. Stack the block of every key of epc onto A and B, and set the box wb of that block's dual variables from the key: free for :eq and :cvar_eq, non-negative for :ineq, and $[-s_{c2},\, s_{c2}]$ for :feq. Raise on any other key. A :feq block is left out when $s_{c2}$ is zero, because that box pins its dual variables to zero and the fixed rows then carry no weight.
  4. Start every dual variable at $1/\sqrt{T}$, clamped into its own box. A :feq box is $[-s_{c2},\, s_{c2}]$, so an s_{c2} below $1/\sqrt{T}$ would otherwise place the start outside it.
  5. Minimise the dual objective over that box with Optim.optimize, through the branch alg selects. Both the objective and its gradient are multiplied by $s_{c1}$.
  6. Raise when Optim.converged reports that the solve failed.
  7. Recover the posterior probabilities from the minimiser, and return them as StatsBase.pweights.

Arguments

  • w: Prior weights (length = number of observations).

  • epc: Dictionary of entropy pooling constraints, mapping keys to (lhs, rhs) pairs.

  • opt: Optim.jl-based entropy pooling optimiser.

    • ::OptimEntropyPooling{<:Any, <:Any, <:Any, <:Any, <:ExpEntropyPooling}: Evaluate the objective through the exponential of the dual variables.
    • ::OptimEntropyPooling{<:Any, <:Any, <:Any, <:Any, <:LogEntropyPooling}: Evaluate the objective in log space.

Validation

  • Every key of epc is one of :eq, :ineq, :cvar_eq and :feq. Any other key raises a KeyError.
  • The solve must converge. A solve that Optim.converged reports as failed raises an ErrorException.
  • An infeasible view set is not caught. Optim.converged is true on x_converged or f_converged alone, and the dual of an infeasible set stops on one of those. The summary paragraph states the shape of that answer and how to recognise it.

Returns

  • pw::StatsBase.ProbabilityWeights: Posterior probability weights satisfying the constraints.

Related

References

  • [78] A. Meucci. Fully flexible views: theory and practice. Risk 21, 97–102 (2008).
source
entropy_pooling(w::VecNum, epc::AbstractDict, opt::JuMPEntropyPooling)

Solve the primal of the entropy pooling problem using JuMP.jl.

entropy_pooling computes posterior probabilities by minimising the Kullback-Leibler divergence of the posterior weights from the prior ones, subject to moment and view constraints. The optimisation is performed using JuMP.jl. This method is used internally by MeucciEntropyPoolingPrior and EntropyPoolingPrior when the optimiser is a JuMPEntropyPooling.

This method registers no model entry of its own. It is the three-argument shape of a problem that states no tail view, and the four-argument method carries the model, its # JuMP formulation and its # Algorithm.

Algorithm

  1. Call the four-argument method with an empty AbstractEntropyPoolingTailView vector, and return what it answers.

Arguments

  • w: Prior weights (length = number of observations).

  • epc: Dictionary of entropy pooling constraints, mapping keys to (lhs, rhs) pairs.

  • opt: JuMP.jl-based entropy pooling optimiser.

    • ::JuMPEntropyPooling{<:Any, <:Any, <:Any, <:Any, <:ExpEntropyPooling}: Write the divergence against the prior probabilities directly.
    • ::JuMPEntropyPooling{<:Any, <:Any, <:Any, <:Any, <:LogEntropyPooling}: Write the divergence against a unit reference and subtract the prior log-probabilities in the objective.

Returns

  • pw::StatsBase.ProbabilityWeights: Posterior probability weights satisfying the constraints.

Related

References

  • [78] A. Meucci. Fully flexible views: theory and practice. Risk 21, 97–102 (2008).
source
entropy_pooling(w::VecNum, epc::AbstractDict, tvs::VecEPTV,
                opt::AbstractEntropyPoolingOptimiser)

Solve an entropy pooling problem that carries tail view constraints.

entropy_pooling extends the three-argument form with the conditional and entropic value-at-risk views of [1]. A tail view needs auxiliary variables, so it is built into the model by add_ep_tail_view! rather than reduced to rows of epc.

These methods carry the body of the JuMPEntropyPooling route. The OptimEntropyPooling method solves the dual, which has no room for an auxiliary variable, so it accepts an empty tvs alone and forwards to the three-argument form.

Mathematical definition

The primal of the entropy pooling problem minimises the Kullback-Leibler divergence of the posterior probabilities from the prior ones, over the probabilities that meet every row:

\[\begin{align} \underset{\boldsymbol{p}}{\min} \;\; &\sum_{t=1}^{T} p_{t} \ln\!\left(\dfrac{p_{t}}{q_{t}}\right)\\ \textrm{s.t.} \;\; &\sum_{t=1}^{T} p_{t} = 1\\ &\boldsymbol{p} \geq \boldsymbol{0}\\ &\mathbf{A}_{\mathrm{eq}} \boldsymbol{p} = \boldsymbol{B}_{\mathrm{eq}}\\ &\mathbf{A}_{\mathrm{ineq}} \boldsymbol{p} \leq \boldsymbol{B}_{\mathrm{ineq}}\,. \end{align}\]

Where:

  • $\boldsymbol{q}$: $T \times 1$ prior probabilities of the observations, summing to one.
  • $\boldsymbol{p}$: $T \times 1$ posterior probabilities of the observations, summing to one. They are the unknown of the entropy pooling problem.
  • $\mathbf{A}$: Constraint coefficient matrix.
  • $\boldsymbol{B}$: Constraint response vector.
  • $\text{eq}$: Subscript for equality constraints.
  • $\text{ineq}$: Subscript for inequality constraints.
  • $T$: Number of observations.
  • $q_{t}$, $p_{t}$: Prior and posterior probability of observation $t$.

Algorithm

  1. Raise when opt is an OptimEntropyPooling and tvs names a tail view, and forward to the three-argument form otherwise.
  2. Return w when epc holds no row and tvs names no tail view. An empty view set states nothing, so the posterior is the prior, and it is answered exactly rather than solved for.
  3. Solve once with ep_jump_entropy_pooling, giving w1.
  4. Read iters, the largest number of re-solves a carrier of tvs asks for, with ep_refine_iters. It is zero where no carrier is sequential, and the steps below then do not run.
  5. Re-read every carrier at w1 with ep_refine_tail_view, which returns the carrier and whether its surrogate row is tight there. Stop where every carrier is tight.
  6. Solve again with the re-read carriers, giving a new w1, and return to step 5. Take at most iters re-solves, so the last posterior can hold a slack row when the sequence has not settled.
  7. Return w1 as StatsBase.pweights.

The re-solves are the sequential convex formulations' half of the work. Each re-read row is tight at the posterior it was read at and still holds there, so that posterior stays feasible and the divergence of the next one is at most its own.

Arguments

  • w: Prior weights (length = number of observations).

  • epc: Dictionary of entropy pooling constraints, mapping keys to (lhs, rhs) pairs.

  • tvs: Tail view constraints.

  • opt: Entropy pooling optimiser.

    • ::JuMPEntropyPooling: Builds every tail view into the model.
    • ::OptimEntropyPooling: Solves the dual, which has no room for an auxiliary variable, so it accepts an empty tvs alone.

Validation

  • isa(opt, OptimEntropyPooling) requires isempty(tvs).

Returns

  • pw::StatsBase.ProbabilityWeights: Posterior probability weights satisfying the constraints.

Related

References

  • [1] D. Cajas. Entropy Pooling with CVaR and EVaR Views. Available at SSRN 7120258 (2026).
source
PortfolioOptimisers.ep_jump_entropy_poolingFunction
ep_jump_entropy_pooling(w::VecNum, epc::AbstractDict, tvs::VecEPTV,
                        opt::JuMPEntropyPooling)

Build and solve the entropy pooling model of one set of tail view carriers, once.

ep_jump_entropy_pooling is the body of the JuMPEntropyPooling route of entropy_pooling, which calls it once per set of carriers and re-calls it after re-reading the sequential ones. The two methods differ only in how they represent the divergence, and agree on every view.

Algorithm

  1. Read sc1, sc2, so and slv off opt, and open an empty JuMP.Model.
  2. Create the posterior variable x and the divergence variable t. Under ExpEntropyPooling x also carries a non-negativity bound, which the cone of step 4 implies in either branch.
  3. Add the row that pins the sum of x to one.
  4. Add the relative entropy cone that bounds t below by the divergence. Under ExpEntropyPooling the cone reads the prior w as its reference, and under LogEntropyPooling it reads a unit reference instead.
  5. Register obj_expr as so * t.
  6. Add every view with ep_jump_views!, which mutates obj_expr when a fixed equality is relaxed.
  7. Set the objective to obj_expr under ExpEntropyPooling, and to obj_expr less so times the inner product of x with the prior log-probabilities under LogEntropyPooling.
  8. Solve with slv, and raise when no solver configuration succeeds.
  9. Return the value of x as StatsBase.pweights.

JuMP formulation

Variables

  • t: $\eta$, the epigraph variable of the divergence, created here.
  • x: $\boldsymbol{p}$, the posterior probabilities, created here. Under ExpEntropyPooling it carries the bound $\boldsymbol{p} \geq \boldsymbol{0}$.

Expressions

  • obj_expr: $s_{o} \eta$. ep_jump_views! adds $s_{o} s_{c2} \eta_{c}$ to it when epc holds a :feq block.

Constraints

The two rows below are registered under no name.

  • $s_{c1} \left(\sum_{t=1}^{T} p_{t} - 1\right) = 0$
  • $\left(s_{c1} \eta,\; s_{c1} \boldsymbol{q},\; s_{c1} \boldsymbol{p}\right) \in \mathcal{K}_{\mathrm{relent}}$, which states $\eta \geq \sum_{t=1}^{T} p_{t} \ln(p_{t} / q_{t})$. Under LogEntropyPooling the reference is $s_{c1} \boldsymbol{1}$ in place of $s_{c1} \boldsymbol{q}$, so the row states $\eta \geq \sum_{t=1}^{T} p_{t} \ln(p_{t})$.

ep_jump_views! registers every further row of the model, and its own # JuMP formulation names them.

Objective

  • Min of obj_expr under ExpEntropyPooling.
  • Min of obj_expr less $s_{o} \boldsymbol{p}^{\intercal} \ln(\boldsymbol{q})$ under LogEntropyPooling. The subtraction turns the cone's $\sum_{t} p_{t} \ln(p_{t})$ into the divergence, so both branches minimise the same quantity.

Relaxation

The encoding is not exact: the entries below bound the quantity instead of reproducing it, and the bound is tight only under the condition stated here.

  • The bound is on obj_expr, and it lies above the divergence of the exactly constrained problem. ep_jump_views! relaxes the :feq rows with a penalised slack, and the penalty enters obj_expr.
  • The bound is tight when that slack is zero, which holds when the :feq rows and the views can be met together, and $s_{c2}$ is large enough to pull the slack down.

Where:

  • $\boldsymbol{q}$: $T \times 1$ prior probabilities of the observations, summing to one.
  • $\boldsymbol{p}$: $T \times 1$ posterior probabilities of the observations, summing to one. They are the unknown of the entropy pooling problem.
  • $T$: Number of observations.
  • $s_{c1}$: Constraint scale of the entropy pooling optimiser. It multiplies both sides of a row, so a positive value leaves the feasible set unchanged.
  • $s_{c2}$: Slack penalty of the fixed equality rows. It weights the norm of the slack in the objective, so a larger value holds those rows tighter.
  • $s_{o}$: Objective scale of the entropy pooling optimiser. It multiplies the objective, so a positive value leaves the argument of the optimum unchanged.
  • $\eta$: Epigraph variable that bounds the divergence from above. Its model key is t.
  • $\eta_{c}$: Norm of the fixed equality slack, which ep_jump_views! creates under the model key tc.
  • $\mathcal{K}_{\mathrm{relent}}$: Relative entropy cone, JuMP.MOI.RelativeEntropyCone(2T + 1).

Arguments

  • w: Prior weights (length = number of observations).
  • epc: Dictionary of entropy pooling constraints, mapping keys to (lhs, rhs) pairs.
  • tvs: Tail view constraints.
  • opt: Entropy pooling optimiser.

Validation

  • One configuration of opt.slv solves the model. Otherwise an ErrorException is raised.

Returns

  • pw::StatsBase.ProbabilityWeights: Posterior probability weights satisfying the constraints.

Related

References

  • [1] D. Cajas. Entropy Pooling with CVaR and EVaR Views. Available at SSRN 7120258 (2026).
source
PortfolioOptimisers.ep_refine_itersFunction
ep_refine_iters(tv::AbstractEntropyPoolingTailView)
ep_refine_iters(tvs::VecEPTV)

Read the number of re-solves a tail view carrier asks entropy_pooling for.

A carrier whose rows are fixed at construction asks for none. A sequential carrier asks for the iters its formulation holds, and a vector of carriers asks for the largest number among its entries, so one loop serves every sequential view of the model.

Arguments

  • tv: Tail view constraint.
  • tvs: Tail view constraints.

Returns

  • iters::Integer: Number of re-solves, zero for a carrier with fixed rows.

Related

source
ep_refine_tail_view(tv::AbstractSequentialTailViewConstraint, w::VecNum)

Re-read the surrogate row of a sequential tail view at a posterior, and say whether the row it held was already tight there.

Algorithm

  1. Return tv and true where the primal side is empty. The view is then convex, its dual blocks are exact, and there is nothing to re-read.
  2. Clamp w below at zero and normalise it to sum to one. A conic solver returns a posterior whose smallest entries sit a rounding error below zero, and the searches behind the rows refuse a negative probability.
  3. For each asset of the primal side, read its bound at w with ep_tail_surrogate_row, and accumulate the coefficient-weighted sum of the bounds into a new row c, b. The value of the new row at w is the coefficient-weighted sum of the measures there.
  4. Read the gap between the row tv held and the new one at w. The old row bounds the same sum from the same side, so the gap is the slack the last solve left.
  5. Return the carrier with the new row, and whether the gap is within tol of the larger of the target and the largest loss the primal side names.

Arguments

  • tv: Sequential tail view constraint.
  • w: Posterior probabilities of the last solve.

Returns

  • tv::AbstractSequentialTailViewConstraint: The carrier with its row re-read at w.
  • tight::Bool: Whether the row tv held before the call was tight at w.

Related

source
PortfolioOptimisers.ep_refine_tail_viewMethod
ep_refine_tail_view(tv::AbstractEntropyPoolingTailView, w::VecNum)

Re-read a tail view carrier at a posterior, and say whether its rows were already tight there.

A carrier whose rows are fixed at construction is returned unchanged, and is always tight. A sequential carrier re-reads the multipliers of its primal side at w, which is what tightens its surrogate row between two solves of entropy_pooling; its method lives beside it in src/10_Prior/06_EntropyPooling/03_EntropyPoolingPrior.jl.

Arguments

  • tv: Tail view constraint.
  • w: Posterior probabilities of the last solve.

Returns

  • tv::AbstractEntropyPoolingTailView: The carrier to solve with next.
  • tight::Bool: Whether the rows tv held before the call were tight at w, within the tolerance the carrier holds.

Related

source
PortfolioOptimisers.ep_sigma_views!Function
ep_sigma_views!(sigma_views::LinearConstraintEstimator, epc::AbstractDict,
                pr::AbstractPriorResult, sets::UniverseSets; strict::Bool = false)

Add the variance views of a group to the entropy pooling constraint dictionary.

ep_sigma_views! parses variance view equations from a LinearConstraintEstimator, replaces any prior references with their actual values, and constructs the corresponding constraints for entropy pooling. The constraints are then added to the entropy pooling constraint dictionary epc. The statistic is the variance of the posterior distribution, not the value at risk, which the var_views family holds.

The variance is quadratic in the returns and linear in the posterior probabilities only once the mean is a constant. This method therefore returns the assets whose mean fix_mu! must hold at the prior, so a variance view does not move the mean it is measured about.

Mathematical definition

The row states the posterior second central moment about the prior mean, which is linear in the posterior probabilities:

\[\begin{align} \mathrm{Var}_{\boldsymbol{p}}[x_{i}] &= \sum_{t=1}^{T} p_{t} \left(x_{t,\,i} - \mu_{i}\right)^{2}\,, \\ \sum_{i=1}^{N} a_{k,\,i} \mathrm{Var}_{\boldsymbol{p}}[x_{i}] &\lessgtr B_{k}\,, \quad \forall\, k = 1,\ldots,K\,. \end{align}\]

The identity holds only while the posterior mean of asset $i$ equals $\mu_{i}$, which is why the assets this method names are handed to fix_mu!.

Where:

  • $\boldsymbol{p}$: $T \times 1$ posterior probabilities of the observations, summing to one. They are the unknown of the entropy pooling problem.
  • $x_{t,\,i}$: Return of asset $i$ at observation $t$.
  • $\mu_{i}$: Prior mean of asset $i$. It is a constant of the view, and a lower moment view or a fixing row holds the posterior mean at it.
  • $T$: Number of observations.
  • $N$: Number of assets.
  • $\boldsymbol{B}$: Constraint response vector.
  • $a_{k,\,i}$: Coefficient asset $i$ takes in view $k$.
  • $K$: Number of views the group states.

Algorithm

  1. Parse the view equations of sigma_views.val, giving one ParsingResult per view.
  2. Replace every group name by the assets it spans.
  3. Replace every prior(...) reference by the prior variance, through replace_prior_views.
  4. Turn the parsed views into the linear constraint blocks lcs, one for :ineq and one for :eq. Under strict = false every row of the group can drop, and lcs is then nothing: the group states no view, and the call returns a to_fix that names no asset.
  5. Build tmp, the squared deviations of every observation from the prior mean, transposed so a row of lcs multiplies it from the left.
  6. For each block present, add A * tmp against B under that key with add_ep_constraint!, and mark in to_fix every asset the block names.

Arguments

  • sigma_views: Variance view constraints.
  • epc: Dictionary of entropy pooling constraints, mapping keys to (lhs, rhs) pairs.
  • pr: Prior result containing asset return information.
  • sets: Asset set mapping asset names to indices.
  • strict: If true, throws error for missing assets; otherwise, issue warnings.

Returns

  • to_fix::BitVector: Boolean vector indicating which assets require their mean to be fixed.

Related

source
PortfolioOptimisers.fix_sigma!Function
fix_sigma!(epc::AbstractDict, fixed::AbstractVector, to_fix::BitVector,
           pr::AbstractPriorResult)

Hold the variance of the named assets at the prior value.

fix_sigma! identifies assets in to_fix that are not yet fixed (i.e., not present in fixed), and adds constraints to the entropy pooling constraint dictionary epc to fix their variance to the prior value. This ensures that higher moment views (e.g., skewness, kurtosis, correlation) do not inadvertently alter the variance of these assets. The function updates fixed in-place to reflect the newly fixed assets.

The rows go in under the :feq key, which the optimiser relaxes with a penalised slack rather than enforcing exactly. A fixing row is a wish, not a view: it competes with the views that were asked for, and it yields where the two cannot both hold.

Mathematical definition

The posterior second central moment of every named asset, taken about the prior mean, is held at the prior variance:

\[\sum_{t=1}^{T} p_{t} \left(x_{t,\,i} - \mu_{i}\right)^{2} = \sigma_{i}^{2}\,, \quad \forall\, i \in \mathcal{F}\,.\]

Where:

  • $\boldsymbol{p}$: $T \times 1$ posterior probabilities of the observations, summing to one. They are the unknown of the entropy pooling problem.
  • $x_{t,\,i}$: Return of asset $i$ at observation $t$.
  • $\mu_{i}$: Prior mean of asset $i$. It is a constant of the view, and a lower moment view or a fixing row holds the posterior mean at it.
  • $\sigma_{i}^{2}$: Prior variance of asset $i$. It is a constant of the view, and a lower moment view or a fixing row holds the posterior variance at it.
  • $T$: Number of observations.
  • $\mathcal{F}$: Assets named by to_fix that fixed does not already hold.

Algorithm

  1. Read the prior variances, the diagonal of pr.sigma, into sigma.
  2. Read the assets that to_fix names and fixed does not already hold into fix.
  3. Return when fix names no asset.
  4. Add one :feq row per named asset, the squared deviations of that asset from its prior mean against sigma[fix], with add_ep_constraint!.
  5. Mark the named assets in fixed, so a later call adds no second row for them.

Arguments

  • epc: Dictionary of entropy pooling constraints, mapping keys to (lhs, rhs) pairs.
  • fixed: Boolean vector indicating which assets have their variance fixed.
  • to_fix: Boolean vector indicating which assets should have their variance fixed.
  • pr: Prior result containing asset return information.

Returns

  • nothing: The function mutates epc and fixed in-place.

Related

source
PortfolioOptimisers.ep_cov_views!Function
ep_cov_views!(cov_views::LinearConstraintEstimator, epc::AbstractDict,
              pr::AbstractPriorResult, sets::UniverseSets; strict::Bool = false)

Add the covariance views of a group to the entropy pooling constraint dictionary.

ep_cov_views! parses covariance view equations from a LinearConstraintEstimator, replaces any prior references with their actual values, and constructs the corresponding linear constraints for entropy pooling. The constraints are then added to the entropy pooling constraint dictionary epc. The statistic is the covariance of the posterior distribution, not the correlation, which the rho_views family holds.

The covariance is a product of returns and is linear in the posterior probabilities only once both means are constants. This method therefore returns the assets whose mean and variance fix_mu! and fix_sigma! must hold at the prior, so a covariance view does not move the lower moments it is measured about.

Mathematical definition

The row states the posterior cross moment about the prior means, which is linear in the posterior probabilities:

\[\begin{align} \mathrm{Cov}_{\boldsymbol{p}}[x_{i}, x_{j}] &= \sum_{t=1}^{T} p_{t} x_{t,\,i} x_{t,\,j} - \mu_{i} \mu_{j}\,, \\ c\, \mathrm{Cov}_{\boldsymbol{p}}[x_{i}, x_{j}] &\lessgtr b\,, \end{align}\]

which the body writes with the constants gathered on the right:

\[\begin{align} d\, c \sum_{t=1}^{T} p_{t} x_{t,\,i} x_{t,\,j} &\lessgtr d \left(c\, \mu_{i} \mu_{j} + b\right)\,. \end{align}\]

Where:

  • $\boldsymbol{p}$: $T \times 1$ posterior probabilities of the observations, summing to one. They are the unknown of the entropy pooling problem.
  • $x_{t,\,i}$: Return of asset $i$ at observation $t$.
  • $\mu_{i}$: Prior mean of asset $i$. It is a constant of the view, and a lower moment view or a fixing row holds the posterior mean at it.
  • $T$: Number of observations.
  • $c$: Coefficient the view gives the pair.
  • $b$: Target of the view, one value per spanned asset pair.
  • $d$: Sign that comparison_sign_ineq_flag reads from the operator, so every inequality row reaches epc in the sense the :ineq key states.

The identity holds only while the posterior means of assets $i$ and $j$ equal $\mu_{i}$ and $\mu_{j}$, which is why the assets this method names are handed to fix_mu! and fix_sigma!.

Algorithm

  1. Parse the view equations of cov_views.val, giving one ParsingResult per view.
  2. Replace every group name by the assets it spans, keeping a pair view a pair view.
  3. Replace every prior(a, b) reference by the prior covariance, through replace_coprior_views. Each view is now a RhoParsingResult carrying its index pairs.
  4. For each view in turn, drop it when step 3 left it with no pair, and raise unless it names exactly one.
  5. Read the sign d and the inequality flag from the operator with comparison_sign_ineq_flag.
  6. Read the index pair (i, j), and build Ai, the product of the two return columns scaled by d and the view's coefficient.
  7. Build Bi, the target moved by the product of the prior means, scaled the same way. A single asset pair gives a scalar, which is wrapped into a one-element vector; a group pair gives one entry per spanned pair.
  8. Add the row against Bi under :ineq or :eq with add_ep_constraint!, and mark both assets of the pair in to_fix.

Arguments

  • cov_views: Covariance view constraints.
  • epc: Dictionary of entropy pooling constraints, mapping keys to (lhs, rhs) pairs.
  • pr: Prior result containing asset return information.
  • sets: Asset set mapping asset names to indices.
  • strict: If true, throws error for missing assets; otherwise, issue warnings.

Validation

  • Every view names exactly one asset pair. A view that mixes pairs raises an ArgumentError. A view left with no pair, because every pair it named holds an asset the universe does not, is dropped with a report under strict = false; strict = true has already raised by then.

Returns

  • to_fix::BitVector: Boolean vector indicating which assets require their mean and variance to be fixed.

Related

source
PortfolioOptimisers.ep_rho_views!Function
ep_rho_views!(rho_views::LinearConstraintEstimator, epc::AbstractDict,
              pr::AbstractPriorResult, sets::UniverseSets; strict::Bool = false)

Add the correlation views of a group to the entropy pooling constraint dictionary.

ep_rho_views! parses correlation view equations from a LinearConstraintEstimator, replaces any prior references with their actual values, and constructs the corresponding linear constraints for entropy pooling. The constraints are then added to the entropy pooling constraint dictionary epc. The statistic is the correlation of the posterior distribution, not the covariance, which the cov_views family holds.

The correlation is a covariance divided by two standard deviations, and it is linear in the posterior probabilities only once both means and both variances are constants. This method therefore returns the assets whose mean and variance fix_mu! and fix_sigma! must hold at the prior, so a correlation view does not move the lower moments it is measured about.

Mathematical definition

The row states the posterior cross moment about the prior means, with the target multiplied by the prior standard deviations, which is linear in the posterior probabilities:

\[\begin{align} \rho_{\boldsymbol{p}}[x_{i}, x_{j}] &= \dfrac{\sum_{t=1}^{T} p_{t} x_{t,\,i} x_{t,\,j} - \mu_{i} \mu_{j}}{\sigma_{i} \sigma_{j}}\,, \\ c\, \rho_{\boldsymbol{p}}[x_{i}, x_{j}] &\lessgtr b\,, \end{align}\]

which the body writes with the constants gathered on the right:

\[\begin{align} d\, c \sum_{t=1}^{T} p_{t} x_{t,\,i} x_{t,\,j} &\lessgtr d \left(c\, \mu_{i} \mu_{j} + b\, \sigma_{i} \sigma_{j}\right)\,. \end{align}\]

Where:

  • $\boldsymbol{p}$: $T \times 1$ posterior probabilities of the observations, summing to one. They are the unknown of the entropy pooling problem.
  • $x_{t,\,i}$: Return of asset $i$ at observation $t$.
  • $\mu_{i}$: Prior mean of asset $i$. It is a constant of the view, and a lower moment view or a fixing row holds the posterior mean at it.
  • $\sigma_{i}^{2}$: Prior variance of asset $i$. It is a constant of the view, and a lower moment view or a fixing row holds the posterior variance at it.
  • $T$: Number of observations.
  • $\sigma_{i}$: Prior standard deviation of asset $i$, the positive root of $\sigma_{i}^{2}$.
  • $c$: Coefficient the view gives the pair.
  • $b$: Target of the view, one value per spanned asset pair, in $[-1, 1]$.
  • $d$: Sign that comparison_sign_ineq_flag reads from the operator, so every inequality row reaches epc in the sense the :ineq key states.

The identity holds only while the posterior means and variances of assets $i$ and $j$ equal the prior ones, which is why the assets this method names are handed to fix_mu! and fix_sigma!.

Algorithm

  1. Parse the view equations of rho_views.val, giving one ParsingResult per view.
  2. Replace every group name by the assets it spans, keeping a pair view a pair view.
  3. Replace every prior(a, b) reference by the prior correlation, through replace_coprior_views. Each view is now a RhoParsingResult carrying its index pairs.
  4. Read the prior variances, the diagonal of pr.sigma, into sigma.
  5. For each view in turn, drop it when step 3 left it with no pair, raise unless it names exactly one, and raise unless every target lies in $[-1, 1]$.
  6. Read the sign d and the inequality flag from the operator with comparison_sign_ineq_flag.
  7. Read the index pair (i, j), and build sigma_ij, the root of the product of the two prior variances.
  8. Build Ai, the product of the two return columns scaled by d and the view's coefficient.
  9. Build Bi, the target multiplied by sigma_ij and moved by the product of the prior means, scaled the same way. A single asset pair gives a scalar, which is wrapped into a one-element vector; a group pair gives one entry per spanned pair.
  10. Add the row against Bi under :ineq or :eq with add_ep_constraint!, and mark both assets of the pair in to_fix.

Arguments

  • rho_views: Correlation view constraints.
  • epc: Dictionary of entropy pooling constraints, mapping keys to (lhs, rhs) pairs.
  • pr: Prior result containing asset return information.
  • sets: Asset set mapping asset names to indices.
  • strict: If true, throws error for missing assets; otherwise, issue warnings.

Validation

  • Every view names exactly one asset pair. A view that mixes pairs raises an ArgumentError. A view left with no pair, because every pair it named holds an asset the universe does not, is dropped with a report under strict = false; strict = true has already raised by then.
  • Every target lies in $[-1, 1]$. A target outside that range raises an ArgumentError.

Returns

  • to_fix::BitVector: Boolean vector indicating which assets require their mean and variance to be fixed.

Related

source
PortfolioOptimisers.ep_sk_views!Function
ep_sk_views!(skew_views::LinearConstraintEstimator, epc::AbstractDict,
             pr::AbstractPriorResult, sets::UniverseSets; strict::Bool = false)

Add the skewness views of a group to the entropy pooling constraint dictionary.

ep_sk_views! parses skewness view equations from a LinearConstraintEstimator, replaces any prior references with their actual values, and constructs the corresponding linear constraints for entropy pooling. The constraints are then added to the entropy pooling constraint dictionary epc. The statistic is the standardised third central moment of the posterior distribution.

The skewness is a third central moment divided by a cube of the standard deviation, and it is linear in the posterior probabilities only once the mean and the variance are constants. This method therefore returns the assets whose mean and variance fix_mu! and fix_sigma! must hold at the prior, so a skewness view does not move the lower moments it is measured about.

Mathematical definition

The third central moment expands into raw moments, and the two lowest of them are the prior constants, so what remains is linear in the posterior probabilities:

\[\begin{align} \mathrm{E}_{\boldsymbol{p}}\!\left[(x_{i} - \mu_{i})^{3}\right] &= \sum_{t=1}^{T} p_{t} x_{t,\,i}^{3} - 3 \mu_{i} \sigma_{i}^{2} - \mu_{i}^{3}\,, \\ \mathrm{Skew}_{\boldsymbol{p}}[x_{i}] &= \dfrac{\mathrm{E}_{\boldsymbol{p}}\!\left[(x_{i} - \mu_{i})^{3}\right]}{\left(\sigma_{i}^{2}\right)^{3/2}} = \sum_{t=1}^{T} p_{t} \dfrac{x_{t,\,i}^{3} - \mu_{i}^{3} - 3 \mu_{i} \sigma_{i}^{2}}{\left(\sigma_{i}^{2}\right)^{3/2}}\,, \\ \sum_{i=1}^{N} a_{k,\,i} \mathrm{Skew}_{\boldsymbol{p}}[x_{i}] &\lessgtr B_{k}\,, \quad \forall\, k = 1,\ldots,K\,. \end{align}\]

The second line uses $\sum_{t} p_{t} = 1$ to carry the two constants inside the sum, which is the form the body builds.

Where:

  • $\boldsymbol{p}$: $T \times 1$ posterior probabilities of the observations, summing to one. They are the unknown of the entropy pooling problem.
  • $x_{t,\,i}$: Return of asset $i$ at observation $t$.
  • $\mu_{i}$: Prior mean of asset $i$. It is a constant of the view, and a lower moment view or a fixing row holds the posterior mean at it.
  • $\sigma_{i}^{2}$: Prior variance of asset $i$. It is a constant of the view, and a lower moment view or a fixing row holds the posterior variance at it.
  • $T$: Number of observations.
  • $N$: Number of assets.
  • $\boldsymbol{B}$: Constraint response vector.
  • $a_{k,\,i}$: Coefficient asset $i$ takes in view $k$.
  • $K$: Number of views the group states.

The identity holds only while the posterior mean and variance of asset $i$ equal $\mu_{i}$ and $\sigma_{i}^{2}$, which is why the assets this method names are handed to fix_mu! and fix_sigma!.

Algorithm

  1. Parse the view equations of skew_views.val, giving one ParsingResult per view.
  2. Replace every group name by the assets it spans.
  3. Replace every prior(...) reference by the prior skewness, through replace_prior_views.
  4. Turn the parsed views into the linear constraint blocks lcs, one for :ineq and one for :eq. Under strict = false every row of the group can drop, and lcs is then nothing: the group states no view, and the call returns a to_fix that names no asset.
  5. Read the prior variances, the diagonal of pr.sigma, into sigma.
  6. Build tmp, the standardised third moment contribution of every observation, transposed so a row of lcs multiplies it from the left.
  7. For each block present, add A * tmp against B under that key with add_ep_constraint!, and mark in to_fix every asset the block names.

Arguments

  • skew_views: Skewness view constraints.
  • epc: Dictionary of entropy pooling constraints, mapping keys to (lhs, rhs) pairs.
  • pr: Prior result containing asset return information.
  • sets: Asset set mapping asset names to indices.
  • strict: If true, throws error for missing assets; otherwise, issue warnings.

Returns

  • to_fix::BitVector: Boolean vector indicating which assets require their mean and variance to be fixed.

Related

source
PortfolioOptimisers.ep_kt_views!Function
ep_kt_views!(kurtosis_views::LinearConstraintEstimator, epc::AbstractDict,
             pr::AbstractPriorResult, sets::UniverseSets; strict::Bool = false)

Add the kurtosis views of a group to the entropy pooling constraint dictionary.

ep_kt_views! parses kurtosis view equations from a LinearConstraintEstimator, replaces any prior references with their actual values, and constructs the corresponding linear constraints for entropy pooling. The constraints are then added to the entropy pooling constraint dictionary epc. The statistic is the standardised fourth central moment of the posterior distribution.

The kurtosis is a fourth central moment divided by a square of the variance, and it is linear in the posterior probabilities only once the mean and the variance are constants. This method therefore returns the assets whose mean and variance fix_mu! and fix_sigma! must hold at the prior, so a kurtosis view does not move the lower moments it is measured about.

Mathematical definition

The fourth central moment expands into raw moments, and every constant of the expansion is a prior one, so what remains is linear in the posterior probabilities:

\[\begin{align} \mathrm{E}_{\boldsymbol{p}}\!\left[(x_{i} - \mu_{i})^{4}\right] &= \sum_{t=1}^{T} p_{t} \left(x_{t,\,i}^{4} - 4 \mu_{i} x_{t,\,i}^{3} + 6 \mu_{i}^{2} x_{t,\,i}^{2} - 3 \mu_{i}^{4}\right)\,, \\ \mathrm{Kurt}_{\boldsymbol{p}}[x_{i}] &= \dfrac{\mathrm{E}_{\boldsymbol{p}}\!\left[(x_{i} - \mu_{i})^{4}\right]}{\left(\sigma_{i}^{2}\right)^{2}}\,, \\ \sum_{i=1}^{N} a_{k,\,i} \mathrm{Kurt}_{\boldsymbol{p}}[x_{i}] &\lessgtr B_{k}\,, \quad \forall\, k = 1,\ldots,K\,. \end{align}\]

The first line uses $\sum_{t} p_{t} = 1$ to carry the constant $3 \mu_{i}^{4}$ inside the sum, which is the form the body builds.

Where:

  • $\boldsymbol{p}$: $T \times 1$ posterior probabilities of the observations, summing to one. They are the unknown of the entropy pooling problem.
  • $x_{t,\,i}$: Return of asset $i$ at observation $t$.
  • $\mu_{i}$: Prior mean of asset $i$. It is a constant of the view, and a lower moment view or a fixing row holds the posterior mean at it.
  • $\sigma_{i}^{2}$: Prior variance of asset $i$. It is a constant of the view, and a lower moment view or a fixing row holds the posterior variance at it.
  • $T$: Number of observations.
  • $N$: Number of assets.
  • $\boldsymbol{B}$: Constraint response vector.
  • $a_{k,\,i}$: Coefficient asset $i$ takes in view $k$.
  • $K$: Number of views the group states.

The identity holds only while the posterior mean and variance of asset $i$ equal $\mu_{i}$ and $\sigma_{i}^{2}$, which is why the assets this method names are handed to fix_mu! and fix_sigma!.

Algorithm

  1. Parse the view equations of kurtosis_views.val, giving one ParsingResult per view.
  2. Replace every group name by the assets it spans.
  3. Replace every prior(...) reference by the prior kurtosis, through replace_prior_views.
  4. Turn the parsed views into the linear constraint blocks lcs, one for :ineq and one for :eq. Under strict = false every row of the group can drop, and lcs is then nothing: the group states no view, and the call returns a to_fix that names no asset.
  5. Build X_sq and mu_sq, the squares of the returns and of the prior means.
  6. Build tmp, the standardised fourth moment contribution of every observation, transposed so a row of lcs multiplies it from the left.
  7. For each block present, add A * tmp against B under that key with add_ep_constraint!, and mark in to_fix every asset the block names.

Arguments

  • kurtosis_views: Kurtosis view constraints.
  • epc: Dictionary of entropy pooling constraints, mapping keys to (lhs, rhs) pairs.
  • pr: Prior result containing asset return information.
  • sets: Asset set mapping asset names to indices.
  • strict: If true, throws error for missing assets; otherwise, issue warnings.

Returns

  • to_fix::BitVector: Boolean vector indicating which assets require their mean and variance to be fixed.

Related

source
PortfolioOptimisers.ep_jump_views!Function
ep_jump_views!(model::JuMP.Model, x, obj_expr, epc::AbstractDict, tvs::VecEPTV,
               sc1::Number, sc2::Number, so::Number)

Add every view constraint of an entropy pooling problem to a JuMP model.

ep_jump_views! is the shared body of the two JuMPEntropyPooling formulations: they differ only in how they represent the divergence, and agree on every view. It adds the linear rows of epc, relaxes the fixed equalities with a norm one cone bounded slack, and hands each tail view to add_ep_tail_view!.

Algorithm

  1. Add the row ceq when epc holds an :eq block.
  2. Add the row cineq when epc holds an :ineq block.
  3. Add the row ccvareq when epc holds a :cvar_eq block.
  4. When epc holds a :feq block, create the slack c and its norm tc, add the row cfeq that lets the block miss by c, bound the norm of c by tc, and add the penalty so * sc2 * tc to obj_expr.
  5. Hand each tail view of tvs to add_ep_tail_view! in turn.

JuMP formulation

Variables

  • x: $\boldsymbol{p}$, the posterior probabilities, read from the model.
  • tc: $\eta_{c}$, the norm of the fixed equality slack, created when epc holds a :feq block.
  • c: $\boldsymbol{c}$, the fixed equality slack, one entry per row of the :feq block, created with tc.

Constraints

  • ceq: $s_{c1} \left(\mathbf{A}_{\mathrm{eq}} \boldsymbol{p} - \boldsymbol{B}_{\mathrm{eq}}\right) = 0$
  • cineq: $s_{c1} \left(\mathbf{A}_{\mathrm{ineq}} \boldsymbol{p} - \boldsymbol{B}_{\mathrm{ineq}}\right) \leq 0$
  • ccvareq: $s_{c1} \left(\mathbf{A}_{\mathrm{cvar}} \boldsymbol{p} - \boldsymbol{B}_{\mathrm{cvar}}\right) = 0$
  • cfeq: $s_{c1} \left(\mathbf{A}_{\mathrm{feq}} \boldsymbol{p} - \boldsymbol{B}_{\mathrm{feq}} - \boldsymbol{c}\right) = 0$
  • Registered under no name, alongside cfeq: $\left(s_{c1} \eta_{c},\; s_{c1} \boldsymbol{c}\right) \in \mathcal{K}_{1}$, which states $\eta_{c} \geq \lVert \boldsymbol{c} \rVert_{1}$.

Each of the four named rows is registered only when epc holds the block it carries. add_ep_tail_view! registers every row a tail view needs, in src/10_Prior/06_EntropyPooling/03_EntropyPoolingPrior.jl.

Relaxation

The encoding is not exact: the entries below bound the quantity instead of reproducing it, and the bound is tight only under the condition stated here.

  • The bound is on the objective, and it lies above the objective of the problem whose :feq rows are enforced exactly. The rows cfeq let the :feq block miss its right-hand side by $\boldsymbol{c}$, and the penalty $s_{o} s_{c2} \eta_{c}$ is what holds $\boldsymbol{c}$ near zero.
  • The bound is tight when $\boldsymbol{c}$ is zero, which holds when the :feq rows and the views can be met together, and $s_{c2}$ is large enough to pull the slack down. A :feq row states a moment the views must leave where the prior put it, so the two can genuinely disagree, and then the row yields.

Where:

  • $\boldsymbol{p}$: $T \times 1$ posterior probabilities of the observations, summing to one. They are the unknown of the entropy pooling problem.
  • $\mathbf{A}$: Constraint coefficient matrix.
  • $\boldsymbol{B}$: Constraint response vector.
  • $\text{eq}$: Subscript for equality constraints.
  • $\text{ineq}$: Subscript for inequality constraints.
  • $s_{c1}$: Constraint scale of the entropy pooling optimiser. It multiplies both sides of a row, so a positive value leaves the feasible set unchanged.
  • $s_{c2}$: Slack penalty of the fixed equality rows. It weights the norm of the slack in the objective, so a larger value holds those rows tighter.
  • $s_{o}$: Objective scale of the entropy pooling optimiser. It multiplies the objective, so a positive value leaves the argument of the optimum unchanged.
  • $\mathrm{cvar}$, $\mathrm{feq}$: Subscripts for the :cvar_eq and :feq blocks of epc.
  • $\boldsymbol{c}$: Slack of the fixed equality rows, one entry per row of the :feq block.
  • $\eta_{c}$: Bound on the $L_{1}$ norm of $\boldsymbol{c}$.
  • $\mathcal{K}_{1}$: Norm one cone, JuMP.MOI.NormOneCone(N + 1).

Arguments

  • model: Entropy pooling JuMP model.
  • x: Vector of posterior probability variables.
  • obj_expr: Objective expression, mutated when a fixed equality is relaxed.
  • epc: Dictionary of entropy pooling constraints, mapping keys to (lhs, rhs) pairs.
  • tvs: Tail view constraints.
  • sc1: Constraint scaling factor.
  • sc2: Fixed equality slack penalty.
  • so: Objective scaling factor.

Returns

  • nothing: The function mutates model and obj_expr in-place.

Related

source

References

[1]
D. Cajas. Entropy Pooling with CVaR and EVaR Views. Available at SSRN 7120258 (2026).
[2]
D. Cajas. Entropy Pooling with Relativistic Value at Risk Views. Available at SSRN 7329718 (2026).
[78]
A. Meucci. Fully flexible views: theory and practice. Risk 21, 97–102 (2008).
[79]
A. Vorobets. Sequential entropy pooling heuristics. Available at SSRN 3936392 (2021).