Skip to content

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 sizes
register ``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 rows

The 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() -> str

metrics

metrics() -> dict[str, Any]

of

of(cfg: RoundsConfig | None) -> Chosen

The 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 | None

The 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) -> None

Promote 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() -> bool

The 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) -> RefitReport

Refit 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.Timestamp

The 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()) -> ExecutionChecks

The 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]]] | None

The 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) -> str

One 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) -> str

A 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] | None

MDA 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 | None

The 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 | None

The 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) -> None

RefitRefused 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) -> Trained

Every model locked on the cards before cutoff (see the module doc).