# Schema — Polymarket NFL Order-Book Slice All files are Parquet (zstd). Rows are sorted by token_id, then timestamp (depth also by side, level). Every day-file in this archive has exactly these columns and types; a column that did not exist yet in an early capture era is NULL there (see COVERAGE.md). ## /quotes/poly_quotes_YYYY-MM-DD.parquet — top of book, one row per token per recorder cycle | column | type | meaning | |---|---|---| | timestamp | timestamp[us, UTC] | The recorder's SAMPLE time for the cycle (~30 s cadence); every row of a cycle shares it. NOT the exchange time of the quote: the recorder writes a row only when the token's last WebSocket price update arrived at most 300 s earlier (its rule since at least 2026-06-15, when the code entered version control), so a price can be up to 300 s old. There is no quote-age column. | | token_id | string | Polymarket CLOB outcome-token id (decimal string). | | sport | string | ZenHodl's sport label from its market cache (e.g. CFB, EPL), not a Polymarket field. Equals the folder name. | | event | string | Event title from the market cache at record time. | | outcome | string | Outcome this token pays on (team, player, Yes/No, Over/Under ...), from the market cache. | | market_type | string | ZenHodl's market-type label from the cache (e.g. GAME, SPREAD, TOTAL). NULL on days before the column existed. | | segment | string | Period/segment parsed by the recorder from the market question (e.g. 'Map 2', '1st Half'); '' when none was parsed. Heuristic. NULL before the column existed. | | line | float64 | Spread/total line parsed from the market question; NULL when none was parsed or before the column existed. Heuristic. | | bid, ask | float64 | Best bid and best ask (USDC per share, 0-1) held by the recorder's WebSocket client at the sample time. | | mid | float64 | (bid + ask) / 2, as computed by the client from those two values. | | spread | float64 | ask - bid. | | bid_size, ask_size | float64 | The size the client last held for the best bid / ask. Polymarket's price-change messages carry no size, so after such a move this can belong to an earlier price. The client's validity flag was not recorded. Not validity-checked. | ## /depth/poly_depth_YYYY-MM-DD.parquet — order-book levels | column | type | meaning | |---|---|---| | timestamp | timestamp[us, UTC] | The depth recorder's sample time (~30 s cycle). A row repeats the LAST FULL BOOK message received for the token (books are not patched by incremental price changes); a book is re-emitted every cycle for up to 300 s after it arrived, so identical consecutive snapshots are re-emissions, not new observations. There is no book-receipt-time column. | | token_id, sport, event, outcome, market_type, segment, line | | As in quotes. segment/line are NULL on days before those columns existed. | | side | string | 'bid' or 'ask'. | | level | int64 | 0 = best price on that side, counting away from the touch; at most 20 levels per side are kept. | | price | float64 | Price of the level (USDC per share, 0-1). The depth recorder drops levels with price or size <= 0 (its rule since at least 2026-07-09, when the code entered version control); COVERAGE.md counts any such rows that remain. | | size | float64 | Shares resting at that level in the book message. | ## /tokens.csv — per-token coverage (derived at build time) token_id, event, outcome, market_type, segment, line (first non-null value seen); first_seen_utc, last_seen_utc; observations (quote rows); cycles_between_first_last (recorder cycles, all sports, between those two moments, inclusive); presence_share (observations / cycles_between_first_last — well below 1.0 means the token went quiet or out of the recorder's set); max_gap_s (largest gap between consecutive observations of the token, including across days). ## FILES.csv — every Parquet member path, bytes, rows, sha256 for each Parquet file; manifest.json carries the sha256 of FILES.csv and of every other member. ## days.csv — per day and sport date, sport, quote_file (ok / missing / unreadable), recorder_cycles, quote_rows, quote_tokens, depth_file, depth_cycles, depth_rows, depth_tokens. Every day of the window appears for every sport, including days with zero rows, so absence is explicit.