Skip to content

betting_combat.services.rounds_inputs

The loaders: the round library’s raw inputs, served from the store exactly as the research read them from its files.

mirror_frame(store, name) pd.read_csv('ufcstats_<name>.csv')
card_segments_frame(store) pd.read_csv('card_segments.csv') (None when empty)
judge_scores_frame(store) pd.read_csv('judge_scores.csv') (None when empty)
wiki_events_json(store) Path('wiki_ufc_events.json').read_text()
ufc_tables(store, ...) UfcTables of all of the above
static_frame(store, name) pd.read_csv('ufc-master.csv' | 'fights_sportsbook.csv')
fights_kalshi_frame(store) pd.read_csv('fights_kalshi.csv')
history_tables(store) UfcTables with those three from the store
tables_before(tables, day) the tables as they stood before a card (nothing dated on
or after it)
kalshi_markets(store, series) json.load('kalshi_markets.json' | 'kalshi_distance_markets.json'
| 'props_<series>_markets.json'), in list order
kalshi_minutes(store, series) json.load('kalshi_minute_candles.json' | ... '_minutes.json'):
ticker -> candles (current shape; the library reads both),
cut to the research's windows (``rounds_windows``)
trade_tapes(store, tickers) ticker -> json.load('mm/<ticker>.json.gz')['trades']

Every CSV-shaped frame is rebuilt by writing the stored cells back as CSV text and reading it with pd.read_csv and no options, as the research did: same cells, same parser, so the same values and dtypes. Two guards sit at this boundary (reviews 2026-09-30):

  • text columns are never object (normalise_text): an object METHOD holding a missing value makes RawData.bouts’ np.select fail (rounds/features/sources.py);
  • an empty card_segments or judge_scores table is served as None, never as an empty frame (RawData._match_pairs raises a KeyError on an empty frame).

Classes

StaticFileError

Bases: StoreError

A static research file missing from the store, or whose text does not match its hash.

TextDtypeError

Bases: TypeError

A text column that cannot be normalised (mixed values): never guessed.

TradeWindowError

Bases: StoreError

A tape asked for a window its pull did not cover.

Functions

card_segments_frame

card_segments_frame(store: RoundsStore) -> pd.DataFrame | None

card_segments.csv as pd.read_csv gives it, rows in the research’s order (the event list’s order of the pages, then running order); None when none are stored.

design_trade_windows

design_trade_windows(fights: pd.DataFrame, markets: pd.DataFrame) -> dict[str, tuple[int, int]]

ticker -> (min_ts, max_ts) epoch seconds of the research’s tape design: the fight’s start - 60 minutes to its winner market’s snap + 5 minutes, in the research’s arithmetic (ufc/market_making._pull_one), on the final (consistent) fight clock.

The research’s own tapes were pulled on earlier fight-start estimates: their end is this end, their start differs by whole half-minutes (-7.5 to +2.5 minutes; the original template clock reproduces 7,261 of 7,713). To reproduce the research’s trade tables, serve the research tapes as pulled (trade_tapes without windows).

Args: fights: live_fights rows (fight_id, start, snap). markets: market_list rows (fight_id, ticker).

fights_kalshi_frame

fights_kalshi_frame(store: RoundsStore) -> pd.DataFrame

fights_kalshi.csv as pd.read_csv gives it: every stored row’s cells, rows in event_ticker order (the research’s groupby order; sorted here, never by the database’s collation), text columns normalised.

frame_from_cells

frame_from_cells(header: Sequence[str], rows: Iterable[Sequence[Any]]) -> pd.DataFrame

pd.read_csv of the CSV text the cells make (each cell written as str, an empty string as an empty cell), text columns normalised.

history_tables

history_tables(store: RoundsStore) -> UfcTables

Every input of the round library from the store: the mirror, Wikipedia, MMADecisions, the two static files and fights_kalshi (ufc_tables).

judge_scores_frame

judge_scores_frame(store: RoundsStore) -> pd.DataFrame | None

judge_scores.csv as pd.read_csv gives it; None when none are stored.

kalshi_markets

kalshi_markets(store: RoundsStore, series: str) -> list[dict[str, Any]]

A series’ markets exactly as Kalshi sent them, in the order they were listed.

kalshi_minutes

kalshi_minutes(store: RoundsStore, series: str, tickers: Sequence[str] | None = None, windows: Mapping[str, tuple[dt.datetime, dt.datetime] | None] | None = None) -> dict[str, list[dict[str, Any]]]

ticker -> the market’s 1-minute candles in time order, for every market of the series whose minutes were pulled (a pulled market without candles maps to [], as in the research’s files; a market never pulled is absent).

With windows (ticker -> [lo, hi) or None, e.g. rounds_windows.minute_windows) only the markets it names are served, each with the candles whose end lies in its window (None: none): the research’s window cut out of the full raw candles.

mirror_frame

mirror_frame(store: RoundsStore, name: str) -> pd.DataFrame

One UFCStats mirror file (ufc_event_details, ufc_fight_results, ufc_fight_stats, ufc_fighter_tott) as pd.read_csv gives it.

normalise_text

normalise_text(frame: pd.DataFrame) -> pd.DataFrame

Every object column whose values are all text (or missing) becomes pandas’ str dtype, whose missing value is NaN and whose .str methods answer False on it (object answers NaN, which np.select refuses). An object column holding anything but text is refused.

static_frame

static_frame(store: RoundsStore, name: str) -> pd.DataFrame

ufc_master or fights_sportsbook exactly as pd.read_csv(<the file>) gave it: the stored text (its SHA-256 checked) read by the same parser with no options.

tables_before

tables_before(tables: UfcTables, day: dt.date) -> UfcTables

The tables as they stood before day: every event dated on or after it removed, with its results and round stats, and every dated odds, segment and judge row on or after it. The fighter table and the Wikipedia list are kept whole (neither holds a result).

trade_tapes

trade_tapes(store: RoundsStore, tickers: Iterable[str], windows: Mapping[str, tuple[int, int]] | None = None, seeded: bool = False) -> dict[str, list[list[Any]]]

ticker -> its trades as the research’s tape rows [ts_ms, yes_price, count, taker_yes, block], sorted as the research sorted them. Only pulled markets are keys (a pulled market with no trades maps to []).

With windows (ticker -> (min_ts, max_ts), e.g. :func:design_trade_windows) each tape keeps the trades with min_ts <= created < max_ts (Kalshi’s own bounds), and a window the pull did not cover raises :class:TradeWindowError. seeded: only the research-seeded trades (the research’s own tapes, whatever a later pull added).

ufc_tables

ufc_tables(store: RoundsStore, ufc_master: pd.DataFrame, fights_sportsbook: pd.DataFrame, fights_kalshi: pd.DataFrame) -> UfcTables

Every input of :class:~betting_combat.rounds.features.RawData. The three frames not landed by this data layer (the bookmakers’ method odds, the de-vigged sportsbook lines and the Kalshi card-start prices) are passed in as pd.read_csv gave them.

wiki_events_json

wiki_events_json(store: RoundsStore) -> str

The latest ‘List of UFC events’ snapshot, verbatim.