---
title: "Horizon Types Reference"
description: "All Rust types exposed to Python via PyO3: enums, Quote, Market, Order, Position, Fill, and more."
icon: "code"
---

All types are implemented in Rust and exposed to Python via PyO3. Import them from `horizon` or `horizon._horizon`.

## Enums

All enums support equality (`==`), hashing, and can be used as dict keys.

```python
from horizon import Side, OrderSide, OrderType, TimeInForce, OrderStatus, AlertLevel, TriggerType, ContingentOrder
```

### Side

Prediction market outcome side.

```python
Side.Yes
Side.No
```

### OrderSide

Buy or sell direction.

```python
OrderSide.Buy
OrderSide.Sell
```

### OrderType

```python
OrderType.Limit
OrderType.Market
```

### TimeInForce

```python
TimeInForce.GTC   # Good Till Cancel (default)
TimeInForce.GTD   # Good Till Date
TimeInForce.FOK   # Fill Or Kill
TimeInForce.FAK   # Fill And Kill
```

### OrderStatus

```python
OrderStatus.New
OrderStatus.Submitted
OrderStatus.Accepted
OrderStatus.PartiallyFilled
OrderStatus.Filled
OrderStatus.Canceled
OrderStatus.Rejected
```

### AlertLevel

```python
AlertLevel.Info
AlertLevel.Warning
AlertLevel.Critical
```

### TriggerType

Contingent order trigger type.

```python
TriggerType.StopLoss
TriggerType.TakeProfit
```

## Quote

A quote represents a bid/ask pair at a given size.

```python
quote = hz.Quote(bid=0.45, ask=0.55, size=10.0)

quote.bid       # 0.45
quote.ask       # 0.55
quote.size      # 10.0
quote.spread()  # 0.10
quote.mid()     # 0.50
```

Helper function:

```python
quotes = hz.quotes(fair=0.50, spread=0.06, size=5.0)
# → [Quote(bid=0.47, ask=0.53, size=5.0)]
```

## Market

A prediction market definition.

```python
market = hz.Market(
    id="will-btc-hit-100k",
    name="Will BTC hit $100k?",
    slug="will-btc-hit-100k",
    exchange="polymarket",           # "polymarket", "kalshi", or "paper"
    expiry="2025-12-31",
    active=True,
    yes_token_id="123456789",        # Polymarket token ID
    no_token_id="987654321",         # Polymarket token ID
    condition_id="0xabc...",         # Polymarket condition ID
    neg_risk=True,                   # Polymarket neg-risk flag
    ticker="KXBTC-25FEB16",         # Kalshi ticker
)

market.token_id(Side.Yes)  # "123456789"
market.token_id(Side.No)   # "987654321"
```

| Field | Type | Default | Description |
|-------|------|---------|-------------|
| `id` | `str` | required | Market identifier |
| `name` | `str` | `""` | Display name |
| `slug` | `str` | `""` | URL slug |
| `exchange` | `str` | `"paper"` | Exchange name |
| `expiry` | `str \| None` | `None` | Expiry date |
| `active` | `bool` | `True` | Whether market is active |
| `yes_token_id` | `str \| None` | `None` | Polymarket Yes token ID |
| `no_token_id` | `str \| None` | `None` | Polymarket No token ID |
| `condition_id` | `str \| None` | `None` | Polymarket condition ID |
| `neg_risk` | `bool` | `False` | Polymarket neg-risk flag |
| `ticker` | `str \| None` | `None` | Kalshi ticker |
| `event_id` | `str \| None` | `None` | Event ID (set when market is part of a multi-outcome event) |
| `outcome_name` | `str \| None` | `None` | Outcome name within the event (e.g., "Trump") |

## Outcome

A single named outcome in a multi-outcome event. Each outcome is itself a binary contract with YES/NO tokens.

```python
from horizon import Outcome, Side

outcome = Outcome(
    name="Trump",
    market_id="trump-wins-2024",
    yes_token_id="token_yes_123",
    no_token_id="token_no_456",
    yes_price=0.55,
)

outcome.token_id(Side.Yes)  # "token_yes_123"
outcome.token_id(Side.No)   # "token_no_456"
```

