Statsmith.io/methods

How we handle stats

UPDATED 2026-07-30 · OUR FORMULAS, OUR CONSTANTS, OUR DISAGREEMENTS

Statsmith answers plain-English questions with real numbers. This page is the long version of what that sentence means — where the numbers come from, how the computed ones are built, and every place ours differ from the numbers you'd find elsewhere. The disagreements are on this page on purpose. A stats site that hides them is a stats site you can't check.

The approach

The model routes and phrases. Postgres computes.

Every number that reaches you is computed from source game data — never generated or estimated by AI. A language model reads your question, decides which data it needs, and writes a query; the database runs it and returns rows. The model never types a figure into an answer.

That holds even for the sentence you read at the top of an answer. The model writes a template — "Bonds hit {home_runs} home runs in {season}" — and the values are substituted server-side from the actual result row. A template that names a column the result doesn't have, or a column that came back empty, is thrown away and replaced with a plain summary of the rows. So is a template with no placeholders at all: a sentence with nothing bound to the data is the model's claim, however plausible it reads. Counts and records ("went 5-4 in these 9 games") are derived in code from the rows, not inferred by a model that can't see them.

The worst case is a clumsy sentence. It is never a wrong number.

Every answer says what it computed

Alongside the answer is an interpretation of the question we actually answered — the seasons covered, the qualification floor applied, whether it's regular season or includes playoffs. Leaderboards say their floor out loud, because a career batting-average board with no floor hands the all-time record to a man who went 1-for-1 and never batted again.

A filter we can't apply is announced, not silently dropped

Ask for "the most goals Gretzky scored on a Monday night" and you'll get his Monday best plus a note saying "night" wasn't applied — because our hockey game records carry no start time at all. We answer anyway (most people saying "Monday night" mean Monday), we keep the unapplied word out of the answer sentence, and we tell you it was dropped. The rule is general: any qualifier the data can't express gets named. Silence isn't an option.

For the record: NHL, NBA and NFL game records have no start-time column. MLB has a first-pitch time for about 70% of games, mostly missing on older ones — so filtering on it would quietly discard a third of history, which is worse than not filtering. We treat it as unavailable unless the answer names what was excluded.

We refuse rather than guess

When the data can't support an answer, you get a refusal that names what's missing — not a plausible-looking number. Concretely:

The record book beats our own reconstruction

Where an authoritative season total exists, we serve it instead of re-adding our own parts. Anything rebuilt from parts silently inherits every hole in the parts: a player-season line assembled from the games we'd ingested once reported a Red Sox outfielder playing 26 games in 2024, because the ingest was 402 games into a 2,430-game season. Season lines now read from the record book through the last archived season and from our own live rollup only after it — and the boundary is a query, not a hardcoded year, so it moves itself when the annual refresh lands.

Same rule in basketball: team-season totals come from the archive's own team file rather than summing player lines, because roughly 5% of turnovers are charged to the team (shot-clock, backcourt, too-many-men) and belong to no player at all. Summing players gave Milwaukee 1,137 turnovers in 2022-23 against a true 1,199 — and inflated every usage rate built on that denominator.

Rate stats are ratios of totals, never averages of rates

Each stat we serve is declared once — its formula over totals, its qualification floor, its exposure column, its era floor, and the caveat that must ride along with it. The database columns, the instructions the query model reads, and our own regression tests are all generated from that one declaration, so they can't drift apart. Averaging season rates to get a career rate is structurally impossible for these stats, which is the single most common way a career number comes out wrong.

Baseball

The metric names are open and the formulas are published; what varies between sites is the inputs. Ours are our own. Every value below is our implementation of a published method, computed from the SABR Lahman Database for season lines and league constants and from our own full-history games table (1901+) for park factors. We never copy computed values from Baseball-Reference, FanGraphs, or anyone else. Because our measured inputs differ from theirs, our values differ from theirs by a few points. That's expected, not a bug.

League constants, re-derived per season

