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.
from horizon import Side, OrderSide, OrderType, TimeInForce, OrderStatus, AlertLevel, TriggerType, ContingentOrder
Side
Market side.
Side.Yes # prediction market YES outcome
Side.No # prediction market NO outcome
Side.Long # equities, crypto, options (non-prediction instruments)
OrderSide
Buy or sell direction.
OrderSide.Buy
OrderSide.Sell
OrderType
OrderType.Limit
OrderType.Market
TimeInForce
TimeInForce.GTC # Good Till Cancel (default)
TimeInForce.GTD # Good Till Date
TimeInForce.FOK # Fill Or Kill
TimeInForce.FAK # Fill And Kill
OrderStatus
OrderStatus.New
OrderStatus.Submitted
OrderStatus.Accepted
OrderStatus.PartiallyFilled
OrderStatus.Filled
OrderStatus.Canceled
OrderStatus.Rejected
AlertLevel
AlertLevel.Info
AlertLevel.Warning
AlertLevel.Critical
TriggerType
Contingent order trigger type.
TriggerType.StopLoss
TriggerType.TakeProfit
AssetClass
Instrument asset class.
AssetClass.PredictionMarket
AssetClass.Equity
AssetClass.Option
AssetClass.Crypto
AssetClass.Future
OptionType
Option contract type.
OptionType.Call
OptionType.Put
Quote
A quote represents a bid/ask pair at a given size.
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 (bid/ask clamped to [0.01, 0.99]; returns [] if crossed):
quotes = hz.quotes(fair=0.50, spread=0.06, size=5.0)
# → [Quote(bid=0.47, ask=0.53, size=5.0)]
Market
A market definition.
# Prediction market
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"
# Equity
market = hz.Market(
id="AAPL",
name="Apple Inc.",
exchange="alpaca",
ticker="AAPL",
asset_class="equity",
)
market.token_id(Side.Long) # None (no token IDs for equities)
| Field | Type | Default | Description |
|---|
id | str | required | Market identifier |
name | str | "" | Display name |
slug | str | "" | URL slug |
exchange | str | "paper" | Exchange name |
expiry | str or None | None | Expiry date |
active | bool | True | Whether market is active |
yes_token_id | str or None | None | Polymarket Yes token ID |
no_token_id | str or None | None | Polymarket No token ID |
condition_id | str or None | None | Polymarket condition ID |
neg_risk | bool | False | Polymarket neg-risk flag |
ticker | str or None | None | Kalshi ticker |
event_id | str or None | None | Event ID (set when market is part of a multi-outcome event) |
outcome_name | str or None | None | Outcome name within the event (e.g., “Trump”) |
asset_class | str or None | None | Asset class: “prediction_market”, “equity”, “option”, “crypto”, “future” |
underlying | str or None | None | Underlying symbol for derivatives (e.g., “AAPL” for AAPL options) |
strike_price | float or None | None | Strike price for options |
option_type | str or None | None | ”call” or “put” |
multiplier | float or None | None | Contract multiplier (100 for US options) |
Outcome
A single named outcome in a multi-outcome event. Each outcome is itself a binary contract with YES/NO tokens.
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 or None | None | YES token ID |
no_token_id | str or 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.
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 or None | None | Polymarket condition ID |
expiry | str or None | None | Expiry date |
See Multi-Outcome Events 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).
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.
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, No, or Long. Use Side.Long for equities/crypto. |
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 or 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.
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, No, or Long |
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 or 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.
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, No, or Long |
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 or None | Token ID (Polymarket) |
exchange | str | Exchange name |
Position has no Python constructor. Positions are created internally by the engine when fills are processed. In tests, build positions via engine.process_fill().
Fill
A fill event from an exchange.
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, No, or Long |
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 or 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().
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, No, or Long |
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 or None | Optional PnL threshold (take-profit only) |
linked_order_id | str or 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 or None | ID of the order submitted after trigger (None until triggered) |
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.
SimPosition
A position for Monte Carlo simulation input.
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().
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 for the full guide.
EngineStatus
A snapshot of the engine’s current state.
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).
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.
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
price_min=0.01, # 0.01 default (prediction markets)
price_max=0.99, # 0.99 default (prediction markets)
)
| Field | Type | Default | Description |
|---|
max_position_per_event | float or None | None | Maximum total position across all outcomes in a registered event. None = disabled. |
price_min | float | 0.01 | Minimum valid limit-order price |
price_max | float | 0.99 | Maximum valid limit-order price. Set to 100000 for equities. |
See Risk Management and Multi-Outcome Events for detailed documentation.