Install
openclaw skills install prediction-market-arbiterCross-platform divergence scanner comparing Kalshi and Polymarket prices on identical events. Fuzzy title matching across 1000+ markets per run, configurable thresholds for volume, divergence percentage, and match quality. Detects arbitrage opportunities and market mispricings automatically. Zero cost — both APIs are free. Part of the OpenClaw Prediction Market Trading Stack — divergences feed into Market Morning Brief and pair with Kalshi Command Center for execution.
openclaw skills install prediction-market-arbiterPrediction Market Arbiter is a systematic price comparison engine for prediction markets. It scans Kalshi and Polymarket simultaneously, identifies identical or near-identical events priced differently across platforms, and alerts you to potential arbitrage opportunities or market mispricings.
Core Function: Compare YES prices for the same event across Kalshi and Polymarket. When the same market exists on both platforms but with significant price divergences, alert the trader to potential inefficiency.
Example:
The arbiter detects these inefficiencies automatically, filters by volume and match quality, and ranks them by spread size.
Kalshi API Key (free, required)
Polymarket Access (free, no authentication)
pip install kalshi-python requests pyyaml
Create or update your config.yaml:
kalshi:
enabled: true
api_key_id: "your-key-id-here"
private_key_file: "path/to/private.key"
prediction_market_arbiter:
enabled: true
check_interval_minutes: 240 # 4 hours (default)
divergence_threshold_pct: 8.0 # Minimum % spread to alert
fuzzy_match_threshold: 0.6 # Jaccard similarity threshold
min_volume: 1000 # Minimum combined volume
kalshi_max_pages: 5 # Pages to fetch (max 5 × 200 = 1000)
polymarket_limit: 200 # Top markets by volume
| Parameter | Default | When to Adjust |
|---|---|---|
divergence_threshold_pct | 8.0% | Lower (5%) for more alerts; higher (15%) for only major spreads |
fuzzy_match_threshold | 0.6 | Raise to 0.7+ to be stricter (fewer false matches); lower to 0.5 for loose matching |
min_volume | 1000 | Lower for illiquid opportunities; raise to 5000+ for only liquid pairs |
Kalshi: Paginate through active markets (status=open), fetching up to 5 pages of 200 markets each (max 1000 markets).
Polymarket: Fetch top 200 markets sorted by volume from Gamma API.
For each Kalshi market, compare against all Polymarket markets using Jaccard similarity:
a_words = set(kalshi_title.lower().split())
b_words = set(polymarket_title.lower().split())
# Remove common stopwords (the, a, will, in, on, etc.)
a_words -= stopwords
b_words -= stopwords
similarity = len(a_words ∩ b_words) / len(a_words ∪ b_words)
Only pairs with similarity >= threshold advance to comparison.
Example — Successful Match:
Kalshi: "Will the Federal Reserve cut rates before June 2026?"
Polymarket: "Will the Fed cut interest rates by June 2026?"
After stopword removal:
a_words = {federal, reserve, cut, rates, june, 2026}
b_words = {fed, cut, interest, rates, june, 2026}
Intersection: {cut, rates, june, 2026}
Union: {federal, reserve, fed, interest, cut, rates, june, 2026}
Similarity: 4/8 = 0.50
Example — No Match (Different Events):
Kalshi: "Will Bitcoin exceed $100,000 by December 2026?"
Polymarket: "Will Ethereum hit $10,000 by end of 2026?"
After stopword removal:
a_words = {bitcoin, exceed, 100000, december, 2026}
b_words = {ethereum, hit, 10000, end, 2026}
Intersection: {2026}
Union: {bitcoin, exceed, 100000, december, ethereum, hit, 10000, end, 2026}
Similarity: 1/9 = 0.11 (NO MATCH — correctly rejected)
Tuning note: Markets with similar topics but different vocabulary (e.g., "Bitcoin" vs "BTC") may not match at default thresholds. Lower fuzzy_match_threshold to 0.4-0.5 for broader matching, or use the aggressive config profile.
Combined volume = Kalshi volume + Polymarket volume
For each matched pair:
delta = |kalshi_price - polymarket_price| (in cents)
midpoint = (kalshi_price + polymarket_price) / 2
delta_pct = (delta / midpoint) × 100
If delta_pct >= threshold, add to alerts.
Sort divergences by largest spread first. Return top N (default 5 in alerts).
Example output:
📊 Cross-platform divergences (>=8%):
Bitcoin exceeds $100,000 by EOY 2026?
Kalshi 72¢ vs PM 58¢ (↓14%)
Will Trump win 2028?
Kalshi 68¢ vs PM 61¢ (↑7%) [No match - below threshold]
Write all matches (top 20) to JSON:
{
"matches": [
{
"kalshi_title": "Will Bitcoin exceed $100,000 by Dec 2026?",
"pm_title": "Bitcoin above 100k by EOY 2026?",
"kalshi_price": 72,
"pm_price": 58,
"delta": 14,
"delta_pct": 19.4,
"match_score": 0.82
}
],
"kalshi_count": 847,
"pm_count": 198,
"timestamp": 1709856000.123
}
1. Polymarket Leads on Hype Events
2. Kalshi Leads on Structural Events
3. Liquidity-Driven Spreads
4. Time-of-Day Effects
# Single run (no persistence)
python arbiter.py
# With custom config
python arbiter.py --config /path/to/config.yaml
# Dry run (display matches, don't send alerts)
python arbiter.py --dry-run
# Force run even if interval hasn't elapsed
python arbiter.py --force
# Add to crontab -e:
0 */4 * * * cd /path/to/prediction-market-arbiter && python scripts/arbiter.py >> /tmp/arbiter.log 2>&1
skills:
prediction-market-arbiter:
enabled: true
schedule: "0 */4 * * *" # Every 4 hours
timeout_seconds: 120
📊 Cross-platform divergences (>=8%):
Will Bitcoin exceed $100,000 by Dec 2026?
Kalshi 72¢ vs PM 58¢ (↓14%)
Will a Democrat win the 2028 presidential election?
Kalshi 48¢ vs PM 55¢ (↑7%) [No match - below threshold]
Will the Federal Reserve cut rates before June 2026?
Kalshi 38¢ vs PM 45¢ (↑7%) [No match - below threshold]
Found 3 divergences across 847 Kalshi + 198 Polymarket markets.
File: state/cross_platform_cache.json
{
"matches": [
{
"kalshi_title": "Will Bitcoin exceed $100,000 by December 2026?",
"pm_title": "Bitcoin above 100k by EOY 2026?",
"kalshi_price": 72,
"pm_price": 58,
"delta": 14,
"delta_pct": 19.4,
"match_score": 0.82
},
{
"kalshi_title": "Will Democrats win the 2028 presidency?",
"pm_title": "Democratic nominee will win the 2028 U.S. Presidential Election",
"kalshi_price": 48,
"pm_price": 55,
"delta": 7,
"delta_pct": 14.3,
"match_score": 0.75
}
],
"kalshi_count": 847,
"pm_count": 198,
"timestamp": 1709856000.123
}
from arbiter import check_cross_platform
result = check_cross_platform(
state={},
dry_run=False,
force=False
)
# result = True if matches found, False otherwise
cfg = {
"divergence_threshold_pct": 8.0,
"fuzzy_match_threshold": 0.6,
"min_volume": 1000,
"kalshi_max_pages": 5,
"polymarket_limit": 200,
"check_interval_minutes": 240,
}
Possible causes:
If fuzzy matching is pairing unrelated markets:
# Increase strictness
fuzzy_match_threshold: 0.75 # Default 0.6
Kalshi API error:
kalshi_api_key_id and private_key_file pathPolymarket API error:
Typical run: 30-90 seconds
For 6 runs per day (every 4 hours):
Cross-platform price comparison engine for the Prediction Market Trading Stack.
| Connected Skill | How It Connects |
|---|---|
| Market Morning Brief | Divergences appear in your daily morning digest |
| Kalshi Command Center | Trade the Kalshi side of flagged divergences |
| Polymarket Command Center | Browse the Polymarket side for context |
| Kalshalyst | Compare contrarian edges against cross-platform pricing |
Install the complete stack:
clawhub install kalshalyst kalshi-command-center polymarket-command-center prediction-market-arbiter xpulse portfolio-drift-monitor market-morning-brief personality-engine
Battle-tested in production trading environments. Design principles:
prediction_market_arbiter:
divergence_threshold_pct: 15.0
fuzzy_match_threshold: 0.8
min_volume: 5000
prediction_market_arbiter:
divergence_threshold_pct: 5.0
fuzzy_match_threshold: 0.5
min_volume: 500
references/fuzzy-matching.md for detailed explanation of title matching and threshold tuningCommon iteration paths:
Author: KingMadeLLC
Found a bug? Have a feature request? Want to share results?
Part of the OpenClaw Prediction Stack — the first prediction market skill suite on ClawHub.