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

# Cross-Venue Arbitrage

> Find price discrepancies across prediction markets

## The Opportunity

The same prediction market often exists on multiple venues (Polymarket, Kalshi) with different prices. These discrepancies create arbitrage opportunities.

Market Motion helps you:

1. **Discover** markets across venues
2. **Match** equivalent markets via entity connections
3. **Compare** prices programmatically

## Finding Cross-Venue Markets

### By Search

```javascript theme={null}
async function findCrossVenueMarkets(query) {
  const [polymarket, kalshi] = await Promise.all([
    fetch(`/api/markets/search?q=${query}&venue=polymarket`),
    fetch(`/api/markets/search?q=${query}&venue=kalshi`)
  ]);

  return {
    polymarket: await polymarket.json(),
    kalshi: await kalshi.json()
  };
}

const markets = await findCrossVenueMarkets('chiefs super bowl');
```

### By Entity

More reliable—same entity links to markets on all venues:

```javascript theme={null}
async function getEntityMarkets(entitySlug) {
  const response = await fetch(`/api/entities/${entitySlug}`);
  const { entity } = await response.json();

  // Group markets by venue
  const byVenue = {};
  for (const exposure of entity.marketExposures || []) {
    const venue = exposure.market.venue;
    if (!byVenue[venue]) byVenue[venue] = [];
    byVenue[venue].push(exposure.market);
  }

  return byVenue;
}

const markets = await getEntityMarkets('kansas-city-chiefs');
// { polymarket: [...], kalshi: [...] }
```

## Price Comparison

```python theme={null}
import requests
from dataclasses import dataclass

@dataclass
class ArbitrageOpportunity:
    entity: str
    market_title: str
    polymarket_price: float
    kalshi_price: float
    spread: float

def find_arbitrage(entity_slug: str, threshold: float = 0.03):
    """Find arbitrage opportunities for an entity's markets."""

    response = requests.get(f"{BASE_URL}/entities/{entity_slug}")
    entity = response.json()["entity"]

    # Group market exposures by similar titles
    markets_by_title = {}
    for exposure in entity.get("marketExposures", []):
        market = exposure["market"]
        # Normalize title for matching
        key = market["title"].lower().strip()
        if key not in markets_by_title:
            markets_by_title[key] = {}
        markets_by_title[key][market["venue"]] = market

    opportunities = []

    for title, venues in markets_by_title.items():
        if "polymarket" in venues and "kalshi" in venues:
            poly_price = venues["polymarket"].get("price", 0)
            kalshi_price = venues["kalshi"].get("price", 0)
            spread = abs(poly_price - kalshi_price)

            if spread > threshold:
                opportunities.append(ArbitrageOpportunity(
                    entity=entity["displayName"],
                    market_title=title,
                    polymarket_price=poly_price,
                    kalshi_price=kalshi_price,
                    spread=spread
                ))

    return opportunities
```

## Systematic Scanning

```python theme={null}
def scan_category_for_arbitrage(category: str):
    """Scan all entities in a category for arbitrage opportunities."""

    # Get all entities in category
    response = requests.get(
        f"{BASE_URL}/entities",
        params={"category": category, "limit": 100}
    )
    entities = response.json()["items"]

    all_opportunities = []

    for entity in entities:
        opps = find_arbitrage(entity["slug"])
        all_opportunities.extend(opps)

    # Sort by spread size
    all_opportunities.sort(key=lambda x: x.spread, reverse=True)

    return all_opportunities

# Find all sports arbitrage opportunities
opps = scan_category_for_arbitrage("sports")
for opp in opps[:10]:
    print(f"{opp.market_title}")
    print(f"  Polymarket: {opp.polymarket_price:.2%}")
    print(f"  Kalshi: {opp.kalshi_price:.2%}")
    print(f"  Spread: {opp.spread:.2%}")
```

## Market Matching Challenges

<Warning>
  Markets on different venues may have:

  * Slightly different titles
  * Different resolution criteria
  * Different expiration times

  Always verify markets are truly equivalent before trading.
</Warning>

### Matching Strategies

| Strategy                | Pros                  | Cons                        |
| ----------------------- | --------------------- | --------------------------- |
| **Title similarity**    | Simple                | False matches possible      |
| **Entity-based**        | Reliable entity match | Markets may differ in scope |
| **Manual verification** | Most accurate         | Doesn't scale               |

### Recommended Approach

```python theme={null}
def are_markets_equivalent(market_a, market_b):
    """Check if two markets are truly equivalent for arbitrage."""

    # Same entity exposure
    if market_a.get("entity_slug") != market_b.get("entity_slug"):
        return False

    # Similar expiration (within 24 hours)
    exp_a = parse_date(market_a.get("endDate"))
    exp_b = parse_date(market_b.get("endDate"))
    if abs((exp_a - exp_b).days) > 1:
        return False

    # Title similarity > 80%
    similarity = calculate_similarity(
        market_a["title"].lower(),
        market_b["title"].lower()
    )
    if similarity < 0.8:
        return False

    return True
```

## Execution Considerations

Market Motion is for **discovery and context**, not execution. For arbitrage:

<Steps>
  <Step title="Discover with Market Motion">
    Find cross-venue opportunities using entity-market links
  </Step>

  <Step title="Verify manually or programmatically">
    Confirm markets are truly equivalent
  </Step>

  <Step title="Execute on venue APIs">
    Use Polymarket and Kalshi APIs directly for trading
  </Step>

  <Step title="Monitor with Market Motion">
    Track entity attributes for signals to close positions
  </Step>
</Steps>

## Dedicated Arbitrage Endpoints

For systematic arbitrage, use the dedicated endpoints instead of manual searching:

* [`/api/markets/cross-venue`](/api/reference/markets#cross-venue-markets) — Pre-matched cross-venue markets with spread data
* [`/api/graph/mispricings`](/api/reference/graph#mispricings) — AI-detected mispricings with actionable signals
* [`/api/graph/mispricings/top`](/api/reference/graph#top-mispricings) — Highest-spread opportunities
* [`/api/graph/mispricings/check/:outcomeId`](/api/reference/graph#check-mispricing) — Check a specific outcome

## Real-Time Considerations

<Note>
  Market Motion caches market data. For real-time arbitrage execution, use venue APIs directly for price quotes. Use Market Motion for:

  * Discovering which markets to monitor
  * Understanding entity relationships
  * Tracking attribute changes that affect prices
</Note>
