Opinion Pooling

PortfolioOptimisers.LinearOpinionPoolingType
struct LinearOpinionPooling <: OpinionPoolingAlgorithm

Pools 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).
source
PortfolioOptimisers.LogarithmicOpinionPoolingType
struct LogarithmicOpinionPooling <: OpinionPoolingAlgorithm

Pools 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$. LinearOpinionPooling needs 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

  • [84] I. J. Good. Rational decisions. Journal of the Royal Statistical Society: Series B (Methodological) 14, 107–114 (1952).
  • [83] F. Dietrich and C. List. Probabilistic opinion pooling generalized. Part one: general agendas. Social Choice and Welfare 48, 747–786 (2017).
source
PortfolioOptimisers.OpinionPoolingPriorType
struct OpinionPoolingPrior{__T_pes, __T_pe1, __T_pe2, __T_p, __T_w, __T_alg, __T_ex} <: AbstractLowOrderPriorEstimator_AF

Opinion 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()) -> OpinionPoolingPrior

Keywords correspond to the struct's fields. All arguments are validated for type and value consistency.

Validation

  • pes must be a non-empty vector of prior estimators.
  • If w is not nothing, !isempty(w), length(w) == length(pes), all(x -> 0 <= x <= 1, w), and sum(w) <= 1.
  • If p is not nothing, p > 0.

Propagated parameters

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

  • pes: Recursively updated via factory.
  • pe1: Recursively updated via factory.
  • pe2: Recursively updated via factory.

View parameters

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

Details

  • If w is nothing, all opinions are equally weighted at 1/length(pes).
  • If w is not nothing and sum(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 of pw and it is penalised alongside the others.
  • If p is nothing, the opinion probabilities are used as given. Otherwise they are adjusted by their Kullback-Leibler divergence from the consensus, through robust_probabilities.
  • p is bounded below by zero strictly. p = nothing is how one asks for no penalty; there is no p = 0.
  • 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.

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

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).
source
PortfolioOptimisers.priorFunction
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: If true, throws error for missing assets; otherwise, issues warnings. Default is false.
  • 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.pe1 is applied to asset returns before pooling, else the original returns are used.
  • Each prior estimator in pe.pes is applied to the asset returns, producing individual prior weights.
  • Opinion probabilities ow are initialised from pe.w or set uniformly if it is nothing; if their sum is less than 1, the remainder is assigned to a uniform prior, which takes the last column of pw and is pooled and penalised like any other opinion.
  • Robust opinion probabilities are computed using robust_probabilities if a penalty parameter pe.p is not nothing. Neither pe.w nor any other field of pe is modified: calling prior twice on one estimator gives the same answer twice.
  • Consensus posterior weights are computed using compute_pooling according to the specified pooling algorithm pe.alg.
  • Post-processing estimator pe.pe2 is applied using the consensus weights, via factory. Every moment of the result is pe.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.ens is exp of the entropy of the consensus weights, so it runs from 1 (all mass on one observation) to T (uniform).
  • pr.kld[i] is $\mathrm{KL}(\boldsymbol{w} \,\|\, \boldsymbol{p}_i)$, from the consensus to opinion i. robust_probabilities reads the divergence in the other direction, so the two vectors are different numbers.

Related

source
PortfolioOptimisers.OpinionPoolingAlgorithmType
abstract type OpinionPoolingAlgorithm <: AbstractAlgorithm

Abstract 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 of pw into 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 column k holds expert k'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.375

Related

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).
source
PortfolioOptimisers.robust_probabilitiesFunction
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 own w field.

Details

  • If p is nothing, i.e. the method with args..., returns the original opinion probabilities.
  • If p is not nothing, 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 p concentrates the mass on the opinions nearest the consensus. As p grows without bound the pool tends to the single closest opinion.
  • Used internally by OpinionPoolingPrior to ensure robust aggregation of opinions.

Related

source
PortfolioOptimisers.compute_poolingFunction
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 (LinearOpinionPooling or LogarithmicOpinionPooling).
  • 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 OpinionPoolingPrior to form the consensus prior distribution.

Related

source

References

[83]
F. Dietrich and C. List. Probabilistic opinion pooling generalized. Part one: general agendas. Social Choice and Welfare 48, 747–786 (2017).
[84]
[85]
C. Martini and J. Sprenger. Opinion Aggregation and Individual Expertise. In: Scientific Collaboration and Collective Knowledge (Oxford University Press, 2017).