Skip to main content
hz.run() is the single entry point for running a strategy. It creates the engine, starts feeds, recovers state, syncs positions, and enters the main loop.

Full Signature

hz.run(
    name: str,                                    # Strategy name
    exchange: Exchange | None = None,             # Single exchange config
    exchanges: list[Exchange] | None = None,      # Multi-exchange configs
    markets: list[str] | None = None,             # Market IDs/slugs
    feeds: dict[str, Feed] | None = None,         # Named feeds
    pipeline: list[Callable] | None = None,       # Pipeline functions
    risk: Risk | RiskConfig | None = None,        # Risk limits
    interval: float = 0.5,                        # Seconds between cycles
    mode: str = "paper",                          # "paper" or "live"
    dashboard: bool = False,                      # Show TUI dashboard
    params: dict[str, Any] | None = None,         # Custom params → ctx.params
    db_path: str | None = ...,                    # SQLite path (... = auto)
    events: list[Event] | None = None,                   # Multi-outcome events
    netting_pairs: list[tuple[str, str]] | None = None,  # Position netting
    api_key: str | None = None,  # API key (or use HORIZON_API_KEY env)
)

Parameters

ParameterTypeDefaultDescription
namestrrequiredStrategy name (shown in logs and dashboard)
exchangeExchange or NoneNoneSingle exchange config: Polymarket(...), Kalshi(...), Alpaca(...), IBKR(...), Coinbase(...), Robinhood(...)
exchangeslistNoneMulti-exchange configs, e.g. [Polymarket(...), Alpaca(...)]
marketslist[str][]Market slugs or tickers to trade
feedsdict[str, Feed]{}Named feeds, e.g. {"btc": BinanceWS("btcusdt")}
pipelinelist[Callable]requiredPipeline functions. Final fn must return list[Quote]
riskRisk or RiskConfigdefaultsRisk configuration
intervalfloat0.5Seconds between strategy cycles
modestr"paper""paper" for simulation, "live" for real trading
dashboardboolFalseEnable TUI dashboard
paramsdict{}Custom params accessible via ctx.params
db_pathstr or NoneautoSQLite path. Default: HORIZON_DB env or ./{name}.db. Set None to disable.
eventslist[Event]NoneMulti-outcome events (each outcome becomes a market). See Multi-Outcome Events.
netting_pairslist[tuple]NoneMarket pairs whose positions offset for risk
api_keystr or NoneNoneHorizon API key. Falls back to HORIZON_API_KEY env var. See Authentication.
exchange and exchanges are mutually exclusive. Use exchange for single-exchange mode or exchanges for multi-exchange mode.

Lifecycle

1

Validate API key

Validates the Horizon API key (from api_key param or HORIZON_API_KEY env var). Uses a local cache at ~/.horizon/license.json - only makes a network call if the cache is expired or missing. See Authentication.
2

Build engine

Creates the Engine with exchange backend(s) and risk config. For multi-exchange, the first exchange in the list becomes the primary.
3

Register events and netting pairs

If events is provided, each event is registered on the engine and its outcomes are expanded into markets. If netting_pairs is provided, registers each pair on the engine for cross-exchange hedging.
4

Recover state

If persistence is enabled, loads the latest position snapshot and replays fills since that snapshot. Detects orphaned orders from previous runs.
5

Resolve markets

In live mode, fetches market metadata from exchange APIs. For Polymarket, queries the Gamma API for token IDs and condition IDs. For Kalshi, sets the ticker. For Alpaca/IBKR, resolves symbols directly.
6

Start feeds

Starts all configured feeds (WebSocket connections, REST pollers) on the engine’s feed manager.
7

Sync positions

In live mode, syncs positions from each exchange to reconcile local state.
8

Main loop

Each cycle:
  • Poll fills from live exchanges
  • Update daily P&L for drawdown tracking
  • For each market: build context → run pipeline → process result
  • Cancel stale orders → submit new quotes
  • Snapshot positions to DB every 50 cycles
  • Evict terminal orders every 100 cycles
9

Shutdown

On Ctrl+C or SIGTERM: snapshot positions, end run record, cancel all orders (with 5s timeout), stop feeds.

Persistence Defaults

The db_path parameter uses a sentinel value (...) to distinguish between “use default” and “explicitly disabled”:
ValueBehavior
... (default)Use HORIZON_DB env var, or ./{name}.db
"/path/to/db"Use the specified path
NoneDisable persistence entirely

Examples

Paper trading (simplest)

hz.run(
    name="simple",
    markets=["test-market"],
    pipeline=[fair_value, quoter],
    risk=hz.Risk(max_position=100),
)

Live on Polymarket

hz.run(
    name="poly_mm",
    exchange=hz.Polymarket(private_key="0x..."),
    markets=["will-btc-hit-100k"],
    feeds={"btc": hz.BinanceWS("btcusdt")},
    pipeline=[fair_value, quoter],
    risk=hz.Risk(max_position=100),
    mode="live",
    dashboard=True,
)

Equity trading on Alpaca

hz.run(
    name="aapl_mm",
    exchange=hz.Alpaca(paper=True),
    markets=[hz.Market(id="AAPL", exchange="alpaca", ticker="AAPL", asset_class="equity")],
    feeds={"aapl": hz.AlpacaFeed(symbols=["AAPL"])},
    pipeline=[fair_value, quoter],
    risk=hz.Risk.equity(max_position=100, max_notional=50_000),
    mode="live",
)

Multi-asset (prediction market + stocks)

hz.run(
    name="hedged",
    exchanges=[hz.Polymarket(private_key="0x..."), hz.Alpaca(paper=True)],
    markets=[
        hz.Market(id="will-fed-cut", exchange="polymarket"),
        hz.Market(id="TLT", exchange="alpaca", asset_class="equity"),
    ],
    feeds={
        "rates": hz.PolymarketBook("will-fed-cut"),
        "tlt": hz.AlpacaFeed(symbols=["TLT"]),
    },
    pipeline=[cross_asset_strategy],
    risk=hz.Risk(max_position=100, price_min=0.01, price_max=1000),
    mode="live",
)

Multi-outcome events

events = hz.discover_events("polymarket", query="election", limit=5)

hz.run(
    name="event_mm",
    exchange=hz.Polymarket(private_key="0x..."),
    events=events,
    pipeline=[fair_value, quoter],
    risk=hz.RiskConfig(max_position_per_market=50, max_position_per_event=150),
    mode="live",
)
See Multi-Outcome Events for the full guide.

Multi-exchange with netting

hz.run(
    name="cross_venue",
    exchanges=[
        hz.Polymarket(private_key="0x..."),
        hz.Kalshi(api_key="..."),
    ],
    markets=["btc-100k-poly", "KXBTC-25FEB16"],
    feeds={"btc": hz.BinanceWS("btcusdt")},
    pipeline=[fair_value, quoter],
    risk=hz.Risk(max_position=50),
    mode="live",
    netting_pairs=[("btc-100k-poly", "KXBTC-25FEB16")],
)