Every fantasy app ranks players by projected points. That is the easy half of the problem and it loses close matchups. This engine optimizes the thing you are actually paid for — the chance you win the week — by modeling outcome variance, player archetype, and the correlations between your own starters — then prices every roster decision in the only unit that settles an argument: change in playoff odds.
Paste a CSV export from ESPN or Yahoo, or edit the sample below. Only
name, pos, proj are required — everything else sharpens the model.
Sum of their starters' projections. This single number decides whether you should be chasing upside or protecting a lead.
24 is typical. Raise it if they start boom-bust players — a volatile opponent makes your own safe lineup less safe.
Scales waiver-add value. Late-season adds are worth a fraction of Week 1 adds.
name required Player name
pos required QB RB WR TE K DST (D/ST and DEF are normalized)
proj required This week's projected points
team NFL team abbreviation — drives stack correlation
opponent Their opponent — drives DST anti-correlation
injury ACTIVE | Q | D | OUT | IR (adjusts mean AND variance)
season_proj Rest-of-season points — drives value over replacement
role volume | steady | neutral | spike | boom_bust
The archetype. This is what separates a target hog
from a deep threat at the same projection.Switch the objective and watch the lineup change. That divergence is the entire point — the highest-scoring lineup and the most-likely-to-win lineup are not the same lineup.
σ is the player's weekly standard deviation — the width of their outcome, not their quality.
| Slot | Player | Pos | Team | Archetype | Proj | σ | Floor | Ceiling |
|---|
Simulated score distribution vs the opponent's projected total. Overlap is your loss region.
Points scored here are worth exactly zero. Bars show what each bench player would have added to your starting total.
Decisions where the objective actually changes the answer. These are the only start/sit calls worth thinking about — everything else is already decided for you.
Your chosen lineup swept against every plausible opponent total. The steeper the curve, the more the matchup is genuinely in play.
Your real Week 1 matchup from the reconstructed schedule, priced by the same engine. This is the page that makes a falsifiable prediction — a number your league-mates can check against the actual result on Sunday night.
Optimized for win probability against this specific opponent — not for points.
| Slot | Player | Pos | Archetype | Proj | σ |
|---|
What each bench player would add to your starting total. Points scored here count for nothing.
| Player | Pos | Proj | Would add |
|---|
Decisions where maximizing win probability disagrees with maximizing points. If this list is empty, the engine and a stock fantasy app would submit an identical lineup — and the honest thing is to say so.
Every player currently sits at
neutral, which makes σ a fixed multiple of projection. When that is true, the
highest-projected player in a position always carries the most variance, no genuine
risk/reward tradeoff can exist, and the win-probability objective provably collapses into a
points-maximizer. Tagging is the five minutes that turns this tool on.
volume target/touch hog, game-script proof · steady reliable role · spike TD-dependent or big-play reliant · boom_bust deep threat, committee back, lottery ticket.
"This trade adds 2 points a week" is an unanswerable claim. Two points is enormous for a bubble team and nearly worthless for a team already 99% in. The only honest unit for a roster decision is change in playoff odds — which means simulating the rest of the season, standings, tiebreakers, seeding and the bracket, thousands of times over.
Set μ to 0 for your own team —
its scoring distribution is derived from your optimal lineup instead. μ is a team's average weekly
points; σ is how wildly they swing.
Your listed matchups are honored exactly. If the league table still holds the real ten team names, every other game is real too — the full 14-week schedule was reconstructed from six team schedules and verified complete. Rename or replace teams and it falls back to a legal rotation for the games it can't know.
Playoff and title odds for all ten teams. Yours is highlighted.
Strength of schedule weighted by how often you actually play someone — facing one juggernaut twice is far worse than two average teams once each, and a list of distinct opponents hides that completely. This only separates the teams once the μ values in the league table differ from one another.
| Team | Opp μ | vs field | Plays twice |
|---|
Not just whether you make it — where you land, which decides whether you get a bye or a coin flip.
—
Win probability week by week against the teams you really play. This is what a real schedule buys you — it names the specific weeks you're likely to lose, which is the difference between a plan and a vague projection.
Odds if only this many weeks remained, from your current record. Being the best team on paper is worth far less over a short run — cut the season short and everyone collapses toward a coin flip. It is also the mirror of why late upgrades move odds more: fewer games left means less time for luck to wash out.
Judged on starter impact and win probability — not on totaled-up player value, which is the exact error that makes people lose 2-for-1 trades.
Ranked by value to your roster. A top-10 free agent is worthless to you if you are already three deep at his position.
| # | Player | Pos | Proj | Starter gain | Scarcity × | Add value | Suggested drop | Read |
|---|
Everything the engine believes, stated plainly. Nothing here is a black box.
The engine rests on three ideas that standard fantasy tools get wrong.
Win probability is Φ((μ−μₒ)/√(σ²+σₒ²)). When you
trail on projection, you raise it by increasing the denominator — deliberately taking on
variance. When you lead, you shrink it. A points-maximizer cannot express either move.
If σ were just proj × position volatility, the best player would always be the most volatile and no real tradeoff could exist. Archetype multipliers (0.72 volume → 1.45 boom-bust) decouple the width of an outcome from its size.
A QB and his WR1 correlate at 0.35. Ignoring that understates a stacked lineup's spread by roughly 13%, which quietly makes every underdog look more hopeless than they are. The simulation uses a Cholesky-decomposed correlation matrix.
"Adds 2 points a week" is unanswerable — enormous for a bubble team, worthless at 99%. The season simulator plays every remaining matchup, ranks by wins then points-for, seeds a bracket with byes, and reports the delta in playoff and title odds. In testing, one identical trade was worth +11.1 playoff points in a strong league and +0.0 in a weak one, and +9.5 at 1-4 versus +0.3 at 4-1. Notice that title odds stayed high even when playoff odds saturated: once you are a lock, an upgrade stops helping you get in and starts helping you win it.
Lineup variance with correlation:
Var(total) = Σ σᵢ² + 2 · Σ_{i<j} ρᵢⱼ · σᵢ · σⱼ
Win probability (normal approximation):
P(win) = Φ( (μ_me − μ_opp) / √(σ_me² + σ_opp²) )
Correlated simulation:
L = cholesky(ρ); shockᵢ = Σ_k L[i][k] · z_k , z ~ N(0,1)
scoreᵢ = max(0, μᵢ + σᵢ · shockᵢ)
Injury-adjusted variance (mixture of plays / sits):
Var = p·(proj·cv)² + p·(1−p)·proj²
Value over replacement:
replacement_rank(pos) = teams·starters(pos) + teams·flex·flex_share(pos) + 1
baseline = mean of the 3 players around that rank
VOR = season_points − baseline
Season simulation (per trial):
for each remaining week, for each matchup (a,b):
score = max(0, N(μ_team, σ_team)); winner += 1 win; both += points_for
standings = sort by (wins desc, points_for desc) ← standard tiebreaker
seeds 1-6 make the bracket; 1 and 2 receive byes
bracket = 3v6, 4v5 → reseed → semifinals → final
Real schedule (hybrid construction):
your game each week is pinned from the export
remaining teams are rotated into legal pairs: R[i] vs R[len-1-i], shifted by week
guarantees every team plays exactly once per week, n/2 games per week
Strength of schedule (frequency-weighted):
SOS = mean(opponent_mu over every scheduled GAME, not distinct opponent)
relative = SOS − mean(mu of all rivals) ← positive means a harder road
Playoff-odds delta of a roster change:
run the full season twice, common random seed, schedule and rivals held fixed
Δ = P(playoff | after) − P(playoff | before)| Pos | CV | Reading |
|---|---|---|
| QB | 0.32 | Most predictable |
| RB | 0.46 | Touch-dependent |
| WR | 0.52 | Target-variance driven |
| TE | 0.55 | TD-dependent |
| K | 0.42 | Situation noise |
| DST | 0.62 | Nearly unforecastable |
| Role | × | Profile |
|---|---|---|
| volume | 0.72 | Touch/target hog, script-proof |
| steady | 0.88 | Defined, reliable role |
| neutral | 1.00 | League-average shape |
| spike | 1.22 | TD or big-play reliant |
| boom_bust | 1.45 | Deep threat, committee back |
| Pair | ρ |
|---|---|
| Same-team QB ↔ WR | +0.35 |
| Same-team QB ↔ TE | +0.28 |
| Same-team QB ↔ RB | +0.10 |
| Same-team skill ↔ skill | −0.08 |
| My DST ↔ player facing it | −0.22 |
Volatility and correlation values are calibrated to widely reported
historical weekly fantasy scoring dispersion, then rounded to defensible round numbers. They are
tunable priors, not measured constants — every one lives in a single dictionary at
the top of ff_engine.py. If you have your own league's scoring history, replace them;
the whole engine reads from those tables.
volume vs
boom_bust meaningfully moves the answer. That is a feature — it makes your read
explicit and testable — but it is still your read.This does not predict football. It makes defensible decisions under uncertainty given a projection source — and it makes the reasoning auditable, which is more than the platform's own start/sit widget will do.