Entropy Pooling

PortfolioOptimisers.EntropyPoolingPriorType
struct EntropyPoolingPrior{__T_pe, __T_mu_views, __T_var_views, __T_cvar_views, __T_evar_views, __T_rlvar_views, __T_sigma_views, __T_sk_views, __T_kt_views, __T_cov_views, __T_rho_views, __T_sets, __T_opt, __T_w, __T_alg, __T_cache} <: AbstractLowOrderPriorEstimator_AF

Reweights the observations of a prior so that its moments and its tails meet a set of views.

EntropyPoolingPrior is a low order prior estimator that computes the mean and covariance of asset returns using entropy pooling. It supports views on the mean, the variance, the covariance, the correlation, the skewness and the kurtosis, views on the value at risk, the conditional and entropic value at risk views of [1], and the relativistic value at risk views of [2].

The tail views are the difference with MeucciEntropyPoolingPrior. There, a CVaR view is a target the recursive algorithm of Meucci et al. hunts by re-solving the whole entropy pooling problem for each candidate value at risk level, which supports equalities alone. Here each tail view is written as constraints of the single entropy pooling problem, so one solve answers every view, and the operators ==, >= and <= are all available, along with views on the entropic and the relativistic value at risk, and views over several assets of every measure: a positive combination is convex and exact, and a relative view with coefficients of both signs takes the integer formulation or a sequential convex one.

Warning

An infeasible view set is not raised on by the OptimEntropyPooling route. The dual of an infeasible set is unbounded, so the minimiser runs away, the posterior collapses onto one observation, and Optim reports the solve as converged. A grossly infeasible view overflows instead, and the non-finite weights reach the moment estimators as an ArgumentError naming Infs or NaNs. Read the result rather than the flag: ens falls to a handful out of the number of observations, one weight sits near one, kld is large, and the posterior statistic the view named is far from its target. entropy_pooling states the mechanism. The JuMPEntropyPooling route does not share it: the solver reports an infeasible model itself.

Algorithm

The constructor derives the prior probabilities, and validates everything else.

  1. When w is nothing, derive nothing. prior builds the uniform weights 1/T at solve time, one per observation.
  2. When w is not nothing, normalise it to sum to one, giving the prior probabilities the pooling starts from. A mutable w.values is normalised in place with LinearAlgebra.normalize!, and an immutable one is replaced by a new StatsBase.pweights over the normalised values.

Fields

  • pe: Prior estimator.
  • mu_views: Expected returns views estimator or result.
  • var_views: Value-at-risk views estimator or result.
  • cvar_views: Conditional value-at-risk views estimator or result.
  • evar_views: Entropic value-at-risk views estimator or result.
  • rlvar_views: Relativistic value-at-risk views estimator or result.
  • sigma_views: Variance views estimator or result.
  • sk_views: Skewness views estimator or result.
  • kt_views: Kurtosis views estimator or result.
  • cov_views: Covariance views estimator or result.
  • rho_views: Correlation views estimator or result.
  • sets: Sets used to map estimator values to assets.
  • opt: Entropy pooling optimisation estimator.
  • w: Prior observation probability weights, on the observations the wrapped estimator answers, which a wrapped estimator that drops rows makes fewer than the observations it is given. If nothing, the wrapped result's own w is used, and the uniform weights where it carries none.
  • alg: Entropy pooling algorithm.
  • cache: Optional partial-fit state. It is nothing until partial_fit! writes one, and the estimator's read-out verb reads it when the caller gives no data matrix. Each propagation channel does one thing with it: factory carries it unchanged, because a factory call resolves configuration rather than the sample; port_opt_view slices it to the selected assets by index copy, so the viewed estimator answers over those assets alone; and obs_weights_view drops it, because no slice of a state exists on the observation axis. A family whose state has no exact asset slice drops it on both axes and names the reason.

Constructors

EntropyPoolingPrior(;    pe::AbstractLowOrderPriorEstimator_A_F_AF = EmpiricalPrior(),    mu_views::Option{<:LinearConstraintEstimator} = nothing,    var_views::Option{<:VV_VecVV} = nothing,    cvar_views::Option{<:CVV_VecCVV} = nothing,    evar_views::Option{<:EVV_VecEVV} = nothing,    rlvar_views::Option{<:RVV_VecRVV} = nothing,    sigma_views::Option{<:LinearConstraintEstimator} = nothing,    sk_views::Option{<:LinearConstraintEstimator} = nothing,    kt_views::Option{<:LinearConstraintEstimator} = nothing,    cov_views::Option{<:LinearConstraintEstimator} = nothing,    rho_views::Option{<:LinearConstraintEstimator} = nothing,    sets::Option{<:UniverseSets} = nothing,    opt::NonCVaREP = OptimEntropyPooling(),    w::Option{<:StatsBase.ProbabilityWeights} = nothing,    alg::AbstractEntropyPoolingAlgorithm = H1_EntropyPooling(),    cache::Option{<:AbstractPartialFitState} = nothing) -> EntropyPoolingPrior

Keywords correspond to the struct's fields.

Validation

  • If any view constraint is not nothing, sets must not be nothing.
  • If cvar_views is not nothing, opt must be a JuMPEntropyPooling.
  • If a view field is a vector, it must not be empty.
  • If w is not nothing, it must be non-empty and match the number of observations.

Propagated parameters

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

View parameters

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

Observation weight parameters

When obs_weights_view is called on this type, the following fields are automatically indexed to the selected observations:

View comparison operators

The comparison operators accepted in each view's constraint strings depend on the moment being constrained. An unsupported operator raises a ParseError listing the operators allowed for that view.

  • mu_views, sigma_views, sk_views, kt_views, cov_views, rho_views accept ==, >= and <=.
  • var_views (Value at Risk) accepts only == and >=.
  • cvar_views, evar_views and rlvar_views accept ==, >= and <=.

