Skip to content
← Research papers

Research paper

What carries across sports

Which reliability comparisons are supported across NBA, MLB, soccer and tennis, and what a shared metric scale does not establish

Abstract

CourtVision runs the same reliability decomposition and the same skill-score formula in more than one sport, but an identical formula run in two places does not by itself make the two results comparable. kernel_transfer.json (recomposed 2026-09-17) records five sport/market comparability decisions and marks only two reliability_comparable: mlb moneyline_ingame and soccer_intl moneyline_ingame, because both score a probability against a resolved binary win/loss outcome on the same 0-1 Murphy reliability scale. The other three are not comparable by construction: mlb totals_margin is scored with CRPS (n=671); nba win-probability stores per-checkpoint Brier means with no reliability split (n=6,371); tennis has no market-side probability at all (n=55,075). Both comparable rows were recomposed from the revision-2 Murphy decomposition on 2026-09-17: the reliability gap is 0.002194 for mlb (n=27,351) and 0.103894 for soccer_intl (n=4,265). The sign agrees in both sports, the model being the less reliable side, but the magnitudes differ by about 47.4-fold and the two rows no longer attribute the gap to the same thing -- mlb's verdict reads resolution-driven, soccer_intl's now reads reliability-driven and fixable by recalibration. The file itself calls a two-sport sign match a coincidence worth re-checking, not a pattern. cross_sport_scoreboard.json, which was not rebuilt and reads a different corpus, mixes a Brier-scale and a CRPS-scale family inside one paired_delta_mean column; its ten mlb_ingame CRPS rows split 7 UNDERPOWERED to 3 MODEL_SHARPER_PROVISIONAL. brier_skill_scores.json, rebuilt on 2026-09-16, still returns a BSS below zero against the market at every grain in both sports.

1 Question

CourtVision computes the same two cross-sport statistics in more than one sport: the Murphy reliability component of a Brier score, and the Brier Skill Score (BSS) against a market and a climatology reference. That raises a question before any cross-sport reading: is the result comparable, or does it only look comparable because the formula and scale match? kernel_transfer.json answers that row by row.

This paper works through three things: the five comparability decisions in kernel_transfer.json, two marked reliability_comparable and three not; cross_sport_scoreboard.json's 17 paired-delta rows, which put one column shape across two different metrics; and brier_skill_scores.json, where the identical BSS formula runs on populations differing sharply in size and base rate.

2 Data and definitions

kernel_transfer.json stores one row per sport/market combination under rows[], with fields including reliability_model, reliability_market, reliability_gap, reliability_unit, reliability_comparable, comparability_reason, verdict and n. Its method field states the file is composition only: numbers copied verbatim from murphy_decomposition.py and soccer_calibration_pack.py output, nothing recomputed. It carries 5 rows and a generated_at of 2026-09-17T00:05:53Z, recomposed after murphy_decomposition.json was rebuilt, so its comparable rows read the segment-clean corpus.

cross_sport_scoreboard.json stores 17 rows (generated_at 2026-07-22T20:15:22Z) with fields sport, market, checkpoint, n, paired_delta_mean, paired_delta_95ci and verdict, plus a source path per row. Its method field is the same composition-only statement. The paired_delta_mean column is not one metric: for the nba and soccer_intl rows it is a Brier-scale delta (-0.1075 to +0.0019), for the mlb rows a CRPS-scale delta (-0.0523 to +1.7940). Nothing in the schema flags that difference.

brier_skill_scores.json reports the standard BSS formula, BSS = 1 - Brier / Brier_ref, against two references (climatology and market) for mlb (n_rows 27,351, sport_base_rate 0.4897) and soccer_intl (n_rows 4,265, sport_base_rate 0.6911). Its floors field states a rule for itself, n>=30 rows per grain before a BSS is reported, and every grain listed carries below_floor: false. It was rebuilt on the segmented corpus; generated_at is 2026-09-16T22:17:01Z.

3 Method

Every number below is copied from kernel_transfer.json, cross_sport_scoreboard.json, murphy_decomposition.json or brier_skill_scores.json; none is re-scored. Two quantities are trivial arithmetic on cited fields: the ratio of mlb to soccer_intl corpus size (27,351 / 4,265, about 6.4), and the share of each sport's named grains that sum to its own all grain. The roughly 47.4-fold ratio between the two reliability gaps is not computed here; kernel_transfer.json's reliability_transfer_summary publishes it.

