#!/usr/bin/env python3 """Kalshi prediction-market odds, prices and volume to a CSV (one row per market). Runs the Apify Actor nexgensignal/kalshi-event-market-data-api once and saves the markets it returns. Default: every live Fed rate-decision market (series KXFED) that traded at least 100 contracts in the last 24 hours. export APIFY_TOKEN=your_apify_token (Windows: set APIFY_TOKEN=...) python kalshi_odds_to_csv.py # Fed rate decisions python kalshi_odds_to_csv.py --series KXCPI # CPI inflation markets python kalshi_odds_to_csv.py --keyword bitcoin --max 25 # any active market mentioning bitcoin Prices are dollars per $1 contract, so 0.31 reads as a 31% implied chance. Each row is a snapshot of what Kalshi's public API returned when the run read it (observed_at); it is not a live feed. Informational only, not trading advice. Python 3.8+, nothing to install. Cost: $0.05 per market on Apify's Free plan ($0.0335 on Gold); --cap stops the run at that many dollars. """ import argparse, csv, json, os, sys, urllib.request ACTOR = "nexgensignal~kalshi-event-market-data-api" COLS = ["ticker", "event_ticker", "market_title", "outcome", "yes_bid", "yes_ask", "last_price", "volume_24h", "volume", "open_interest", "status", "close_time", "observed_at", "source_url"] def main(): p = argparse.ArgumentParser() p.add_argument("--series", default="KXFED", help="Kalshi series ticker, e.g. KXFED, KXCPI ('' for none)") p.add_argument("--keyword", default=None, help="title or outcome contains this text") p.add_argument("--min-volume-24h", type=int, default=100) p.add_argument("--max", type=int, default=10, help="most markets returned (each is charged)") p.add_argument("--cap", type=float, default=1.00, help="maximum charge for the run, USD") p.add_argument("--out", default="kalshi_markets.csv") a = p.parse_args() token = os.environ.get("APIFY_TOKEN") or sys.exit("Set APIFY_TOKEN to your own Apify API token first.") run_input = {"statusFilter": "active", "minVolume24h": a.min_volume_24h, "maxRecords": a.max} if a.series: run_input["seriesTickers"] = [a.series] if a.keyword: run_input["keyword"] = a.keyword url = (f"https://api.apify.com/v2/acts/{ACTOR}/run-sync-get-dataset-items" f"?maxTotalChargeUsd={a.cap}&clean=true") req = urllib.request.Request(url, data=json.dumps(run_input).encode(), method="POST", headers={"Content-Type": "application/json", "Authorization": "Bearer " + token}) try: rows = json.load(urllib.request.urlopen(req, timeout=320)) except Exception as e: # noqa: BLE001 sys.exit(f"No answer from Apify ({e}). A run may still have started: check Apify Console > Runs " f"before running this again.") with open(a.out, "w", newline="", encoding="utf-8") as f: w = csv.DictWriter(f, fieldnames=COLS, extrasaction="ignore") w.writeheader() w.writerows(rows) print(f"{len(rows)} market(s) saved to {a.out}") for r in rows[:10]: print(f" {r.get('ticker')} ({r.get('outcome')}): last {r.get('last_price')} " f"(bid {r.get('yes_bid')} / ask {r.get('yes_ask')}), 24h volume {r.get('volume_24h')}") if __name__ == "__main__": main()