| Field | Type | Default | Description |
|-------|------|---------|-------------|
| `name` | `str` | required | Outcome name (e.g., "Trump") |
| `market_id` | `str` | required | Binary contract market ID for this outcome |
| `yes_token_id` | `str \| None` | `None` | YES token ID |
| `no_token_id` | `str \| None` | `None` | NO token ID |
| `yes_price` | `float` | `0.0` | Current YES price |

## Event

Groups multiple outcomes under a shared event/condition. Used for multi-outcome prediction markets.

```python
from horizon import Event, Outcome

event = Event(
    id="election-2024",
    name="Who wins the 2024 election?",
    outcomes=[trump, biden, desantis],
    neg_risk=True,
    exchange="polymarket",
    condition_id="0xabc...",
)

event.outcome_count()           # 3
event.outcome_names()           # ["Trump", "Biden", "DeSantis"]
event.outcome_by_name("Trump")  # Outcome or None
event.to_markets()              # list[Market] with event_id/outcome_name set
```

| Field | Type | Default | Description |
|-------|------|---------|-------------|
| `id` | `str` | required | Event identifier |
| `name` | `str` | required | Display name |
| `outcomes` | `list[Outcome]` | required | List of outcomes |
| `neg_risk` | `bool` | `False` | Polymarket neg-risk flag |
| `exchange` | `str` | `"polymarket"` | Exchange name |
| `condition_id` | `str \| None` | `None` | Polymarket condition ID |
| `expiry` | `str \| None` | `None` | Expiry date |

See [Multi-Outcome Events](/multi-outcome) for full usage guide.

## EventArbitrageOpportunity

