Skip to main content
Horizon supports trading on multiple exchanges simultaneously from a single strategy. This enables cross-exchange arbitrage, hedging, and diversified market making.

Quick Start

How It Works

1

Register exchanges

The first exchange in the exchanges list becomes the primary exchange (default for routing). Additional exchanges are added via add_exchange().
2

Market resolution

Each market is resolved against the appropriate exchange. Markets with exchange="polymarket" resolve via Gamma API, exchange="kalshi" resolve via ticker mapping.
3

Order routing

Orders are routed to the correct exchange based on market.exchange. The pipeline’s _process_result() reads market.exchange and passes it to submit_quotes().
4

Fill polling

poll_fills() polls all live exchanges each cycle and processes fills from every venue.
5

Cancel across venues

cancel_all() and cancel_market() operate across all registered exchanges.

Engine API

Adding exchanges

Querying exchanges

Explicit routing

Netting Pairs

Netting pairs allow correlated positions across exchanges to offset for risk purposes. This reduces the effective portfolio notional when positions are hedged.

Configuration

Or via the Engine API:

How Netting Works

For each netting pair (market_a, market_b):
  1. Compute the absolute exposure for each market
  2. The hedged amount is min(exposure_a, exposure_b)
  3. Reduce the portfolio notional by hedged * 0.5 (prediction markets are 0-1 priced)
Netting only affects the notional limit risk check. Position limits per market are still enforced independently.

Example

If you hold:
  • 50 contracts on Polymarket will-btc-hit-100k (exposure = 50)
  • 30 contracts on Kalshi KXBTC-25FEB16 (exposure = 30)
The hedged amount is min(50, 30) = 30, reducing notional by 30 * 0.5 = 15.0. This allows larger total positions because 30 contracts are effectively hedged.

Cross-Exchange Strategy Pattern

Unified Position Tracking

The PositionTracker maintains positions from all exchanges in a single map. Each position carries its exchange field:
Similarly, fills and orders track their exchange: