Standard sports arbitrage compares odds across sportsbooks — DraftKings vs. FanDuel vs. BetMGM. Cross-market arbitrage goes further: it compares pricing across entirely different types of platforms — sportsbooks, prediction markets like Polymarket and Kalshi, and betting exchanges.

These different market types have different participants, different fee structures, different liquidity profiles, and different settlement mechanisms. The result: larger and more persistent pricing gaps than you’ll find between two sportsbooks.

This guide covers how to find and exploit these gaps, with working code you can run today.

Prerequisites: Familiarity with how sportsbook arb works, basic understanding of Polymarket and Kalshi APIs.


The Cross-Market Opportunity

Why Different Markets Misprice the Same Event

A sportsbook and a prediction market can price the same event differently because they serve different audiences and operate under different constraints:

FactorSportsbooksPrediction Markets
ParticipantsRecreational bettors, sports fansTraders, analysts, political junkies
Pricing mechanismOdds set by traders/algorithms, adjusted by actionOrder book driven by supply/demand
Information speedReact to sharp money within secondsReact to news, polls, model outputs
LiquidityDeep on popular sports, thin on nicheVaries — deep on elections, thin on sports
RegulationState-by-state gambling licensesCFTC-regulated (Kalshi), crypto-native (Polymarket)
Time horizonGame-day or short-term futuresWeeks to months for political/economic events

These structural differences mean that when a piece of news breaks — a key player injury, a new poll, an economic report — the two market types may take minutes or hours to converge. In traditional sports arb, the window is seconds.

Event Categories with Cross-Market Overlap

Not all events are priced on both sportsbooks and prediction markets. The overlap is concentrated in specific categories:

CategorySportsbook MarketsPrediction MarketsArb Potential
U.S. ElectionsFutures (president, senate)Deep liquidity on Polymarket + KalshiHigh — different information sources
Championship WinnersFutures (Super Bowl, NBA Finals)Available on both platformsMedium — sportsbooks have more liquidity
AwardsMVP, Heisman, OscarGrowing on PolymarketMedium — sportsbooks price earlier
Economic IndicatorsLimited (some offshore)Kalshi specializes hereLow — limited sportsbook coverage
GeopoliticsRareActive on PolymarketLow — few sportsbook markets

The highest-value cross-market arbs typically appear in U.S. elections and championship futures — events with deep liquidity on both sides.

Scale of the Opportunity

Research from IMDEA Networks Institute (arXiv:2508.03474, 2025) analyzed on-chain Polymarket trading data during the 2024 U.S. election cycle and found:

  • Traders captured over $40 million in arbitrage profits from cross-market pricing discrepancies
  • Systematic price differences between Polymarket and other markets persisted for hours
  • A small number of sophisticated traders accounted for the majority of arb profits
  • Arb activity increased market efficiency over time but never fully eliminated pricing gaps

This isn’t theoretical — cross-market arb is a proven, profitable strategy at meaningful scale.


How Different Markets Price the Same Event

To detect arbs, you need to normalize prices across platforms into a common format. Each platform uses a different representation.

Price Format Comparison

PlatformFormatExample: “60% chance”Range
PolymarketPrice per share (0–1)$0.60 per YES share$0.01 – $0.99
KalshiPrice in cents (1–99)60¢ per YES contract1¢ – 99¢
Sportsbooks (fav)American odds (negative)-150-10000 to -100
Sportsbooks (dog)American odds (positive)+150+100 to +10000
SportsbooksDecimal odds1.6671.01 – 100+

Normalizing to Implied Probability

def polymarket_to_implied(price: float) -> float:
    """Polymarket price (0-1) is already implied probability."""
    return price

def kalshi_to_implied(price_cents: float) -> float:
    """Kalshi price in cents to implied probability."""
    return price_cents / 100

def american_to_implied(odds: float) -> float:
    """American odds to implied probability."""
    if odds > 0:
        return 100 / (odds + 100)
    return abs(odds) / (abs(odds) + 100)

def decimal_to_implied(odds: float) -> float:
    """Decimal odds to implied probability."""
    return 1 / odds

Once all prices are expressed as implied probabilities, cross-market comparison becomes straightforward addition.


Fee Structure Comparison

Fees are the silent killer of cross-market arbs. A 3% arb looks great until you realize 2.5% goes to fees.

PlatformFee TypeFee AmountApplied When
PolymarketTrading fee~2% on net winningsWhen you sell or contract resolves in your favor
KalshiTrading fee$0 on contractsFree to trade (fee in the spread)
DraftKingsVig (built into odds)4–10% overroundAlready reflected in the odds
FanDuelVig (built into odds)4–8% overroundAlready reflected in the odds
Betfair ExchangeCommission2–5% on net winningsWhen you win

Minimum Arb Threshold by Platform Combination

