Baseball generates two data streams for every hitter and every pitcher. The box score tracks outcomes: hits, outs, home runs, earned runs. Statcast tracks the contact that produced those outcomes: exit velocity and launch angle on every batted ball, measured by high-speed cameras in every MLB stadium since 2015.

From those measurements, Baseball Savant computes expected statistics. A line drive at 105 mph has an expected batting average around .700. A weak grounder at 75 mph has an xBA around .100. Aggregate every batted ball a hitter produces in a season, weight each one by its historical outcome probability, and you get xBA, xSLG, and xwOBA: what the contact quality says the stat line should look like, independent of where any particular ball happened to land.

A hitter batting .238 on an xBA of .295 is making hard contact that keeps finding gloves. The .238 is what happened. The .295 is what the contact quality says should be happening. Over a full season, these two numbers converge. The contact quality wins. The question for a fantasy manager is what to do in the meantime, and how much to trust the convergence at any given point.

The xStats system in League Donation answers that question in two ways. The first takes the season-to-date gap between Statcast’s expected stats and the preseason projection and uses it to adjust every player’s fantasy value. The adjustment starts small when the sample is thin. It grows as plate appearances accumulate and the contact data earns authority over the projection. The second extends expected stats to window lengths Savant doesn’t publish: last seven games, last fourteen games, last thirty games. The reader can compare recent actual to recent expected at matched window length instead of comparing recent actual to a season-level reference blurred across months of accumulated play. This page documents both systems. Every formula, every derivation, every omission.

Windowed xStats

Savant publishes expected stats at season granularity. A player’s xwOBA on the leaderboard is the cumulative value across every plate appearance from opening day to today. The leaderboard does not publish a last-seven-games xwOBA, a last-fourteen-games xwOBA, or any other window cut. Tools that want expected stats at shorter windows typically fetch per-game batted-ball feeds and re-aggregate them. The League Donation tool skips the per-game fetch entirely.

The snapshot pipeline captures Savant’s expected_statistics leaderboard once a day and writes it to a blob keyed by date. Two snapshots of the same player at two different dates are enough to reconstruct the xStat value for the window between them. The window boundaries are game-aligned: the tool finds the date of the Nth-most-recent game in the player’s game log and fetches the snapshot from the day before that game. The delta between that snapshot and today’s captures exactly those N games of Statcast data. The window’s numerator is the later cumulative numerator minus the earlier. The window’s denominator is the later cumulative denominator minus the earlier. Divide. The result is exact, and the window covers the same games as the counting-stat rolling window in the adjacent column.

x_window = (x_T2 · denom_T2 − x_T1 · denom_T1) / (denom_T2 − denom_T1)

Denominators are not interchangeable. Each stat lives on the denominator that matches how Savant computed it in the first place. xwOBA uses plate appearances, the same as Savant’s published value. Exit velocity, barrel rate, and hard-hit rate use batted-ball events, because each one is an average across balls actually struck. Mixing denominators produces numbers that look plausible and mean nothing. The tool stores plate appearances and batted-ball events in the daily snapshot and applies the right one per stat.

xBA and xSLG use at-bats as their denominator. The expected_statistics leaderboard publishes plate appearances and batted-ball events but not at-bats, so the snapshot pipeline reads a second leaderboard alongside it. Savant’s custom-leaderboard endpoint accepts a column-selection parameter, and an ‘ab’ selection produces a CSV with at-bats per player. A merge step pulls those values into each daily snapshot. The delta arithmetic gets its denominator. xBA and xSLG window the same way xwOBA does.

Exit velocity and barrel rate carry information the rate stats don’t. They’re leading indicators that move before the rate stats catch up. A hitter who is squaring up the ball harder over the last seven games than over the season is in a real hot streak grounded in mechanics, separate from the BABIP noise that shows up in batting average. xBA is downstream of contact quality. Watching contact quality directly is the closer look.

Two constraints govern when the tool shows a window value. Savant serves expected stats to three decimals, so the truncation error on each endpoint compounds across the subtraction. At small sample sizes the arithmetic noise exceeds the signal. The tool requires a minimum of fifteen plate appearances in the window before emitting a number. If the player entered the Savant qualification pool inside the window, whether by mid-season callup or by an injury return, the earlier endpoint does not exist and the tool reports insufficient history.

