🧪 Skills

Prediction Market Arbiter

Cross-platform divergence scanner comparing Kalshi and Polymarket prices on identical events. Fuzzy title matching across 1000+ markets per run, configurable...

v1.0.1
❤️ 0
⬇️ 48
👁 1
Share

Description


name: Prediction Market Arbiter description: "Cross-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."

Prediction Market Arbiter — Cross-Platform Divergence Scanner

Overview

Prediction 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.

What It Does

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:

  • Market: "Will Bitcoin exceed $100k by Dec 2026?"
  • Kalshi price: 72¢ (72% implied probability)
  • Polymarket price: 58¢ (58% implied probability)
  • Divergence: 14 percentage points = arbitrage signal

The arbiter detects these inefficiencies automatically, filters by volume and match quality, and ranks them by spread size.

Key Features

  • Fuzzy Title Matching — Uses Jaccard similarity (word overlap) to identify the same event across different platform vocabularies
  • Configurable Divergence Threshold — Default 8%; tune for your risk tolerance
  • Volume Filtering — Combines Kalshi + Polymarket volume; minimum 1000 default
  • Match Confidence — Only pairs markets with >60% title similarity (configurable)
  • Pagination — Fetches top 1000 Kalshi markets (5 pages × 200) + top 200 Polymarket (by volume)
  • Cache Results — Saves matches to JSON for programmatic access and follow-up analysis
  • Multi-Platform Support — Uses Kalshi API (with client authentication) and Polymarket Gamma API (no auth required)

When to Use This Skill

  • You trade across Kalshi and Polymarket and want to find price inefficiencies automatically
  • You're arbitraging prediction markets and need real-time divergence alerts
  • You want to understand market structure: which platform leads on certain event types?
  • You're analyzing prediction market microstructure and efficiency
  • You need a scheduled divergence scanner (every 4 hours, daily, etc.)

Requirements

API Keys & Credentials

  1. Kalshi API Key (free, required)

    • Sign up at https://kalshi.com
    • Navigate to Settings → API
    • Generate API credentials (key ID + private key file)
    • Cost: Free tier, unlimited reads
  2. Polymarket Access (free, no authentication)

Python & Dependencies

  • Python 3.10 or higher
  • Required packages:
    pip install kalshi-python requests pyyaml
    

Configuration

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

Threshold Tuning Guide

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

How It Works

Phase 1: Fetch Markets

Kalshi: Paginate through active markets (status=open), fetching up to 5 pages of 200 markets each (max 1000 markets).

  • Extract: title, ticker, yes_price (cents), volume, open interest

Polymarket: Fetch top 200 markets sorted by volume from Gamma API.

  • Extract: question text (title), yes_price (in cents), volume

Phase 2: Fuzzy Title Matching

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.

Phase 3: Filter by Volume

Combined volume = Kalshi volume + Polymarket volume

  • If combined volume < min_volume threshold, drop the pair
  • Filters out low-liquidity pairs where arbitrage isn't actionable

Phase 4: Calculate Divergence

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.

Phase 5: Sort & Alert

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]

Cache Results

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
}

Divergence Interpretation

Common Divergence Patterns

1. Polymarket Leads on Hype Events

  • Recent news → PM moves first (higher volume of retail traders)
  • Look for Kalshi underpriced relative to PM on breaking news

2. Kalshi Leads on Structural Events

  • Predictable political cycles, economic data → Kalshi often ahead
  • PM sometimes lags

3. Liquidity-Driven Spreads

  • If one platform is much more liquid, it's often the "fair price"
  • Illiquid platform = potential mispricing

4. Time-of-Day Effects

  • US markets hours: Kalshi often tighter
  • Asian hours: PM may be the pricing engine

Scheduling

Command-Line Usage

# 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

As a Cron Job (Every 4 Hours)

# Add to crontab -e:
0 */4 * * * cd /path/to/prediction-market-arbiter && python scripts/arbiter.py >> /tmp/arbiter.log 2>&1

As an OpenClaw Scheduled Task

skills:
  prediction-market-arbiter:
    enabled: true
    schedule: "0 */4 * * *"  # Every 4 hours
    timeout_seconds: 120

Example Output

Alert Message

📊 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.

Cache Output

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
}

API Reference

Main Function

from arbiter import check_cross_platform

result = check_cross_platform(
    state={},
    dry_run=False,
    force=False
)

# result = True if matches found, False otherwise

Configuration Object

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,
}

Troubleshooting

No Matches Found

Possible causes:

  1. Threshold too strict — Raise divergence_threshold_pct or lower fuzzy_match_threshold
  2. Low market overlap — Most Kalshi markets don't have Polymarket equivalents
  3. Both platforms down — Check status pages

Wrong Matches (False Positives)

If fuzzy matching is pairing unrelated markets:

# Increase strictness
fuzzy_match_threshold: 0.75  # Default 0.6

API Failures

Kalshi API error:

  • Check API key validity: kalshi_api_key_id and private_key_file path
  • Verify key has "read-only" permission (sufficient for this skill)

Polymarket API error:

Performance

Typical run: 30-90 seconds

  • Kalshi fetch: 10-30s (paginating 5 pages)
  • Polymarket fetch: 5-15s
  • Matching: <5s
  • If taking >2 minutes, reduce kalshi_max_pages or polymarket_limit

Performance & Cost

Typical Run Metrics

  • Runtime: 30-90 seconds
  • API calls:
    • Kalshi: 5 calls (page 1-5)
    • Polymarket: 1 call (top 200 by volume)
  • Cost: $0 (all reads, no premium APIs)

Scaling

For 6 runs per day (every 4 hours):

  • Kalshi: 30 API calls/day (free tier handles 1000s)
  • Polymarket: 6 API calls/day (public API)
  • Total cost: $0

OpenClaw Ecosystem Integration

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

Implementation Notes

Battle-tested in production trading environments. Design principles:

  1. Standalone config/logging — works with any OpenClaw setup
  2. Direct alerts — sends divergences straight to you
  3. All thresholds and matching logic — refined through live cross-platform trading
  4. Standalone scripts — zero external dependencies beyond listed packages

Configuration Example

conservative.yaml (Wide Spreads Only)

prediction_market_arbiter:
  divergence_threshold_pct: 15.0
  fuzzy_match_threshold: 0.8
  min_volume: 5000

aggressive.yaml (Early Detection)

prediction_market_arbiter:
  divergence_threshold_pct: 5.0
  fuzzy_match_threshold: 0.5
  min_volume: 500

Further Reading

  • See references/fuzzy-matching.md for detailed explanation of title matching and threshold tuning

Support & Iteration

Common iteration paths:

  1. Threshold Tuning — Adjust divergence_threshold_pct and fuzzy_match_threshold based on your alert frequency
  2. Volume Filtering — Increase min_volume to focus on only liquid pairs
  3. Scheduling — Change check_interval_minutes (240 = 4 hours, 60 = 1 hour, etc.)
  4. Platform Expansion — Add new platforms (Manifold Markets, Betfair, etc.) by implementing _fetch_X_markets() functions

License & Attribution

Author: KingMadeLLC


Feedback & Issues

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.

Reviews (0)

Sign in to write a review.

No reviews yet. Be the first to review!

Comments (0)

Sign in to join the discussion.

No comments yet. Be the first to share your thoughts!

Compatible Platforms

Pricing

Free

Related Configs