Guide · 10 min read

Build a Kalshi event-contract bot.

Kalshi lists binary event contracts on economics, weather, politics, and more. Because contracts resolve to 0 or 100, position sizing and fill discipline matter more than any clever signal. This guide walks through the minimum viable Kalshi bot end-to-end.

What you'll build

A Python bot that authenticates against Kalshi's REST API, subscribes to a handful of event contracts, quotes both sides with an inventory-aware skew, and writes every fill to a log the ScriptSamurai forward-test harness can replay.

1. Prerequisites

  • — A verified Kalshi account with API access enabled.
  • — Python 3.11+, with httpx, pydantic, websockets.
  • — A written hypothesis you can invalidate. "Election contracts are inefficient" is not one.
pip install httpx pydantic websockets python-dotenv

2. Authenticate

Kalshi uses email + password for a short-lived token. Cache it and refresh before it expires; never re-login on every request.

import httpx, os

BASE = "https://trading-api.kalshi.com/trade-api/v2"

def login() -> str:
    r = httpx.post(f"{BASE}/login", json={
        "email": os.environ["KALSHI_EMAIL"],
        "password": os.environ["KALSHI_PASSWORD"],
    }, timeout=10)
    r.raise_for_status()
    return r.json()["token"]

TOKEN = login()
HEADERS = {"Authorization": f"Bearer {TOKEN}"}

3. Pick a market universe

Do not scan the whole exchange. Pick 3–5 event series where you understand the resolution mechanics — CPI, non-farm payrolls, temperature ranges — and pull their active contracts.

def list_markets(series_ticker: str):
    r = httpx.get(f"{BASE}/markets",
                  params={"series_ticker": series_ticker, "status": "open"},
                  headers=HEADERS, timeout=10)
    return r.json()["markets"]

4. Quote with inventory skew

The simplest edge on event contracts is disciplined liquidity provision. Quote both sides around a fair value, but shade the price away from any inventory you already hold — so fills push you back toward flat.

SPREAD = 0.02          # 2¢ minimum spread
SKEW   = 0.005         # nudge per unit of inventory

def quote(mid: float, inventory: int) -> tuple[float, float]:
    adj = SKEW * inventory
    bid = round(mid - SPREAD/2 - adj, 2)
    ask = round(mid + SPREAD/2 - adj, 2)
    return max(0.01, bid), min(0.99, ask)

5. Send orders safely

Cancel-replace on every quote change. Always cap notional per market and enforce a daily loss kill.

def place(ticker: str, side: str, price: float, count: int):
    r = httpx.post(f"{BASE}/portfolio/orders",
                   json={
                     "action": "buy", "side": side, "ticker": ticker,
                     "type": "limit", "yes_price": int(price * 100),
                     "count": count, "client_order_id": new_uuid(),
                   },
                   headers=HEADERS, timeout=10)
    r.raise_for_status()
    return r.json()["order"]

MAX_NOTIONAL = 500     # $ per market
DAILY_LOSS_KILL = 100  # $ hard stop

6. Forward-test wiring

Wrap the client so every intended order is written to a JSONL log with the top-of-book snapshot at the same timestamp. That log is exactly what the ScriptSamurai forward-test harness replays, so if you build it this way from day one you can submit without refactoring.

# Every strategy call should return a list of intents, not raw HTTP calls.
class Intent(pydantic.BaseModel):
    ts: float
    ticker: str
    side: Literal["yes", "no"]
    price: float
    count: int

def on_tick(state) -> list[Intent]: ...

7. Common mistakes

  • Assuming symmetry. A YES fill at 0.30 is not the same risk as a NO fill at 0.70 — resolution is 0/100, not gaussian.
  • Ignoring settlement risk. Contracts resolve at the source's official data print. Model the exact release you're trading, not a headline number.
  • No cancel discipline. Stale quotes on Kalshi's thin books get picked off first.

8. Frequently asked questions

Is Kalshi legal for automated trading?
Kalshi is a CFTC-regulated exchange in the United States and permits API-based trading under its published terms. Confirm you meet the eligibility requirements and follow the exchange's rate limits.
How is Kalshi different from Polymarket for a bot?
Kalshi contracts are regulated event contracts settled in USD, with a different fee schedule and tighter identity requirements. Liquidity is often shallower per market but more predictable around scheduled events like CPI or FOMC.
What edges actually work on event contracts?
Short-horizon mispricing around scheduled data releases, liquidity provision on thin books, and cross-venue arbitrage against related markets. Deep directional bets almost always underperform disciplined quoting.
How do I forward-test on Kalshi without risking capital?
Route your orders through a shadow account or wrap the Kalshi client in a paper-trading layer that logs intended fills against live top-of-book. That is the same shape the ScriptSamurai forward-test harness uses.

Built one? Prove it in the dojo.