The windowed values appear in the player modal alongside the existing counting-stat rolling windows. A reader looking at a hitter whose last-seven-games wOBA has collapsed sees the last-seven-games xwOBA directly below it, and the last-seven-games exit velocity, barrel rate, and hard-hit rate two rows further down. The comparison is at matched window length. Whether the contact quality collapsed with the results or held steady while they fell is visible in the same column.

The Blend

The entire system rests on one equation. The adjustment blends a projected stat with a Statcast-derived expected stat using a weight that increases with sample size:

adjusted = projection × (1 − w) + xStat × w where   w = PA / (PA + k)

PA is plate appearances for batters, batters faced for pitchers. k is a stabilization constant, different for each stat, calibrated to how quickly that stat produces reliable signal from batted-ball data.

The equation blends two opinions about a player. The preseason projection is what the experts believed before the season started. The Statcast expected stat is what the contact quality says right now. The blend starts by trusting the projection entirely and transfers authority to the contact data as the season generates evidence.

At zero PA, w is 0. The projection holds. At 200 PA with k of 200, w reaches 0.50 and both opinions carry equal weight. By 600 PA, the contact quality holds 75% of the authority. The projection fades but never fully disappears, a receding prior that the season gradually talks over.

This is empirical Bayes shrinkage applied to within-season updating. The projection is the prior. The expected stat is the likelihood. The stabilization constant governs how quickly the posterior moves away from the prior. The formula ensures the system never overreacts to a hot week or underreacts to three months of evidence.

The stabilization constants documented in the next section are season-length calibrations. Whether optimal constants vary with window length is an open empirical question, and the windowed xStats feature combined with a season of daily snapshots makes it a measurable one.

Stabilization Rates

Different stats deserve different patience. A hitter’s expected batting average requires more plate appearances to stabilize than a pitcher’s strikeout rate, because batting average involves more moving parts. The system assigns a stabilization constant k to each stat, governing how quickly the blend shifts from projection to contact quality:

WEIGHT (w) 0% 50% 100% PLATE APPEARANCES 0 100 200 300 400 50 k=100 (K%, BB%) k=200 (AVG) k=300 (RBI)
The projection anchor dissolves at different rates per stat. Fast-stabilizing stats trust the contact data sooner.
Category Source k w@50 w@150 w@300 Tier
AVGxBA2000.200.430.60● Strong
SLGxSLG1800.220.450.63● Strong
wOBAxwOBA1500.250.500.67● Strong
ERAxERA1200.290.560.71● Strong
WHIPDerived xWHIP1500.250.500.67● Strong
HRBarrel rate2000.200.430.60◑ Moderate
RxwOBA2500.170.380.55◑ Moderate
RBIxwOBA + ctx3000.140.330.50◑ Moderate
K (pit)Savant K%700.420.680.81◑ Moderate
BB (pit)Savant BB%1700.230.470.64◑ Moderate
K (bat)Savant K%1000.330.600.75○ Weak
BB (bat)Savant BB%1000.330.600.75○ Weak

Pitcher K% is the most impatient stat in the system (k=70). Strikeout rates converge quickly. RBI is the most patient (k=300): the derivation involves several inference steps and lineup-dependent context, so the system waits longer before trusting it.

Five categories get no adjustment. Stolen bases have no contact-quality analog. You can barrel every pitch you touch and still be slow. Wins depend on run support. Saves depend on whether the manager hands you the ball in the ninth. The system leaves these at their projected values and doesn’t pretend otherwise.

The Derivations

Savant publishes xBA, xSLG, xwOBA, and xERA. Those map directly onto AVG, SLG, wOBA, and ERA, and the blend is straightforward: Savant number in, projection blended, adjusted value out. Most fantasy leagues score counting stats. There is no xHR on Baseball Savant. No xR. No xRBI. The system derives expected rates from contact quality data, blends those rates with projected rates, and scales back to counting stats using projected playing time as the denominator.

Each derivation is a small model. Each model introduces inference beyond the raw Statcast data. Each one is labeled accordingly.

Expected HR Rate

Barrel rate measures the fraction of batted balls with exit velocity and launch angle in the zone that historically produces a .500+ batting average and 1.500+ slugging. About 53% of barrels leave the park in the modern era, a coefficient that has held within a few points year to year. Park effects, wind, and wall height create variance around it.

xHR/PA = barrel_rate × contact_rate × 0.526