The verdict labels quoted throughout are copied verbatim from the source files' own verdict fields; this paper does not re-run the underlying tests.

4 Results: the five comparability decisions

Cross-sport reliability comparability decisions (source kernel_transfer.json, generated 2026-09-17, recomposed from the revision-2 Murphy decomposition)
SportMarketnReliability comparableReliability gap (model - market, 0-1 scale)
mlbmoneyline_ingame (murphy reliability decomposition)27351yes+0.002194
mlbtotals_margin (CRPS distributional, pregame+ingame)671nonot applicable -- CRPS, no reliability axis
soccer_intlmoneyline_ingame (murphy reliability decomposition)4265yes+0.103894
nbawinprob_ingame (Brier, no reliability decomposition)6371nonot applicable -- no 10-bin split computed
tennispregame_prior + ingame_surface_gate (no market side)55075nonot applicable -- no market_prob side

yes/no reproduces reliability_comparable verbatim; the gap is reliability_model minus reliability_market as stored. Both comparable rows are revision 2 on the segment-clean corpus; the withdrawn revision-1 pair is not restated here.

  • Both comparable rows share one reason, given verbatim: both sides are Brier scores of a probability against a binary outcome, so the reliability component sits on the same 0-1 scale.
  • mlb totals_margin (n=671), not comparable: CRPS scores a run-count distribution, not a binary outcome probability, so Murphy reliability is undefined for it here.
  • nba winprob_ingame (n=6,371), not comparable: nba's benchmark stores per-checkpoint Brier MEANS only -- no 10-bin Murphy split has been computed for nba win-prob.
  • tennis pregame_prior + ingame_surface_gate (n=55,075), not comparable: both gate receipts are model-vs-model checks, so there is no market_prob to decompose against.

Where the comparison is supported, the reliability_gap agrees in sign: the model is the less reliable side in both sports. The recomposed file reads 0.002194 for mlb (reliability_model 0.005619 against reliability_market 0.003425, n=27,351) and 0.103894 for soccer_intl (0.191982 against 0.088088, n=4,265), and its reliability_transfer_summary states the sign agreement and puts the magnitude difference at about 47.4-fold. Revision 1 quoted a far closer pair on the joined corpus; those values are withdrawn, not restated. The rebuild changed more than the spread. The two verdict strings no longer describe the same mechanism: mlb still reads driven mainly by resolution, information not fixable by recalibration, while soccer_intl now reads driven mainly by reliability, calibration and fixable. So the shared sign means less than it did: in mlb the Brier gap is mostly an information deficit, in soccer_intl mostly a calibration one, and a recalibration that closed most of the soccer gap would leave most of the mlb gap standing. kernel_transfer.json's own summary states that two sports is not enough to call this a general cross-sport pattern.

5 Results: the 17-row paired-delta scoreboard

Cross-sport paired-delta scoreboard, all 17 rows (source cross_sport_scoreboard.json, generated 2026-07-22)
SportMarketCheckpointnPaired delta mean95% CIVerdict
nbawinprob (Brier)end_q11592-0.0084[-0.0161, -0.0008]MARKET_SHARPER_PROVISIONAL
nbawinprob (Brier)halftime1593-0.0040[-0.0098, 0.0015]UNDERPOWERED
nbawinprob (Brier)end_q315930.0011[-0.0028, 0.0052]UNDERPOWERED
nbawinprob (Brier)q4_under515930.0019[-0.0010, 0.0048]UNDERPOWERED
mlb_ingameCRPShome_margin|end_inning_3200.4499[-0.0206, 1.0687]UNDERPOWERED
mlb_ingameCRPShome_margin|end_inning_5260.8555[0.3259, 1.4888]UNDERPOWERED
mlb_ingameCRPShome_margin|end_inning_6220.9583[0.2409, 1.8046]UNDERPOWERED
mlb_ingameCRPShome_margin|end_inning_7211.0660[0.2656, 1.9602]UNDERPOWERED
mlb_ingameCRPShome_margin|end_inning_8211.7940[0.8721, 3.0590]UNDERPOWERED
mlb_ingameCRPStotal_runs|end_inning_3490.0891[-0.3032, 0.5064]UNDERPOWERED
mlb_ingameCRPStotal_runs|end_inning_5540.2604[-0.0526, 0.5751]UNDERPOWERED
mlb_ingameCRPStotal_runs|end_inning_6540.6392[0.3174, 0.9632]MODEL_SHARPER_PROVISIONAL
mlb_ingameCRPStotal_runs|end_inning_7550.7327[0.4131, 1.0810]MODEL_SHARPER_PROVISIONAL
mlb_ingameCRPStotal_runs|end_inning_8491.4201[0.9659, 1.8977]MODEL_SHARPER_PROVISIONAL
mlb_pregameCRPS (total_runs)pregame300-0.0523[-0.1657, 0.0482]UNDERPOWERED
soccer_intlwinprob (Brier)home_win_prob|minute_6022-0.0931[-0.1570, -0.0336]UNDERPOWERED
soccer_intlwinprob (Brier)home_win_prob|minute_7517-0.1075[-0.1885, -0.0316]UNDERPOWERED

