Build a custom OpenClaw skill that normalizes prices across Polymarket, Kalshi, and sportsbooks into a single implied-probability format. One SKILL.md file, three API sources, and your agent can spot mispricings across platforms instantly.

Why Your Agent Needs a Cross-Market Pricer

Polymarket gives you 0.62. Kalshi gives you $0.58. DraftKings gives you -150. Are these the same price? Different prices? Is there an arb?

Your agent can’t answer that question without a normalization layer. Each platform uses a different native format for expressing the same underlying probability — and those formats aren’t directly comparable. A Polymarket share at 0.62 is 62% implied probability. A Kalshi contract at $0.58 is 58% implied probability. DraftKings at -150 is 60% implied probability. Now you can see the 4-point gap between Kalshi and Polymarket, and the 2-point gap between DraftKings and Polymarket.

This is the data layer that makes cross-market arbitrage possible. Without it, your agent is comparing apples to oranges to bananas.

The cross-market-pricer skill handles the translation — fetching prices from each platform and outputting them in a unified format so downstream skills like arb-finder can do their job.

What You’re Building

The cross-market-pricer skill teaches your OpenClaw agent four operations:

  1. Fetch sportsbook odds as probability — Pull American odds from The Odds API and convert to implied probability
  2. Fetch Polymarket prices — Pull market prices from Polymarket’s Gamma API (already in probability format)
  3. Fetch Kalshi contract prices — Pull contract prices from Kalshi’s public API and convert cents to probability
  4. Unified cross-market comparison — Side-by-side normalized output for the same event across all three platform types

The skill uses curl, jq, and python3 — the Python is inline math only, no external packages.

Prerequisites

  • OpenClaw installed and running — Any channel (WhatsApp, Telegram, Discord, WebChat)
  • The Odds API key — Free at the-odds-api.com (500 requests/month)
  • Polymarket Gamma API — No key required for market data reads
  • Kalshi Public API — No key required for market data reads
  • curl, jq, and python3 — Pre-installed on macOS and most Linux distributions

The Math Behind Cross-Market Normalization

Every format converts to and from implied probability with simple formulas:

American odds → Implied probability:

If odds are negative (favorite): probability = |odds| / (|odds| + 100)
If odds are positive (underdog): probability = 100 / (odds + 100)

Example: -150 → 150 / (150 + 100) = 0.600
Example: +200 → 100 / (200 + 100) = 0.333

Polymarket share price → Implied probability:

probability = share_price (already in 0.00–1.00 format)

Example: 0.62 → 0.620

Kalshi contract price → Implied probability:

probability = contract_price_cents / 100

Example: $0.58 → 0.580

Implied probability → American odds (for display):

If probability > 0.50: american = -(probability / (1 - probability)) * 100
If probability < 0.50: american = ((1 - probability) / probability) * 100
If probability = 0.50: american = +100

Example: 0.600 → -(0.600 / 0.400) * 100 = -150
Example: 0.333 → (0.667 / 0.333) * 100 = +200

These conversions are exact (before vig), which means any difference between platforms after normalization represents either vig, liquidity premium, or a genuine mispricing your agent can exploit.

The Complete SKILL.md

Create the skill directory:

mkdir -p ~/.openclaw/skills/cross-market-pricer

Create ~/.openclaw/skills/cross-market-pricer/SKILL.md with this content:

---
name: cross-market-pricer
description: "Normalize odds across Polymarket, Kalshi, and sportsbooks into a unified implied-probability format. Enables apples-to-apples comparison for the same event across platforms. Use when asked to compare odds across markets, normalize pricing, or find cross-platform mispricings."
metadata:
  openclaw:
    emoji: "⚖️"
    requires:
      bins: ["curl", "jq", "python3"]
    credentials:
      - id: "odds-api-key"
        name: "The Odds API Key"
        description: "Free API key from https://the-odds-api.com/"
        env: "ODDS_API_KEY"
---

# Cross-Market Pricer

Normalize and compare odds across Polymarket, Kalshi, and traditional sportsbooks.

# When to Use

