> ## Documentation Index
> Fetch the complete documentation index at: https://mathematicalcompany.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Portfolio Management

> Portfolio construction, optimization (Kelly, risk parity, min variance), rebalancing, and Monte Carlo risk metrics.

# 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.

<CardGroup cols={2}>
  <Card title="4 Optimizers" icon="scale-balanced">
    Kelly, equal-weight, risk parity, and minimum variance allocation.
  </Card>

  <Card title="Monte Carlo Risk" icon="dice">
    VaR, CVaR, win probability, and Greeks via Rust simulation engine.
  </Card>

  <Card title="Auto-Rebalance" icon="arrows-rotate">
    Generate rebalancing orders to move from current to target weights.
  </Card>

  <Card title="Live Engine Sync" icon="bolt">
    Build a Portfolio from a running Engine with `Portfolio.from_engine()`.
  </Card>
</CardGroup>

***

## Quick Start

```python theme={null}
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

```python theme={null}
portfolio = hz.Portfolio(
    name="my_portfolio",   # Portfolio name (default: "portfolio")
    capital=10000.0,       # Total capital budget (default: 10000.0)
)
```

| Parameter | Type    | Default       | Description                           |
| --------- | ------- | ------------- | ------------------------------------- |
| `name`    | `str`   | `"portfolio"` | Portfolio identifier                  |
| `capital` | `float` | `10000.0`     | Total capital for weight calculations |

***

## Managing Positions

### add\_position

Add or replace a position in the portfolio.

```python theme={null}
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.

```python theme={null}
portfolio.update_price("trump-win", 0.58)
```

### remove\_position

Remove a position from the portfolio.

```python theme={null}
portfolio.remove_position("trump-win")
```

### from\_engine

Build a Portfolio from a running Engine's live positions and feed data.

```python theme={null}
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).

```python theme={null}
w = portfolio.weights()
# {"trump-win": 0.63, "fed-cut": 0.22, "btc-100k": 0.14}
```

### pnl / pnl\_pct

```python theme={null}
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.

```python theme={null}
hhi = portfolio.concentration()
# 0.33 for 3 equal positions, 1.0 for single position
```

### summary

Human-readable portfolio overview.

```python theme={null}
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.

```python theme={null}
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.

```python theme={null}
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.

```python theme={null}
# 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.

```python theme={null}
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.

```python theme={null}
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.

```python theme={null}
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:

| Key              | Type    | Description              |
| ---------------- | ------- | ------------------------ |
| `market_id`      | `str`   | Market identifier        |
| `side`           | `str`   | `"yes"` or `"no"`        |
| `action`         | `str`   | `"buy"` or `"sell"`      |
| `size_delta`     | `float` | Size to trade            |
| `current_weight` | `float` | Current portfolio weight |
| `target_weight`  | `float` | Target portfolio weight  |

***

## Risk Metrics

### metrics

Compute portfolio risk metrics including Monte Carlo simulation.

```python theme={null}
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:

| Field                 | Type                          | Description                                     |                                     |
| --------------------- | ----------------------------- | ----------------------------------------------- | ----------------------------------- |
| `num_positions`       | `int`                         | Number of positions                             |                                     |
| `total_value`         | `float`                       | Total portfolio value                           |                                     |
| `total_pnl`           | `float`                       | Total unrealised PnL                            |                                     |
| `total_pnl_pct`       | `float`                       | PnL as percentage                               |                                     |
| `concentration`       | `float`                       | HHI index                                       |                                     |
| `var_95`              | `float`                       | 95% Value at Risk (Monte Carlo)                 |                                     |
| `cvar_95`             | `float`                       | 95% Conditional VaR                             |                                     |
| `win_probability`     | `float`                       | Probability of positive PnL                     |                                     |
| `greeks`              | `dict[str, PredictionGreeks]` | Per-position Greeks (delta, gamma, theta, vega) |                                     |
| `correlation_matrix`  | \`list\[list\[float]]         | None\`                                          | Ledoit-Wolf covariance if available |
| `shrinkage_intensity` | `float`                       | Shrinkage parameter                             |                                     |

```python theme={null}
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

```python theme={null}
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']}")
```
