> ## Documentation Index
> Fetch the complete documentation index at: https://mathematicalcompany.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Market Discovery

> Search and discover markets on Polymarket, Kalshi, Limitless, Alpaca, and IBKR. Find markets by keyword, category, volume, and more. Returns Market objects ready for hz.run().

# Market Discovery

Horizon provides built-in market discovery for Polymarket, Kalshi, Limitless, Alpaca, and IBKR. Search by keyword, filter by volume or category, and get back `Market` objects ready to use with `hz.run()`.

<Tip>
  **Prediction Markets:** Polymarket, Kalshi, Limitless
  **Equities & Options:** Alpaca, IBKR
  **Crypto:** Coinbase, Robinhood
</Tip>

<Note>
  Discovery queries are public API calls - no exchange credentials needed. The returned `Market` objects include token IDs, condition IDs, and all metadata needed for trading.
</Note>

## Overview

<CardGroup cols={2}>
  <Card title="Search Markets" icon="magnifying-glass">
    `hz.discover_markets()` searches by keyword with volume, category, and sort filters.
  </Card>

  <Card title="Top Markets" icon="trophy">
    `hz.top_markets()` returns the highest-volume active markets.
  </Card>

  <Card title="Discover Events" icon="calendar">
    `hz.discover_events()` finds multi-outcome events with all outcomes populated.
  </Card>

  <Card title="All Exchanges" icon="arrows-split-up-and-left">
    Works with Polymarket, Kalshi, Limitless, Alpaca, and IBKR.
  </Card>
</CardGroup>

***

## hz.discover\_markets

Search for markets across exchanges.

```python theme={null}
import horizon as hz

# Search Polymarket for election markets
markets = hz.discover_markets(
    exchange="polymarket",
    query="election",
    active=True,
    limit=10,
    min_volume=10000.0,
    sort_by="volume",
)

for m in markets:
    print(f"{m.name}")
    print(f"  ID: {m.id}")
    print(f"  Token IDs: yes={m.yes_token_id}, no={m.no_token_id}")
    print(f"  Condition: {m.condition_id}")
```

### Parameters

| Parameter    | Type    | Default        | Description                                                        |
| ------------ | ------- | -------------- | ------------------------------------------------------------------ |
| `exchange`   | `str`   | `"polymarket"` | `"polymarket"`, `"kalshi"`, `"limitless"`, `"alpaca"`, or `"ibkr"` |
| `query`      | `str`   | `""`           | Search text (market title/slug/ticker)                             |
| `active`     | `bool`  | `True`         | Only return active (open) markets                                  |
| `limit`      | `int`   | `20`           | Max number of results                                              |
| `min_volume` | `float` | `0.0`          | Minimum 24h volume filter (Polymarket only)                        |
| `category`   | `str`   | `""`           | Category/tag filter (exchange-specific)                            |
| `sort_by`    | `str`   | `"volume"`     | Sort order                                                         |

### Sort Options

| Value         | Description                  |
| ------------- | ---------------------------- |
| `"volume"`    | 24h trading volume (default) |
| `"newest"`    | Most recently created        |
| `"liquidity"` | Deepest orderbook liquidity  |
| `""`          | No specific ordering         |

### Exchange-Specific Behavior

**Polymarket:**

* `query` matches market question text and slugs (client-side filtering on `/markets` endpoint)
* `category` uses server-side tag filtering via the `/events` endpoint (e.g., `"crypto"`, `"politics"`, `"sports"`). Returns all markets from matching events. This is the recommended way to browse by topic.
* `min_volume` filters markets below the USD threshold
* Returns `Market` objects with `yes_token_id`, `no_token_id`, and `condition_id`

**Kalshi:**

* `query` with an uppercase ticker prefix (e.g., `"KXBTC"`) uses server-side series filtering for fast results. Lowercase text queries use client-side matching over paginated results, which may miss markets deep in the listing.
* `category` is the recommended way to search Kalshi. Maps to series tickers (e.g., `"KXBTC"` for all BTC markets, `"KXETH"` for ETH, `"INX"` for S\&P 500).
* `min_volume` is not supported (Kalshi API limitation)
* Returns `Market` objects with `ticker` field

<Tip>
  For Kalshi, prefer `category` over `query` for reliable results. Kalshi's API doesn't support server-side text search, so text queries only scan a limited number of markets.
</Tip>

**Alpaca:**

* Searches the Alpaca assets API (`/v2/assets`) for US equities
* `query` matches symbol or company name (client-side)
* Returns `Market` objects with `exchange="alpaca"` and `asset_class="equity"`
* Requires `APCA_API_KEY_ID` and `APCA_API_SECRET_KEY` environment variables

**IBKR:**

* Searches via the IBKR symbol search endpoint
* `query` matches symbol or company name
* Returns `Market` objects with `exchange="ibkr"` and contract ID as the market ID
* Requires IBKR Client Portal or TWS running locally

***

## hz.top\_markets

Convenience wrapper to get the highest-volume active markets.

```python theme={null}
# Top 10 Polymarket markets by volume
markets = hz.top_markets(limit=10)

# Top Kalshi markets in crypto category
markets = hz.top_markets(exchange="kalshi", category="KXBTC", limit=5)
```

