Opinion Pooling
PortfolioOptimisers.LinearOpinionPooling — Type
struct LinearOpinionPooling <: OpinionPoolingAlgorithmPools the opinions as a weighted arithmetic mean of their scenario weights.
Each scenario's consensus weight is the opinion-weighted average of what the experts assign to it, so the pooled distribution keeps every scenario any one expert believes in.
Mathematical definition
\[\begin{align} p_t^{*} &= \sum_{k=1}^{K} \alpha_k\, p_{tk}\,. \end{align}\]
Details
- Suitable where the opinions are independent and additive.
- A scenario reaches zero in the consensus only when every opinion assigns it zero probability, because a sum of non-negative terms vanishes only when all of them do. This is the property that separates it from
LogarithmicOpinionPooling, where one zero is enough. - The pooled distribution is a mixture, so it is at least as dispersed as the most dispersed opinion.
Related
References
- [83] F. Dietrich and C. List. Probabilistic opinion pooling generalized. Part one: general agendas. Social Choice and Welfare 48, 747–786 (2017).
PortfolioOptimisers.LogarithmicOpinionPooling — Type
struct LogarithmicOpinionPooling <: OpinionPoolingAlgorithmPools the opinions as a weighted geometric mean of their scenario weights, renormalised.
The result is the distribution that minimises the opinion-weighted Kullback-Leibler divergence to the individual opinions, which makes it the information-theoretic consensus.
Mathematical definition
\[\begin{align} p_t^{*} &= \frac{\exp\!\left(\sum_{k=1}^{K} \alpha_k \log p_{tk}\right)}{\sum_{s=1}^{T} \exp\!\left(\sum_{k=1}^{K} \alpha_k \log p_{sk}\right)}\,. \end{align}\]
Details
- Robust to extremes, because it down-weights a scenario that any one opinion doubts.
- A scenario reaches zero in the consensus as soon as one opinion assigns it zero probability, since a single $\log 0$ sends the exponent to $-\infty$.
LinearOpinionPoolingneeds all of them to agree. - The normalisation runs through
LogExpFunctions.logsumexp, so the exponent is shifted before it is exponentiated and a very negative sum does not underflow to a vector of zeros.
Related
References
PortfolioOptimisers.OpinionPoolingPrior — Type
struct OpinionPoolingPrior{__T_pes, __T_pe1, __T_pe2, __T_p, __T_w, __T_alg, __T_ex} <: AbstractLowOrderPriorEstimator_AFOpinion pooling prior estimator for asset returns.
OpinionPoolingPrior is a low order prior estimator that computes the mean and covariance of asset returns by combining multiple prior estimations into a consensus prior using opinion pooling algorithms. It supports both linear and logarithmic pooling, flexible weighting of opinions, and optional pre- and post-processing estimators.
Fields
pes: Vector of prior estimators.
pe1: Pre-processing prior estimator.
pe2: Post-processing prior estimator.
p: Opinion pooling blending parameter.
w: Opinion pooling weights.
alg: Opinion pooling algorithm.
ex: Parallel execution strategy.
Constructors
OpinionPoolingPrior(; pes::VecEP, pe1::Option{<:AbstractLowOrderPriorEstimator_A_F_AF} = nothing, pe2::AbstractLowOrderPriorEstimator_A_F_AF = EmpiricalPrior(), p::Option{<:Number} = nothing, w::Option{<:VecNum} = nothing, alg::OpinionPoolingAlgorithm = LinearOpinionPooling(), ex::FLoops.Transducers.Executor = FLoops.Transducers.ThreadedEx()) -> OpinionPoolingPriorKeywords correspond to the struct's fields. All arguments are validated for type and value consistency.
Validation
pesmust be a non-empty vector of prior estimators.- If
wis notnothing,!isempty(w),length(w) == length(pes),all(x -> 0 <= x <= 1, w), andsum(w) <= 1. - If
pis notnothing,p > 0.
Propagated parameters
When factory is called on this type, the following @fprop-tagged fields are automatically propagated:
pes: Recursively updated viafactory.pe1: Recursively updated viafactory.pe2: Recursively updated viafactory.
View parameters
When port_opt_view is called on this type, the following @vprop-tagged fields are automatically subset to the selected indices:
pes: Recursively viewed viaport_opt_view.pe1: Recursively viewed viaport_opt_view.pe2: Recursively viewed viaport_opt_view.
Details
- If
wisnothing, all opinions are equally weighted at1/length(pes). - If
wis notnothingandsum(w) < 1, the remaining weight is assigned to a uniform prior over the observations. That remainder is an opinion in its own right: it takes a column ofpwand it is penalised alongside the others. - If
pisnothing, the opinion probabilities are used as given. Otherwise they are adjusted by their Kullback-Leibler divergence from the consensus, throughrobust_probabilities. pis bounded below by zero strictly.p = nothingis how one asks for no penalty; there is nop = 0.- The opinions contribute observation weights alone. Every moment of the result comes from refitting
pe2under the pooled weights, which is whypesis typed to the entropy-pooling estimators — they are the ones whose result carries aw.
Examples
julia> sets = UniverseSets(; xkey = "nx", dict = Dict("nx" => ["A", "B", "C"]));julia> OpinionPoolingPrior(; pes = [EntropyPoolingPrior(; sets = sets, mu_views = LinearConstraintEstimator(; val = ["A == 0.03", "B + C == 0.04"])), EntropyPoolingPrior(; sets = sets, mu_views = LinearConstraintEstimator(; val = ["A == 0.05", "B + C >= 0.06"]))])OpinionPoolingPrior pes ┼ 2-element Vector{EntropyPoolingPrior} │ EntropyPoolingPrior ⋯ │ EntropyPoolingPrior ⋯ pe1 ┼ nothing pe2 ┼ EmpiricalPrior │ ce ┼ PortfolioOptimisersCovariance │ │ ce ┼ Covariance │ │ │ me ┼ SimpleExpectedReturns │ │ │ │ w ┴ nothing │ │ │ ce ┼ GeneralCovariance │ │ │ │ ce ┼ StatsBase.SimpleCovariance: StatsBase.SimpleCovariance(true) │ │ │ │ w ┴ nothing │ │ │ alg ┴ FullMoment() │ │ 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 p ┼ nothing w ┼ nothing alg ┼ LinearOpinionPooling() ex ┴ Transducers.ThreadedEx{@NamedTuple{}}: Transducers.ThreadedEx()Related
OpinionPoolingAlgorithmLinearOpinionPoolingLogarithmicOpinionPoolingpriorrobust_probabilitiescompute_poolingfactoryport_opt_view
References
- [83] F. Dietrich and C. List. Probabilistic opinion pooling generalized. Part one: general agendas. Social Choice and Welfare 48, 747–786 (2017).
- [85] C. Martini and J. Sprenger. Opinion Aggregation and Individual Expertise. In: Scientific Collaboration and Collective Knowledge (Oxford University Press, 2017).
PortfolioOptimisers.prior — Function
prior(pe::OpinionPoolingPrior, X::MatNum, F::Option{<:MatNum} = nothing;
dims::Int = 1, strict::Bool = false, kwargs...)Compute opinion pooling prior moments for asset returns.
prior estimates the mean and covariance of asset returns by combining multiple prior estimations into a consensus prior using opinion pooling algorithms. Supports both linear and logarithmic pooling, robust opinion probability adjustment, and optional pre- and post-processing estimators.
Arguments
pe: Opinion pooling prior estimator.X: Asset returns matrix (observations × assets).F: Optional factor matrix.dims: Dimension along which to perform the computation.strict: Iftrue, throws error for missing assets; otherwise, issues warnings. Default isfalse.kwargs...: Additional keyword arguments passed to underlying estimators and solvers.
Validation
dims in (1, 2).
Returns
pr::LowOrderPrior: Result object containing asset returns, posterior mean vector, posterior covariance matrix, consensus weights, entropy, Kullback-Leibler divergence, opinion probabilities, and optional factor moments.
Details
- Optional pre-processing estimator
pe.pe1is applied to asset returns before pooling, else the original returns are used. - Each prior estimator in
pe.pesis applied to the asset returns, producing individual prior weights. - Opinion probabilities
oware initialised frompe.wor set uniformly if it isnothing; if their sum is less than 1, the remainder is assigned to a uniform prior, which takes the last column ofpwand is pooled and penalised like any other opinion. - Robust opinion probabilities are computed using
robust_probabilitiesif a penalty parameterpe.pis notnothing. Neitherpe.wnor any other field ofpeis modified: callingpriortwice on one estimator gives the same answer twice. - Consensus posterior weights are computed using
compute_poolingaccording to the specified pooling algorithmpe.alg. - Post-processing estimator
pe.pe2is applied using the consensus weights, viafactory. Every moment of the result ispe.pe2's; the opinions contribute observation weights alone. - The result includes the effective number of scenarios, Kullback-Leibler divergence to each opinion, robust opinion probabilities, and optional factor moments.
pr.ensisexpof the entropy of the consensus weights, so it runs from 1 (all mass on one observation) toT(uniform).pr.kld[i]is $\mathrm{KL}(\boldsymbol{w} \,\|\, \boldsymbol{p}_i)$, from the consensus to opinioni.robust_probabilitiesreads the divergence in the other direction, so the two vectors are different numbers.
Related
PortfolioOptimisers.OpinionPoolingAlgorithm — Type
abstract type OpinionPoolingAlgorithm <: AbstractAlgorithmAbstract supertype for opinion pooling algorithms.
OpinionPoolingAlgorithm is the base type for all algorithms that combine multiple prior estimations into a consensus prior using opinion pooling. All concrete opinion pooling algorithms should subtype this type to ensure a consistent interface for consensus formation in portfolio optimisation workflows.
Interfaces
In order to implement a new concrete type that works seamlessly with the library, subtype OpinionPoolingAlgorithm and implement the following method:
Required method name
compute_pooling(alg::OpinionPoolingAlgorithm, ow::VecNum, pw::MatNum) -> StatsBase.ProbabilityWeights: Aggregate the columns ofpwinto one consensus scenario-weight vector.
Arguments
alg: The concrete subtype instance.ow: $K \times 1$ vector of opinion probabilities, summing to 1.pw: $T \times K$ matrix whose columnkholds expertk's scenario weights.
Returns
w::StatsBase.ProbabilityWeights: $T \times 1$ consensus scenario weights, summing to 1.
Examples
julia> struct MedianOpinionPooling <: PortfolioOptimisers.OpinionPoolingAlgorithm endjulia> function PortfolioOptimisers.compute_pooling(::MedianOpinionPooling, ow, pw) w = vec(mapslices(PortfolioOptimisers.Statistics.median, pw; dims = 2)) return PortfolioOptimisers.StatsBase.pweights(w / sum(w)) endjulia> PortfolioOptimisers.compute_pooling(MedianOpinionPooling(), [0.5, 0.5], [0.5 0.25; 0.25 0.25; 0.25 0.5])3-element ProbabilityWeights{Float64, Float64, Vector{Float64}}: 0.375 0.25 0.375Related
References
- [83] F. Dietrich and C. List. Probabilistic opinion pooling generalized. Part one: general agendas. Social Choice and Welfare 48, 747–786 (2017).
- [85] C. Martini and J. Sprenger. Opinion Aggregation and Individual Expertise. In: Scientific Collaboration and Collective Knowledge (Oxford University Press, 2017).
PortfolioOptimisers.robust_probabilities — Function
robust_probabilities(ow::VecNum, args...)
robust_probabilities(ow::VecNum, pw::MatNum, p::Number)Compute robust opinion probabilities for consensus formation in opinion pooling.
robust_probabilities adjusts the vector of opinion probabilities (ow) used in opinion pooling algorithms to account for robustness against outlier or extreme opinions. If a penalty parameter p is not nothing, the method penalises opinions that diverge from the consensus by down-weighting them according to their Kullback-Leibler divergence from the pooled distribution. If no penalty parameter is set, the original opinion probabilities are returned unchanged.
Mathematical definition
\[\begin{align} D_k &= \sum_{t=1}^{T} p_{tk} \log\!\frac{p_{tk}}{c_t}\,, \quad c_t = \sum_{k=1}^{K} \alpha_k p_{tk}\,, \\ \tilde{\alpha}_k &= \frac{\alpha_k \exp(-\rho D_k)}{\sum_{j=1}^{K} \alpha_j \exp(-\rho D_j)}\,. \end{align}\]
Where:
- $\alpha_k$: Opinion probability of expert $k$, the input
ow. - $p_{tk}$: Scenario weight for scenario $t$ from expert $k$, column $k$ of
pw. - $c_t$: Consensus scenario weight.
- $D_k$: Kullback-Leibler divergence from expert $k$ to the consensus.
- $\rho$: Penalty parameter, the argument
p. - $T$: Number of observations.
The consensus $\boldsymbol{c}$ is always the linear pool, whatever OpinionPoolingAlgorithm the caller selected. The divergence is also directed: it reads $D_k = \mathrm{KL}(\boldsymbol{p}_k \,\|\, \boldsymbol{c})$, from each opinion to the consensus. The kld field of the result runs the other way, from the consensus to each opinion, so the two are different numbers and neither is the other's mirror.
Arguments
ow: Vector of opinion probabilities (length = number of opinions).pw: Matrix of prior weights for each opinion (observations × opinions).p: Robustness penalty parameter.
Returns
ow::VecNum: A new vector of opinion probabilities for pooling, summing to 1. The argument is never modified, because it may be the estimator's ownwfield.
Details
- If
pisnothing, i.e. the method withargs..., returns the original opinion probabilities. - If
pis notnothing, computes the consensus distribution, computes the Kullback-Leibler divergence for each opinion, and applies an exponential penalty to each probability. The adjusted probabilities are normalised to sum to 1. - A larger
pconcentrates the mass on the opinions nearest the consensus. Aspgrows without bound the pool tends to the single closest opinion. - Used internally by
OpinionPoolingPriorto ensure robust aggregation of opinions.
Related
PortfolioOptimisers.compute_pooling — Function
compute_pooling(::LinearOpinionPooling, ow::VecNum, pw::MatNum)
compute_pooling(::LogarithmicOpinionPooling, ow::VecNum, pw::MatNum)Compute the consensus posterior return distribution from individual prior distributions using opinion pooling.
compute_pooling aggregates multiple prior probability distributions (pw) into a single consensus posterior distribution according to the specified opinion pooling algorithm and opinion probabilities (ow). Supports both linear and logarithmic pooling.
Mathematical definition
Let $\boldsymbol{\alpha}$ be the opinion probabilities and $\mathbf{P}$ the $T \times K$ matrix of scenario weights for $K$ experts:
Linear (weighted arithmetic mean):
\[\begin{align} \boldsymbol{p}^* &= \mathbf{P} \boldsymbol{\alpha}\,. \end{align}\]
Logarithmic (weighted geometric mean, normalised):
\[\begin{align} p_t^* &= \frac{\exp\!\left(\sum_{k=1}^{K} \alpha_k \log p_{tk}\right)}{\sum_{s=1}^{T} \exp\!\left(\sum_{k=1}^{K} \alpha_k \log p_{sk}\right)}\,. \end{align}\]
Where:
- $\boldsymbol{p}^*$: $T \times 1$ pooled posterior weight vector.
- $\mathbf{P}$: $T \times K$ matrix of scenario weights for $K$ experts.
- $\boldsymbol{\alpha}$: $K \times 1$ opinion probability vector (weights summing to 1).
- $p_{tk}$: Scenario weight for scenario $t$ from expert $k$.
- $T$: Number of observations.
Arguments
alg: Opinion pooling algorithm (LinearOpinionPoolingorLogarithmicOpinionPooling).ow: Vector of opinion probabilities (length = number of opinions).pw: Matrix of prior weights for each opinion (observations × opinions).
Returns
w::StatsBase.ProbabilityWeights: Consensus posterior probability weights.
Details
- For
LinearOpinionPooling, computes the weighted arithmetic mean of the individual prior weights:w = pw * ow. - For
LogarithmicOpinionPooling, computes the weighted geometric mean of the individual prior weights:w = exp.(log.(pw) * ow - LogExpFunctions.logsumexp(log.(pw) * ow)). - Used internally by
OpinionPoolingPriorto form the consensus prior distribution.
Related
References
- [83]
- F. Dietrich and C. List. Probabilistic opinion pooling generalized. Part one: general agendas. Social Choice and Welfare 48, 747–786 (2017).
- [84]
- I. J. Good. Rational decisions. Journal of the Royal Statistical Society: Series B (Methodological) 14, 107–114 (1952).
- [85]
- C. Martini and J. Sprenger. Opinion Aggregation and Individual Expertise. In: Scientific Collaboration and Collective Knowledge (Oxford University Press, 2017).