Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mathematicalcompany.mintlify.app/llms.txt

Use this file to discover all available pages before exploring further.

Portfolio Management

The Portfolio class provides position tracking, weight optimization, risk analytics, and rebalancing for prediction market portfolios. All heavy computation (Monte Carlo, Greeks, Ledoit-Wolf) runs in Rust.

4 Optimizers

Kelly, equal-weight, risk parity, and minimum variance allocation.

Monte Carlo Risk

VaR, CVaR, win probability, and Greeks via Rust simulation engine.

Auto-Rebalance

Generate rebalancing orders to move from current to target weights.

Live Engine Sync

Build a Portfolio from a running Engine with Portfolio.from_engine().

Quick Start

import horizon as hz

# Create portfolio
portfolio = hz.Portfolio(name="my_portfolio", capital=10000.0)

# Add positions
portfolio.add_position("trump-win", "yes", 200.0, 0.45, 0.55)
portfolio.add_position("fed-cut", "yes", 100.0, 0.60, 0.65)
portfolio.add_position("btc-100k", "no", 150.0, 0.30, 0.25)

# Check current state
print(portfolio.weights())       # {"trump-win": 0.63, "fed-cut": 0.22, "btc-100k": 0.14}
print(portfolio.pnl())           # Total unrealised PnL
print(portfolio.pnl_pct())       # PnL as percentage of cost basis
print(portfolio.concentration()) # HHI concentration index
print(portfolio.summary())       # Human-readable summary

Constructor

portfolio = hz.Portfolio(
    name="my_portfolio",   # Portfolio name (default: "portfolio")
    capital=10000.0,       # Total capital budget (default: 10000.0)
)
ParameterTypeDefaultDescription
namestr"portfolio"Portfolio identifier
capitalfloat10000.0Total capital for weight calculations

Managing Positions

add_position

Add or replace a position in the portfolio.
portfolio.add_position(
    market_id="trump-win",   # Market identifier
    side="yes",              # "yes" or "no"
    size=200.0,              # Position size in contracts
    entry_price=0.45,        # Average entry price
    current_price=0.55,      # Current market probability
)

update_price

Update the current market price for an existing position.
portfolio.update_price("trump-win", 0.58)

remove_position

Remove a position from the portfolio.
portfolio.remove_position("trump-win")

from_engine

Build a Portfolio from a running Engine’s live positions and feed data.
engine = hz.Engine(risk_config=hz.RiskConfig(max_position_per_market=500))
# ... trade for a while ...

portfolio = hz.Portfolio.from_engine(engine, capital=10000.0)
print(portfolio.summary())

Portfolio Analytics

weights

Current weight per market (position value / total portfolio value).
w = portfolio.weights()
# {"trump-win": 0.63, "fed-cut": 0.22, "btc-100k": 0.14}

pnl / pnl_pct

print(portfolio.pnl())      # Total unrealised PnL in dollars
print(portfolio.pnl_pct())  # PnL as percentage of cost basis

concentration

Herfindahl-Hirschman Index (HHI): sum of squared weights. Lower is more diversified. 1/N = perfectly equal.
hhi = portfolio.concentration()
# 0.33 for 3 equal positions, 1.0 for single position

summary

Human-readable portfolio overview.
print(portfolio.summary())
# Portfolio 'my_portfolio' (3 positions)
# Capital: $10,000.00 | PnL: $25.00 (16.7%)
# ...

Weight Optimization

All optimizers return target weights as dict[str, float] mapping market IDs to target allocations.

Kelly Criterion

Compute Kelly-optimal weights given your probability estimates.
kelly_weights = portfolio.optimize_kelly(
    probs={
        "trump-win": 0.60,   # Your estimated probability
        "fed-cut": 0.70,
        "btc-100k": 0.20,   # Probability of YES (you hold NO)
    },
    max_total=1.0,  # Cap total Kelly allocation (default: 1.0)
)
# {"trump-win": 0.37, "fed-cut": 0.28, "btc-100k": 0.13}

Equal Weight

Simple 1/N allocation across all positions.
equal_weights = portfolio.optimize_equal_weight()
# {"trump-win": 0.33, "fed-cut": 0.33, "btc-100k": 0.33}

Risk Parity