Contact rate is approximated as (1 − K%), using the player’s Statcast strikeout rate. A hitter who barrels 10% of batted ball events and puts the ball in play 75% of the time produces an expected HR rate of 0.039 per PA. Over 600 PA, that’s 24 home runs. Moderate tier.

Expected R Rate

Runs derive from xwOBA through the standard wOBA-to-runs conversion, a formula stable enough across seasons that the league average constants barely shift:

xR/PA = (xwOBA − 0.320) / 1.252 + 0.119

An xwOBA of .360 produces an expected 0.151 runs per PA. Multiply by projected PA for the counting stat. Moderate tier.

Expected RBI Rate

RBI depends on who’s on base when you come up: your teammates, your lineup slot, the manager’s whims. Contact quality can’t measure any of that. The derivation bridges the gap using the projection source:

xRBI/PA = xR/PA × (proj RBI / proj R)

The contact quality updates the rate. The projection supplies the lineup context. If the projection system had the player driving in 1.1 runs for every run scored, the system preserves that ratio while the underlying rate shifts. This derivation carries the highest k (300) and the Moderate tier. At 300 PA the system still gives the projection half the weight, because the inference chain is long.

Expected WHIP

Savant doesn’t publish xWHIP, but all the ingredients are available:

xWHIP = (xBA_against × BF + BB) / est_IP

xBA for pitchers is expected batting average against. BF is batters faced. BB comes from the pitcher’s Savant walk rate. IP is estimated from batters faced at roughly 4.3 BF per inning, the league-wide average. Every input is event-level or fast-stabilizing. Strong tier.

Worked Example

A hitter enters mid-May with 120 plate appearances. Preseason projection: .270 AVG, 22 HR over 550 PA. Current actual line: .238 AVG, 6 HR. The stat line looks like a bust.

Savant says otherwise. xBA: .295. Barrel rate: 8.2%. K%: 22%. The bat is performing. The results haven’t caught up.

The AVG adjustment:

w = 120 / (120 + 200) = 0.375 adj AVG = 0.270 × 0.625 + 0.295 × 0.375 = 0.279

The projection said .270. The contact quality says .295. At 120 PA the system trusts the contact quality 37.5% and moves the projection to .279. By August, if the xBA holds, the weight will pass 60% and the adjusted AVG will approach .290.

The HR adjustment uses the barrel rate derivation:

xHR/PA = 0.082 × 0.78 × 0.526 = 0.0337 proj HR/PA = 22 / 550 = 0.040   w = 0.375 adj HR/PA = 0.040 × 0.625 + 0.0337 × 0.375 = 0.0376 adj HR = 0.0376 × 550 = ≈ 21

The xBA pulls the AVG up. The barrel rate pulls the HR slightly down. Same player, opposite directions, different categories. The bat is making harder contact than the .238 actual line suggests, which is what xBA is for. The barrel rate is league-average, which is what the projection priced in. The system doesn’t average the two opinions into one summary score. It updates each category against the evidence that category has, then re-derives the player’s value from the updated stat line.

This is what per-category blending is for. A composite x value would hide the disagreement. Surfacing it lets the reader see which part of the projection earned support and which part didn’t.

Both adjustments, and every other mapped category, produce a new stat line. That line gets fed through the same z-score and VORP pipeline the projection-based rankings use, recomputed against the adjusted population. The adjusted VORP minus the projected VORP is the xΔ. Adjustments smaller than ±0.3 VORP are suppressed as noise.

The xΔ answers a specific question: how much is the contact quality disagreeing with the projection, expressed in positional fantasy value, in your league’s scoring system, at the current sample size? Green means the bat or arm is worth more than the projections suggest. Red means less.

Signal Tiers

Every xΔ carries a confidence label. The tier reflects which stat category drove the largest z-score shift in the recomputation, then caps that tier by the blending weight.

SIGNAL QUALITY ● STRONG Event-level xStats · minimal inference AVG ← xBA SLG ← xSLG wOBA ← xwOBA ERA ← xERA ◑ MODERATE Derived rates · one conversion step HR ← barrel rate R, RBI ← xwOBA-to-runs K% (pit) BB% (pit) ○ WEAK No contact signal · slow stabilization K% (bat) · BB% (bat) EXCLUDED: SB · W · SV. No contact-quality analog, passed through at projection unchanged
Each category’s correction is labeled by the quality of evidence behind it.