Verdict labels copied verbatim. CRPS-scale rows (mlb_ingame, mlb_pregame) and Brier-scale rows (nba, soccer_intl) are not on one numeric axis -- see the caution below.

Within a metric family, most rows do not resolve in either direction. All four nba winprob checkpoints and the mlb_pregame row are labeled UNDERPOWERED or MARKET_SHARPER_PROVISIONAL, and the ten mlb_ingame CRPS rows split 7 UNDERPOWERED to 3 MODEL_SHARPER_PROVISIONAL, the split kernel_transfer.json's mlb totals_margin row states verbatim. The three are consecutive mlb_ingame total_runs checkpoints (end_inning_6, n=54; end_inning_7, n=55; end_inning_8, n=49), each still PROVISIONAL, not settled.

A verdict in this file is not a reading of the interval beside it. cross_sport_scoreboard.json's method field states that every value is copied verbatim from the artifact named in rows[].source, so the support rule behind each label lives in that upstream benchmark. That is why an interval excluding zero and an UNDERPOWERED label can coexist: mlb_ingame home_margin|end_inning_5 (n=26, CI 0.3259 to 1.4888) and the two soccer_intl rows (n=22 and n=17) each exclude zero and each read UNDERPOWERED. This file was not rebuilt; its rows read the CRPS benchmark files, a different corpus from Tables 1 and 3.

6 Results: the same BSS formula in two sports

Table 3 gives the BSS-vs-market figures for every grain brier_skill_scores.json reports.

Brier Skill Score vs market by sport and grain, all 11 reported grains (source brier_skill_scores.json, generated 2026-09-16)
SportGrainnBrier (model)Brier (market)BSS model vs marketBelow floor
mlball273510.1667590.151008-0.1043false
mlbearly (inn1-3)94890.2300770.207874-0.1068false
mlbmid (inn4-6)97520.1538680.138203-0.1133false
mlblate (inn7+)74420.0945480.086598-0.0918false
soccer_intlall42650.3325220.195157-0.7039false
soccer_intl0-155660.2676530.123304-1.1707false
soccer_intl15-305950.2788720.140209-0.9890false
soccer_intl30-455540.3034190.166302-0.8245false
soccer_intl45-607830.3093550.186015-0.6631false
soccer_intl60-754950.3825160.260446-0.4687false
soccer_intl75-90+3900.4094240.245001-0.6711false

Revision 2, recomputed on the segment-clean corpus. Do not rank the all row against its sport's named grains: all is the full corpus, not a sum of the named grains. Below floor reproduces the file's own below_floor field.

BSS against market is negative at every one of the 11 grains listed, in both sports -- from -0.0918 (mlb, late) to -0.1133 (mlb, mid), and from -0.4687 (soccer_intl, 60-75) to -1.1707 (soccer_intl, 0-15). Every grain carries below_floor: false. That verdict is the one thing the re-segmentation did not change: revision 1 also returned a negative BSS at every grain, on a corpus three times the size. What changed is the spread. MLB's range tightened (revision 1 ran from -0.0794 to -0.4635) while soccer's widened, and both populations shrank: mlb's all grain now holds 27,351 rows against soccer_intl's 4,265 (about 6.4 times), base rates 0.4897 against 0.6911, and the named grains cover 97.6 and 79.3 percent of their sports.

Running the same formula on two populations of very different size, base rate and grain coverage is where a shared scale does not establish a shared population. The negative-against-market result holds in both sports, but that difference is why this paper reports two separately measured, same-direction results rather than one combined finding.

