Opinion Pooling
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.375The incremental fit
This prior has no exact incremental recursion, so it takes the online step by refitting from a sample buffer: Online seeds cache, partial_fit! appends each observation to it verbatim, and the one-argument prior runs this estimator's own batch verb over the rows the buffer kept. The answer is therefore exactly a batch fit over those rows, and a max_history on the wrapper windows the whole fit.
cache travels the three propagation channels as every partial-fit state does: factory carries it unchanged, port_opt_view slices it to the selected assets, and obs_weights_view drops it, because no slice of a state exists on the observation axis. It is not rendered, because a running buffer is not the configuration a reader looks the type up for.
Related
References
- [81] F. Dietrich and C. List. Probabilistic opinion pooling generalized. Part one: general agendas. Social Choice and Welfare 48, 747–786 (2017).
- [82] C. Martini and J. Sprenger. Opinion Aggregation and Individual Expertise. In: Scientific Collaboration and Collective Knowledge (Oxford University Press, 2017).
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. It suits opinions that are independent and additive.
Mathematical definition
\[\begin{align} p_t^{*} &= \sum_{k=1}^{K} \alpha_k\, p_{tk}\,. \end{align}\]
Where:
- $p_t^{*}$: Pooled weight of scenario $t$.
- $\alpha_k$: Opinion probability of expert $k$.
- $p_{tk}$: Scenario weight for scenario $t$ from expert $k$.
- $K$: Number of opinions.
A sum of non-negative terms vanishes only when every term does, so $p_t^{*} = 0$ requires that every opinion assigns scenario $t$ zero probability. The pooled distribution is a mixture of the opinions, so it is at least as dispersed as the most dispersed one.
Related
OpinionPoolingAlgorithmLogarithmicOpinionPooling: the sibling rule, under which one zero opinion is enough to zero a scenario.OpinionPoolingPriorcompute_pooling
References
- [81] 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. It is robust to extremes, because it down-weights a scenario that any one opinion doubts.
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}\]
Where:
- $p_t^{*}$: Pooled weight of scenario $t$.
- $\alpha_k$: Opinion probability of expert $k$.
- $p_{tk}$: Scenario weight for scenario $t$ from expert $k$.
- $K$: Number of opinions.
- $T$: Number of observations.
A single $\log 0$ sends the exponent to $-\infty$, so $p_t^{*} = 0$ as soon as one opinion assigns scenario $t$ zero probability. The product $\alpha_k \log p_{tk}$ is undefined when both factors vanish, which is the one case the form above does not cover.
Related
OpinionPoolingAlgorithmLinearOpinionPooling: the sibling rule, under which every opinion must agree before a scenario reaches zero.OpinionPoolingPriorcompute_pooling: its# Validationsection states what the code does at the undefined product.
References
PortfolioOptimisers.OpinionPoolingPrior — Type
struct OpinionPoolingPrior{__T_pes, __T_pe1, __T_pe2, __T_p, __T_w, __T_alg, __T_ex, __T_cache} <: 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.
The opinions contribute observation weights alone. Every moment of the result comes from refitting pe2 under the pooled weights, which is why pes is typed to the entropy-pooling estimators — they are the ones whose result carries a w. A w of nothing weights every opinion equally at 1/length(pes), and a p of nothing uses the opinion probabilities as given rather than adjusting them through robust_probabilities.
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.
cache: Optional partial-fit state. It isnothinguntilpartial_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:factorycarries it unchanged, because a factory call resolves configuration rather than the sample;port_opt_viewslices it to the selected assets by index copy, so the viewed estimator answers over those assets alone; andobs_weights_viewdrops 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
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(), cache::Option{<:AbstractPartialFitState} = nothing) -> 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. - The last is an inequality on purpose. When
sum(w) < 1,priorgives the remaining weight to a uniform prior over the observations, which becomes an opinion in its own right: it takes a column ofpw, and it is pooled and penalised alongside the others. - If
pis notnothing,p > 0. The bound is strict, sop = 0raises;p = nothingis how one asks for no penalty.
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.
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() │ │ │ 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 p ┼ nothing w ┼ nothing alg ┼ LinearOpinionPooling() ex ┴ Transducers.ThreadedEx{@NamedTuple{}}: Transducers.ThreadedEx()Related
OpinionPoolingAlgorithmLinearOpinionPoolingLogarithmicOpinionPoolingpriorrobust_probabilitiescompute_poolingfactoryport_opt_view
References
- [81] F. Dietrich and C. List. Probabilistic opinion pooling generalized. Part one: general agendas. Social Choice and Welfare 48, 747–786 (2017).
- [82] C. Martini and J. Sprenger. Opinion Aggregation and Individual Expertise. In: Scientific Collaboration and Collective Knowledge (Oxford University Press, 2017).
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.
Algorithm
Under LinearOpinionPooling:
- Multiply
pwbyow, giving the consensus weightsw.
Under LogarithmicOpinionPooling:
- Multiply the elementwise logarithm of
pwbyow, giving the exponent vectoru. - Read
LogExpFunctions.logsumexp(u)intolse. This shiftsuby its own maximum before exponentiating it, so a very negative exponent does not underflow to a vector of zeros. - Exponentiate
u .- lse, giving the consensus weightsw.
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).
Validation
- The result carries no
Infand noNaN, checked byStatsBase.pweights. UnderLogarithmicOpinionPoolingan opinion probability of exactly0against a scenario weight of exactly0makes the product0 * log(0), which isNaN, and the call raisesArgumentError. A zero scenario weight alone is safe: it gives-Inf, which exponentiates to a consensus weight of0.
Returns
w::StatsBase.ProbabilityWeights: Consensus posterior probability weights.
Related
OpinionPoolingPrior: the estimator that calls this to form its consensus prior distribution.LinearOpinionPoolingLogarithmicOpinionPooling
PortfolioOptimisers.prior — Function
prior(pe::OpinionPoolingPrior, X::MatNum, F::Option{<:MatNum} = nothing,
pnl::Option{<:AssetPanel} = 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.
No field of pe is modified, so calling prior twice on one estimator gives the same answer twice. Every moment of the result is pe.pe2's; the opinions contribute observation weights alone.
Algorithm
- Orient
XandFbydims. - When
pe.pe1is notnothing, replaceXwith the returns of that estimator's prior. - Read the opinion probabilities
ow, frompe.wwhen it is set and from a uniformrangeoverlength(pe.pes)when it isnothing. - Take the remainder
rwofowagainst one. Whenrwexceedseps, append it toowand givepwa last column of1/T, the uniform prior over the observations. - Fit every estimator of
pe.pesover the executorpe.ex. Check that each answered on the pool's own observation axis, and write its weights into a column ofpw. - Penalise
owthroughrobust_probabilities, which is the identity whenpe.pisnothing. - Pool the columns of
pwunderpe.algthroughcompute_pooling, giving the consensus weightsw. - Refit
pe.pe2underwthroughfactory, giving the moments of the result. - Read
ensasexpof the entropy ofw, andkldas the divergence fromwto each column ofpw.
Arguments
pe: Opinion pooling prior estimator.X: Asset returns matrix (observations × assets).F: Optional factor matrix.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. It reachespe.pe1, every opinion inpe.pes, and the refitpe.pe2.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).- Every opinion answers on the pool's observation axis, which is
pe.pe1's result whenpe.pe1is set andXwhen it isnothing. An opinion whose own wrapped estimator drops rows answers on a shorter axis, and its probabilities are then over other scenarios. That raises aDimensionMismatchnaming the opinion, both counts, and the fix: move the estimator that drops rows intope1.
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.pr.ens:expof the entropy of the consensus weights, so it runs from 1 (all mass on one observation) toT(uniform).pr.kld[i]: $\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.pr.ow: The penalised opinion probabilities, one entry per column ofpw. It is one entry longer thanpe.wwhen the uniform-prior remainder took a column.
Related
References
- [81]
- F. Dietrich and C. List. Probabilistic opinion pooling generalized. Part one: general agendas. Social Choice and Welfare 48, 747–786 (2017).
- [82]
- C. Martini and J. Sprenger. Opinion Aggregation and Individual Expertise. In: Scientific Collaboration and Collective Knowledge (Oxford University Press, 2017).
- [83]
- I. J. Good. Rational decisions. Journal of the Royal Statistical Society: Series B (Methodological) 14, 107–114 (1952).