For the complete documentation index, see llms.txt. This page is also available as Markdown.

Cross-Platform Price Monitor

Compare live best bids across Kalshi, Polymarket, and Outpoll

Use this script to compare market pricing across multiple platforms.

What this script does

  • connects to Kalshi, Polymarket, and Outpoll

  • subscribes to live order book data

  • shows the current best bid in one terminal view

Start with Setup & Helpers if you still need the shared prerequisites.

cross_platform_price_monitor.py
#!/usr/bin/env python3
"""Compare best bids across Kalshi, Polymarket, and Outpoll in real time."""
import asyncio, json, time
import websockets
from cryptography.hazmat.primitives import serialization, hashes
from cryptography.hazmat.primitives.asymmetric import padding
import base64

# ─── Configuration ───────────────────────────────────────────────────
# Kalshi (requires RSA key pair — see https://docs.kalshi.com)
KALSHI_KEY_ID = "your_kalshi_api_key_id"
KALSHI_PRIVATE_KEY_PATH = "path/to/kalshi_private_key.pem"
KALSHI_MARKET_TICKER = "KXBTC-25DEC31-T100000"  # example: BTC $100k by end 2025
KALSHI_WS = "wss://api.elections.kalshi.com/trade-api/ws/v2"

# Polymarket (no auth for market data)
POLYMARKET_ASSET_ID = "12345..."  # YES token asset_id from gamma-api.polymarket.com/markets
POLYMARKET_WS = "wss://ws-subscriptions-clob.polymarket.com/ws/market"

# Outpoll (no auth for order book)
OUTPOLL_EVENT_ID = "54ccea1a-16fd-469c-8018-84b375243e8a"
OUTPOLL_OUTCOME_ID = "b21f6fd8-b9d1-4b9f-bb79-ef141e3dcb76"  # YES runner
OUTPOLL_WS = "wss://order-book.outpoll.com/event/ws"

# ─── Shared state ────────────────────────────────────────────────────
prices = {
    "Kalshi": None,
    "Polymarket": None,
    "Outpoll": None,
}


def display():
    """Print the current best bids."""
    print("\033[H\033[J", end="")  # clear terminal
    print("═══ Cross-Platform Best Bid ═══\n")
    for platform, bid in prices.items():
        if bid is not None:
            print(f"  {platform:12s} Best Bid: ${bid:.2f}")
        else:
            print(f"  {platform:12s} Best Bid: waiting...")
    print(f"\n  Updated: {time.strftime('%H:%M:%S')}")


# ─── Kalshi ──────────────────────────────────────────────────────────
async def watch_kalshi():
    with open(KALSHI_PRIVATE_KEY_PATH, "rb") as f:
        private_key = serialization.load_pem_private_key(f.read(), password=None)

    # Sign handshake
    ts = str(int(time.time() * 1000))
    msg_to_sign = ts + "GET" + "/trade-api/ws/v2"
    sig = private_key.sign(
        msg_to_sign.encode(),
        padding.PSS(mgf=padding.MGF1(hashes.SHA256()), salt_length=padding.PSS.DIGEST_LENGTH),
        hashes.SHA256(),
    )
    headers = {
        "KALSHI-ACCESS-KEY": KALSHI_KEY_ID,
        "KALSHI-ACCESS-SIGNATURE": base64.b64encode(sig).decode(),
        "KALSHI-ACCESS-TIMESTAMP": ts,
    }

    async with websockets.connect(KALSHI_WS, additional_headers=headers) as ws:
        # Subscribe to orderbook
        await ws.send(json.dumps({
            "id": 1,
            "cmd": "subscribe",
            "params": {"channels": ["orderbook_delta"], "market_ticker": KALSHI_MARKET_TICKER},
        }))

        yes_book = {}  # price_str -> size

        async for raw in ws:
            data = json.loads(raw)
            msg_type = data.get("type")

            if msg_type == "orderbook_snapshot":
                yes_book = {p: float(s) for p, s in data["msg"].get("yes_dollars_fp", [])}
            elif msg_type == "orderbook_delta" and data["msg"]["side"] == "yes":
                p = data["msg"]["price_dollars"]
                delta = float(data["msg"]["delta_fp"])
                yes_book[p] = yes_book.get(p, 0) + delta
                if yes_book[p] <= 0:
                    yes_book.pop(p, None)

            if yes_book:
                prices["Kalshi"] = float(max(yes_book, key=float))
                display()


# ─── Polymarket ──────────────────────────────────────────────────────
async def watch_polymarket():
    async with websockets.connect(POLYMARKET_WS) as ws:
        await ws.send(json.dumps({
            "assets_ids": [POLYMARKET_ASSET_ID],
            "type": "market",
            "custom_feature_enabled": True,
        }))

        # Heartbeat
        async def heartbeat():
            while True:
                await asyncio.sleep(10)
                try:
                    await ws.send("PING")
                except Exception:
                    break

        hb = asyncio.create_task(heartbeat())

        try:
            async for raw in ws:
                if raw == "PONG":
                    continue
                events = json.loads(raw)
                if not isinstance(events, list):
                    events = [events]

                for event in events:
                    if event.get("event_type") == "book":
                        bids = event.get("bids", [])
                        if bids:
                            prices["Polymarket"] = float(max(bids, key=lambda x: float(x["price"]))["price"])
                            display()

                    elif event.get("event_type") == "price_change":
                        for pc in event.get("price_changes", []):
                            if pc.get("best_bid"):
                                prices["Polymarket"] = float(pc["best_bid"])
                                display()
        finally:
            hb.cancel()


# ─── Outpoll ─────────────────────────────────────────────────────────
async def watch_outpoll():
    async with websockets.connect(OUTPOLL_WS) as ws:
        await ws.send(json.dumps({"t": "SUBSCRIBE", "e": OUTPOLL_EVENT_ID}))

        async for raw in ws:
            data = json.loads(raw)
            if data["t"] not in ("PRICES_DATA", "PRICES_DATA_DELTA"):
                continue
            for p in data["d"]:
                if p["o"] == OUTPOLL_OUTCOME_ID:
                    prices["Outpoll"] = p["b"]
                    display()


# ─── Main ────────────────────────────────────────────────────────────
async def main():
    display()
    await asyncio.gather(
        watch_kalshi(),
        watch_polymarket(),
        watch_outpoll(),
    )

asyncio.run(main())

Example output

For a meaningful cross-platform comparison, Kalshi and Polymarket IDs must point to the same underlying event. Use the Kalshi API and Polymarket CLOB API to resolve matching market identifiers.

Last updated

Was this helpful?