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:
| Factor | Sportsbooks | Prediction Markets |
|---|---|---|
| Participants | Recreational bettors, sports fans | Traders, analysts, political junkies |
| Pricing mechanism | Odds set by traders/algorithms, adjusted by action | Order book driven by supply/demand |
| Information speed | React to sharp money within seconds | React to news, polls, model outputs |
| Liquidity | Deep on popular sports, thin on niche | Varies — deep on elections, thin on sports |
| Regulation | State-by-state gambling licenses | CFTC-regulated (Kalshi), crypto-native (Polymarket) |
| Time horizon | Game-day or short-term futures | Weeks 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:
| Category | Sportsbook Markets | Prediction Markets | Arb Potential |
|---|---|---|---|
| U.S. Elections | Futures (president, senate) | Deep liquidity on Polymarket + Kalshi | High — different information sources |
| Championship Winners | Futures (Super Bowl, NBA Finals) | Available on both platforms | Medium — sportsbooks have more liquidity |
| Awards | MVP, Heisman, Oscar | Growing on Polymarket | Medium — sportsbooks price earlier |
| Economic Indicators | Limited (some offshore) | Kalshi specializes here | Low — limited sportsbook coverage |
| Geopolitics | Rare | Active on Polymarket | Low — 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
| Platform | Format | Example: “60% chance” | Range |
|---|---|---|---|
| Polymarket | Price per share (0–1) | $0.60 per YES share | $0.01 – $0.99 |
| Kalshi | Price in cents (1–99) | 60¢ per YES contract | 1¢ – 99¢ |
| Sportsbooks (fav) | American odds (negative) | -150 | -10000 to -100 |
| Sportsbooks (dog) | American odds (positive) | +150 | +100 to +10000 |
| Sportsbooks | Decimal odds | 1.667 | 1.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.
| Platform | Fee Type | Fee Amount | Applied When |
|---|---|---|---|
| Polymarket | Trading fee | ~2% on net winnings | When you sell or contract resolves in your favor |
| Kalshi | Trading fee | $0 on contracts | Free to trade (fee in the spread) |
| DraftKings | Vig (built into odds) | 4–10% overround | Already reflected in the odds |
| FanDuel | Vig (built into odds) | 4–8% overround | Already reflected in the odds |
| Betfair Exchange | Commission | 2–5% on net winnings | When you win |
Minimum Arb Threshold by Platform Combination
Given these fees, here’s the minimum arb percentage needed for profit:
| Platform Combination | Minimum Arb % for Profit | Notes |
|---|---|---|
| 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
| Platform | Resolution Authority | Typical Timeline | Dispute Process |
|---|---|---|---|
| Polymarket | UMA optimistic oracle | Hours to days after event | Community-driven challenge system |
| Kalshi | Internal resolution team | Minutes to hours | CFTC-regulated process |
| Sportsbooks | Internal oddsmakers | Minutes after event | Customer support |
Resolution Mismatch Example
Consider the market: “Will [Candidate] win the 2028 Presidential Election?”
| Scenario | Sportsbook | Polymarket | Kalshi |
|---|---|---|---|
| Candidate wins, clean result | Settles YES within hours | Settles YES within 1-2 days | Settles YES within hours |
| Contested election, recount | May void/suspend bets | Waits for UMA oracle resolution | Waits for official certification |
| Candidate wins but is later disqualified | Already settled and paid | May trigger resolution challenge | Depends 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
- Read the settlement rules for every market on every platform before entering a position
- Focus on events with clear, binary outcomes — “Who wins?” is cleaner than “Will X happen by Y date?”
- Avoid events with subjective resolution — “Will the economy improve?” has too much interpretation room
- Factor in capital lock-up costs — money locked in a Polymarket position for 30 days has an opportunity cost
- 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 Event | Polymarket | Kalshi | Sportsbook |
|---|---|---|---|
| Super Bowl winner | “Who will win Super Bowl LX?” | SUPERBOWL-LX-WINNER | NFL > Futures > Super Bowl Winner |
| Presidential election | “Presidential Election Winner 2028” | PRES-2028-WINNER | Politics > President > Next President |
There’s no universal event ID. Matching requires:
- Manual mapping for high-value events (most reliable)
- Fuzzy string matching on event names (fragile but scalable)
- 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
| Action | Sportsbook | Polymarket | Kalshi |
|---|---|---|---|
| Place a bet/order | 1–3 seconds | 2–10 seconds (on-chain) | 1–3 seconds |
| Confirmation | Instant | Block confirmation (~2s on Polygon) | Instant |
| Cancel/modify | Usually possible pre-event | Cancel open orders, sell position | Cancel 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:
| Pattern | Example | Typical Arb % | Duration |
|---|---|---|---|
| News divergence | Poll released, Polymarket moves faster than sportsbooks | 3–8% | Minutes to hours |
| Liquidity gap | Sportsbook line moves on sharp money, prediction market is thin | 2–5% | Hours |
| Settlement premium | Sportsbook settles instantly, prediction market locks capital | 1–3% | Persistent |
| New market launch | Kalshi launches a market already priced on Polymarket | 5–15% | Hours to days |
| Time zone gap | U.S. sportsbooks close overnight, prediction markets trade 24/7 | 2–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
- Cross-Market Arb Finder — Live cross-market arb scanner tool
- Sports Betting Arbitrage Bot Guide — Traditional sportsbook-to-sportsbook arb
- Arbitrage Calculator — Calculate arb percentages and stakes
- Odds Converter — Convert between odds formats
- Polymarket API Guide — Full Polymarket API reference
- Kalshi API Guide — Full Kalshi API reference
- Prediction Market API Reference — Side-by-side API comparison
This guide is maintained by AgentBets.ai. Found an error? Let us know on Twitter.
Not financial advice. Built for builders.