Entropy Pooling

Meucci's framework for blending views with market-implied probabilities

Entropy pooling lets you take a prior distribution (e.g., historical returns or market-implied probabilities) and tilt it toward your views while staying as close to the original as possible. It solves a convex optimization that minimizes KL divergence between the posterior and prior, subject to your view constraints. The result is a new set of scenario probabilities you can feed into any portfolio optimizer.

API

Core function

python
# prior_probs: (S,) array of scenario probabilities summing to 1
# view_matrix: (K, S) matrix encoding K linear view constraints
# view_bounds: (K,) array of view targets
posterior = hz.entropy_pool(prior_probs, view_matrix, view_bounds)

posterior.probabilities   # (S,) re-weighted scenario probabilities
posterior.kl_divergence   # KL(posterior || prior)
posterior.effective_n     # effective number of scenarios (entropy-based)

Moments from the posterior

python
# scenarios: (S, N) matrix of N asset returns across S scenarios
mu = hz.posterior_mean(scenarios, posterior.probabilities)
cov = hz.posterior_covariance(scenarios, posterior.probabilities)

Equality and inequality views

python
import numpy as np

S = 1000   # scenarios
N = 3      # assets
scenarios = np.random.randn(S, N) * 0.02  # simulated returns

prior = np.ones(S) / S  # equal-weighted prior

# View 1 (equality): "expected return of asset 0 = 0.5%"
H_eq = scenarios[:, 0].reshape(1, -1)
h_eq = np.array([0.005])

# View 2 (inequality): "expected return of asset 1 > 0.2%"
# Encode as equality on a slack variable, or use the inequality interface:
posterior = hz.entropy_pool(
    prior_probs=prior,
    view_matrix=H_eq,
    view_bounds=h_eq,
)

How it works

Given prior probabilities p and view constraints Hp' = h, entropy pooling finds posterior p' by solving:

minimize  KL(p' || p) = sum(p'_s * ln(p'_s / p_s))
subject to  H @ p' = h
            sum(p') = 1
            p' >= 0

The dual problem is unconstrained and low-dimensional (K variables for K views), so it solves quickly even with thousands of scenarios.

Example: tilting election probabilities

python
# 500 Monte Carlo scenarios for 3 prediction markets
scenarios = np.random.randn(500, 3) * 0.05
prior = np.ones(500) / 500

# Your view: market 0 will return +3% on average (you think the odds are mispriced)
H = scenarios[:, 0].reshape(1, -1)
h = np.array([0.03])

post = hz.entropy_pool(prior, H, h)

# New moments reflecting your view
mu = hz.posterior_mean(scenarios, post.probabilities)
cov = hz.posterior_covariance(scenarios, post.probabilities)

print(f"Prior mean[0]: {scenarios[:, 0].mean():.4f}")
print(f"Posterior mean[0]: {mu[0]:.4f}")  # should be ~0.03

When to use

  • View blending: you have a statistical model (prior) but also a qualitative view. Entropy pooling merges them without discarding either.
  • Stress testing: tilt the distribution so that a crash scenario has higher probability, then re-optimize the portfolio under the stressed distribution.
  • Cross-asset allocation: start from market-implied returns and overlay your edge estimates for specific markets.

Next