Robust Portfolio

Worst-case optimization under parameter uncertainty

Standard mean-variance optimization treats your estimated mean and covariance as exact. They never are. Robust portfolio optimization assumes the true parameters lie within an uncertainty set around your estimates and optimizes for the worst case. The result is a portfolio that degrades gracefully when your estimates are wrong.

API

Robust optimization

python
import numpy as np

mu = np.array([0.05, 0.03, 0.04])           # estimated expected returns
cov = np.array([                              # estimated covariance matrix
    [0.04, 0.01, 0.005],
    [0.01, 0.02, 0.008],
    [0.005, 0.008, 0.03],
])

result = hz.robust_optimize(
    mu=mu,
    cov=cov,
    uncertainty_radius=0.02,  # radius of ellipsoidal uncertainty set
)

result.weights           # optimal portfolio weights
result.worst_case_mu     # worst-case expected return within the set
result.worst_case_return # portfolio return under worst-case parameters
result.objective         # objective value (worst-case utility)

Worst-case return for a given portfolio

python
w = np.array([0.4, 0.3, 0.3])
wcr = hz.worst_case_return(w, mu, cov, uncertainty_radius=0.02)

wcr.return_value   # worst-case expected return
wcr.worst_mu       # the adversarial mean vector

Robust efficient frontier

python
frontier = hz.robust_efficient_frontier(
    mu=mu,
    cov=cov,
    uncertainty_radius=0.02,
    n_points=20,
)

frontier.weights     # (20, N) array of portfolio weights
frontier.returns     # (20,) worst-case returns
frontier.risks       # (20,) portfolio standard deviations

Ellipsoidal uncertainty

The model assumes the true mean mu_true lies within an ellipsoid:

(mu_true - mu)^T @ Sigma^{-1} @ (mu_true - mu) <= epsilon^2

where epsilon is the uncertainty_radius. The optimizer finds the mu_true within this set that minimizes your portfolio return, then maximizes over weights:

max_w  min_{mu_true in set}  w^T @ mu_true - (gamma/2) * w^T @ Sigma @ w

This has a closed-form reduction to a second-order cone program.

Example: robust allocation across prediction markets

python
# Three prediction markets with uncertain edge estimates
mu = np.array([0.03, 0.015, 0.025])  # estimated edges
cov = np.array([
    [0.01, 0.003, 0.002],
    [0.003, 0.008, 0.001],
    [0.002, 0.001, 0.012],
])

# Standard optimization (fragile)
standard = hz.robust_optimize(mu, cov, uncertainty_radius=0.0)

# Robust optimization (accounts for estimation error)
robust = hz.robust_optimize(mu, cov, uncertainty_radius=0.015)

print(f"Standard weights: {standard.weights}")  # concentrated
print(f"Robust weights:   {robust.weights}")     # more diversified

When to use

  • Small sample sizes: when you have limited historical data, your mean/covariance estimates have wide confidence intervals. Robust optimization respects this.
  • Regime uncertainty: if you suspect the market regime may shift, a larger uncertainty radius protects against the new regime’s parameters.
  • Prediction markets: edge estimates are noisy (based on model probabilities vs. market prices). Robust optimization prevents over-concentration in the market where your edge estimate happens to be highest.
  • Complements entropy pooling: use entropy pooling to form your best estimate, then robust optimization to protect against that estimate being wrong.

Next