betting_combat.rounds.features.checks
Checks on the built round datasets. Each check recomputes something the build produced by a
different, slower route, or states an invariant that must hold. Returns a structured report
(CheckReport) a job or dashboard can store and alert on; CheckReport.text() is the
research’s printout.
rows and targets rows per fight = rounds started; round_idx 0..n-1; one finish row, the last; targets constant within a fight; A/B random; W/L results only as-of features n_fights and momentum (h=2) recomputed by brute force for sampled rows round stats cumulative A-B significant strikes = the rounds before (the research’s 400-fight sample, and every row); Group 3 zero at round 0 matchup symmetry every d_x = x_a - x_b leak scan no input almost determines a target (|corr| > 0.8 flagged) missingness a blank must not predict the result beyond the market’s price model features the walk-forward clock is calibrated every year, never degenerate live markets Kalshi / Polymarket on the right side and clock set 3 no empty cells; the book price = Set 1’s Kalshi price; window sums recomputed from the raw trades before the row coverage per year: fights, round rows, the share with each price source
Every check takes its tables as arguments (nothing reads a file). passed is None where
the research states no rule (a number to look at).
Research: ufc/dataset/checks.py and ufc/dataset/report.py (data_section: the
coverage table and the column counts). Where the research left an order to a sort’s tie
handling (the 400 fights of the round-stats check) or to Python’s hashing (the result set),
the order here is fixed; the numbers are the research’s. One check is added
(cum_sig_l_all_rows): the research’s round-stats sample cannot fail.
Classes
Check
One result: section and name identify it, value is the measurement,
passed True / False where a rule exists (None: informational), line the text.
CheckReport
section
section(name: str) -> list[Check]text
text() -> strThe research’s printout: ’== section’ then every line indented.
to_frame
to_frame() -> pd.DataFrameFunctions
check_asof
check_asof(fp: pd.DataFrame, fights_long: pd.DataFrame, n: int = 300, seed: int = 0) -> list[Check]Recompute n_fights and mom_h2 for n sampled fighter rows by brute force from
fights_long (FighterHistory.fights_long: fighter_id, date, won, lost).
check_leak_scan
check_leak_scan(d2: pd.DataFrame, catalogue: pd.DataFrame, targets: Sequence[str] = LEAK_TARGETS) -> list[Check]No model input should almost determine a target: the two numeric columns (targets and live prices left out) with the largest |corr| per target; above 0.8 fails.
check_markets
check_markets(d1: pd.DataFrame) -> list[Check]Live prices: on the right side (A) and the right clock.
check_missingness
check_missingness(d1: pd.DataFrame) -> list[Check]For every fighter-A column blank on > 0.5 % of pre-fight rows with a market price: A’s win rate on the blank rows minus the market’s price there; > 3 standard errors fails (’<— LOOK’).
check_model_features
check_model_features(d1: pd.DataFrame) -> list[Check]Walk-forward model outputs: calibrated on average every year, never degenerate.
check_round_stats
check_round_stats(d2: pd.DataFrame, round_stats: pd.DataFrame, catalogue: pd.DataFrame, n_fights: int = 400) -> list[Check]round_stats: RawData.round_stats (fight_id, round, slot, sig_l). A fight’s last row
must carry the A-B significant strikes of the rounds before it (the research’s check:
the first n_fights fights by their last row’s round, ties in data order; those are
first-round finishes, where both sides are 0) and so must every row (added here).
check_rows
check_rows(d1: pd.DataFrame, bouts: pd.DataFrame) -> list[Check]bouts: RawData.bouts (fight_id, kind, sched_rounds, finish_round).
check_set3
check_set3(set3_main: pd.DataFrame, set3_side: pd.DataFrame, d1: pd.DataFrame, trades_main: pd.DataFrame, fights: pd.DataFrame, n: int = 200, seed: int = 0) -> list[Check]Set 3: no empty cells; the book price = Set 1’s Kalshi price; the last round’s traded
dollars recomputed by brute force from the raw trades before the row (n sampled
rows). trades_main: fight_id, ts, dollars; fights: fight_id, start (the clock).
check_symmetry
check_symmetry(d1: pd.DataFrame) -> list[Check]Every d_ column must equal its _a minus _b pair where one exists (blanks as 0).
column_groups
column_groups(catalogue: pd.DataFrame) -> pd.DataFrameColumns per (group, when known), and which datasets carry them (the stats-feed columns are Dataset 2 only).
coverage_by_year
coverage_by_year(d1: pd.DataFrame) -> pd.DataFramePer year: fights, round rows, and the share of fights with each source (pre-fight odds, method odds, Wikipedia card segment, judges, Kalshi, Polymarket), and the split.
run_checks
run_checks(d1: pd.DataFrame, d2: pd.DataFrame, fp: pd.DataFrame, catalogue: pd.DataFrame, bouts: pd.DataFrame, round_stats: pd.DataFrame, fights_long: pd.DataFrame, set3_main: pd.DataFrame | None = None, set3_side: pd.DataFrame | None = None, trades_main: pd.DataFrame | None = None, fights: pd.DataFrame | None = None) -> CheckReportEvery check in the research’s order (Set 3 only when its tables are given).