Every batting constant is computed per season and league from non-pitcher batting rows, so pitchers hitting doesn't deflate the baseline that hitters are measured against. Era adjustment falls out by construction — a 1930 hitter is measured only against 1930.

A pitcher with zero earned runs would have infinite ERA+, so ERA+ is empty for those seasons, and leaderboards apply an innings floor we state in the answer (Lahman has no official-qualifier column, so we derive qualification ourselves and say which rule we used).

Park factors — ours, from our own games

For every team-season we can match to our games table, we compute runs per game at home against runs per game on the road, counting both teams' runs:

pf_raw = (runs/game in home games) / (runs/game in road games)

Then it's pooled over a centered three-season window, regressed toward 1.0 with an 81-game prior (pf = (pf_raw·G + 81) / (G + 81)), halved for application because players play about half their games at home, and normalized per season and league so the exposure-weighted mean applied factor is exactly 1.0. That last step is what makes the league-mean invariants above exact. Sanity checks land where they should: Coors 1997 ≈ 1.20, Dodger Stadium 1966 ≈ 0.92, PacBell 2002 ≈ 0.95. Teams we can't match — pre-1901, the Federal League, some Negro Leagues seasons — get a neutral 1.0, and a mid-window stadium move smears across one season, which we accept and disclose.

sWAR

sWAR is our own WAR implementation, built from published components:

There is no fielding component yet. A TotalZone-style build from our play-level data is planned; until it lands, defense-first careers — Ozzie Smith, Mazeroski, Andruw Jones — are understated by exactly their fielding value, and we refuse "best defensive X" outright rather than answer it badly. Our play-by-play carries per-play fielder credits from 1950 onward, which is also roughly where the industry's TotalZone era begins.

sWAR is not bWAR and it is not fWAR. Different park factors, different inputs, no fielding term, earned runs where bWAR uses total runs allowed. Answers that serve it say so. Here is what that actually costs, measured against the published values:

SeasonOursPublished
Ruth 1923 sWAR13.614.1 bWAR
Bonds 2001 sWAR12.811.9 bWAR / 12.9 fWAR
Gibson 1968 sWAR10.911.9 bWAR
Pedro 2000 sWAR9.511.7 bWAR / ~9.9 fWAR
Bonds 2002 wRC+253244 FanGraphs
Ruth 1920 wRC+244239 FanGraphs
Bonds 2002 OPS+269268 B-Ref
Gibson 1968 ERA+259258 B-Ref
Pedro 2000 ERA+288291 B-Ref
Pedro 1999 FIP1.461.39 FanGraphs

The park-normalized stats land within a point or two. wRC+ runs about five to nine points high at the extremes — a weights-and-scale lineage difference, visible only where the values are already absurd. The top of our career sWAR table is Ruth 180.3 (bWAR 182.6), Cobb 153.6, Cy Young 141.2, Walter Johnson 139.8, Bonds 137.1: the inner circle, in a familiar order, with pitchers and pre-integration stars sitting a few wins off bWAR for the reasons above.

Career rate stats

Career BA/OBP/SLG/OPS, ERA, WHIP, K/9 and stolen-base success rate are formed from career component sums, with the published leaderboard floors (3,000 PA for batting, 1,000 IP for pitching) stated in the answer. Ted Williams' .4817 OBP matches the published all-time record exactly. Where the historical record is thin, the formula follows it rather than patching it: Lahman records no sacrifice flies before 1954 and no hit-by-pitch before 1884, so older OBP denominators omit them, and caught stealing is largely unrecorded before 1951, so stolen-base success rate carries a 1951 floor instead of a flattering number.

One disagreement to know about: our source's Negro Leagues coverage differs from MLB's official record book, which credits Josh Gibson with a .372 career batting average as the all-time record. Ours comes out .364, behind Cobb. That is a coverage difference, not a verdict, and answers say the values follow our data.

Hockey

Era-adjusted points

Our own metric, not an official NHL stat, and answers that serve it say so. For each season of a career:

era_adjusted_points = points × (reference_gpg / league_gpg_that_season)