The tier is then capped by the blending weight. An xBA gap that looks dramatic at 40 PA gets a Weak label because 40 PA hasn’t earned the system’s confidence. The cap logic: w < 0.25 forces Weak. w < 0.50 forces Strong down to Moderate. The tier tells you how seriously to take the system’s own number.

The Regression Score

Separate from the adjusted value pipeline, the xStats tab displays a regression score per player: a single number capturing how far actual stats have drifted from expected.

For batters, the primary component is the xwOBA-minus-wOBA gap, with the xSLG-minus-SLG gap as a secondary power signal. The xBA gap is excluded because xwOBA already incorporates it, and including both would double-count contact quality on singles. For pitchers, the secondary component is ERA-minus-xERA, picking up strand rate and sequencing divergence.

The raw gaps are normalized to pool-based z-scores. A regression score of +1.5 means the player’s gap sits 1.5 standard deviations above the pool average. The normalization self-calibrates through the season: early on, the standard deviation is wide because everyone’s gaps are noisy. A +1.5 is hard to reach. As the season progresses, the distribution tightens. A smaller absolute gap can carry the same relative weight.

Starting pitchers and relievers are normalized in separate sub-pools. SP wOBA distributions are structurally tighter than RP distributions. Combining them inflates the standard deviation and buries genuine SP signals at 0.5 sigma even when they’re meaningful.

The Trend Matrix

The xStats tab classifies each player by overlaying two questions: is the player recently hot or cold (last 14 games versus season average, minimum 30 PA), and does the contact quality explain the streak (expected-versus-actual gap at the season level).

CONTACT SAYS UNLUCKY NEUTRAL CONTACT SAYS LUCKY RECENTLY COLD RECENTLY HOT Unlucky Slump contact above results Cold Streak neutral contact signal Correction contact confirms decline Breakout contact confirms surge Hot Streak neutral contact signal Unsustainable contact below results Direction of recent performance × mechanism behind it = classification

These are observations, each backed by a specific data relationship. “Unlucky Slump” means the player is cold and the contact quality says the cold streak has no support in the batted-ball data. “Unsustainable” means the player is hot and the contact data says the hot streak is running ahead of the quality of contact. The contact data doesn’t care about your roster. It reports what the bat did.

The classifier compares the last fourteen games of actual wOBA against a window-matched expected wOBA when the snapshot series has enough history to reconstruct it. When the history isn’t there, when the player sits outside Savant’s qualification pool, or when the batch trend pipeline populates the indicator column before any modal has been opened, the comparison falls back to a season-aggregate expected wOBA. Threshold calibration for the window-matched version remains an open question that the Midseason Validation should answer.

What This System Omits

Park effects. Expected stats use leaguewide batted-ball distributions. A fly ball at Coors Field has different outcomes than the same ball at Oracle Park. A full-season Coors hitter will show an expected-versus-actual gap that partly reflects altitude. The system doesn’t adjust for park. The gap is displayed. Evaluating how much is park is the user’s job.

Defensive positioning. Shifts turn expected hits into outs. The system treats this as noise that should resolve over a season. Whether it actually resolves depends on how persistently teams align against a specific hitter. The system doesn’t model that persistence.

Spray angle. Savant’s own xwOBA model excludes spray angle. Tom Tango’s research showed that without spray angle, xwOBA on contact better predicts future performance than with it. The system follows Savant’s decision.

Stolen bases. No contact-quality signal. Speed lives outside the batted-ball data entirely.

Wins and saves. A pitcher’s contact quality tells you how well he pitched. Whether his team scored runs or whether his manager used him in the ninth inning is a different question that exit velocity cannot answer.

What This System Is

Tom Tango, the architect of expected statistics at MLB, has stated that xStats were designed to be descriptive. They tell you what should have happened given the contact quality. They were never built to forecast next season.

This system uses them for something narrower than forecasting and more actionable than description: within-season belief revision. Given what the contact quality says right now, and given how much season has accumulated, how much should we revise our beliefs about this player’s value in this specific league?

The stabilization constants determine when the revision begins. The derivations extend it to the stats leagues actually score. The signal tiers quantify how much confidence the revision has earned. The regression score and trend matrix display the raw gap the revision is built from. The omissions catalog where the system has no opinion.