Schema doc shipped inside kalshi_settled_archive.zip (v1.0, June 2026), published unchanged. Erratum (2026-09-27): the attribution line at the end should read https://zenhodl.net, the ZenHodl site. # Kalshi Sports Markets — Settled Outcomes Layer — Schema & Methodology **Version 1.0 — June 2026** --- ## What you bought A **settlement layer** for Kalshi sports prediction markets: the final, real-world outcome of every game we captured, reconstructed directly from the live market feed and delivered as a clean, joinable table. Kalshi does not publish historical results synced to its market tickers. To know who won — and to tie that to the quotes that were trading at the time — you would have to ingest the live feed yourself, track every game's score progression, and infer the terminal state. We did that across **~94 million classified quote snapshots** so you don't have to. The result is a one-row-per-game outcomes table you can join to any Kalshi tick data on `game_id`. ``` kalshi_settled_archive/ ├── kalshi_game_outcomes.parquet — ALL 2,621 captured games: final score + winner ├── kalshi_ticks_sample.parquet — full tick stream for 5 example games (demo/join) ├── quickstart.py — load → join → analyze in ~20 lines ├── README.txt — quick reference └── SCHEMA.md — this document ``` **Coverage** | Sport | Games | Settled (derived winner) | | --- | ---: | ---: | | NCAAMB (men's college basketball) | 1,268 | 1,032 | | NCAAWB (women's college basketball) | 766 | 594 | | NHL | 253 | 177 | | NBA | 228 | 143 | | Soccer | 58 | 0 *(draws — not binary)* | | NFL | 30 | 29 | | CFB | 18 | 15 | | **Total** | **2,621** | **1,990 (75.9%)** | Window: **December 2025 – April 2026**. NCAA women's basketball settlement at this breadth is effectively unavailable anywhere else. --- ## `kalshi_game_outcomes.parquet` — one row per game | Column | Type | Description | | --- | --- | --- | | `game_id` | string | Stable game identifier shared by every market/tick of that game. **Join key.** | | `sport` | string | Normalized: `NBA`, `NHL`, `NFL`, `CFB`, `NCAAMB`, `NCAAWB`, `Soccer`. | | `series_ticker` | string | The Kalshi series ticker the game's markets belong to (e.g. `KXNBAGAME`). | | `home_team` | string | Home team abbreviation as reported by Kalshi (e.g. `BOS`, `USC`). | | `away_team` | string | Away team abbreviation. | | `final_home_score` | int | Final home score, derived as the max observed home score over the game (scores are monotonic). `null` if never populated. | | `final_away_score` | int | Final away score (same method). | | `winner` | string | `HOME`, `AWAY`, or `null` (tie / undetermined). | | `home_won` | int | `1` if home won, `0` if away won, `null` otherwise. Convenient binary label. | | `settled` | bool | `True` when the game reached a terminal state **and** a winner is determined. Use this to filter to clean, labeled games. | | `last_game_state` | string | The game-state string at the last observed tick (e.g. `final`, `in`, `post`). | | `n_ticks` | int | Number of quote snapshots captured for this game across all its markets. | | `first_tick_utc` | datetime | UTC of the first captured quote for the game. | | `last_tick_utc` | datetime | UTC of the last captured quote. | ## `kalshi_ticks_sample.parquet` — sample tick stream (5 games) Full-resolution quotes for five complete, settled games (3 NFL, 2 NBA) so you can run the join end-to-end. Same schema as the Kalshi side of the **Polymarket & Kalshi Orderbook Archive** (where the complete multi-month tick history lives). | Column | Type | Description | | --- | --- | --- | | `timestamp` | float64 | Unix epoch seconds (UTC). | | `ticker` | string | Kalshi market ticker (the series + event + outcome side). | | `title` | string | Human-readable market title (e.g. `"Duke at Stanford: Total Points"`, `"... Winner?"`). | | `game_id` | string | Join key to `kalshi_game_outcomes.parquet`. | | `home_team` / `away_team` | string | Team abbreviations. | | `home_score` / `away_score` | int | Live score at the snapshot. | | `score_diff` | int | `home_score - away_score`. | | `period` | string | Game period/quarter/inning label. | | `time_remaining` | string | Clock remaining in the period, where applicable. | | `game_state` | string | Live state (`pre`, `in`, `halftime`, `final`, …). | | `yes_bid` / `yes_ask` | float | Best YES bid / ask, in cents (0–100). | | `mid` | float | `(yes_bid + yes_ask) / 2`, cents. | | `spread` | float | `yes_ask - yes_bid`, cents. | | `yes_bid_size` / `yes_ask_size` | int | Size at the top of book. | | `open_interest` | int | Contracts outstanding. | | `volume_24h` | int | Trailing 24-hour contract volume. | | `liquidity_score` | float | Internal composite liquidity score. | | `tradable_now` | bool | Whether the market was open/tradable at the snapshot. | | `datetime_utc` | datetime | UTC timestamp (decoded from `timestamp`). | --- ## Settlement methodology - **Final scores** are taken as `max(home_score)` / `max(away_score)` over every tick of a game. Kalshi's in-game scores are monotonic non-decreasing, so the maximum equals the final — robust to out-of-order or sparse snapshots. - **`settled`** is `True` only when (a) the game's last observed `game_state` is a terminal one (`final`/`complete`/`closed`/`post`/`ended`/`settled`) **and** (b) the final scores yield a non-tie winner. Always filter on `settled` for training/backtests. - **Soccer** is captured (58 games) but excluded from binary settlement because draws are a valid third outcome; final scores are still present. - Markets **not tied to a game** (null/blank `game_id` — season/futures markets) are excluded from this layer. ## How to use it ```python import pandas as pd games = pd.read_parquet("kalshi_game_outcomes.parquet") ticks = pd.read_parquet("your_kalshi_ticks.parquet") # or the sample labeled = ticks.merge(games[["game_id","winner","home_won"]], on="game_id") labeled = labeled[labeled["home_won"].notna()] # clean, labeled panel ``` For the complete multi-month Kalshi (and Polymarket) tick history this layer is designed to label, see the **Polymarket & Kalshi Orderbook Archive**. --- *Licensed for the purchaser's internal analytical use. Redistribution or resale of the raw files is prohibited. Derived models and predictions may be used commercially. Attribution: "Data provided by ZenHodl (https://zenhodl.com)". This dataset carries a unique watermark traceable to the purchaser.*