betting_combat.rounds.backtest.capacity
Capacity: the same bets (the same final probability, meta probability and sizing) filled
through every instrument that pays on exactly the same event, cheapest first (research
ufc/modeling/capacity.py).
VIEWS and their instruments
rounds view ‘ends this round’ k_rounds Kalshi rounds market (‘ends before round k’)
p_before Polymarket ‘ends before round k’
distance view ‘goes to the judges’ k_distance Kalshi distance
k_pair Kalshi decision pair: ‘A wins by decision’ and
‘B wins by decision’ bought together; exactly
one pays $1 on a decision with a winner (YES
bets only); a decision without one (draw, no
contest) pays the pair nothing (pair_won:
an event’s no_winner = 1; the datasets
exclude draws, so the backtests have none)
p_distance Polymarket ‘Fight to Go the Distance?’
Per bet and instrument: the quote at t, the fill from the instrument’s own tape (arrival price
after latency_s, limit = quote + cushion), its own volume cap (a share of its $ in the
window before t) and its own fee (Polymarket: 0 unless poly_fee_as_kalshi).
run(): per bet in decision order (stop.decision_order)
skip a pre-fight event (round_idx 0) unless config.trade_round0
want 0.25 x Kelly x AFML meta size x bankroll, Kelly priced on the Kalshi instrument; or,
with cap ‘joint’, 0.25 x the stake maximising expected log wealth over the fight’s
three scenarios given the distance position already held, x AFML meta size
room min(fight cap - staked on the fight, card cap - staked on the card, max_bet, want)
route cheapest all-in price (price + fee) first (equal prices in instrument-name order),
each instrument up to its volume cap, only while our probability still beats its
all-in price and more than $1 of room is left
skip the whole bet once the card’s SETTLED P&L <= -daily_stop x bankroll
(stop.SettledStop)
Functions
capacity_fight_cap
capacity_fight_cap(cap: str | float) -> floatThe fight cap as a share of bankroll (‘joint’ caps at 20 %).
instruments
instruments(events: pd.DataFrame, kalshi_trades: pd.DataFrame, minutes: pd.DataFrame | None, poly_trades: pd.DataFrame | None = None, config: BacktestConfig = BacktestConfig()) -> pd.DataFrameOne row per (bet, instrument): bet (the event’s index label), inst, price
paid if filled (NaN if missed), fee per contract at the arrival price, window $.
events needs fight_id, round_idx, market, t_entry, entry, side. minutes: the
Kalshi minute books with fight_id, ts and the pair’s asks mov_a_dec_ask,
mov_b_dec_ask (the pair is quoted only when the event’s t_entry is one of its minutes
and both asks are finite). poly_trades: the Polymarket tape, or None.
joint_kelly_stake
joint_kelly_stake(p_now: float, p_later: float, p_dec: float, held_dist: float, bet_kind: str, bet_yes: bool, price: float, wealth: float, frac: float = 0.25, max_share: float = 0.2, points: int = 201) -> floatExtra stake ($) on the new bet maximising expected log wealth over three scenarios
(finish this round / finish later / decision), given held_dist contracts already held
that pay $1 on a decision; searched on points stakes in [0, max_share x wealth].
Returns frac x the optimum.
pair_won
pair_won(won: int, no_winner: Any) -> intWhat the decision pair pays per contract: the distance bet’s result (1 on a decision),
except 0 when the decision has no winner (draw / no contest; no_winner = 1): neither
‘A by decision’ nor ‘B by decision’ pays then.
run
run(events: pd.DataFrame, inst: pd.DataFrame, use: Sequence[str], cap: str | float, vol: float, config: BacktestConfig = BacktestConfig(), kelly: float = 0.25, sizing: Literal['kelly_x_meta', 'kelly_meta_filter'] = 'kelly_x_meta') -> pd.DataFrameThe fills of every bet through the instruments in use, in time order:
event_id, fight_id, event_date, inst, meta, cost, pnl (one row per instrument filled).
events needs t_entry, t_end, fight_id, round_idx, event_id, event_date, market, side, meta, p_side, cost, won (and finish_final, finish_l1, decision_l1 for cap
‘joint’; no_winner optional); inst is instruments on the same events.
sizing: the want’s meta factor — the AFML size (the research’s capacity rule, the
default) or 1 when meta >= 0.5 else 0 (research execution.Book’s kelly_0.10).