where league goals per game comes from our own game finals and the reference is the plain mean across all seasons. One correction matters: NHL final scores include the shootout winner's extra goal, which is a standings artifact nobody scored. Leaving it in overstated league scoring by 1–2.6% in every season since 2005-06 and therefore deflated every shootout-era player's career value. We subtract one goal per shootout-decided game.

Career rates from raw components

Career save percentage and goals-against average are computed once from career component sums — saves over shots against, and 3600 × goals_against / seconds_played — never by averaging season rates. Hasek's .9223 and Alec Connell's 1.92 GAA match the published all-time records, on the standard 250-goalie-game floor. Skater boards use 400 games for points and assists per game and 200 career goals for goals per game and shooting percentage, which is where Gretzky's 1.921 and Bossy's .762 sit as published.

Era guards are structural, not advisory. Goals against and minutes exist from 1917-18, so GAA is a clean full-history ratio. Saves and shots against were only recorded from 1955-56, and skater shots from 1959-60 — so a career that began earlier stores no value at all, and the question is refused with the floor named. Sawchuk and Plante have no career save percentage here, because they have none anywhere. Separately, all-time GAA boards are dominated by the low-scoring 1920s and 30s; when you mean the modern game, we'll say so and offer the filter.

Basketball

Usage and the team-share family

Usage rate, assist percentage, rebound percentage and the rest all measure a player's box line against his team's totals, so they're only as good as the team denominator — which is why those totals come from the archive's authoritative team file rather than a sum of player lines (see above). Usage is the published box-score formula:

USG% = 100 × ((FGA + 0.44·FTA + TOV) × (team MP / 5)) ÷ (MP × (team FGA + 0.44·team FTA + team TOV))

Turnovers weren't recorded before 1977-78, so usage and every turnover-dependent share carry a 1978 floor and are refused below it. Efficiency stats follow their published definitions exactly — TS% = PTS / (2 × (FGA + 0.44·FTA)), eFG% = (FG + 0.5·3P) / FGA — over career sums, on the record-book floors (400 games for per-game boards, 2,000 field goals made for shooting boards, 1,200 free throws made, 250 threes made).

Era floors and league span

Rebounds are recorded from 1950-51, steals, blocks and the offensive /defensive rebound split from 1973-74, turnovers from 1977-78, and the NBA three-point line arrives in 1979-80 (the ABA had it from 1967-68, and those threes are counted). Rebounds per game divides by games in rebound-recorded seasons, so a career straddling 1950-51 — George Mikan's — comes out at his published 13.4 rather than being quietly understated. Career totals span NBA, ABA and BAA regular-season play combined, which is why Julius Erving's scoring average here is his combined 24.2 and not his NBA-only 22.0; the answer says so.

Win Shares are our own computation of the published Kubatko method across all leagues from 1947 — the same tier as public Win Share boards, not the same numbers, and labeled as ours.

Football

Passer rating, computed exactly

Career passer rating uses the published NFL formula over career sums, with each of the four terms clamped to the 0–2.375 range the formula specifies — which is precisely why it can't be expressed as a simple ratio and is computed in its own pass. It checks out against the published figures: Brady 97.2, Brees 98.7.

The 1999 floor, and what it does to careers

Our football archive starts in 1999. Every career number is therefore a "since 1999" number, and we say it in the answer:

Where our source disagrees with the official record

What we have, by sport and era

Coverage is not one number per sport. Each layer — final scores, box scores, play-by-play, on-ice units — has its own floor, set either by the season the league started recording the thing or by the season our source starts carrying it. Those floors don't line up, and pretending they do is how a site ends up serving a career total that quietly covers half a career. The spans below were measured against the loaded database, not copied from a source's own description of itself. Below a floor we refuse and name it.

Baseball

Three depths, deliberately kept apart: the record book reaches 1871, our own game log starts in 1901, and play-by-play starts in 1950.

