Skip to content

betting_combat.services.rounds_odds

The Kalshi card-start prices (fights_kalshi): one row per settled Kalshi fight with both winner books before the card’s first fight and before its own close, the de-vigged price and its quality flag — the pre-fight price p_mkt_a of every fight without a sportsbook line (rounds.features.sources.RawData.odds) and the fight list of the market chain (rounds.market.build_fights).

Port of the research ufc/pull.py (pull_kalshi, _quote, _close, pull_events, classify_event, _attach_events) with identical numbers and the file’s own cell text:

sides every winner market with a YES/NO result: its fighter, whether it won,
its close, the card date in its event ticker
card_first_close per card date: the earliest close among the markets closing within 8
hours of the date's median close (the card-start proxy)
quote the last hourly candle ending at or before a limit: its YES bid and ask
closes, and the contracts traded in the 24 hours to the limit
fight rows per event with exactly two sides, one YES: 'card' quotes at
card_first_close - 60 min, 'close' quotes at the fight's close - 60 min;
prob = each mid normalised across the two books (``card_quote``, the
live engine's rule), vig, payout net of the taker fee, quote_ok
events Wikipedia's past events (event name, date), attached by date (+/- 1 day),
unlisted midweek cards = Dana White's Contender Series

The hourly candles are the research’s: the 48 hours to each market’s close, 60-minute periods, from /historical when the market settled before Kalshi’s cutoff.

A fight’s row is derived once, the day after its card at the earliest (every fight of a card has settled by then; the research pulled after the cards), and never rewritten.

Classes

OddsReport

RoundsOddsService

Extends fights_kalshi after every card: the hourly candles of every settled winner market of a derivable fight, then the fight’s row.

derive

derive(s: pd.DataFrame, events_todo: Sequence[str]) -> OddsReport

The rows of events_todo whose two markets’ candles are stored (the others wait for their candles), inserted; a stored row is never rewritten.

derive_due

derive_due(since: dt.date | None = None) -> OddsReport

The datasets job’s half: the rows of every due fight whose candles are stored.

land

land(kalshi: KalshiRest, since: dt.date | None = None) -> OddsReport

Pull the candles of every due fight, then derive the rows (both halves at once).

land_candles

land_candles(kalshi: KalshiRest, since: dt.date | None = None) -> OddsReport

The data job’s half: the hourly candles of every due fight’s winner markets (raw).

pending

pending(since: dt.date | None = None) -> tuple[pd.DataFrame, list[str]]

(sides of every stored winner market, the events whose row is due and missing): exactly two YES/NO markets, one YES, the card at least a day old (dated on or after since when given).

pull_candles

pull_candles(kalshi: KalshiRest, tickers: Sequence[str]) -> OddsReport

The hourly candles of tickers not stored yet (the research’s window and endpoint rule); a market whose pull raises is counted and retried next run.

Functions

attach_events

attach_events(df: pd.DataFrame, events: pd.DataFrame) -> pd.DataFrame

event_name and event_type by date: the first event listed on that date, else one day either side (research _attach_events); event_type ‘unknown’ when none.

card_date_of

card_date_of(event_ticker: str) -> dt.date | None

cell

cell(value: Any) -> str

One value as the research’s to_csv wrote it: ” for a missing value, True/False, a float by its shortest round-trip repr, a date ISO, a timestamp as pandas prints it.

cells_of

cells_of(df: pd.DataFrame) -> list[dict[str, str]]

Each row as {column: its CSV cell}.

classify_event

classify_event(name: str) -> str

The card type from its Wikipedia name (research classify_event).

fight_rows

fight_rows(s: pd.DataFrame, candles: Mapping[str, Sequence[Mapping[str, Any]]], events: pd.DataFrame) -> pd.DataFrame

The research’s fights_kalshi rows of every event in s with exactly two sides and one winner (s: sides rows, possibly of a few events, their card_first_close computed over every market of their dates; candles: ticker -> hourly candles, one entry per market of those events).

past_events

past_events(list_json: str) -> pd.DataFrame

Wikipedia’s past events (research pull_events): event_name (footnotes removed), date (a datetime.date), location and event_type, in the list’s order (newest first), rows without a readable date dropped.

quote

quote(candles: Sequence[Mapping[str, Any]], limit: pd.Timestamp) -> dict[str, Any]

The book at limit from hourly candles (research _quote): the last candle ending at or before it, and the contracts traded in the 24 hours to it.

research_close

research_close(candle: Mapping[str, Any], side: str) -> float | None

A candle’s close for side (research _close: live candles carry ‘close_dollars’, archived ones ‘close’).

sides

sides(markets: Iterable[Mapping[str, Any]]) -> pd.DataFrame

Every winner market with a YES/NO result (research pull_kalshi’s sides): ticker, event_ticker, fighter, won, close_time, date, card_first_close.