Use this skill when the user asks about:
- Comparing odds across different platforms (sportsbooks vs prediction markets)
- Normalizing prices to a common format
- Side-by-side pricing for the same event on Polymarket, Kalshi, and a sportsbook
- Whether there's a pricing gap or mispricing between platforms
- Converting between American odds, implied probability, and contract prices

# Conversion Reference

American to probability: negative → |odds|/(|odds|+100), positive → 100/(odds+100)
Polymarket to probability: price is already probability
Kalshi to probability: contract_price (already 0.00–1.00 from API)
Probability to American: >0.5 → -(prob/(1-prob))*100, <0.5  +((1-prob)/prob)*100

# Operations

### 1. Fetch Sportsbook Odds as Normalized Probability

Pull American odds from The Odds API and convert to implied probability inline:

```bash
curl -s "https://api.the-odds-api.com/v4/sports/SPORT_KEY/odds?apiKey=$ODDS_API_KEY&regions=us&markets=h2h&oddsFormat=american" \

  | jq '[.[] | {

    event: "\(.away_team) vs \(.home_team)",
    start: .commence_time,
    source: "sportsbooks",
    outcomes: [.bookmakers[0].markets[0].outcomes[] | {
      name: .name,
      american_odds: .price,
      implied_prob: (if .price < 0 then (-((.price)) / (-((.price)) + 100)) else (100 / (.price + 100)) end) | (. * 1000 | round / 1000)
    }]
  }]'
```

Replace SPORT_KEY with the appropriate key (e.g., basketball_nba, americanfootball_nfl).

### 2. Fetch Polymarket Prices as Normalized Probability

Pull market prices from Polymarket's Gamma API:

```bash
curl -s "https://gamma-api.polymarket.com/markets?closed=false&limit=10&tag=CATEGORY" \

  | jq '[.[] | {

    event: .question,
    source: "polymarket",
    outcomes: [{
      name: "Yes",
      polymarket_price: (.outcomePrices | fromjson | .[0] | tonumber | (. * 1000 | round / 1000)),
      implied_prob: (.outcomePrices | fromjson | .[0] | tonumber | (. * 1000 | round / 1000))
    }, {
      name: "No",
      polymarket_price: (.outcomePrices | fromjson | .[1] | tonumber | (. * 1000 | round / 1000)),
      implied_prob: (.outcomePrices | fromjson | .[1] | tonumber | (. * 1000 | round / 1000))
    }],
    volume: .volume,
    liquidity: .liquidity
  }]'
```

Replace CATEGORY with the relevant tag (e.g., "sports", "nba", "politics", "crypto").

### 3. Fetch Kalshi Contract Prices as Normalized Probability

Pull contract prices from Kalshi's public market data API:

```bash
curl -s "https://api.elections.kalshi.com/trade-api/v2/markets?status=open&limit=10&series_ticker=SERIES_TICKER" \

  | jq '[.markets[] | {

    event: .title,
    source: "kalshi",
    ticker: .ticker,
    outcomes: [{
      name: "Yes",
      kalshi_price: (.yes_ask / 100),
      implied_prob: (.yes_ask / 100)
    }, {
      name: "No",
      kalshi_price: (.no_ask / 100),
      implied_prob: (.no_ask / 100)
    }],
    volume: .volume
  }]'
```

Replace SERIES_TICKER with the Kalshi series ticker for the event category.

### 4. Unified Cross-Market Comparison

When the user wants to compare the SAME event across platforms, fetch from each source and use Python to produce a unified table:

