betting_combat.services.rounds_refit
Strategy 4’s weekly refit (ROUND_SYSTEM_FLOW §4 rounds_refit_weekly): every model locked
on the cards before the next card and landed as a candidate model version, with the
diagnostics its Monitoring dashboard reads (§7).
inputs the store's raw tables (``history_tables``) and the datasets the datasets job built from them (``services.rounds_datasets``: the dataset tables, read back whole, with the cutoff year's clock fit); the refit REFUSES (``RefitRefused``, nothing registered) when the datasets were built from other raw tables or code than the store holds now, lack the cutoff year's clock, any raw input holds a row dated on or after the cutoff, or the datasets read a trade at or after it (``late_frames``)train ``rounds.training.forward_inputs`` at the cutoff (layer 1 by purged CPCV, layer 2's rows, nested layer 2, the candidate bets and their meta features), the meta model out of sample (``final_events``' three families), then ``fit_locked`` (layer 1, layer 2, meta on every card before the cutoff), the card year's clock (Group 5) and Set 3's fills -> ``RoundsArtifact``judge out-of-sample scores against the market, reliability bins, MDI importances and layer 2's MDA (``layer2_mda``), layer 2's kept features per outer fold, the PBO of the research's 7,200-setting execution grid on the out-of-sample bets (with the chosen setting among its candidates), and the chosen setting's P&L along each path of the meta model's purged CPCV (``meta_oos``). The chosen setting (``Chosen``) sizes as the strategy row says (``sizing_rule``, ``kelly_mult``, ``min_edge``), as the live engine sizesregister ``model_versions`` (strategy 'rounds', status 'candidate'), the per-version diagnostics tables, then promotion: automatic when ``auto_promote`` is on and the gate passes (``promotion_gate``; never over an active version locked to a later cutoff), else the version stays a candidate with a finding; then the model registry rowsThe cutoff is the next card’s date: the first event after today (in New York, where cards are
dated) in the stored Wikipedia list, unless given. The research froze the joint models’ final
feature lists (eval/dataset{1,2}_final_features.csv); they are fixed here
(FINAL_FEATURES_1/2).
Classes
Chosen
The execution setting the refit judges (the CPCV paths’ P&L, and a candidate of the
PBO): the strategy row’s sizing_rule, kelly_mult and min_edge, sized as the
live engine sizes (rounds.live.decision.wanted, Book.add_sizing); the fight cap,
volume share, latency and cushion are the research’s chosen ones (manifest ‘chosen’).
The defaults are the research’s chosen setting: 0.25 Kelly x the meta size, any edge,
fight cap 5 %, 15 % of the window’s volume, 1 s latency, 1 c cushion.
describe
describe() -> strmetrics
metrics() -> dict[str, Any]of
of(cfg: RoundsConfig | None) -> ChosenThe strategy row’s sizing (None: no row, the research’s chosen setting).
ExecutionChecks
The execution backtest’s judgement of a refit: the PBO of the research’s grid (with the
chosen setting among its candidates: chosen_in_grid False when it was added to the
grid), and the chosen setting’s per-card P&L (card order) and bets along each CPCV
path.
RefitRefused
Bases: RuntimeError
A training input holds a row dated on or after the cutoff: nothing is registered.
RefitReport
RoundsRefitService
One refit to a cutoff (see the module doc). The heavy steps are injectable (tests).
cutoff
cutoff(given: dt.date | None, override: bool = False) -> dt.date | NoneThe card the refit locks to. Not given: the next card in the stored Wikipedia list
after today. Given (a prep chain’s card, or the launchpad’s): taken as it is, unless it
is before today (New York) or the stored list holds a card that would be skipped (dated
from today to two days before it: a card a day either side of it is the same card,
dated by Wikipedia where it happens); then RefitRefused unless override. A
refit locked so would trade the card with models that did not see the cards before
it.
load
load(cutoff: dt.date) -> tuple[UfcTables, Frames]The raw inputs and the datasets (services.rounds_datasets), refused when the
datasets do not match the raw tables and code the store holds now or lack the cutoff
year’s clock fit, and when any input or dataset is dated on or after the cutoff.
frames (tests): the frames given instead of the dataset tables.
promote
promote(report: RefitReport) -> NonePromote the new candidate when auto_promote is on and its gate passed (never over
an active version locked to a later cutoff); otherwise it stays a candidate, with a
finding saying why.
publish_registry
publish_registry() -> boolThe Model Health registry rows of every rounds version; False when not written (logged).
refit
refit(cutoff: dt.date | None = None, force: bool = False, override_cutoff: bool = False, hold: Callable[[], contextlib.AbstractAsyncContextManager[bool]] | None = None) -> RefitReportRefit to cutoff (default and only, unless override_cutoff: the next card).
hold: the data flow’s lock, held for the whole read phase (the fingerprint, the
check for an existing refit, every input and frame), so no landing is read half-way;
not taken -> skipped. force: refit even when a version on these inputs exists. The
fingerprint is recorded on the version only once its diagnostics, promotion and
registry have all succeeded: a run that fails part-way is re-run.
Trained
Functions
cutoff_instant
cutoff_instant(cutoff: dt.date) -> pd.TimestampThe cutoff as an instant: its first moment in New York, where cards are dated (a trade at or after it is dated on or after the cutoff).
diagnostic_rows
diagnostic_rows(version: int, trained: Trained, frames: Frames, execution: ExecutionChecks | None, trained_at: dt.datetime, trained_through: dt.date | None, code_version: str | None, notes: str | None = None, mda: Mapping[str, float] | None = None) -> dict[str, list[dict[str, Any]]]Every per-version diagnostics row of one refit (ROUND_SYSTEM_FLOW §7). mda:
layer 2’s MDA (layer2_mda), written as its ‘mda’ importances.
execution_checks
execution_checks(events: pd.DataFrame, fills_grid: pd.DataFrame, paths: Sequence[pd.Series], chosen: Chosen = Chosen(), config: BacktestConfig = BacktestConfig()) -> ExecutionChecksThe PBO of the research’s execution grid (ufc/modeling/execution.py main: every
sizing rule x edge x fight cap x volume share x latency x cushion; cards as the unit,
backtest.metrics.pbo) on the out-of-sample bets, the chosen setting added as one
more candidate when the grid lacks it; and the chosen setting run once per CPCV path (the
bets’ meta replaced by the path’s), sized by the strategy’s own rule (Chosen). The
fills are tape_fills’ own, computed per fight once and read from the grid
(rounds_cache.fills_for).
folds_of
folds_of(selections: Sequence[tuple[str, list[str]]]) -> dict[str, list[list[str]]] | NoneThe kept features per outer fold of each nested target, or None when the record does not have exactly one selection per outer fold for each (the panel then stays empty).
grid_key
grid_key(*setting: str | float) -> strOne setting’s column name in the execution grid’s per-card P&L: rule, edge, fight cap, volume share, latency and cushion, joined by ’|’.
importances
importances(artifact: RoundsArtifact) -> dict[str, tuple[dict[str, float], dict[str, float] | None]]MDI per model: layer 1 (its round model’s gain shares), layer 2 (the bagged trees’ mean gain share, sd over the seeds) and the meta model (the extra trees’ mean decrease in impurity, sd over the trees / sqrt(trees): AFML 8.2). A model without trees is absent.
inputs_fingerprint
inputs_fingerprint(store: RoundsStore, cutoff: dt.date, code_version: str | None) -> strA digest of what a refit reads: every rounds table’s size and latest time, the mirror’s and the static files’ hashes, the Wikipedia list’s, the cutoff and the code version.
late_frames
late_frames(cutoff: dt.date, frames: Frames) -> dict[str, int]Rows of the built frames dated on or after cutoff, per frame (only those with any).
trades: the fights whose LAST trade the frames used (Frames.trade_last: Set 3
and the fills read their trade tapes) is at or after the cutoff’s first moment.
late_tables
late_tables(cutoff: dt.date, tables: UfcTables) -> dict[str, int]Rows of the raw inputs dated on or after cutoff, per table (only those with any).
layer2_mda
layer2_mda(trained: Trained) -> dict[str, float] | NoneMDA of the locked layer 2 (AFML 8.3, Layer2.mda): the out-of-sample log-loss
increase when one of its kept features is shuffled within the test rows, averaged over
layer 2’s own purged CPCV (6 groups, 2 out: 15 splits; one fit per split, seed 0), on the
rows it is locked on (training.fit_layer2: the side-market rows before the cutoff,
target ‘ends this round’ on layer 1’s prior). None when layer 2 kept no feature.
locked_before
locked_before(versions: Sequence[Any], card_date: dt.date) -> Any | NoneThe newest rounds version locked before card_date (its cutoff on or before it),
whatever its status: the models the card would have been traded with.
meta_oos
meta_oos(events: pd.DataFrame, feats: list[str], ind: Any) -> tuple[dict[str, pd.Series], list[pd.Series]]One pass over the meta model’s purged CPCV giving both (a) each META3 family’s
out-of-sample probability exactly as meta.oos_family computes it (every split’s
held-out predictions, concatenated in split order, averaged per bet) and (b) the meta
probability along each CPCV path (AFML 12.4): path j takes, for each time group, the j-th
split (in order) holding that group out, the three families’ mean. Each path is a complete
out-of-sample run whose meta model varies with the split; the finish probability under it
is layer 1 / 2’s split-averaged one (the pipeline keeps no other).
next_card_date
next_card_date(list_json: str, today: dt.date) -> dt.date | NoneThe first event after today in Wikipedia’s list (its scheduled events included).
promotion_gate
promotion_gate(metrics: Mapping[str, Any], min_meta_auc: float) -> list[str]Why a refit’s candidate may not be promoted by itself (empty: it may). Every refit guard
passed (a refit that failed one registered nothing; here: no problem recorded, the artifact
reloads locked to its cutoff), layer 2’s out-of-sample log loss no worse than the market’s
on the same rows, the meta model’s out-of-sample AUC at least min_meta_auc.
refuse_late_frames
refuse_late_frames(cutoff: dt.date, frames: Frames) -> NoneRefitRefused when any built frame is dated on or after the cutoff (late_frames):
run on the frames before anything is cached or registered.
scores
scores(trained: Trained) -> dict[str, Any]The refit’s out-of-sample scores (target: the fight ends this round; the market: the Kalshi ‘ends before round k+2’ mid on the same rows) and the meta model’s.
selection_log
selection_log() -> Iterator[list[tuple[str, list[str]]]]Records every forward selection layer 2’s nested procedure makes, in order, as
(target, the features kept): nested calls layer2.select_forward once per outer
fold. The library keeps no record of its outer folds; this reads them without changing a
number (the selection’s own result is returned untouched).
train
train(frames: Frames, cutoff: dt.date) -> TrainedEvery model locked on the cards before cutoff (see the module doc).