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 abovestatic_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 storetables_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 orderkalshi_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 objectMETHODholding a missing value makesRawData.bouts’np.selectfail (rounds/features/sources.py); - an empty
card_segmentsorjudge_scorestable is served asNone, never as an empty frame (RawData._match_pairsraises 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 | Nonecard_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.DataFramefights_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.DataFramepd.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) -> UfcTablesEvery 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 | Nonejudge_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.DataFrameOne 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.DataFrameEvery 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.DataFrameufc_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) -> UfcTablesThe 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) -> UfcTablesEvery 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) -> strThe latest ‘List of UFC events’ snapshot, verbatim.