```bash
python3 -c "
import json, subprocess

def american_to_prob(odds):
    if odds < 0:
        return round(abs(odds) / (abs(odds) + 100), 4)
    else:
        return round(100 / (odds + 100), 4)

def prob_to_american(prob):
    if prob >= 1: return -99999
    if prob <= 0: return 99999
    if prob > 0.5:
        return round(-(prob / (1 - prob)) * 100)
    elif prob < 0.5:
        return round(((1 - prob) / prob) * 100)
    else:
        return 100

## Input: user provides these values (agent fills from context)
platforms = {
    'PLATFORM_1_NAME': {'yes_prob': PLATFORM_1_YES_PROB, 'no_prob': PLATFORM_1_NO_PROB},
    'PLATFORM_2_NAME': {'yes_prob': PLATFORM_2_YES_PROB, 'no_prob': PLATFORM_2_NO_PROB},
}

print('=== Cross-Market Comparison ===')
print(f\"{'Platform':<18} {'Yes Prob':>10} {'Yes Amer':>10} {'No Prob':>10} {'No Amer':>10} {'Total':>8}\")
print('-' * 70)
for name, data in platforms.items():
    y = data['yes_prob']
    n = data['no_prob']
    total = round(y + n, 4)
    y_am = prob_to_american(y)
    n_am = prob_to_american(n)
    y_am_s = f'+{y_am}' if y_am > 0 else str(y_am)
    n_am_s = f'+{n_am}' if n_am > 0 else str(n_am)
    print(f'{name:<18} {y:>10.1%} {y_am_s:>10} {n:>10.1%} {n_am_s:>10} {total:>8.1%}')

## Flag mispricings
probs = [d['yes_prob'] for d in platforms.values()]
gap = max(probs) - min(probs)
if gap > 0.03:
    print(f'\n⚠ MISPRICING: {gap:.1%} gap across platforms on Yes side')
else:
    print(f'\nPricing aligned within {gap:.1%} — no actionable gap')
"
```

The agent must fill in PLATFORM_1_NAME, PLATFORM_1_YES_PROB, etc. from the results of Operations 13. If an event only exists on two platforms, remove the third entry.

### 5. Quick Single-Value Conversion

For ad-hoc conversions when the user gives a single odds value:

```bash
python3 -c "
odds_input = 'INPUT_VALUE'
## Detect format and normalize
if odds_input.startswith('+') or odds_input.startswith('-'):
    odds = int(odds_input)
    prob = abs(odds)/(abs(odds)+100) if odds < 0 else 100/(odds+100)
    fmt = 'American'
elif '.' in odds_input and float(odds_input) <= 1:
    prob = float(odds_input)
    fmt = 'Probability'
elif odds_input.startswith('\$'):
    prob = float(odds_input.replace('\$',''))
    fmt = 'Kalshi contract'
else:
    prob = float(odds_input)
    fmt = 'Decimal probability' if float(odds_input) <= 1 else 'Unknown'

## Convert to all formats
if prob > 0.5:
    american = round(-(prob/(1-prob))*100)
elif prob < 0.5:
    american = round(((1-prob)/prob)*100)
else:
    american = 100

am_str = f'+{american}' if american > 0 else str(american)
print(f'Input: {odds_input} ({fmt})')
print(f'Implied probability: {prob:.1%}')
print(f'American odds: {am_str}')
print(f'Kalshi contract: \${prob:.2f}')
print(f'Polymarket share: {prob:.4f}')
"
```

Replace INPUT_VALUE with the user's value (e.g., "-150", "0.62", "$0.58").

# Output Rules

1. Always show implied probability as the primary format (3 decimal places, e.g., 0.620)
2. Include American odds equivalent alongside probability for sportsbook-familiar users
3. When comparing platforms, display as a table with columns: Platform, Yes Prob, Yes American, No Prob, No American, Total
4. The "Total" column (Yes + No probabilities) reveals vig  anything over 100% is overround
5. Flag gaps greater than 3% between platforms as potential mispricings
6. Always note which sportsbook was used as the sportsbook source (since books differ)
7. Report The Odds API quota after any sportsbook fetch

# Error Handling

- If ODDS_API_KEY is not set, tell the user to get a free key at https://the-odds-api.com/
- If Polymarket returns empty results for a category, try broader search terms or list available categories
- If Kalshi returns empty results, the event may not have an active contract  suggest checking kalshi.com directly
- If an event exists on one platform but not another, report what's available and note the gap
- If rate limited on any API, report the limitation and suggest which platforms can still be queried

Set Your API Key

export ODDS_API_KEY=your_key_here

