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 Nonebooks 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 tProbabilities (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) -> Nonerelease
release(fight_id: str, usd: float) -> NoneGive back a reservation that did not fill.
room
room(fight_id: str, want: float, s: Settings) -> floatThe 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) -> NoneA settled position: no longer at risk; its P&L counts toward the stop loss.
stopped
stopped(s: Settings) -> boolThe 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) -> floatThe 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]) -> FinishP(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.DataFrameLayer 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) -> strThe 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.DataFrameThe 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) -> floatDollars 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).