Meucci Entropy Pooling: private API
PortfolioOptimisers.VecMeucciEP — Type
const VecMeucciEP = AbstractVector{<:MeucciEntropyPoolingPrior}Groups the vector forms of MeucciEntropyPoolingPrior that a method can dispatch on.
The group exists because a caller that pools opinions holds many estimators rather than one, and a method that takes such a collection needs a single type to sign. The library's own opinion pooling signs the wider VecEP instead, which admits EntropyPoolingPrior beside this estimator, so VecMeucciEP is the narrower name for a method that takes the recursive CVaR route alone.
Related
MeucciEntropyPoolingPrior: the element type the vector holds.VecEP: the wider groupOpinionPoolingPriorsigns, which admits both entropy pooling estimators.
PortfolioOptimisers.ep_cvar_views_setup — Function
ep_cvar_views_setup(cvar_views::Nothing, args...; kwargs...)No-op pass-through for conditional value at risk view constraints when none are specified.
Arguments
cvar_views::Nothing: Indicates that no CVaR view constraints are specified.args...: Additional positional arguments (ignored).kwargs...: Additional keyword arguments (ignored).
Returns
nothing: There is no search to prepare, whichep_cvar_views_solve!dispatches on.
Related
ep_cvar_views_setup(cvar_views::CVV_VecCVV, pr::AbstractPriorResult,
sets::UniverseSets, w::StatsBase.ProbabilityWeights,
ds_opt::Option{<:ConditionalValueatRiskEntropyPooling},
dm_opt::Option{<:OptimEntropyPooling}; strict::Bool = false)Resolve the conditional value at risk views once, into the data one search reads.
ep_cvar_views_setup parses and validates the CVaR view groups, resolves every prior(...) reference against pr under w, and flattens the groups into one loss matrix, one target vector and one level vector. It also chooses the search the targets need. ep_cvar_views_solve! reads that answer and adds nothing to it.
The staged route of ep_prior searches up to three times, once per stage, and the reference each stage reads must be the same number. This verb is therefore called once, before any solve, against the first fit of the wrapped estimator and the prior probabilities that fit was read at. A later stage carries a refit pr whose weights are the previous stage's posterior, so a reference resolved there would state a different target at each stage, and the view the caller wrote would move under it. Resolving once also lifts the whole parse out of the search, which repeated it at every stage. Issue #628 owns that rule.
Algorithm
- Wrap
cvar_viewsin a vector when it is a single view, so one loop serves both shapes. - For each view group, read its significance level into
alpha, and parse its equations accepting==alone. - Replace every group name by the assets it spans, and every
prior(...)reference by the prior conditional value at risk atalphaunderw, throughreplace_prior_views. - Turn the parsed views into the equality block
lcs, and check the two preconditions of the section below that read it. Understrict = falseevery row of the group can drop, andlcsis thennothing: the group states no view, and the loop skips it. - For each row of the block, read the asset it names into
cols, its target intoB, its level intoalphasand its text intoeqns. The groups flatten into one search: each level enters only as the divisor of its own view's positive part. - Read the worst realisation of every named asset into
min_X, and raise when any target reaches it. - Choose the search
d_opt. One view takesds_opt, or a defaultConditionalValueatRiskEntropyPooling. More than one takesdm_opt, or a defaultOptimEntropyPoolingoverOptim.Fminbox. - Return
nothingwhen every group stated no view, which sends the stage down the plain solve. Otherwise return the loss columnsX, the targetsB, the levelsalphasand the searchd_opt.
Arguments
cvar_views: CVaR view constraints.pr: Prior result theprior(...)references are read from. It is the first fit of the wrapped estimator, not a refit of a later stage.sets: Asset set mapping asset names to indices.w: Observation weightsprwas read at, which theprior(...)references resolve under.ds_opt: CVaR-specific optimiser (for single view).dm_opt: General optimiser (for multiple views).strict: Iftrue, throws error for missing assets; otherwise, issue warnings.
Validation
- No view group declares a view formulation. A group whose
algis notnothingraises anArgumentError: this route writes no constraint formulation, so it has nothing to apply the declaration to. - 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. - Every target stays below the worst realisation of the asset it names. A target that reaches it raises a
DomainErrornaming every offending view beside the largest target its asset admits. The per-view route ofep_assert_reachable_viewraises the same type, so onecatchreads both.
Returns
nothing: Every group stated no view, becausestrictisfalseand every row of every group was dropped.cvv::NamedTuple: The search data, carrying the loss columnsX, the targetsB, the levelsalphasand the searchd_opt.
Related
ep_cvar_views_solve!: runs the search this verb prepares.replace_prior_views: resolves theprior(...)reference a target may carry.get_pr_value: reads the prior conditional value at risk that reference resolves to.ConditionalValueatRiskEntropyPoolingOptimEntropyPoolingMeucciEntropyPoolingPrior
References
- [80] A. Meucci, D. Ardia and S. Keel. Fully flexible extreme views. The Journal of Risk 14, 39–49 (2011).
PortfolioOptimisers.ep_cvar_views_solve! — Function
ep_cvar_views_solve!(cvv::Nothing, epc::AbstractDict,
w::StatsBase.ProbabilityWeights,
opt::AbstractEntropyPoolingOptimiser)Solve the entropy pooling problem when no CVaR views are specified.
ep_cvar_views_solve! is an internal API compatibility method that solves the entropy pooling problem when ep_cvar_views_setup prepared no search, which happens when no conditional value at risk view constraint is present. It delegates to the main entropy pooling solver using the provided prior weights, constraint dictionary, and optimiser.
Algorithm
- Call
entropy_poolingwith the prior weightsw, the constraint dictionaryepcand the optimiseropt, giving the posterior probability weightspw. - Return
pw. With no conditional value at risk view there is no value at risk to search over, so the whole staged solve collapses to the one call of step 1.
Arguments
cvv: Indicates thatep_cvar_views_setupprepared no search.epc: Dictionary of entropy pooling constraints, mapping keys to(lhs, rhs)pairs.w: Prior probability weights.opt: Entropy pooling optimiser.
Returns
pw::StatsBase.ProbabilityWeights: Posterior probability weights satisfying the constraints.
Related
ep_cvar_views_solve!(cvv::NamedTuple, epc::AbstractDict,
w::StatsBase.ProbabilityWeights,
opt::AbstractEntropyPoolingOptimiser)Solve the entropy pooling problem with Conditional Value-at-Risk (CVaR) view constraints.
ep_cvar_views_solve! reads the targets, the levels and the search that ep_cvar_views_setup resolved, and solves for the posterior probability weights by either root-finding (for a single CVaR view) or optimisation (for more than one). It parses nothing and resolves nothing: the staged route calls it once per stage, and every stage must read the same targets.
The search runs over the value at risk levels etas, one per view, each bounded by [0, B]. This is a continuous relaxation of the recursive algorithm, which searches over discrete tail sizes instead; it reaches the same target and it takes more than one view.
The single-view bracket stops a hair inside B, at B * (1 - sqrt(eps)). At B itself the constraint demands a posterior tail contribution of exactly zero, and no interior posterior carries one, so the problem there is degenerate and its dual is unbounded. Roots evaluates both ends of a bracket before it searches, so every single-view solve would run that problem once. The root sits near half of B, so the shrunk end holds it.
Mathematical definition
The conditional value at risk of asset $i$ is the value of the Rockafellar-Uryasev programme, whose minimiser is the value at risk. A view that pins it to $\bar{c}$ is therefore a pair of conditions on the posterior, one linear in $\boldsymbol{p}$ at a fixed $\eta$ and one that fixes $\eta$:
\[\begin{align} \mathrm{CVaR}_{\alpha}(X) &= \underset{\eta}{\min}\; \left\{ \eta + \frac{1}{\alpha} \sum_{t=1}^{T} p_{t} \left(-x_{t,\,i} - \eta\right)^{+} \right\}\,, \\ \frac{1}{\alpha} \sum_{t=1}^{T} p_{t} \left(-x_{t,\,i} - \eta\right)^{+} &= \bar{c} - \eta\,, \\ \sum_{t=1}^{T} p_{t} \mathbb{1}\left\{-x_{t,\,i} > \eta\right\} &= \alpha\,. \end{align}\]
The second line is linear in $\boldsymbol{p}$, so one entropy pooling solve answers it at any candidate $\eta$. The third line holds exactly when that $\eta$ is the value at risk of the posterior it produced, so the two lines share one fixed point and the view is met only there.
Where:
- $\mathrm{CVaR}_{\alpha}(X)$: Conditional value at risk of the loss series $\boldsymbol{x}$ at level $\alpha$.
- $\bar{c}$: Target conditional value at risk of the view.
- $\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)$.
- $T$: Number of observations.
- $\eta$: Value at risk of asset $i$ at level $\alpha$, the minimiser of the first line and the variable the outer search carries.
- $(\cdot)^{+}$: Positive part, $\max(\cdot,\, 0)$.
Algorithm
- Read the loss columns
X, the targetsB, the levelsalphasand the searchd_optoffcvv. - Define
func(etas). It writes the second line above intoepcunder the key:cvar_eqat the candidateetas, solves the whole constraint set withentropy_poolingintowi, and returnswibeside the residual of the third line. One view residual is the posterior tail mass minusalpha. More than one is thenorm_errorof the posterior conditional value at risk minus the target, over the views. - Search for the value at risk
res. One view root-finds the residual offuncover[0, B[1] * (1 - sqrt(eps))]withRoots.find_zero. More than one minimises it over the box[0, B]from the start0.5 * BwithOptim.optimize. - Call
func(res)once more, and return the posterior probability weights it produces.
Arguments
cvv: Search data, answered byep_cvar_views_setup.epc: Dictionary of entropy pooling constraints, mapping keys to(lhs, rhs)pairs.w: Prior probability weights the stage projects.opt: Main entropy pooling optimiser.
Validation
- Every candidate value at risk stays in
[0, B]. A candidate outside the box raises aDomainError. - The search must succeed. A
Roots.find_zerothat raises is rethrown as anErrorException, and anOptim.optimizethatOptim.convergedreports as failed raises anErrorException. - An infeasible view set is not caught. The residual this search minimises is the posterior tail mass minus
alpha, and a posterior that sits on one observation carries a small residual while it misses the view by any margin. The summary paragraph ofMeucciEntropyPoolingPriorstates how to recognise that answer. - A missed feasible view is not caught either. The search reads its own residual, and never how closely the inner
entropy_poolingsolve met the constraint that carries the view. With more than one viewOptim.convergedaccepts a solve that stopped on the step inxrather than on stationarity. The warning ofMeucciEntropyPoolingPriorstates the mechanism, the measured sizes and how to read the answer. See issue #573.
Returns
pw::StatsBase.ProbabilityWeights: Posterior probability weights satisfying CVaR view constraints.
Related
ep_cvar_views_setup: resolves the targets this search reads.ConditionalValueatRiskEntropyPoolingOptimEntropyPooling: its stopping rule sets how closely each inner solve meets the constraint this search re-solves, and its own tip states the size of that.MeucciEntropyPoolingPriorentropy_poolingnorm_error: scores the residual of a search over more than one view.ConditionalValueatRisk: reads the posterior statistic that residual scores.
References
- [80] A. Meucci, D. Ardia and S. Keel. Fully flexible extreme views. The Journal of Risk 14, 39–49 (2011).
PortfolioOptimisers.ep_prior — Function
ep_prior(alg::StagedEP, pe::MeucciEntropyPoolingPrior, X::MatNum,
F::Option{<:MatNum}, pnl::Option{<:AssetPanel} = nothing;
strict::Bool = false, kwargs...)Compute entropy pooling prior moments for asset returns with iterative constraint enforcement.
ep_prior estimates the mean and covariance of asset returns using the entropy pooling framework, supporting iterative constraint enforcement via the H1_EntropyPooling and H2_EntropyPooling algorithms. It integrates moment and view constraints (mean, variance, CVaR, skewness, kurtosis, correlation), flexible confidence specification, and composable optimisation algorithms. The method iteratively applies constraints, updating prior weights and moments at each step, and ensures that higher moment views do not inadvertently alter lower moments.
Each stage refits the wrapped estimator before it parses its own views, so a prior(...) reference in a later stage resolves against the previous stage's posterior rather than against the estimator's own prior. A sigma_views target written as prior(A)*1.3 under a mu_views or cvar_views view therefore asks for 1.3 times the variance stage one left, which is not 1.3 times the empirical variance. Write the target as a number where the reference must be the empirical one.
Mathematical definition
The staged posterior is a chain of Kullback-Leibler projections rather than one. Stage $k$ carries the constraint set $\mathcal{C}_{k}$ of every stage up to and including itself, and it projects a reference $\boldsymbol{r}^{(k)}$ that the algorithm tag fixes:
\[\begin{align} \boldsymbol{p}^{(k)} &= \underset{\boldsymbol{p} \in \mathcal{C}_{k}}{\arg\min} \sum_{t=1}^{T} p_{t} \ln\!\frac{p_{t}}{r_{t}^{(k)}}\,, \\ \mathcal{C}_{k} &= \left\{ \boldsymbol{p} : \mathbf{A}_{k} \boldsymbol{p} = \boldsymbol{B}_{k},\; \boldsymbol{p} \geq \boldsymbol{0},\; \boldsymbol{1}^\intercal \boldsymbol{p} = 1 \right\}\,, \\ \mathcal{C}_{1} &\supseteq \mathcal{C}_{2} \supseteq \mathcal{C}_{3}\,, \\ \boldsymbol{r}^{(k)} &= \begin{cases} \boldsymbol{q} & \text{under } \texttt{H1\_EntropyPooling} \\ \boldsymbol{p}^{(k-1)} & \text{under } \texttt{H2\_EntropyPooling} \end{cases}\,, \\ \boldsymbol{p}^{*} &= \boldsymbol{p}^{(K)}\,. \end{align}\]
The three stages hold the mean and value at risk views, the variance and covariance views, and the correlation, skewness and kurtosis views. The sets nest, so the last stage's posterior meets every view, and the two references differ only in what the chain is measured from: $\texttt{H1\_EntropyPooling}$ gives the projection of the prior onto the whole view set, and $\texttt{H2\_EntropyPooling}$ gives the projection of each stage onto the next. A conditional value at risk view is not a row of $\mathbf{A}_{k}$: it enters every stage as the fixed point of ep_cvar_views_solve!, whose own section states it.
Where:
- $\boldsymbol{p}$: $T \times 1$ posterior probabilities of the observations, summing to one. They are the unknown of the entropy pooling problem.
- $\boldsymbol{q}$: $T \times 1$ prior probabilities of the observations, summing to one.
- $T$: Number of observations.
- $\boldsymbol{p}^{(k)}$: Posterior probabilities of stage $k$, and $\boldsymbol{p}^{*}$ those of the last stage $K$.
- $\boldsymbol{r}^{(k)}$: Reference probabilities stage $k$ projects.
- $\mathcal{C}_{k}$: Constraint set of stage $k$, carrying the rows of every stage up to it.
- $\mathbf{A}_{k}$, $\boldsymbol{B}_{k}$: Rows and right-hand side that state $\mathcal{C}_{k}$.
Posterior moments are then read as probability-weighted sample statistics under $\boldsymbol{p}^{*}$.
Algorithm
- Fit the wrapped prior estimator, giving
pr. The fit states the observation axis:Tissize(pr.X, 1), which a nested prior that drops rows makes smaller thansize(X, 1). - Read the prior probabilities
w0on that axis withep_prior_probabilities. They arepe.wwhere the caller set one,pr.wwhere the fit answered one, and the uniform1/Totherwise. A caller'spe.wreaches the wrapped estimator throughfactory, andpris refitted under it. - Build the empty constraint dictionary
epcand the fixing ledgerfixed. Resolve thecvar_viewsonce against that fit, throughep_cvar_views_setupintocvv. Every stage searches the targets it holds, so aprior(...)reference states one number for the whole chain. - Stage one, the mean and the value at risk. Write the
mu_viewsandvar_viewsrows intoepc. Whenepcholds a row orcvvstates a search, solve throughep_cvar_views_solve!intow1, and refitprunder it. - Stage two, the variance and the covariance. Write the
sigma_viewsandcov_viewsrows intoepc, and pin the mean of every asset those rows read withfix_mu!, so the stage cannot move a moment an earlier stage set. Under the same emptiness test, solve intow1, and refitprunder it. - Stage three, the correlation, the skewness and the kurtosis. Write the
sk_views,kt_viewsandrho_viewsrows intoepc, and pin the mean and the variance of every asset those rows read withfix_mu!andfix_sigma!. Under the same emptiness test, solve intow1, and refitprunder it. - Read the reference each of steps 5 and 6 solves from:
w0underH1_EntropyPooling, and the previous stage'sw1underH2_EntropyPooling. - Read the effective number of scenarios
ensas the exponential of the entropy ofw1, and the divergencekldas the Kullback-Leibler divergence ofw1fromw0. - Return a
LowOrderPriorcarrying the last refit's moments,w1,ensandkld. The feature matrixZand the factor blockfprare forwarded from that refit unchanged.
Arguments
alg: Staged entropy pooling algorithm, taken frompe.algbyprior.pe: Entropy pooling prior estimator.X: Asset returns matrix (observations × assets), oriented byprior.F: Optional factor matrix, oriented byprior.pnl: OptionalAssetPanel, 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.strict: Iftrue, throws error for missing assets; otherwise, issues warnings.kwargs...: Additional keyword arguments passed to underlying estimators and solvers.
Validation
- If any view constraint is not
nothing,!isnothing(sets). - If prior weights
pe.ware provided,length(pe.w) == size(pr.X, 1), the observations the wrapped estimator answered. A length that does not match raises aDimensionMismatchnaming that count, the count the estimator was handed, and the rule. - Every view equation carries a comparison operator its own family accepts. An unsupported operator raises a
Meta.ParseErrornaming the operators that family accepts.
Returns
pr::LowOrderPrior: Result object containing asset returns, posterior mean vector, posterior covariance matrix, weights, effective number of scenarios, Kullback-Leibler divergence, and optional factor moments.
Related
PortfolioOptimisers.ep_prior — Function
ep_prior(alg::H0_EntropyPooling, pe::MeucciEntropyPoolingPrior, X::MatNum,
F::Option{<:MatNum}, pnl::Option{<:AssetPanel} = nothing;
strict::Bool = false, kwargs...)Compute entropy pooling prior moments for asset returns with single-shot constraint enforcement.
ep_prior estimates the mean and covariance of asset returns using the entropy pooling framework, enforcing all moment and view constraints in a single optimisation step via the H0_EntropyPooling algorithm. This approach is fast but may distort lower moments when higher moment views are present, as all constraints are applied simultaneously.
Mathematical definition
The single-shot posterior is one Kullback-Leibler projection of the prior onto the intersection of every view's constraint set. It is the staged chain of ep_prior collapsed to one stage:
\[\begin{align} \boldsymbol{p}^{*} &= \underset{\boldsymbol{p} \in \mathcal{C}}{\arg\min} \sum_{t=1}^{T} p_{t} \ln\!\frac{p_{t}}{q_{t}}\,, \\ \mathcal{C} &= \mathcal{C}_{1} \cap \mathcal{C}_{2} \cap \mathcal{C}_{3}\,. \end{align}\]
One reference and one feasible set carry every view, so no moment can be pinned between stages: a higher moment view is free to move a lower moment of the same asset. That is the whole difference from the staged chain, whose $\mathcal{C}_{k}$ this $\mathcal{C}$ intersects. A conditional value at risk view is not a row of $\mathcal{C}$: it enters as the fixed point of ep_cvar_views_solve!, whose own section states it.
Where:
- $\boldsymbol{p}$: $T \times 1$ posterior probabilities of the observations, summing to one. They are the unknown of the entropy pooling problem.
- $\boldsymbol{q}$: $T \times 1$ prior probabilities of the observations, summing to one.
- $T$: Number of observations.
- $\boldsymbol{p}^{*}$: Posterior probabilities of the one solve.
- $\mathcal{C}$, $\mathcal{C}_{k}$: Feasible set of the one solve, and the stage sets it intersects.
Posterior moments are then read as probability-weighted sample statistics under $\boldsymbol{p}^{*}$.
Algorithm
- Fit the wrapped prior estimator, giving
pr. The fit states the observation axis:Tissize(pr.X, 1), which a nested prior that drops rows makes smaller thansize(X, 1). - Read the prior probabilities
w0on that axis withep_prior_probabilities. They arepe.wwhere the caller set one,pr.wwhere the fit answered one, and the uniform1/Totherwise. A caller'spe.wreaches the wrapped estimator throughfactory, andpris refitted under it. - Build the empty constraint dictionary
epc. Resolve thecvar_viewsonce against that fit, throughep_cvar_views_setupintocvv. - Write the
mu_viewsandvar_viewsrows intoepc. - Write the
sigma_viewsandcov_viewsrows intoepc. No moment is pinned, sofix_mu!is never called on this route. - Write the
sk_views,kt_viewsandrho_viewsrows intoepc. - When
epcholds a row orcvvstates a search, solve the whole accumulated set once throughep_cvar_views_solve!intow1, and refitprunder it. Every row of every family can drop understrict = false, and the view set then states nothing:w1isw0,kldis zero, and no refit runs. - Read the effective number of scenarios
ensas the exponential of the entropy ofw1, and the divergencekldas the Kullback-Leibler divergence ofw1fromw0. - Return a
LowOrderPriorcarrying the refit's moments,w1,ensandkld. The feature matrixZand the factor blockfprare forwarded from that refit unchanged.
Arguments
alg: Single-shot entropy pooling algorithm, taken frompe.algbyprior.pe: Entropy pooling prior estimator.X: Asset returns matrix (observations × assets), oriented byprior.F: Optional factor matrix, oriented byprior.pnl: OptionalAssetPanel, 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.strict: Iftrue, throws error for missing assets; otherwise, issues warnings.kwargs...: Additional keyword arguments passed to underlying estimators and solvers.
Validation
- If any view constraint is not
nothing,!isnothing(pe.sets). - If prior weights
pe.ware provided,length(pe.w) == size(pr.X, 1), the observations the wrapped estimator answered. A length that does not match raises aDimensionMismatchnaming that count, the count the estimator was handed, and the rule. - Every view equation carries a comparison operator its own family accepts. An unsupported operator raises a
Meta.ParseErrornaming the operators that family accepts.
Returns
pr::LowOrderPrior: Result object containing asset returns, posterior mean vector, posterior covariance matrix, weights, effective number of scenarios, Kullback-Leibler divergence, and optional factor moments.
Related
PortfolioOptimisers.show_fields — Method
show_fields(
_::MeucciEntropyPoolingPrior
) -> NTuple{15, Symbol}
Renders every field of a MeucciEntropyPoolingPrior except cache.
The state a cache holds is the running detail of an incremental fit, not the configuration a reader looks the type up for, and it prints under the estimator at every site that renders one. Set set_show_nothing_fields!(:MeucciEntropyPoolingPrior, true) to render it.
Arguments
::MeucciEntropyPoolingPrior: Prior estimator, read for its type alone.
Returns
fields::Tuple: The field names to render, which is(:pe, :mu_views, :var_views, :cvar_views, :sigma_views, :sk_views, :kt_views, :cov_views, :rho_views, :sets, :ds_opt, :dm_opt, :opt, :w, :alg).
Related
References
- [80]
- A. Meucci, D. Ardia and S. Keel. Fully flexible extreme views. The Journal of Risk 14, 39–49 (2011).