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

# Compare Prices Across Venues

> Compare prices across venues and find related markets.

Use [matching](/router/matching) to find cross-venue clusters, then use
price helpers or venue exchanges to inspect executable bid/ask data.

## compareMarketPrices

Side-by-side bid/ask for identity matches in the hosted catalog.

This returns a flat side-by-side view for one anchored market. For
cluster-first discovery across the whole catalog, use
[`fetchMatchedMarketClusters`](/router/matching#fetchmatchedmarketclusters).

<Tabs>
  <Tab title="Python">
    ```python theme={null}
    import pmxt

    router = pmxt.Router(pmxt_api_key="pmxt_live_...")

    prices = router.compare_market_prices(market_id="d35bc8c6-...")

    for p in prices:
        print(f"{p.venue:12s}  bid {p.best_bid:.2f}  ask {p.best_ask:.2f}")
    ```

    ```
    polymarket    bid 0.60  ask 0.65
    kalshi        bid 0.58  ask 0.63
    limitless     bid 0.61  ask 0.66
    ```
  </Tab>

  <Tab title="TypeScript">
    ```typescript theme={null}
    import pmxt from "pmxtjs";

    const router = new pmxt.Router({ pmxtApiKey: "pmxt_live_..." });

    const prices = await router.compareMarketPrices({
      marketId: "d35bc8c6-...",
    });

    for (const p of prices) {
      console.log(`${p.venue.padEnd(12)}  bid ${p.bestBid}  ask ${p.bestAsk}`);
    }
    ```
  </Tab>
</Tabs>

### Response shape

```json theme={null}
{
  "market": { "marketId": "...", "title": "..." },
  "relation": "identity",
  "confidence": 0.95,
  "reasoning": "Same resolution condition.",
  "bestBid": 0.60,
  "bestAsk": 0.65,
  "venue": "kalshi"
}
```

## fetchRelatedMarkets

Find related markets across venues — markets whose resolution condition
is a subset or superset of the target market.

<Tabs>
  <Tab title="Python">
    ```python theme={null}
    # Positional dict, camelCase keys. Returns a list of plain dicts
    # (not typed dataclasses like compare_market_prices).
    related = router.fetch_related_markets({"marketId": "d35bc8c6-..."})

    for r in related:
        print(f"{r['relation']:10s} {r['venue']:12s}  {r['market']['title']}")
        print(f"           confidence {r['confidence']:.0%}  bid {r['bestBid']}  ask {r['bestAsk']}")
        print(f"           {r['reasoning']}")
    ```

    ```
    subset     kalshi        Will the nominee be from California?
               confidence 85%  bid 0.40  ask 0.45
               Narrower market — California origin implies Democratic candidacy.
    superset   polymarket    Will a Democrat win the popular vote?
               confidence 72%  bid 0.70  ask 0.73
               Broader — popular vote does not guarantee election win.
    ```
  </Tab>

  <Tab title="TypeScript">
    ```typescript theme={null}
    const related = await router.fetchRelatedMarkets({ marketId: "d35bc8c6-..." });

    for (const r of related) {
      console.log(r.relation, r.venue, r.market.title);
      console.log(`  confidence: ${r.confidence}  reasoning: ${r.reasoning}`);
    }
    ```
  </Tab>
</Tabs>

Subset means A=YES implies B=YES but not vice versa. Superset is the
reverse. See [relation types](/router/matching#relation-types) for the
full taxonomy.

<Info>
  `fetchRelatedMarkets` filters to `subset` and `superset` relations.
  If you want all relation types, use
  [`fetchMatchedMarketClusters`](/router/matching#fetchmatchedmarketclusters)
  with `relation` or `relations` filters.
</Info>

## fetchMatchedMarketClusters

Returns matched market clusters across venues. Use this when you want
the full group of markets first, then inspect prices and outcomes inside
each venue market.

<Tabs>
  <Tab title="Python">
    ```python theme={null}
    clusters = router.fetch_matched_market_clusters(
        relation="identity",
        sort="volume",
        min_venues=2,
        limit=20,
    )

    for cluster in clusters:
        print(cluster.canonical_title)
        for market in cluster.markets:
            price = market.yes.price if market.yes else None
            print(f"  {market.source_exchange:12s} @ {price}")
    ```

    ```
    Will BTC hit $100k by Dec 31?
      polymarket   @ 0.58
      kalshi       @ 0.63
    ```
  </Tab>

  <Tab title="TypeScript">
    ```typescript theme={null}
    const clusters = await router.fetchMatchedMarketClusters({
      relation: "identity",
      sort: "volume",
      minVenues: 2,
      limit: 20,
    });

    for (const cluster of clusters) {
      console.log(cluster.canonicalTitle);
      for (const market of cluster.markets) {
        console.log(`  ${market.sourceExchange} @ ${market.yes?.price}`);
      }
    }
    ```
  </Tab>
</Tabs>

### Parameters

| Parameter   | Type      | Default  | Notes                                           |
| ----------- | --------- | -------- | ----------------------------------------------- |
| `relation`  | `string`  | —        | Filter to a relation type such as `identity`.   |
| `minVenues` | `integer` | —        | Require at least this many venues in a cluster. |
| `sort`      | `string`  | `volume` | Sort by `volume` or `confidence`.               |
| `limit`     | `integer` | `50`     | Maximum number of clusters to return.           |

Results are cluster-first. PMXT does not pick a best opportunity for
you; inspect the returned markets, outcomes, and order books before
deciding how to trade.

### Response shape

```json theme={null}
{
  "clusterId": "mcl_...",
  "canonicalTitle": "Will BTC hit $100k by Dec 31?",
  "relations": ["identity"],
  "confidence": 0.98,
  "markets": [
    { "marketId": "pm_...", "sourceExchange": "polymarket", "title": "Will BTC hit $100k by Dec 31?" },
    { "marketId": "kalshi_...", "sourceExchange": "kalshi", "title": "Bitcoin above $100,000 on Dec 31" }
  ]
}
```

## Composing with venue exchanges

The Router is read-only. To trade, use venue-specific exchange
classes with the local SDK. A typical workflow:

```python theme={null}
import pmxt

router = pmxt.Router(pmxt_api_key="pmxt_live_...")
poly   = pmxt.Polymarket(private_key="0x...")

# 1. Browse matched market clusters across venues
clusters = router.fetch_matched_market_clusters(relation="identity", limit=20)

# 2. Direct self-hosted calls need IDs returned by the venue client.
# Re-fetch or reverse-resolve the matched venue market, then verify the outcome.
polymarket_match = next(m for m in clusters[0].markets if m.source_exchange == "polymarket")
venue_markets = poly.fetch_markets({"query": polymarket_match.title, "limit": 1})
venue_outcome = venue_markets[0].outcomes[0]  # verify label/slug before trading

# 3. Inspect the order book on that venue with its native outcome ID
book = poly.fetch_order_book(outcome_id=venue_outcome.outcome_id)

# 4. Place an order locally (never proxied through PMXT)
# poly.create_order(...)
```

Router returns the same [unified schema](/concepts/unified-schema) as
every other exchange, but identifiers keep their address space. Catalog
IDs work for Router and hosted flows; direct self-hosted venue calls need
venue-native IDs returned by that venue client.
