---
title: "Horizon hz.run()"
description: "The single entry point for running a Horizon strategy."
icon: "play"
---

`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

```python
hz.run(
    name: str,                                    # Strategy name
    exchange: Polymarket | Kalshi | None = None,  # Single exchange config
    exchanges: list[Polymarket | Kalshi] | 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

| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `name` | `str` | required | Strategy name (shown in logs and dashboard) |
| `exchange` | `Polymarket \| Kalshi \| None` | `None` | Single exchange config (backward compatible) |
| `exchanges` | `list` | `None` | Multi-exchange configs, e.g. `[Polymarket(...), Kalshi(...)]` |
| `markets` | `list[str]` | `[]` | Market slugs or tickers to trade |
| `feeds` | `dict[str, Feed]` | `{}` | Named feeds, e.g. `{"btc": BinanceWS("btcusdt")}` |
| `pipeline` | `list[Callable]` | required | Pipeline functions. Final fn must return `list[Quote]` |
| `risk` | `Risk \| RiskConfig` | defaults | Risk configuration |
| `interval` | `float` | `0.5` | Seconds between strategy cycles |
| `mode` | `str` | `"paper"` | `"paper"` for simulation, `"live"` for real trading |
| `dashboard` | `bool` | `False` | Enable TUI dashboard |
| `params` | `dict` | `{}` | Custom params accessible via `ctx.params` |
| `db_path` | `str \| None` | auto | SQLite path. Default: `HORIZON_DB` env or `./{name}.db`. Set `None` to disable. |
| `events` | `list[Event]` | `None` | Multi-outcome events (each outcome becomes a market). See [Multi-Outcome Events](/multi-outcome). |
| `netting_pairs` | `list[tuple]` | `None` | Market pairs whose positions offset for risk |
| `api_key` | `str \| None` | `None` | Horizon API key. Falls back to `HORIZON_API_KEY` env var. See [Authentication](/authentication). |

<Warning>
  `exchange` and `exchanges` are mutually exclusive. Use `exchange` for single-exchange mode or `exchanges` for multi-exchange mode.
</Warning>

## Lifecycle

<Steps>
  <Step title="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](/authentication).
  </Step>
  <Step title="Build engine">
    Creates the Engine with exchange backend(s) and risk config. For multi-exchange, the first exchange in the list becomes the primary.
  </Step>
  <Step title="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.
  </Step>
  <Step title="Recover state">
    If persistence is enabled, loads the latest position snapshot and replays fills since that snapshot. Detects orphaned orders from previous runs.
  </Step>
  <Step title="Resolve markets">
    In live mode, fetches market metadata (token IDs, condition IDs) from exchange APIs. For Polymarket, queries the Gamma API. For Kalshi, sets the ticker.
  </Step>
  <Step title="Start feeds">
    Starts all configured feeds (WebSocket connections, REST pollers) on the engine's feed manager.
  </Step>
  <Step title="Sync positions">
    In live mode, syncs positions from each exchange to reconcile local state.
  </Step>
  <Step title="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
  </Step>
  <Step title="Shutdown">
    On `Ctrl+C` or `SIGTERM`: snapshot positions, end run record, cancel all orders (with 5s timeout), stop feeds.
  </Step>
</Steps>

## Persistence Defaults

The `db_path` parameter uses a sentinel value (`...`) to distinguish between "use default" and "explicitly disabled":

| Value | Behavior |
|-------|----------|
| `...` (default) | Use `HORIZON_DB` env var, or `./{name}.db` |
| `"/path/to/db"` | Use the specified path |
| `None` | Disable persistence entirely |

## Examples

### Paper trading (simplest)

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

### Live on Polymarket

```python
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,
)
```

### Multi-outcome events

```python
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](/multi-outcome) for the full guide.

### Multi-exchange with netting

```python
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")],
)
```