Inverse-volatility weighting. Uses Ledoit-Wolf shrinkage covariance if return history is provided.
# Without history (falls back to equal weight)
rp_weights = portfolio.optimize_risk_parity()

# With return history (T x N matrix)
returns_history = [
    [0.01, -0.02, 0.03],   # time step 1: 3 assets
    [0.02, -0.01, 0.04],   # time step 2
    [-0.01, 0.03, -0.02],  # time step 3
    # ... more observations
]
rp_weights = portfolio.optimize_risk_parity(returns_history)

Minimum Variance

Minimum variance allocation using Ledoit-Wolf covariance estimation.
returns_history = [
    [0.01, -0.02, 0.03],
    [0.02, -0.01, 0.04],
    [-0.01, 0.03, -0.02],
]
mv_weights = portfolio.optimize_min_variance(returns_history)

Rebalancing

needs_rebalance

Check whether any position deviates from target weights by more than a threshold.
target = {"trump-win": 0.33, "fed-cut": 0.33, "btc-100k": 0.33}
if portfolio.needs_rebalance(target, threshold=0.05):
    print("Portfolio drift exceeds 5%")

rebalance_orders

Generate the orders needed to move from current to target weights.
target = portfolio.optimize_equal_weight()
orders = portfolio.rebalance_orders(target)

for order in orders:
    print(f"{order['action']} {order['size_delta']:.1f} of {order['market_id']}")
    print(f"  Current weight: {order['current_weight']:.2%}")
    print(f"  Target weight:  {order['target_weight']:.2%}")
Each order dict contains:
KeyTypeDescription
market_idstrMarket identifier
sidestr"yes" or "no"
actionstr"buy" or "sell"
size_deltafloatSize to trade
current_weightfloatCurrent portfolio weight
target_weightfloatTarget portfolio weight

Risk Metrics

metrics

Compute portfolio risk metrics including Monte Carlo simulation.
m = portfolio.metrics(
    n_simulations=10000,  # Monte Carlo scenarios
    seed=42,              # Reproducible results
    t_hours=24.0,         # Hours to expiry for Greeks
    vol=0.2,              # Implied vol for Greeks
)
Returns a PortfolioMetrics object:
FieldTypeDescription
num_positionsintNumber of positions
total_valuefloatTotal portfolio value
total_pnlfloatTotal unrealised PnL
total_pnl_pctfloatPnL as percentage
concentrationfloatHHI index
var_95float95% Value at Risk (Monte Carlo)
cvar_95float95% Conditional VaR
win_probabilityfloatProbability of positive PnL
greeksdict[str, PredictionGreeks]Per-position Greeks (delta, gamma, theta, vega)
correlation_matrix`list[list[float]]None`Ledoit-Wolf covariance if available
shrinkage_intensityfloatShrinkage parameter
m = portfolio.metrics(seed=42)
print(f"VaR 95:   ${m.var_95:.2f}")
print(f"CVaR 95:  ${m.cvar_95:.2f}")
print(f"Win Prob: {m.win_probability:.1%}")
print(f"HHI:      {m.concentration:.3f}")

# Per-position Greeks
for market_id, g in m.greeks.items():
    print(f"{market_id}: delta={g.delta:.2f}, gamma={g.gamma:.2f}")

Full Workflow Example

import horizon as hz

# 1. Build portfolio from live engine
engine = hz.Engine(
    exchange=hz.Paper(),
    risk_config=hz.RiskConfig(max_position_per_market=500),
)
# ... run strategy, accumulate positions ...

portfolio = hz.Portfolio.from_engine(engine, capital=10000.0)

# 2. Analyze risk
metrics = portfolio.metrics(n_simulations=50000, seed=42)
print(f"Portfolio VaR 95: ${metrics.var_95:.2f}")
print(f"Win probability:  {metrics.win_probability:.1%}")

# 3. Optimize weights
kelly_targets = portfolio.optimize_kelly(
    probs={"trump-win": 0.60, "fed-cut": 0.70},
)

# 4. Check if rebalancing is needed
if portfolio.needs_rebalance(kelly_targets, threshold=0.05):
    orders = portfolio.rebalance_orders(kelly_targets)
    for o in orders:
        print(f"{o['action']} {o['size_delta']:.1f} {o['market_id']}")