Given these fees, here’s the minimum arb percentage needed for profit:

Platform CombinationMinimum Arb % for ProfitNotes
Sportsbook ↔ Sportsbook~0.5%Vig already in odds
Sportsbook ↔ Polymarket~2.5%Polymarket’s 2% fee on wins
Sportsbook ↔ Kalshi~0.5%Kalshi has no explicit fee
Polymarket ↔ Kalshi~2.0%Polymarket fee only
Sportsbook ↔ Betfair~3.0%Both have win-based fees

Fee-Adjusted Arb Check

def check_cross_market_arb(
    implied_probs: list[float],
    platform_fees: list[float],
    total_stake: float = 1000,
) -> dict:
    """Check for cross-market arb with fee adjustment.

    Args:
        implied_probs: Implied probability for each outcome (from best source).
        platform_fees: Fee percentage for each outcome's platform (e.g., [0, 2] for
                       sportsbook=0%, Polymarket=2%).
        total_stake: Total capital to deploy.

    Returns:
        Dict with arb status, stakes, and profit after fees.
    """
    total_implied = sum(implied_probs)

    # Calculate stakes (proportional to implied probability)
    stakes = [total_stake * (ip / total_implied) for ip in implied_probs]

    # Calculate payouts after platform fees
    payouts = []
    for i, (ip, fee) in enumerate(zip(implied_probs, platform_fees)):
        gross_payout = stakes[i] / ip  # payout if this outcome wins
        net_payout = gross_payout - (gross_payout - stakes[i]) * (fee / 100)
        payouts.append(net_payout)

    # Profit is minimum payout minus total stake
    profits = [p - total_stake for p in payouts]
    min_profit = min(profits)

    return {
        "is_arb": min_profit > 0,
        "arb_pct_raw": ((1 / total_implied) - 1) * 100,
        "profit_after_fees": min_profit,
        "roi": (min_profit / total_stake) * 100,
        "stakes": stakes,
        "payouts": payouts,
    }

Settlement Rules and Resolution Risk

The biggest hidden risk in cross-market arb isn’t fees — it’s settlement mismatch. Different platforms can resolve the same real-world event differently.

How Each Platform Resolves

PlatformResolution AuthorityTypical TimelineDispute Process
PolymarketUMA optimistic oracleHours to days after eventCommunity-driven challenge system
KalshiInternal resolution teamMinutes to hoursCFTC-regulated process
SportsbooksInternal oddsmakersMinutes after eventCustomer support

Resolution Mismatch Example

Consider the market: “Will [Candidate] win the 2028 Presidential Election?”

ScenarioSportsbookPolymarketKalshi
Candidate wins, clean resultSettles YES within hoursSettles YES within 1-2 daysSettles YES within hours
Contested election, recountMay void/suspend betsWaits for UMA oracle resolutionWaits for official certification
Candidate wins but is later disqualifiedAlready settled and paidMay trigger resolution challengeDepends on market terms

The time difference alone creates capital lock-up risk: your sportsbook bet pays out in hours, but your Polymarket hedge might be locked for days.

Mitigating Resolution Risk

  1. Read the settlement rules for every market on every platform before entering a position
  2. Focus on events with clear, binary outcomes — “Who wins?” is cleaner than “Will X happen by Y date?”
  3. Avoid events with subjective resolution — “Will the economy improve?” has too much interpretation room
  4. Factor in capital lock-up costs — money locked in a Polymarket position for 30 days has an opportunity cost
  5. Size positions conservatively — leave room for the possibility of a settlement disagreement

The Full Data Stack

Cross-market arb requires data from three distinct source types. Here’s the architecture:

┌─────────────────────────────────────────────────────────────────────┐
│                     CROSS-MARKET ARB ENGINE                        │
├─────────────────────────────────────────────────────────────────────┤
│                                                                     │
│  ┌─────────────────┐  ┌─────────────────┐  ┌─────────────────┐    │
│  │   SPORTSBOOKS   │  │   POLYMARKET    │  │     KALSHI      │    │
│  │  (TheOddsPulse) │  │  (CLOB + Gamma) │  │   (REST API)    │    │
│  │                 │  │                 │  │                 │    │
│  │  10+ books      │  │  Binary prices  │  │  Binary prices  │    │
│  │  Normalized     │  │  Order book     │  │  Order book     │    │
│  │  odds + lines   │  │  depth          │  │  depth          │    │
│  └────────┬────────┘  └────────┬────────┘  └────────┬────────┘    │
│           │                    │                     │             │
│           ▼                    ▼                     ▼             │
│  ┌─────────────────────────────────────────────────────────────┐   │
│  │              EVENT MATCHING + NORMALIZATION                  │   │
│  │  Match same real-world event across platforms                │   │
│  │  Normalize all prices to implied probability (0-1)          │   │
│  └──────────────────────────┬──────────────────────────────────┘   │
│                             │                                      │
│                             ▼                                      │
│  ┌─────────────────────────────────────────────────────────────┐   │
│  │              ARB DETECTION + STAKE CALCULATION               │   │
│  │  Find best price per outcome across all platforms            │   │
│  │  Calculate fee-adjusted arb percentage                       │   │
│  │  Compute optimal stake distribution                          │   │
│  └─────────────────────────────────────────────────────────────┘   │
│                                                                     │
└─────────────────────────────────────────────────────────────────────┘

