Skip to content

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 listed
minute candles every candle of every settled market, from its open to its close
trades every trade of every settled market, from its open to its close
hourly candles the winner markets' (``services.rounds_odds``), as before
UFCStats the four mirror files, whole, swapped as one snapshot
Wikipedia the event list, then each past event page's results table
MMADecisions each year's listing -> events -> decisions not stored yet

A 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) -> LandReport

The 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) -> int

One 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]) -> LandReport

Every 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) -> LandReport

Every 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) -> LandReport

Every 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) -> LandReport

Every 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) -> LandReport

Parse (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) -> LandReport

The 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) -> UfcTables

pull_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) -> str

trade_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]) -> Span

covered and the spans just pulled, as one span (they adjoin it).