Cross Sport Scoreboard
Cross Sport Scoreboard chart
scripts/platformkit/analytics_showcase/out/cross_sport_scoreboard.jsonSnapshot generated 2026-07-22n not published

The 17-row paired-delta scoreboard behind Table 2, with intervals and the file's own verdict labels.

BSS = 1 - Brier_model / Brier_reference; reliability_gap = reliability_model - reliability_market (Murphy decomposition, 0-1 probability scale, mlb and soccer_intl moneyline_ingame only)

7 Robustness and what would falsify this

  • If a third sport gained a market-side Murphy reliability decomposition and its reliability_gap sign disagreed, the sign-agrees-across-two-sports reading would have to be dropped. The mechanism attribution is already split -- resolution in mlb, reliability in soccer_intl -- so the shared sign is the only part of that reading still carrying weight.
  • Verdict labels and confidence intervals are separate columns in cross_sport_scoreboard.json: the labels are copied verbatim from the upstream benchmark, so if its support rule changed, rows whose interval already excludes zero could change label with no new measurement.
  • If the three mlb_ingame total_runs MODEL_SHARPER_PROVISIONAL checkpoints (n=49-55) reversed sign at a larger n, reading that run as a stable pattern would have to be dropped.
  • If soccer_intl's BSS-vs-market figures moved into mlb's range (roughly -0.09 to -0.12) on a larger soccer_intl corpus, the current gap between the two sports would have to be re-read; at the sizes published here this paper attributes that gap to nothing.

8 Limitations

  • Only 2 of the 5 kernel_transfer.json rows support a cross-sport reliability comparison; the other 3 are excluded by construction.
  • The reliability_gap sign match rests on two sports; kernel_transfer.json's own reliability_transfer_summary calls it a coincidence worth re-checking, not a pattern.
  • cross_sport_scoreboard.json mixes a Brier-scale and a CRPS-scale metric family inside one paired_delta_mean column; comparing raw magnitudes across that boundary is not supported by the file.
  • Verdict labels in cross_sport_scoreboard.json are copied verbatim from the upstream benchmark, not derived from the interval beside them, so a row can carry an interval excluding zero and still read UNDERPOWERED.
  • brier_skill_scores.json's named grains do not sum to each sport's all row (97.6 percent of mlb rows, 79.3 percent of soccer_intl rows), so the per-grain and all-row figures describe overlapping but not identical sets of games.
  • None of these artifacts tests why the market is more reliable than the model; a measured gap is reported, not a mechanism.
  • kernel_transfer.json, murphy_decomposition.json and brier_skill_scores.json are revision 2 on the segment-clean corpus, while cross_sport_scoreboard.json reads the CRPS benchmark files and was not rebuilt, so Table 2 does not describe the same set of games as Tables 1 and 3.

9 How to read this on the site

The cross-sport-comparability inspector is built on kernel_transfer.json; check there whether a pair is one of the two marked reliability_comparable before reading any number across sports. The score-decomposition inspector carries the Murphy detail, the calibration inspector the per-grain BSS context behind Table 3.

Table 2 and the figure above are one view of the paired-delta rows the cross_sport_scoreboard module renders; check comparability first, then read its scoreboard or BSS numbers. The in-game join-integrity finding, linked below, records which artifacts were rebuilt and which were not.