| Parameter  | Type  | Default        | Description       |
| ---------- | ----- | -------------- | ----------------- |
| `exchange` | `str` | `"polymarket"` | Exchange to query |
| `limit`    | `int` | `10`           | Number of markets |
| `category` | `str` | `""`           | Category filter   |

Equivalent to `discover_markets(active=True, sort_by="volume", ...)`.

***

## hz.discover\_events

Discover multi-outcome events from Polymarket. Returns `Event` objects with all outcomes populated, including YES/NO token IDs and current prices.

```python theme={null}
events = hz.discover_events(
    query="election",
    active=True,
    limit=5,
    min_volume=50000.0,
)

for event in events:
    print(f"\n{event.name} ({event.outcome_count()} outcomes)")
    for outcome in event.outcomes:
        print(f"  {outcome.name}: {outcome.yes_price:.2f}")

    # Convert to Market objects for trading
    markets = event.to_markets()
```

| Parameter    | Type    | Default        | Description                             |
| ------------ | ------- | -------------- | --------------------------------------- |
| `exchange`   | `str`   | `"polymarket"` | Currently only `"polymarket"` supported |
| `query`      | `str`   | `""`           | Search text                             |
| `active`     | `bool`  | `True`         | Only active events                      |
| `limit`      | `int`   | `10`           | Max events                              |
| `min_volume` | `float` | `0.0`          | Minimum total volume across outcomes    |

<Note>
  Event discovery only returns events with 2+ outcomes. Single-outcome binary markets are returned by `discover_markets()` instead.
</Note>

***

## Using Discovered Markets with hz.run

The main purpose of discovery is to get `Market` objects for trading:

```python theme={null}
import horizon as hz

# Discover a market
markets = hz.discover_markets(query="bitcoin", limit=1)
market = markets[0]

# Use it directly in hz.run()
def fair_value(ctx):
    return ctx.feeds["book"].price or 0.5

def quoter(ctx, fair):
    return hz.quotes(fair, spread=0.04, size=5)

hz.run(
    name="discovered_strategy",
    markets=[market],
    feeds={"book": hz.PolymarketBook(market.id)},
    pipeline=[fair_value, quoter],
    risk=hz.Risk(max_position=100),
)
```

### Using Discovered Events

```python theme={null}
import horizon as hz

events = hz.discover_events(query="bitcoin", limit=1)
event = events[0]

# Register the event and trade all outcomes
markets = event.to_markets()
market_ids = [m.id for m in markets]

hz.run(
    name="event_strategy",
    markets=markets,
    feeds={m.id: hz.PolymarketBook(m.id) for m in markets},
    pipeline=[fair_value, quoter],
    risk=hz.Risk(max_position=100),
)
```

***

## Examples

### Scanning for High-Volume Opportunities

```python theme={null}
import horizon as hz

# Find liquid markets on both exchanges
poly_markets = hz.top_markets("polymarket", limit=20)
kalshi_markets = hz.top_markets("kalshi", limit=20)

print("=== Polymarket ===")
for m in poly_markets:
    print(f"  {m.name[:60]} ({m.id})")

print("\n=== Kalshi ===")
for m in kalshi_markets:
    print(f"  {m.name[:60]} ({m.ticker})")
```

### Category-Based Discovery

```python theme={null}
import horizon as hz

# Polymarket categories: politics, crypto, sports, etc.
crypto_markets = hz.discover_markets(
    exchange="polymarket",
    category="crypto",
    min_volume=5000,
    limit=10,
)

# Kalshi series: KXBTC, KXETH, etc.
btc_markets = hz.discover_markets(
    exchange="kalshi",
    category="KXBTC",
    limit=10,
)
```

### Finding Cross-Exchange Pairs

```python theme={null}
import horizon as hz

# Find matching markets on both exchanges
poly = hz.discover_markets("polymarket", query="bitcoin", limit=5)
kalshi = hz.discover_markets("kalshi", query="KXBTC", limit=5)

print("Polymarket:")
for m in poly:
    print(f"  {m.id}: {m.name}")

print("Kalshi:")
for m in kalshi:
    print(f"  {m.ticker}: {m.name}")

# Use for cross-exchange arb
# hz.run(markets=[poly[0], kalshi[0]], exchanges=[...], ...)
```

### Event Parity Check

```python theme={null}
import horizon as hz

events = hz.discover_events(limit=5, min_volume=100000)

for event in events:
    prices = [o.yes_price for o in event.outcomes]
    total = sum(prices)
    print(f"{event.name}")
    print(f"  Outcomes: {event.outcome_count()}")
    print(f"  Price sum: {total:.3f} (should be ~1.0)")
    if abs(total - 1.0) > 0.03:
        print(f"  *** Potential arb: {abs(total - 1.0):.3f} overround ***")
```

### Equity Discovery

```python theme={null}
import horizon as hz

# Search for stocks on Alpaca
stocks = hz.discover_markets(exchange="alpaca", query="AAPL", limit=5)
for s in stocks:
    print(f"{s.ticker}: {s.name} (asset_class={s.asset_class})")

# Search IBKR for options
contracts = hz.discover_markets(exchange="ibkr", query="AAPL", limit=10)
```

<Warning>
  Discovery results depend on exchange API availability. If the Polymarket Gamma API or Kalshi API is down, functions return empty lists and log an error. Always check the returned list length before indexing.
</Warning>
