Academic research documented $40 million in arbitrage profits extracted from Polymarket between April 2024–April 2025.[1] The opportunity persists because retail-dominated orderbooks lack institutional market-making infrastructure. Here’s the technical implementation for capturing structural mispricings.
The Structural Inefficiency
Prediction markets exhibit chronic pricing failures exploitable through systematic strategies.
Single-condition arbitrage: When YES + NO token prices deviate from $1.00, profit is guaranteed. Research analyzed 86 million bets across 17,218 conditions.[1] When sum-price falls below $1.00, arbitrageurs buy both positions and realize guaranteed profit at resolution.
Cross-platform fragmentation: Kalshi captured 62% of prediction market volume during September 11–17, 2025, processing $500+ million weekly with $189 million average open interest.[2] Polymarket accounted for 37% during the same period.[2] Material price divergences between platforms create cross-venue arbitrage opportunities.
Mispricings cluster during volatility events — polling releases, debate nights, economic data. Price discovery lags real-world information by minutes to hours. Top arbitrageur generated $2.01 million across 4,049 transactions.[1] Strategy requires execution frequency over position size.
System Architecture
API Integration
Polymarket operates a hybrid-decentralized Central Limit Order Book (CLOB) on Polygon (Chain ID 137).[3] Off-chain matching, on-chain settlement via conditional token framework.
from py_clob_client.client import ClobClient
from web3 import Web3
import asyncio
import websockets
import json
class ArbitrageMonitor:
def __init__(self, private_key: str):
self.host = “https://clob.polymarket.com”
self.chain_id = 137
self.w3 = Web3(Web3.HTTPProvider(’https://polygon-rpc.com’))
# Initialize CLOB client
self.client = ClobClient(
self.host,
key=private_key,
chain_id=self.chain_id
)
async def monitor_markets(self, market_ids: list):
“”“Real-time WebSocket monitoring”“”
ws_url = f”{self.host}/ws/market”
async with websockets.connect(ws_url) as ws:
subscribe_msg = json.dumps({
“type”: “subscribe”,
“markets”: market_ids
})
await ws.send(subscribe_msg)
async for message in ws:
data = json.loads(message)
await self.process_update(data)Kalshi integration: REST API with tiered rate limits. Basic tier: 20 requests/second (read), 10 requests/second (write). Premier tier: up to 100 requests/second.[4] Implement per-second throttling with token bucket algorithm.
Mispricing Detection
from decimal import Decimal
from typing import Optional, Dict
def detect_single_condition_arbitrage(
yes_price: Decimal,
no_price: Decimal,
liquidity: Decimal,
min_profit: Decimal = Decimal(’0.05’),
max_capital: Decimal = Decimal(’10000’)
) -> Optional[Dict]:
“”“
Identify YES+NO≠$1 opportunities
Returns None if below profitability threshold
“”“
sum_price = yes_price + no_price
# Long arbitrage: Buy both if sum < $1
if sum_price < (Decimal(’1.00’) - min_profit):
profit_per_dollar = Decimal(’1.00’) - sum_price
position_size = min(liquidity, max_capital)
return {
‘type’: ‘buy_both’,
‘yes_price’: yes_price,
‘no_price’: no_price,
‘position_size’: position_size,
‘expected_profit’: profit_per_dollar * position_size,
‘roi’: profit_per_dollar / sum_price
}
# Short arbitrage: Sell both if sum > $1
elif sum_price > (Decimal(’1.00’) + min_profit):
profit_per_dollar = sum_price - Decimal(’1.00’)
position_size = min(liquidity, max_capital)
return {
‘type’: ‘sell_both’,
‘yes_price’: yes_price,
‘no_price’: no_price,
‘position_size’: position_size,
‘expected_profit’: profit_per_dollar * position_size,
‘roi’: profit_per_dollar / sum_price
}
return NoneResearch shows extreme outliers exist: one trader converted $0.02 into $58,983.36 exploiting severe mispricing where both YES/NO traded below $0.02.[1]
Order Execution: Managing Leg Risk
Critical constraint: Non-atomic execution. One position may fill while hedge fails, creating directional exposure.
class OrderExecutor:
def __init__(self, client: ClobClient):
self.client = client
self.timeout = 5 # seconds
async def execute_atomic_arbitrage(
self,
market_id: str,
opportunity: Dict
) -> bool:
“”“
Execute both legs with timeout protection
Cancel unfilled orders if partial fill occurs
“”“
try:
# Place orders simultaneously
yes_task = self.place_order(
market_id,
‘YES’,
opportunity[’position_size’],
opportunity[’yes_price’]
)
no_task = self.place_order(
market_id,
‘NO’,
opportunity[’position_size’],
opportunity[’no_price’]
)
yes_order, no_order = await asyncio.gather(
yes_task,
no_task,
timeout=self.timeout
)
# Verify both fills
yes_filled = await self.check_fill_status(yes_order[’id’])
no_filled = await self.check_fill_status(no_order[’id’])
if yes_filled and no_filled:
return True
# Cancel unfilled leg, exit partial position
await self.handle_partial_fill(yes_order, no_order)
return False
except asyncio.TimeoutError:
await self.cancel_pending_orders(market_id)
return FalseResearch data: 75% of matched orders execute within 950 blocks (~1 hour on Polygon).[1] Sub-5-second timeouts minimize leg risk.
Gas Optimization: Polygon Transaction Costs
Polymarket executes on Polygon. Gas fees compress net returns on small arbitrages.
from decimal import Decimal
from web3 import Web3
class GasOptimizer:
def __init__(self, w3: Web3):
self.w3 = w3
self.matic_usd_price = self.fetch_matic_price()
def estimate_transaction_cost(self, order_size: Decimal) -> Decimal:
“”“
Calculate total transaction cost
Typical Polygon gas: 30-100 gwei
“”“
gas_price = self.w3.eth.gas_price
gas_limit = 150000 # Standard CLOB order
gas_cost_wei = gas_price * gas_limit
gas_cost_matic = Decimal(gas_cost_wei) / Decimal(10**18)
gas_cost_usd = gas_cost_matic * Decimal(str(self.matic_usd_price))
# Polymarket: 0% platform fee (current)
return gas_cost_usd
def is_profitable_after_costs(
self,
expected_profit: Decimal,
order_size: Decimal,
min_roi: Decimal = Decimal(’0.02’)
) -> bool:
“”“Minimum threshold: 2% net ROI after costs”“”
total_cost = self.estimate_transaction_cost(order_size)
net_profit = expected_profit - total_cost
net_roi = net_profit / order_size
return net_roi > min_roiPosition Tracking: Oracle Risk Management
Critical risk factor: Cross-platform arbitrage faces resolution divergence.
Polymarket uses UMA’s Optimistic Oracle with token-holder governance. March 2025 incident: whale with 5 million UMA tokens (25% of voting power) manipulated $7M market resolution on Ukraine mineral deal despite no formal agreement.[5] Largest winner netted $55,000; biggest loser forfeited $73,000.[5]
Kalshi operates under CFTC-regulated settlement mechanisms. Oracle mismatch can destroy cross-platform hedges.
from typing import Dict, List
from decimal import Decimal
class PositionTracker:
def __init__(self):
self.positions = {
‘polymarket’: {},
‘kalshi’: {}
}
def calculate_oracle_risk_exposure(self) -> Dict:
“”“
Flag positions with cross-platform oracle exposure
“”“
exposure = {
‘capital_deployed’: Decimal(’0’),
‘oracle_risk_positions’: []
}
for platform, positions in self.positions.items():
for market_id, pos in positions.items():
exposure[’capital_deployed’] += pos[’capital’]
# Identify cross-platform hedges
if self.has_cross_platform_hedge(market_id):
exposure[’oracle_risk_positions’].append({
‘market’: market_id,
‘exposure’: pos[’capital’],
‘platforms’: self.get_hedge_platforms(market_id),
‘risk_type’: ‘oracle_divergence’
})
return exposure
def has_cross_platform_hedge(self, market_id: str) -> bool:
“”“Check if position exists across multiple platforms”“”
platforms_with_position = [
platform for platform, positions in self.positions.items()
if market_id in positions
]
return len(platforms_with_position) > 1Mitigation strategy: Avoid cross-platform positions unless spread exceeds 15 cents — sufficient buffer to absorb potential oracle manipulation.
Alert Infrastructure
Top arbitrageurs executed 4,000+ transactions over 12 months.[1] Frequency matters more than per-trade size.
from dataclasses import dataclass
from decimal import Decimal
import asyncio
import time
@dataclass
class ArbitrageAlert:
market_id: str
platform: str
opportunity_type: str
expected_profit: Decimal
roi: Decimal
timestamp: float
urgency: str
class AlertSystem:
def __init__(self, notification_handler):
self.handler = notification_handler
self.min_profit = Decimal(’10.00’) # $10 minimum
async def process_opportunity(self, opp: Dict):
“”“
Filter and prioritize by profit potential
High urgency: ROI > 10% or profit > $100
“”“
if not self.meets_criteria(opp):
return
alert = ArbitrageAlert(
market_id=opp[’market_id’],
platform=opp[’platform’],
opportunity_type=opp[’type’],
expected_profit=opp[’expected_profit’],
roi=opp[’roi’],
timestamp=time.time(),
urgency=self.calculate_urgency(opp)
)
await self.handler.send(alert)
def calculate_urgency(self, opp: Dict) -> str:
“”“
High: ROI > 10% or profit > $100
Medium: ROI > 5% or profit > $50
Low: Below thresholds but above minimum
“”“
if (opp[’roi’] > Decimal(’0.10’) or
opp[’expected_profit’] > Decimal(’100’)):
return ‘high’
elif (opp[’roi’] > Decimal(’0.05’) or
opp[’expected_profit’] > Decimal(’50’)):
return ‘medium’
return ‘low’Deploy multi-threaded monitoring across 100+ active markets. Mispricings cluster during polling releases, debate nights, economic data prints.
P&L Analysis: Historical Extraction
Research findings from April 2024–April 2025 measurement period:[1]
Single-condition arbitrage:
Buy both positions (sum < $1.00): $5.90M extracted
Sell both positions (sum > $1.00): $4.68M extracted
Total single-condition: $10.58M
Market rebalancing (multi-condition):
Buy YES across conditions: $11.09M
Buy NO across conditions: $17.31M
Sell strategies combined: $4.88M
Total market rebalancing: $28.99M
Combinatorial arbitrage (cross-market):
13 dependent market pairs identified during 2024 election cycle
5 pairs showed realized extraction: ~$95K total
Cross-market opportunities significantly rarer than single-market
Total extracted: $39,587,585.02
Top 10 arbitrageurs captured $8.18M (21% of total).[1] Distribution confirms power law: sophisticated actors dominate.
Market Microstructure Analysis
Trading volume growth:
Polymarket processed $3.7B total volume during 2024 U.S. election cycle[1]
Weekly prediction market volume reached $2B+ by October 2025[6]
Kalshi processed $500M+ weekly during peak periods[2]
Institutional capital influx:
ICE invested $2B in Polymarket at $8B pre-money valuation (October 2025)[7]
Signals professionalization of market infrastructure
Parallel to early crypto exchange arbitrage before institutional market makers
Competitive dynamics:
September 11–17, 2025: Kalshi 62% market share, Polymarket 37%[2]
Politics markets: largest absolute profits
Sports markets: higher opportunity frequency, lower per-trade margins
The window compresses as institutional capital enters. Early crypto arbitrage generated 1,000%+ returns before market makers professionalized infrastructure. Prediction markets follow identical trajectory.
Execution Constraints
Capital efficiency: Most conditions offer $50-$500 maximum profit at available liquidity. Scaling requires:
Monitoring 100+ markets simultaneously
Accepting 1–5% per-trade margins
Deploying capital through frequency, not position size
Latency requirements: Information propagation lag creates 5–10 minute windows during volatility events. Sub-second execution required for high-frequency opportunities.
Regulatory risk: Massachusetts sued Kalshi September 2025 for allegedly operating unlicensed sports betting.[8] Over $1B in wagers January-June 2025, 75% sports-related volume.[8] Polymarket settled with CFTC in 2022, restructured operations.
Key Takeaway
Prediction market arbitrage is market microstructure exploitation — not outcome forecasting. The strategy requires:
Infrastructure:
Real-time monitoring: WebSocket feeds across 100+ markets
Execution speed: Sub-5-second order placement with atomic timeout protection
Capital allocation: $10K-$50K per condition based on liquidity
Risk management: Avoid oracle exposure; implement leg-risk controls
Profitability: Accept 1–5% returns per trade. Scale through frequency. Top performer: 4,049 transactions generating $2.01M ($496 average per trade).
Timeline: ICE’s $2B investment signals professionalization. Spread compression inevitable as institutional infrastructure deploys. Research captured peak inefficiency during 2024 election cycle — future returns require faster execution, larger capital, and more sophisticated cross-venue strategies.
Deploy now or wait for compressed spreads. The opportunity window follows crypto’s 2016–2018 trajectory: early arbitrageurs extract outsized returns before market makers eliminate structural inefficiency.
References
[1] Saguillo, O., Ghafouri, V., Kiffer, L., & Suarez-Tangil, G. (2025). “Unravelling the Probabilistic Forest: Arbitrage in Prediction Markets.” arXiv:2508.03474. IMDEA Networks Institute. https://arxiv.org/abs/2508.03474
[2] Rodriguez, F. (2025, September 20). “Kalshi Outpaces Polymarket in Prediction Market Volume Amid Surge in U.S. Trading.” CoinDesk. https://www.coindesk.com/markets/2025/09/20/kalshi-outpaces-polymarket-in-prediction-market-volume
[3] Polymarket Documentation. (2025). “CLOB Introduction.” https://docs.polymarket.com/developers/CLOB/introduction
[4] Kalshi API Documentation. (2025). “Rate Limits and Tiers.” https://docs.kalshi.com/getting_started/rate_limits
[5] Yahoo Finance. (2025, March 27). “Polymarket Suffers UMA Governance Attack After Rogue Actor Becomes Top-5 Token Staker.” https://finance.yahoo.com/news/polymarket-suffers-uma-governance-attack-101646076.html
[6] Yahoo Finance. (2025, October 21). “Prediction Markets Hit All-Time High of $2 Billion in Weekly Volume.” https://finance.yahoo.com/news/prediction-markets-hit-time-high-200142742.html
[7] Intercontinental Exchange. (2025, October 7). “ICE Announces Strategic Investment in Polymarket.” https://ir.theice.com/press/news-details/2025/ICE-Announces-Strategic-Investment-in-Polymarket/
[8] Campbell, A.J. (2025, September 12). “Massachusetts Attorney General Files Lawsuit Against Kalshi.” WBUR News. https://www.wbur.org/news/2025/09/16/massachusetts-kalshi-lawsuit-predictions-sports-wagering
Cover photograph: Brynn Who Likes Editing, public domain, via Wikimedia Commons.




Amazing post. can't wait to see it on GitHub
Have you tried to do this with the information in your Substack?