Sportsbook Layer: TheOddsPulse

For the sportsbook side, you need normalized odds from multiple books. TheOddsPulse aggregates real-time odds from 10+ sportsbooks into a single API, handling all the normalization:

import requests

def get_sportsbook_odds(sport: str, event_type: str = "futures") -> list[dict]:
    """Fetch odds from multiple sportsbooks via TheOddsPulse."""
    response = requests.get(
        "https://api.theoddspulse.com/v1/odds",
        params={
            "sport": sport,
            "markets": event_type,
            "oddsFormat": "decimal",
            "apiKey": "your-key",
        }
    )
    return response.json()

Prediction Market Layer: Polymarket + Kalshi

def get_polymarket_price(token_id: str) -> float:
    """Get Polymarket midpoint price for a token."""
    response = requests.get(
        "https://clob.polymarket.com/midpoint",
        params={"token_id": token_id}
    )
    return float(response.json()["mid"])

def get_kalshi_price(ticker: str) -> dict:
    """Get Kalshi market price."""
    response = requests.get(
        f"https://api.elections.kalshi.com/trade-api/v2/markets/{ticker}"
    )
    market = response.json()["market"]
    return {
        "yes_price": market.get("yes_ask", 0) / 100,
        "no_price": market.get("no_ask", 0) / 100,
    }

For detailed API usage, see the Polymarket API Guide and Kalshi API Guide.

The Event Matching Challenge

The hardest part of cross-market arb isn’t the math — it’s matching the same real-world event across platforms. Each platform names markets differently:

Real EventPolymarketKalshiSportsbook
Super Bowl winner“Who will win Super Bowl LX?”SUPERBOWL-LX-WINNERNFL > Futures > Super Bowl Winner
Presidential election“Presidential Election Winner 2028”PRES-2028-WINNERPolitics > President > Next President

There’s no universal event ID. Matching requires:

  1. Manual mapping for high-value events (most reliable)
  2. Fuzzy string matching on event names (fragile but scalable)
  3. Category + date heuristics — same sport, same date, similar participants

For a working implementation, manual mapping of the top 20-50 events gives you 80% of the arb value with near-zero matching errors.


Building a Cross-Market Arb Scanner

Here’s a complete scanner that checks a pre-mapped set of events across all three market types:

from dataclasses import dataclass

@dataclass
class CrossMarketEvent:
    name: str
    polymarket_token_yes: str = ""
    polymarket_token_no: str = ""
    kalshi_ticker: str = ""
    sportsbook_key: str = ""
    sport: str = ""

# Pre-mapped events (maintain this manually for high-value markets)
MAPPED_EVENTS = [
    CrossMarketEvent(
        name="Super Bowl LX Winner: Chiefs",
        polymarket_token_yes="0x...",  # YES token ID
        polymarket_token_no="0x...",   # NO token ID
        kalshi_ticker="SUPERBOWL-LX-CHIEFS",
        sportsbook_key="chiefs_super_bowl",
        sport="football_nfl",
    ),
    # Add more events as needed
]