Evidence

  1. kernel_transfer.jsonas_of 2026-09-17T00:05:53.901291+00:00Source path: /analytics/m/kernel_transfer/
    Evidence field inventory (18 paths)
    • generated_at
    • n_rows
    • rows[].sport
    • rows[].market
    • rows[].n
    • rows[].reliability_model
    • rows[].reliability_market
    • rows[].reliability_gap
    • rows[].reliability_unit
    • rows[].reliability_comparable
    • rows[].comparability_reason
    • reliability_transfer_summary
    • honest_note
    • rows[].verdict
    • rows[].sources
    • method
    • comparable_groups
    • novelty.verdict
  2. cross_sport_scoreboard.jsonas_of 2026-07-22T20:15:22.041778+00:00Source path: /analytics/m/cross_sport_scoreboard/
    Evidence field inventory (12 paths)
    • generated_at
    • n_rows
    • rows[].sport
    • rows[].market
    • rows[].checkpoint
    • rows[].n
    • rows[].paired_delta_mean
    • rows[].paired_delta_95ci
    • rows[].verdict
    • honest_note
    • method
    • rows[].source
  3. brier_skill_scores.jsonas_of 2026-09-16T22:17:01.245611+00:00Source path: /analytics/m/brier_skill_scores/
    Evidence field inventory (63 paths)
    • source_artifact
    • generated_at
    • floors
    • sports.mlb.n_rows
    • sports.mlb.sport_base_rate
    • sports.mlb.grains.all.n
    • sports.mlb.grains.all.brier_model
    • sports.mlb.grains.all.brier_market
    • sports.mlb.grains.all.bss_model_vs_market
    • sports.mlb.grains.all.below_floor
    • sports.mlb.grains["early(inn1-3)"].n
    • sports.mlb.grains["early(inn1-3)"].brier_model
    • sports.mlb.grains["early(inn1-3)"].brier_market
    • sports.mlb.grains["early(inn1-3)"].bss_model_vs_market
    • sports.mlb.grains["early(inn1-3)"].below_floor
    • sports.mlb.grains["mid(inn4-6)"].n
    • sports.mlb.grains["mid(inn4-6)"].brier_model
    • sports.mlb.grains["mid(inn4-6)"].brier_market
    • sports.mlb.grains["mid(inn4-6)"].bss_model_vs_market
    • sports.mlb.grains["mid(inn4-6)"].below_floor
    • sports.mlb.grains["late(inn7+)"].n
    • sports.mlb.grains["late(inn7+)"].brier_model
    • sports.mlb.grains["late(inn7+)"].brier_market
    • sports.mlb.grains["late(inn7+)"].bss_model_vs_market
    • sports.mlb.grains["late(inn7+)"].below_floor
    • sports.soccer_intl.n_rows
    • sports.soccer_intl.sport_base_rate
    • sports.soccer_intl.grains.all.n
    • sports.soccer_intl.grains.all.brier_model
    • sports.soccer_intl.grains.all.brier_market
    • sports.soccer_intl.grains.all.bss_model_vs_market
    • sports.soccer_intl.grains.all.below_floor
    • sports.soccer_intl.grains.0-15.n
    • sports.soccer_intl.grains.0-15.brier_model
    • sports.soccer_intl.grains.0-15.brier_market
    • sports.soccer_intl.grains.0-15.bss_model_vs_market
    • sports.soccer_intl.grains.0-15.below_floor
    • sports.soccer_intl.grains.15-30.n
    • sports.soccer_intl.grains.15-30.brier_model
    • sports.soccer_intl.grains.15-30.brier_market
    • sports.soccer_intl.grains.15-30.bss_model_vs_market
    • sports.soccer_intl.grains.15-30.below_floor
    • sports.soccer_intl.grains.30-45.n
    • sports.soccer_intl.grains.30-45.brier_model
    • sports.soccer_intl.grains.30-45.brier_market
    • sports.soccer_intl.grains.30-45.bss_model_vs_market
    • sports.soccer_intl.grains.30-45.below_floor
    • sports.soccer_intl.grains.45-60.n
    • sports.soccer_intl.grains.45-60.brier_model
    • sports.soccer_intl.grains.45-60.brier_market
    • sports.soccer_intl.grains.45-60.bss_model_vs_market
    • sports.soccer_intl.grains.45-60.below_floor
    • sports.soccer_intl.grains.60-75.n
    • sports.soccer_intl.grains.60-75.brier_model
    • sports.soccer_intl.grains.60-75.brier_market
    • sports.soccer_intl.grains.60-75.bss_model_vs_market
    • sports.soccer_intl.grains.60-75.below_floor
    • sports.soccer_intl.grains["75-90+"].n
    • sports.soccer_intl.grains["75-90+"].brier_model
    • sports.soccer_intl.grains["75-90+"].brier_market
    • sports.soccer_intl.grains["75-90+"].bss_model_vs_market
    • sports.soccer_intl.grains["75-90+"].below_floor
    • honest_note
  4. murphy_decomposition.jsonas_of 2026-09-16Source path: /analytics/m/murphy_decomposition/
    Evidence field inventory (8 paths)
    • as_of
    • corpus
    • sports.mlb.n_rows
    • sports.mlb.model_prob.reliability
    • sports.mlb.market_prob.reliability
    • sports.soccer_intl.n_rows
    • sports.soccer_intl.model_prob.reliability
    • sports.soccer_intl.market_prob.reliability
Back to research papers