Monthly RoS Ranks

← Projections · Rest-of-season fantasy value snapshots stamped the 1st of each month. 5x5 / 6x6.

How the projections workclick to expand
TL;DR — rest-of-season projections are a live Bayesian blend that mixes prior-year stats, current-season totals, and recent rolling windows (L7 / L14), then multiplied by a context-aware playing-time projection. Not a static off-the-shelf system.
Rate stats (AVG / OPS / K% / ERA / WHIP / K/9)
current_blend = season-to-date weighted with L14 + L7 windows
prior_rate = 65% × season N-1 + 35% × season N-2 (qualified ≥50 PA / ≥20 IP)
For ERA specifically: prior FIP wins over prior ERA — FIP is a better predictor of next-year ERA than ERA itself.
anchor_weight = max(0, min(0.85, 1 − current_PA / k))
final = anchor_weight × prior + (1 − anchor_weight) × current
An April hitter with 15 PA leans ~85% on prior years; by ~400 PA the projection is fully driven by current season.
Stabilization half-lives (per-stat k values)
K%: 60 PA · OPS: 350 PA · AVG: 940 PA
K/9: 70 IP · WHIP: 90 IP · FIP: 120 IP · ERA: 430 IP
Different stats reach 50% predictive validity at different sample sizes. K% stabilizes fast (60 PA), so a 100-PA strikeout-rate spike gets trusted. AVG needs almost a full season — 100 PA of .310 is still mostly noise.
Playing-time projection (PA / IP)
Hitter PA = role_baseline × platoon_adjustment × injury_adjustment × L14_pace
Role baselines: regular (~5.5 PA per team game) · platoon (~3.5) · reserve (~1.5).
SP IP = starts_remaining × ip_per_start
RP IP = appearances_remaining × ip_per_appearance
Injury adjustment uses each player's actual expected_return_date from the injuries table — NOT a static "IL-type × 60% derate" used by most systems. A Skubal-class 30-day stint deducts the right number of starts.
Rotation-demotion signal — if a pitcher's L14 GS rate dropped below 0.3× his season GS rate, his IP gets capped (catches mid-season demotions to bullpen before the surface stats catch up).
Closer save share weighted 75% L14 / 25% season — role changes show up in L14 first.
Counting stats (HR / R / RBI / SB / W / SV / K / QS)
Counting stat = rate × volume.
HR rate gets a +20% L14 nudge — catches hot/cold streaks earlier than pure stabilization would. SP wins come from `starts_remaining × team_win% × inning-efficiency`. Closer saves use the closer_grid save-share allocation.
Ranking math (this page)
5x5: R · HR · RBI · SB · AVG (hitters) · W · SV · K · ERA · WHIP (pitchers)
6x6 adds: OBP (hitters) · QS (pitchers)
Each player gets a z-score per category against the qualifying pool (≥100 PA hitters · ≥20 IP pitchers). ERA and WHIP get inverse z-scores so lower is better. The composite total_z is the sum across categories. Hitters and pitchers are combined and ranked together.
How this differs from off-the-shelf
SystemWhat it does
Steamer / ATC / ZiPSPure projection — published once, doesn't re-blend in-season
FG blendStatic average of the three systems above
Naive carry-forwardTreats season-to-date rates as the projection
Kodo RoSLive Bayesian blend: prior-years + current season + L7/L14 windows, sized to each stat's stabilization curve, then multiplied by a context-aware playing-time projection that uses actual IL return dates + role-change detection
Source code
backend/app/services/projections.py:570 → compute_ros_batting_projections
backend/app/services/projections.py:1217 → compute_ros_pitching_projections
0 players
Loading…