Tail views

A tail view needs auxiliary variables, so it is expressed in the JuMP model rather than reduced to rows that multiply the posterior probabilities. Two consequences follow.

The alg field of a view group picks the formulation. A single formulation applies to every view in that group, a vector supplies one per view, and nothing lets each view take the cheapest formulation that expresses it exactly: LinearConditionalValueatRiskView, ConicEntropicValueatRiskView and ConicRelativisticValueatRiskView where they apply, IntegerConditionalValueatRiskView, GridEntropicValueatRiskView and GridRelativisticValueatRiskView otherwise.

Tail views at several significance levels

A significance level is part of the statistic, not a detail of the solve: the conditional value at risk at 1% and at 10% are different numbers on the same series. So the level lives on the view rather than on the estimator. var_views, cvar_views, evar_views and rlvar_views each take one ValueatRiskView, ConditionalValueatRiskView, EntropicValueatRiskView or RelativisticValueatRiskView, or a vector of them, and each group carries the alpha its equations are read under. A RelativisticValueatRiskView carries a kappa as well, on the same reasoning: the deformation parameter is part of the statistic. A prior(...) reference inside a group is replaced by the prior value at that group's level.

A tail view group also carries alg, the formulation. For EntropicValueatRiskView that is where the grid of dual variables and the big-M constant live, so a GridEntropicValueatRiskView there gives one group its own pct, K and M. ValueatRiskView has no such field: a value at risk view is linear in the posterior probabilities, so there is no formulation to choose.

Examples

julia> EntropyPoolingPrior(;                           sets = UniverseSets(; xkey = "nx",                                               dict = Dict("nx" => ["A", "B", "C"])),                           mu_views = LinearConstraintEstimator(;                                                                val = ["A == 0.03",                                                                       "B + C == 0.04"]))EntropyPoolingPrior           pe ┼ EmpiricalPrior              │           ce ┼ PortfolioOptimisersCovariance              │              │   ce ┼ Covariance              │              │      │    me ┼ SimpleExpectedReturns              │              │      │       │   w ┴ nothing              │              │      │    ce ┼ GeneralCovariance              │              │      │       │   ce ┼ StatsBase.SimpleCovariance: StatsBase.SimpleCovariance(true)              │              │      │       │    w ┴ nothing              │              │      │   alg ┼ FullMoment()              │              │      │     w ┴ nothing              │              │   mp ┼ MatrixProcessing              │              │      │     pdm ┼ Posdef              │              │      │         │      alg ┼ UnionAll: NearestCorrelationMatrix.Newton              │              │      │         │   kwargs ┴ @NamedTuple{}: NamedTuple()              │              │      │      dn ┼ nothing              │              │      │      dt ┼ nothing              │              │      │     alg ┼ nothing              │              │      │   order ┴ NTuple{4, Symbol}: (:pdm, :dn, :dt, :alg)              │           me ┼ SimpleExpectedReturns              │              │   w ┴ nothing              │      horizon ┼ nothing              │   fill_limit ┴ nothing     mu_views ┼ LinearConstraintEstimator              │   val ┼ Vector{String}: ["A == 0.03", "B + C == 0.04"]              │   key ┴ nothing    var_views ┼ nothing   cvar_views ┼ nothing   evar_views ┼ nothing  rlvar_views ┼ nothing  sigma_views ┼ nothing     sk_views ┼ nothing     kt_views ┼ nothing    cov_views ┼ nothing    rho_views ┼ nothing         sets ┼ UniverseSets              │     xkey ┼ String: "nx"              │    uxkey ┼ String: "ux"              │    tfkey ┼ String: "nf"              │   utfkey ┼ String: "uf"              │    cfkey ┼ String: "ncf"              │   ucfkey ┼ String: "ucf"              │    nikey ┼ String: "ni"              │     dict ┴ Dict{String, Vector{String}}: Dict("nx" => ["A", "B", "C"])          opt ┼ OptimEntropyPooling              │     args ┼ Tuple{}: ()              │   kwargs ┼ @NamedTuple{}: NamedTuple()              │      sc1 ┼ Int64: 1              │      sc2 ┼ Float64: 1000.0              │      alg ┼ ExpEntropyPooling()              │      err ┴ nothing            w ┼ nothing          alg ┴ H1_EntropyPooling()

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).
  • [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).
source
PortfolioOptimisers.priorFunction
prior(pe::EntropyPoolingPrior, X::MatNum, F::Option{<:MatNum} = nothing,
      pnl::Option{<:AssetPanel} = nothing; dims::Int = 1, strict::Bool = false,
      kwargs...)

Compute the entropy pooling prior of asset returns with tail views.

prior orients the data and forwards the estimator's algorithm as a value to ep_prior, which enforces the views in stages or in one optimisation.

Algorithm

  1. Orient X and F along dims with dims_oriented, so the observations lie in the rows.
  2. Forward pe.alg as a value to ep_prior, and return the LowOrderPrior it builds.

Arguments

  • pe: Entropy pooling prior estimator.
  • X: Asset returns matrix.
  • F: Optional factor returns matrix.
  • pnl: Optional AssetPanel, the panel the carrier held. A wrapping prior forwards it unchanged, so that it can compose an estimator that is fitted on a panel. An estimator that reads no panel ignores it.
  • dims: Dimension along which the observations lie.
  • strict: If true, throws error for missing assets; otherwise, issue warnings.
  • kwargs...: Additional keyword arguments forwarded to the wrapped prior estimator.

Validation

  • dims in (1, 2).

Returns

  • pr::LowOrderPrior: Prior result carrying the posterior probability weights.

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).