Represents an arbitrage opportunity across outcomes in a multi-outcome event (when outcome prices don't sum to 1.0).

```python
opp.event_id        # "election-2024"
opp.exchange         # "polymarket"
opp.price_sum        # 0.97 (sum of all outcome YES prices)
opp.overround        # -0.03 (price_sum - 1.0)
opp.direction        # "buy_all" (buy all outcomes for guaranteed profit)
opp.net_edge         # 0.01 (edge after fees)
opp.is_executable    # True
```

| Field | Type | Description |
|-------|------|-------------|
| `event_id` | `str` | Event identifier |
| `exchange` | `str` | Exchange name |
| `price_sum` | `float` | Sum of all outcome YES prices |
| `overround` | `float` | `price_sum - 1.0` |
| `direction` | `str` | `"buy_all"` or `"sell_all"` |
| `net_edge` | `float` | Edge after fees |
| `is_executable` | `bool` | Whether the edge exceeds fees |

## OrderRequest

An order request submitted to the engine.

```python
req = hz.OrderRequest(
    market_id="will-btc-hit-100k",
    side=Side.Yes,
    order_side=OrderSide.Buy,
    size=10.0,
    price=0.55,
    order_type=OrderType.Limit,     # default
    time_in_force=TimeInForce.GTC,  # default
    post_only=True,                 # default
    token_id="123456789",           # optional, for Polymarket
    neg_risk=False,                 # optional, for Polymarket
)
```

| Field | Type | Default | Description |
|-------|------|---------|-------------|
| `market_id` | `str` | required | Market identifier |
| `side` | `Side` | required | Yes or No |
| `order_side` | `OrderSide` | required | Buy or Sell |
| `size` | `float` | required | Order size |
| `price` | `float` | required | Limit price |
| `order_type` | `OrderType` | `Limit` | Limit or Market |
| `time_in_force` | `TimeInForce` | `GTC` | Time in force |
| `post_only` | `bool` | `True` | Post-only flag |
| `token_id` | `str \| None` | `None` | Polymarket token ID |
| `neg_risk` | `bool` | `False` | Polymarket neg-risk flag |

## Order

An order with current state. Created by the engine when an `OrderRequest` is submitted.

```python
order.id                # "abc123"
order.market_id         # "will-btc-hit-100k"
order.side              # Side.Yes
order.order_side        # OrderSide.Buy
order.price             # 0.55
order.size              # 10.0
order.filled_size       # 5.0
order.remaining_size    # 5.0
order.status            # OrderStatus.PartiallyFilled
order.created_at        # 1700000000.0 (unix timestamp)
order.status_reason     # None or "risk violation: ..."
order.exchange          # "polymarket"
order.amendment_count   # 0 (incremented on each successful amend)
order.is_open()         # True
```

| Field | Type | Description |
|-------|------|-------------|
| `id` | `str` | Order ID (exchange-assigned) |
| `market_id` | `str` | Market identifier |
| `side` | `Side` | Yes or No |
| `order_side` | `OrderSide` | Buy or Sell |
| `price` | `float` | Limit price |
| `size` | `float` | Original order size |
| `filled_size` | `float` | Size filled so far |
| `remaining_size` | `float` | Remaining unfilled size |
| `order_type` | `OrderType` | Limit or Market |
| `time_in_force` | `TimeInForce` | Time in force |
| `status` | `OrderStatus` | Current status |
| `created_at` | `float` | Unix timestamp |
| `status_reason` | `str \| None` | Reason for rejection/cancel |
| `exchange` | `str` | Exchange name |
| `amendment_count` | `u32` | Number of times this order has been amended (starts at 0) |

## Position

A position in a market.

```python
pos.market_id           # "will-btc-hit-100k"
pos.side                # Side.Yes
pos.size                # 10.0
pos.avg_entry_price     # 0.55
pos.realized_pnl        # 0.50
pos.unrealized_pnl      # 0.30
pos.token_id            # "123456789" or None
pos.exchange            # "polymarket"
pos.total_pnl()         # 0.80
pos.notional()          # 5.50 (size * avg_entry_price)
```

| Field | Type | Description |
|-------|------|-------------|
| `market_id` | `str` | Market identifier |
| `side` | `Side` | Yes or No |
| `size` | `float` | Position size |
| `avg_entry_price` | `float` | Average entry price |
| `realized_pnl` | `float` | Realized P&L |
| `unrealized_pnl` | `float` | Unrealized P&L |
| `token_id` | `str \| None` | Token ID (Polymarket) |
| `exchange` | `str` | Exchange name |

<Note>
  `Position` has no Python constructor. Positions are created internally by the engine when fills are processed. In tests, build positions via `engine.process_fill()`.
</Note>

## Fill

A fill event from an exchange.

```python
fill = hz.Fill(
    fill_id="fill_001",
    order_id="order_001",
    market_id="will-btc-hit-100k",
    side=Side.Yes,
    order_side=OrderSide.Buy,
    price=0.55,
    size=10.0,
    fee=0.01,
    timestamp=1700000000.0,
    token_id="123456789",
    exchange="polymarket",
    is_maker=False,
)
```

| Field | Type | Default | Description |
|-------|------|---------|-------------|
| `fill_id` | `str` | required | Fill identifier |
| `order_id` | `str` | required | Associated order ID |
| `market_id` | `str` | required | Market identifier |
| `side` | `Side` | required | Yes or No |
| `order_side` | `OrderSide` | required | Buy or Sell |
| `price` | `float` | required | Fill price |
| `size` | `float` | required | Fill size |
| `fee` | `float` | `0.0` | Fee |
| `timestamp` | `float` | `0.0` | Unix timestamp |
| `token_id` | `str \| None` | `None` | Token ID |
| `exchange` | `str` | `""` | Exchange name |
| `is_maker` | `bool` | `False` | Whether this fill was a maker (added liquidity) or taker (removed liquidity) |

## ContingentOrder

A contingent order (stop-loss or take-profit) that triggers when market conditions are met. Created via `engine.add_stop_loss()`, `engine.add_take_profit()`, or `engine.submit_bracket()`.

```python
order.id                 # "contingent_1"
order.trigger_type       # TriggerType.StopLoss
order.market_id          # "will-btc-hit-100k"
order.side               # Side.Yes
order.order_side         # OrderSide.Sell
order.size               # 10.0
order.trigger_price      # 0.45
order.trigger_pnl        # None or 5.0
order.linked_order_id    # "contingent_2" (OCO partner) or None
order.exchange           # "paper"
order.triggered          # False
order.child_order_id     # None (set after trigger)
```

| Field | Type | Description |
|-------|------|-------------|
| `id` | `str` | Contingent order identifier (e.g., `"contingent_1"`) |
| `trigger_type` | `TriggerType` | `StopLoss` or `TakeProfit` |
| `market_id` | `str` | Market identifier |
| `side` | `Side` | Yes or No |
| `order_side` | `OrderSide` | Buy or Sell |
| `size` | `float` | Order size to submit when triggered |
| `trigger_price` | `float` | Price threshold that activates the order |
| `trigger_pnl` | `float \| None` | Optional PnL threshold (take-profit only) |
| `linked_order_id` | `str \| None` | OCO partner ID (set by `submit_bracket`) |
| `exchange` | `str` | Target exchange name |
| `triggered` | `bool` | Whether this order has been triggered |
| `child_order_id` | `str \| None` | ID of the order submitted after trigger (None until triggered) |

<Note>
  `ContingentOrder` is read-only. Use `engine.add_stop_loss()`, `engine.add_take_profit()`, or `engine.submit_bracket()` to create contingent orders. Use `engine.cancel_contingent()` to remove them.
</Note>

## SimPosition

A position for Monte Carlo simulation input.

```python
pos = hz.SimPosition(
    market_id="election-winner",
    side="yes",           # "yes" or "no" (case insensitive)
    size=100.0,
    entry_price=0.50,
    current_price=0.60,
)

pos.market_id      # "election-winner"
pos.side           # "yes" (always lowercased)
pos.size           # 100.0
pos.entry_price    # 0.50
pos.current_price  # 0.60
```

| Field | Type | Description |
|-------|------|-------------|
| `market_id` | `str` | Market identifier |
| `side` | `str` | `"yes"` or `"no"` (lowercased automatically) |
| `size` | `float` | Position size |
| `entry_price` | `float` | Average entry price |
| `current_price` | `float` | Current probability estimate (used as Bernoulli probability) |

## SimulationResult

Result of a Monte Carlo simulation. Created by `hz.monte_carlo()` or `hz.simulate()`.

```python
result = hz.monte_carlo(positions, 10000, None, 42)

result.mean_pnl          # Average PnL
result.median_pnl        # Median PnL
result.std_dev           # Standard deviation
result.var_95            # Value at Risk (5th percentile)
result.var_99            # Value at Risk (1st percentile)
result.cvar_95           # Conditional VaR (expected shortfall)
result.max_loss          # Worst scenario
result.max_gain          # Best scenario
result.win_probability   # Fraction with positive PnL
result.scenario_pnl      # list[float]  - all scenario PnLs (sorted)
result.percentiles       # list[tuple[float, float]]  - percentile distribution
```

| Field | Type | Description |
|-------|------|-------------|
| `mean_pnl` | `float` | Mean PnL across scenarios |
| `median_pnl` | `float` | Median PnL |
| `std_dev` | `float` | PnL standard deviation |
| `var_95` | `float` | 5th percentile PnL |
| `var_99` | `float` | 1st percentile PnL |
| `cvar_95` | `float` | Average of worst 5% scenarios |
| `max_loss` | `float` | Minimum scenario PnL |
| `max_gain` | `float` | Maximum scenario PnL |
| `win_probability` | `float` | Fraction of positive-PnL scenarios |
| `scenario_pnl` | `list[float]` | All scenario PnLs (ascending) |
| `percentiles` | `list[tuple[float, float]]` | Percentile distribution |

See [Monte Carlo Simulation](/simulation) for the full guide.

## EngineStatus

A snapshot of the engine's current state.

```python
status = engine.status()

status.running               # True
status.kill_switch_active    # False
status.kill_switch_reason    # None
status.open_orders           # 4
status.active_positions      # 2
status.total_realized_pnl    # 1.50
status.total_unrealized_pnl  # 0.30
status.daily_pnl             # 1.80
status.uptime_secs           # 3600
status.total_pnl()           # 1.80
```

## FeedSnapshot

A snapshot of a feed's current data (engine level).

```python
snap = engine.feed_snapshot("btc")

snap.price       # 100250.0
snap.bid         # 100248.0
snap.ask         # 100252.0
snap.timestamp   # 1700000000.0
snap.source      # "binance_ws"
snap.volume_24h  # 50000.0
```

## RiskConfig

Risk configuration for the engine.

```python
config = RiskConfig(
    max_position_per_market=100.0,
    max_portfolio_notional=1000.0,
    max_daily_drawdown_pct=5.0,
    max_order_size=50.0,
    rate_limit_sustained=50,
    rate_limit_burst=300,
    dedup_window_ms=1000,
    max_position_per_event=200.0,  # Optional, None = disabled
)
```

| Field | Type | Default | Description |
|-------|------|---------|-------------|
| `max_position_per_event` | `float \| None` | `None` | Maximum total position across all outcomes in a registered event. `None` = disabled. |

See [Risk Management](/core-concepts/risk) and [Multi-Outcome Events](/multi-outcome) for detailed documentation.
