betting_combat.services.rounds_data
Landing strategy 4’s raw inputs: fetch, validate, store. Every landing is idempotent
(upserts on the natural key, or an atomic swap of a whole snapshot) and resumable (each
market’s pull is recorded in kalshi_pulls with the span its stored rows cover).
The Kalshi layer is GENERIC and complete, cut to no system’s windows (the datasets layer cuts
what it needs: services.rounds_windows):
Kalshi markets the six series, archive then live settled markets, as listedminute candles every candle of every settled market, from its open to its closetrades every trade of every settled market, from its open to its closehourly candles the winner markets' (``services.rounds_odds``), as beforeUFCStats the four mirror files, whole, swapped as one snapshotWikipedia the event list, then each past event page's results tableMMADecisions each year's listing -> events -> decisions not stored yetA market’s pull is due while its recorded span does not cover its whole life; a pull asks
only for the uncovered part before and after the span (never inside it: the research tapes
seeded by the bootstrap stay exactly as they are) and widens the span to the union. So a
later rounds_data_build backfills the full history of markets first stored with a
research window.
Classes
LandReport
MirrorShrankError
Bases: RuntimeError
The mirror lost rows beyond what a correction explains: refused, never landed.
RoundsDataService
land_card_segments
land_card_segments(wiki: Wikipedia, today: dt.date, since: dt.date | None) -> LandReportThe event list, then the results table of each past event page: pages dated
from since on (re-scraped: cards change), or with since=None every past
page not stored yet. A page that cannot be read or has no table keeps its rows.
land_event_list
land_event_list(body: str, fetched_at: dt.datetime) -> intOne snapshot of the event list; returns the events it lists. A snapshot the parsers read no events or venues from is refused before anything is stored (the latest snapshot is what the feature library reads).
land_judge_scores
land_judge_scores(mmad: MmaDecisions, years: Sequence[int]) -> LandReportEvery decision of the years’ UFC events not stored yet, appended in scrape order. Positions are allocated under an advisory lock: two landings never share one.
land_kalshi_markets
land_kalshi_markets(kalshi: KalshiRest) -> LandReportEvery market of the six series: the archive, then the live settled markets; a
market in both keeps its first place and its latest JSON (research _all_markets).
A stored market’s list order is never changed (the research’s order, seeded, stays; the first of two listings of one fight stays first); a new market is appended after the series’ last.
land_kalshi_minutes
land_kalshi_minutes(kalshi: KalshiRest, closed_since: dt.datetime | None) -> LandReportEvery minute candle of every settled market (closed since closed_since, or
all), from its open to its close: only the part of its life its stored candles do not
cover yet, a few days per request. A market whose pull raises is skipped and recorded;
a price in cents fails the step once the others have landed.
land_kalshi_trades
land_kalshi_trades(kalshi: KalshiRest, closed_since: dt.datetime | None) -> LandReportEvery trade of every settled market (closed since closed_since, or all), from
its open to its close: only the part of its life its stored trades do not cover yet,
from the endpoints :meth:_trades picks with Kalshi’s cutoff read again for every
batch (one trade id is one trade: research market_making._pull_one). A market
whose pull raises is skipped and recorded.
land_mirror
land_mirror(texts: Mapping[str, str], base_url: str) -> LandReportParse (the header and shape are checked), refuse a file with 1% fewer rows than the largest snapshot landed (so a slow leak is refused too), count the bout sides whose fighter id the library must split by a shared name (a finding), and swap the snapshot in one transaction.
land_ufcstats
land_ufcstats(client: httpx.AsyncClient, base_url: str) -> LandReportThe four mirror files, fetched whole and landed as one snapshot.
Functions
card_rows
card_rows(event_date: dt.date, page: str, parsed: Sequence[Mapping[str, Any]], at: dt.datetime) -> list[dict[str, Any]]fighter_id_rows
fighter_id_rows(frames: Mapping[str, pd.DataFrame]) -> list[dict[str, Any]]Each bout side’s fighter id exactly as the feature library assigns it
(RawData.bouts), with the shared names it had to split marked (the mirror landing
counts those as a finding; nothing stores the rows: the library derives them itself).
gaps
gaps(life: Span, covered: Span | None) -> list[Span]The parts of life outside covered (before it, after it): what a pull still
has to ask for. covered None: the whole life.
judge_rows
judge_rows(parsed: Sequence[Mapping[str, Any]], first_position: int, at: dt.datetime) -> list[dict[str, Any]]Scorecard rows in page order from first_position on; dup numbers repeats.
judge_years
judge_years(today: dt.date, build: bool) -> list[int]The listing years to read: every year from 2005 on a build; this year (and last year during January, when late decisions of December still land) on an update.
market_life
market_life(m: Mapping[str, Any]) -> Span | None[open - 1 hour, the later of close and settlement + 1 hour) of a market (None: Kalshi gave no open or close time).
market_rows
market_rows(series: str, listed: Sequence[tuple[dict[str, Any], str]]) -> list[dict[str, Any]]A series’ markets in list order (listed: (market JSON, ‘historical’ | ‘live’ |
‘research’)) as rows of kalshi_series_markets.
minute_rows
minute_rows(ticker: str, candles: Sequence[dict[str, Any]]) -> tuple[list[dict[str, Any]], int]One market’s candles as rows of kalshi_series_minutes (a price in cents raises
CentsPriceError), and how many repeated a minute already seen (the first is kept,
as the research’s candle_frame keeps it).
partial_tables
partial_tables(**given: Any) -> UfcTablespull_row
pull_row(ticker: str, kind: str, window: tuple[dt.datetime, dt.datetime] | None, rows: int, source: str, at: dt.datetime, base: tuple[dt.datetime, dt.datetime] | None = None) -> dict[str, Any]A kalshi_pulls row: window = the span [lo, hi) the market’s stored rows cover;
base = a research-seeded market’s own span (kept through every later widening).
series_of
series_of(ticker: str) -> strtrade_rows
trade_rows(ticker: str, trades: Mapping[str, tuple[Trade, str]]) -> list[dict[str, Any]]One market’s trades (trade_id -> (trade, endpoint)) as rows of kalshi_trades.
venue_rows
venue_rows(list_json: str, sha256: str) -> list[dict[str, Any]]Every event’s venue, as the feature library reads it (CardContext.venues): the
event-list landing’s check that the library can read the snapshot (nothing stores them:
the library reads the venues from the snapshot itself).
widened
widened(covered: Span | None, asked: Sequence[Span]) -> Spancovered and the spans just pulled, as one span (they adjoin it).