LayerCovered
Final scores, team records, our own game log1901 →
Player box lines, batting and pitching1901 →
Play-by-play: game state, comebacks, win probability1950 →
Season stat lines, official team records, standings, All-Star, Hall of Fame1871 →
Award winners1877 → (voting detail 1911 →)
Amateur draft1965 → (the first draft)
Fielding by position — games, putouts, assists, errors, double plays1871 →
Per-play fielder credits (the input a future defensive metric needs)1950 →
Statcast velocity/spin/exit velocity, DRS/UZR/OAAnever — proprietary

Football

One hard floor and a few later ones. Everything about the NFL here is a "since 1999" number, said out loud in the answer.

LayerCovered
Games, play-by-play, game state, win probability1999 →
Player stat lines, per game and per season1999 →
Team-season totals1999 →
Head coaches, stadium, roof, surface, weather, rest days, referee, closing spread1999 →
Kickoff time and weekday2000 → (blank for 1999)
Closing moneyline2006 →
Snap counts2013 → (the source's 2012 file is a broken stub; we don't claim it)
Draft picks1980 →
Award winners — AP MVP1957 → (DPOY 1971 →, OPOY 1972 →, rookie awards 1967 →)
Targets and catch rate1999 → (2003–2008 missing entirely — see below)
PFF grades, Next Gen Stats trackingnever — proprietary

Basketball

Box scores go back to the BAA; play-by-play starts fifty years later. The gap between those two lines is where most basketball refusals come from.

LayerCovered
Game finals, including the BAA years and defunct franchises1946-47 →
Player box lines1946-47 →, complete from 1954-55 (1946-47 has box lines for 18 of 350 games)
Play-by-play: game state, per-quarter scoring, five-man on-court units1996-97 →
Authoritative team-season totals1946-47 →
Season stat lines and our computed Win Shares1946-47 →
Draft; All-Star selections; MVP voting1947 →; 1950-51 →; 1955-56 →
RAPTOR; DARKO1977–2022 (frozen); 1996-97 →
Per-stat recording floorsrebounds 1950-51, steals/blocks and the offensive-defensive rebound split 1973-74, turnovers 1977-78, three-pointers 1979-80
Overtime flagunknown for many games before 1996-97 — OT counts there are refused, not guessed
One player's own minutes, fouls or plus-minus in a single gamenot carried (lineup minutes and lineup plus-minus are)
Tracking, who-guarded-whom, RAPM / EPM / LEBRONnever — proprietary

Hockey

The deepest history we hold, and the widest spread between layers: the goal-by-goal record runs to 1917-18, the real box score to 1948-49, and the modern tracked columns only to the late nineties.

LayerCovered
Official finals, goal-by-goal sequences, comebacks and blown leads1917-18 → (2004-05 was cancelled by the lockout — no games exist)
Team-season lines: standings points, W/L/T/OTL, goals, shots, penalties1917-18 →
Power play and penalty kill1977-78 → (earlier seasons are junk at the source, so we null them)
Team hits and blocked shots2005-06 →
Playoff series structure — round, participants, series winner1917-18 →
Player season lines, career totals and rates1917-18 →
Per-game scoring lines, derived from the goal events1917-18 →, except 1934-35 and 1935-36, whose assist records don't reconcile with the official totals and are excluded
Full per-game box: games played, goalie line, penalties1948-49 →
Shots on goal and plus-minus, per game1959-60 →
Skater ice time, hits, blocks, giveaways, takeaways, faceoff %1997-98 → (goalie ice time is carried at every era)
On-ice five-man units by strength — 5v5, power play, penalty kill, 3-on-3, empty net2010-11 →
Draft; trophies, winner / runner-up / finalist1963 → (the first draft); 1917-18 →
Award vote sharesnot published in the source — winner status only

One cross-sport gap worth naming

Win probability exists for baseball and football only. Both carry a per-play model estimate on every play we hold. The hockey and basketball models aren't fitted yet, so "most improbable win" in those two sports is a refusal that says the stat is coming — not a number we invented in the meantime.

Known limits

These are real, and we'd rather print them than let you discover them:

Where the data comes from

Full attribution, the affiliation disclaimer and the privacy practices live on the terms page. We never redistribute raw source feeds — answers are computed results with their provenance, not data dumps.