Skip to content

betting_combat.rounds.live.decision

The rounds strategy’s decision at one round start: layer 1 -> layer 2 -> the distance chain -> candidates -> meta -> size -> caps -> route. Pure: every input is an argument.

pre the fight's Dataset 1 row for this round start (``upcoming.build_upcoming``)
l1 layer 1's columns for it (``layer1_table``, once per card)
main Set 3 main at t (``windows.main_row``); side: Set 3 side, or None
books the Kalshi books at t by A-framed market name (``win_a``, ``distance``,
``rounds_before_<k>``, ``mov_a_dec``, ``mov_b_dec``, ...), ``windows.Book``
windows each market's dollars traded in the ``vol_window_minutes`` before t

Probabilities (research training.forward, models.candidates.candidate_events): ends this round layer 2 = mean(bagged trees, logistic) where the row has side markets, else layer 1 decision (1 - ends this round) x layer 1’s no-finish chance in every later round = (1 - p_round) x l1_p_decision / (1 - l1_p_finish_round) Candidates: per market (rounds ‘ends before round r+2’, distance) the YES edge p - ask - fee(ask) and the NO edge (1 - p) - (1 - bid) - fee(1 - bid); a candidate has a positive edge; its side is the larger edge (YES on a tie). No outcome is read (a live row has none).

Size (research backtest.capacity.run; wanted, CardState): want = kelly x max((p_side - cost) / (1 - cost), 0) x the meta factor x sizing_bankroll, the factor by sizing_rule (the AFML size of meta, or 1 when meta >= 0.5 else 0); room = min(fight_cap_pct x sizing_bankroll - staked on the fight, card_cap_usd - staked on the card (no card term when card_cap_usd is None), max_order_pct x sizing_bankroll, want); nothing once the card’s SETTLED P&L <= -stop_loss_usd (never with no stop loss). The room has no cash term: live, the Kalshi cash at send time caps every send (trading.rounds.RoundsRunner.finalize).

Route: the bet’s instruments cheapest all-in first (rounds: the rounds market; distance: the distance market and the decision pair — both fighters’ ‘by decision’, YES only, which pays NOTHING on a draw, so its probability is p x (1 - draw share)); each while its probability beats its all-in price and more than $1 of room is left, up to the volume share of its recent dollars. Live orders are IOC limits at the quote + the cushion, in whole contracts sized on that limit (the worst fill), so no cap can be passed by a fill. contracts='fractional' is the research’s accounting (contracts = dollars / the fill price, the fee on top): the parity replay only.

Classes

Allocation

What one instrument buys: contracts (of each leg) and the dollars reserved for them (whole contracts: at the limits, the most they can cost; fractional: the research’s cost at the price).

Candidate

One bet the model would make at the quote (models.candidates.candidate_events without the outcome columns).

as_json

as_json() -> dict[str, Any]

CardState

The card’s running money: staked per fight and on the card, at risk now (unsettled) and the settled P&L. Every dollar is the all-in cost of FILLED contracts (a reservation for an order in flight counts until its fill is known).

commit

commit(fight_id: str, usd: float) -> None

release

release(fight_id: str, usd: float) -> None

Give back a reservation that did not fill.

room

room(fight_id: str, want: float, s: Settings) -> float

The most this bet may stake (research capacity.run’s room): the fight cap, the card cap (when set), the order cap and the want.

settle

settle(staked: float, pnl: float) -> None

A settled position: no longer at risk; its P&L counts toward the stop loss.

stopped

stopped(s: Settings) -> bool

The stop loss: the card’s settled P&L has lost stop_loss_usd (never with none).

Finish

P(ends this round) and what it came from.

Instrument

One way to hold a bet’s view: its price per contract (a pair: both legs), the fee, the probability it pays, and the dollars traded in it in the window before t (a pair: the thinner leg’s).

limits

limits(cushion: float) -> tuple[float, ...]

Each leg’s IOC limit: its quote + the cushion, in whole cents (rounded up, so the quote itself is inside), at most 99c.

worst_allin

worst_allin(cushion: float) -> float

The most one contract (of each leg) can cost filled at the limits, fees in.

Leg

One market an instrument buys: its A-framed name, the side and the quote.

Settings

The strategy’s knobs (strategies row rounds, the optimised core) and Sam’s money controls in dollars (trading.rounds.resolve_money of the portfolio row): sizing_bankroll (the base of every core %), card_cap_usd (the most staked on one card; None = no card cap) and stop_loss_usd (no new bet once the card’s SETTLED P&L <= -it; None = no stop). Live, the Kalshi cash at send time caps every send (trading.rounds.RoundsRunner.finalize).

Functions

candidates

candidates(fin: Finish, books: Mapping[str, Book], fight_id: str, round_idx: int, markets: Iterable[str] = MARKETS, band: tuple[float, float] | None = None) -> list[Candidate]

The bets with a positive edge at these books: the rounds market, then the distance market (the research’s order). A market without a two-sided book (0 < bid < ask < 1) gives none; no book at all gives no candidates. band: a market whose mid is outside it (its result already priced) gives none (live; the research had no such rule).

finish

finish(artifact: RoundsArtifact, row: Mapping[str, Any]) -> Finish

P(ends this round) for one layer-2 row.

kalshi_instruments

kalshi_instruments(c: Candidate, books: Mapping[str, Book], windows: Mapping[str, float], s: Settings) -> list[Instrument]

The bet’s Kalshi instruments in the research’s order: rounds -> the rounds market; distance -> the distance market, then (YES only, both asks known) the decision pair.

layer1_table

layer1_table(artifact: RoundsArtifact, pre_rows: pd.DataFrame) -> pd.DataFrame

Layer 1’s columns (l1_*) for every round start the clock prices (m_S_1 and m_p0 known), keyed by fight_id and round_idx. Computed once per card: layer 1 reads only pre-fight columns and the round number.

layer2_row

layer2_row(pre: Mapping[str, Any], l1: Mapping[str, Any], main: Mapping[str, Any], side: Mapping[str, Any] | None) -> dict[str, Any]

One layer-2 row (layer2.layer2_data): Set 3 main, Dataset 1, layer 1’s columns and Set 3 side as side_<column> (absent when the row has no side markets).

market_name

market_name(market: str, round_idx: int) -> str

The Kalshi market a candidate is quoted on (A-framed): the rounds market ‘ends before round r+2’ at the start of round r+1, or the distance market.

meta_frame

meta_frame(cands: Sequence[Candidate], row: Mapping[str, Any], core: Sequence[str]) -> pd.DataFrame

The meta model’s rows (models.meta.features): the round start’s core features (layer 1, Set 3 trade, Set 1, Set 3 side), then the bet’s own (they win a clash: spread is the bet’s book spread, as in training) and layer 2’s own probability l2_p_finish (the bagged trees; NaN without side markets).

route

route(instruments: Sequence[Instrument], room: float, s: Settings) -> list[Allocation]

Spread one bet’s room over its instruments cheapest all-in first (research capacity.run): stop once no more than $1 of room is left or the instrument’s probability no longer beats its all-in price; each takes at most the volume share of its window’s dollars; fewer than one contract is skipped.

wanted

wanted(c: Candidate, meta: float, s: Settings) -> float

Dollars the bet wants before caps: kelly x Kelly fraction x the meta factor x bankroll (research capacity.run / execution.Book, in their order of operations). The meta factor (sizing_rule): ‘kelly_x_meta’ the AFML size of the meta probability (2 N(z) - 1, 0 at or below one half; research kelly_x_meta); ‘kelly_meta_filter’ 1 when the meta probability is at least one half, else 0 (research kelly_0.10 at kelly 0.10).