Add to your shell profile (~/.zshrc, ~/.bashrc) so it persists across sessions:

echo 'export ODDS_API_KEY=your_key_here' >> ~/.zshrc

Polymarket and Kalshi market data endpoints are publicly accessible — no additional keys needed for read-only price data.

Test It

Restart OpenClaw (or wait for hot-reload if your version supports it), then try these prompts:

PromptExpected Behavior
“Compare Polymarket vs sportsbook odds on the NBA Finals winner”Fetches from both sources, normalizes to probability, shows side-by-side table
“What does -150 mean in implied probability?”Quick conversion: 60.0%, Kalshi $0.60, Polymarket 0.6000
“Is there a pricing gap on the Super Bowl between Kalshi and DraftKings?”Fetches from both, normalizes, flags any gap > 3%
“Normalize odds across all platforms for tonight’s NBA games”Fetches sportsbook odds, checks Polymarket/Kalshi for matching markets, outputs unified table
“Convert 0.72 Polymarket price to American odds”Quick conversion: -257, Kalshi $0.72

Your agent reads the SKILL.md, identifies the relevant operation, fetches from the appropriate APIs, and runs the normalization pipeline.

How It Works Under the Hood

OpenClaw skills are not plugins that execute code directly. The SKILL.md is a set of instructions that the LLM (Claude, GPT, etc.) reads and follows at runtime.

When you ask “compare Polymarket vs sportsbook odds on the election,” this is what happens:

User message → OpenClaw Gateway
  → LLM reads system prompt + active skills
  → LLM matches "compare across platforms" to cross-market-pricer skill
  → LLM runs Operation 1 (sportsbook fetch + normalize) via curl + jq
  → LLM runs Operation 2 (Polymarket fetch) via curl + jq
  → LLM collects normalized probabilities from both
  → LLM runs Operation 4 (unified comparison) via python3
  → LLM formats output table for user, flags any mispricings

The agent chains operations within a single skill — fetching from multiple sources and synthesizing the results. This is more complex than single-source skills like odds-scanner, which is why the SKILL.md includes explicit instructions for how to combine the outputs.

Extending the Skill

Add Cron-Based Cross-Market Monitoring

OpenClaw supports cron jobs. Add a scheduled task that scans for cross-market mispricings every hour:

{
  "cron": "0 * * * *",
  "prompt": "Use cross-market-pricer to compare NBA game odds across Polymarket and sportsbooks. If any event has a probability gap > 5% between platforms, alert me on Telegram with the event, gap size, and which platform is cheaper."
}

This turns manual comparison into automated mispricing surveillance — the first step toward autonomous cross-market trading.

Chain with Other Skills

The cross-market-pricer outputs normalized probability data that downstream skills consume:

  • arb-finder — Takes normalized cross-market data, calculates guaranteed-profit stake distributions when a true arbitrage exists
  • odds-scanner — Feeds raw sportsbook odds into cross-market-pricer as one of the three input sources
  • vig-calculator — Uses the “Total” column (Yes + No probability) to measure overround per platform
  • ev-calculator — Compares your true probability estimate against each platform’s normalized price to find the best +EV entry point
  • kelly-sizer — Takes the best cross-market price plus your edge estimate, outputs optimal position size

This is the power of the Agent Betting Stack — composable skills where each layer feeds the next. The cross-market-pricer is the normalization glue that makes multi-platform strategies possible.

Full Skill Series

This guide is part of the AgentBets OpenClaw Skills series. We’re building a complete library of betting-specific OpenClaw skills that map to the four layers of the Agent Betting Stack:

  • Layer 1 — Identity: Agent reputation tracking via Moltbook
  • Layer 2 — Wallet: Bankroll management, balance checking across platforms
  • Layer 3 — Trading: Odds scanning, Polymarket monitoring, Kalshi tracking, cross-market pricing (this guide), arbitrage detection
  • Layer 4 — Intelligence: EV calculation, Kelly sizing, CLV tracking, news sentiment

Each skill is a standalone SKILL.md you can install independently or compose into a full autonomous betting pipeline.

What’s Next