def scan_cross_market_arbs(
    events: list[CrossMarketEvent],
    min_arb_pct: float = 2.0,
) -> list[dict]:
    """Scan for cross-market arbitrage opportunities.

    Compares the YES price on prediction markets against the implied
    probability from sportsbook odds, and vice versa.
    """
    opportunities = []

    for event in events:
        prices = {}

        # Get prediction market prices
        if event.polymarket_token_yes:
            try:
                poly_yes = get_polymarket_price(event.polymarket_token_yes)
                poly_no = 1 - poly_yes  # Simplified; ideally get NO token price
                prices["polymarket"] = {"yes": poly_yes, "no": poly_no, "fee": 2.0}
            except Exception:
                pass

        if event.kalshi_ticker:
            try:
                kalshi = get_kalshi_price(event.kalshi_ticker)
                prices["kalshi"] = {
                    "yes": kalshi["yes_price"],
                    "no": kalshi["no_price"],
                    "fee": 0.0,
                }
            except Exception:
                pass

        if event.sportsbook_key:
            try:
                sb_odds = get_sportsbook_odds(event.sport)
                # Extract relevant odds (simplified)
                for ev in sb_odds:
                    for bm in ev.get("bookmakers", []):
                        for mkt in bm.get("markets", []):
                            for outcome in mkt.get("outcomes", []):
                                if event.sportsbook_key in outcome.get("name", "").lower():
                                    imp = 1 / outcome["price"]
                                    key = f"sb_{bm['key']}"
                                    prices[key] = {
                                        "yes": imp,
                                        "no": 1 - imp,
                                        "fee": 0.0,  # Vig in odds
                                    }
            except Exception:
                pass

        if len(prices) < 2:
            continue

        # Find best YES and best NO across all platforms
        best_yes = min(prices.items(), key=lambda x: x[1]["yes"])
        best_no = min(prices.items(), key=lambda x: x[1]["no"])

        # Check: can we buy YES cheap on one platform and NO cheap on another?
        total_implied = best_yes[1]["yes"] + best_no[1]["no"]

        if total_implied < 1.0:
            arb_pct = ((1 / total_implied) - 1) * 100

            # Fee-adjusted check
            result = check_cross_market_arb(
                implied_probs=[best_yes[1]["yes"], best_no[1]["no"]],
                platform_fees=[best_yes[1]["fee"], best_no[1]["fee"]],
            )

            if result["profit_after_fees"] > 0 and arb_pct >= min_arb_pct:
                opportunities.append({
                    "event": event.name,
                    "yes_source": best_yes[0],
                    "yes_price": best_yes[1]["yes"],
                    "no_source": best_no[0],
                    "no_price": best_no[1]["no"],
                    "raw_arb_pct": arb_pct,
                    "profit_after_fees": result["profit_after_fees"],
                    "roi": result["roi"],
                })

    return opportunities

Execution Considerations

Cross-market execution is more complex than single-market arb because you’re dealing with fundamentally different platform types.

Speed Asymmetry

ActionSportsbookPolymarketKalshi
Place a bet/order1–3 seconds2–10 seconds (on-chain)1–3 seconds
ConfirmationInstantBlock confirmation (~2s on Polygon)Instant
Cancel/modifyUsually possible pre-eventCancel open orders, sell positionCancel open orders

Sportsbooks and Kalshi execute faster than Polymarket. If you need to place both legs simultaneously, start the Polymarket leg first.

Capital Lock-Up

Sportsbook bets on futures can lock capital for months. Polymarket positions are theoretically liquid (you can sell on the order book), but thin markets may force you to sell at a loss. Factor this into your position sizing — don’t commit capital you’ll need before the event resolves.

Multi-Leg Risk

Cross-market arbs have the same partial execution risk as traditional arbs, but with an added wrinkle: if one leg is on-chain (Polymarket), reversal may be impossible once the transaction is confirmed. Always check both legs’ availability before submitting either.


Real-World Patterns

Based on IMDEA research and observed market behavior, cross-market arbs tend to appear in predictable patterns:

PatternExampleTypical Arb %Duration
News divergencePoll released, Polymarket moves faster than sportsbooks3–8%Minutes to hours
Liquidity gapSportsbook line moves on sharp money, prediction market is thin2–5%Hours
Settlement premiumSportsbook settles instantly, prediction market locks capital1–3%Persistent
New market launchKalshi launches a market already priced on Polymarket5–15%Hours to days
Time zone gapU.S. sportsbooks close overnight, prediction markets trade 24/72–4%Hours

The most consistent arbs come from structural differences (settlement premium, time zone gaps) rather than information asymmetry. These are also the easiest to automate.


Frequently Asked Questions

What is cross-market arbitrage in betting?

Cross-market arbitrage exploits pricing differences between different types of betting platforms — typically sportsbooks vs. prediction markets like Polymarket or Kalshi. Because these platforms have different participants, fee structures, and pricing mechanisms, the same event can be priced differently across them, creating arbitrage opportunities.

Can you arb between Polymarket and sportsbooks?

Yes. Events like election outcomes, championship winners, and economic indicators are priced on both Polymarket and sportsbooks. The different market structures create pricing gaps. However, you must account for Polymarket’s ~2% fee on net winnings and the sportsbook’s vig, plus different settlement timelines.

What are the risks of cross-market arbitrage?

The main risks are: (1) settlement mismatch — platforms may resolve the same event differently based on their rules, (2) execution timing — prediction markets trade 24/7 while sportsbook lines may not be available, (3) capital lock-up — prediction market positions may be locked until event resolution, and (4) fee erosion — combined fees across platforms can eliminate thin arb margins.

How much arbitrage profit has been made on Polymarket?

Research from IMDEA Networks (2025) found that traders captured over $40 million in arbitrage profits on Polymarket during the 2024 U.S. election cycle, primarily from cross-market price discrepancies. The study analyzed on-chain data and identified systematic pricing differences between Polymarket and other markets.


See Also


This guide is maintained by AgentBets.ai. Found an error? Let us know on Twitter.

Not financial advice. Built for builders.