Docs / PigPro / What is PigPro?

The one 0โ€“10 score that drives every draft board, roster card, and waiver pick in the app.

What is PigPro?

PigPro is a 0โ€“10 composite score that ranks every fantasy-relevant NFL player using a weighted blend of eight independent signals. It replaces a scattering of per-source rankings (Yahoo's default projection, Sleeper's search_rank, FFC ADP, our own ML regressor, Firecrawl news sentiment) with one number โ€” and a transparent breakdown so you can see exactly why a player got that number.

Every page in the app that shows a player (/draft-analytics, /lineup, /waivers, /offseason-intel, /players, /draft-room) opens the same PigPro trading card when you click. One source of truth, one UI.

Currently shipping: v1.1 Hybrid. Promoted in April 2026 after a backtest cycle where v1's seven-component formula was extended with a Usage component (top-N precision improved at every skill position; rank correlation +0.019). See the formula page for the exact weights and ml-backtest for the head-to-head numbers.

The eight components

flowchart LR
    A[ML Projection
27.6%] --> SCORE B[ADP Value
23%] --> SCORE C[VOR
18.4%] --> SCORE D[Consistency
9.2%] --> SCORE E[Boom Potential
9.2%] --> SCORE H[Usage / Opportunity
8%] --> SCORE F[Situation
2.3%] --> SCORE G[News Impact
2.3%] --> SCORE SCORE[PigPro 0โ€“10]

Each component is itself a 0โ€“10 score, multiplied by its weight and summed. A position multiplier (~0.9โ€“1.35) tilts the final raw sum into positional reality โ€” RBs and QBs can't earn the same "value" by the same yardage.

1. ML Projection (27.6%)

Our in-house draft-value regressor (GradientBoostingRegressor per position, trained on 2022 + 2023 NFL seasons + rookie features) predicts the upcoming season's total PPR points. That prediction is converted to a 0โ€“10 score using per-position maxes (QB 24 PPG, RB 22, WR 22, TE 16).

Source: ml_draft_predictions table, populated nightly by the ml-draft-retrain CronJob.

QB special case: for QB-position players, the Usage weight (8%) is folded into ML Projection, making QB ML weight 35.6%. This is because the Usage formula (snap-share + rushing-share) under-weighted pocket passers in early testing. See the formula page for details.

2. ADP Value (23%)

The "market price." ADP 1 โ†’ 10.0, ADP 200+ โ†’ 1.0. Linear scale. Pulled from Sleeper search_rank with FFC ADP overriding when available. Both sources now persist nightly to adp_records (April 2026), so we have movement history per player. Yahoo ADP is also collected daily into adp_records (source='yahoo', one global pool) and powers the Yahoo ADP arbitrage overlay โ€” the edge badges on the Value Map and Draft War Room.

A player with a strong ML projection but high ADP is a value target; a player with a weak projection but low ADP is a fade regardless of name recognition.

3. Value Over Replacement (18.4%)

max(0, replacement_ADP โ€“ player_ADP) / (replacement / 10). Replacement ADP is position-specific (e.g., RB replacement is around ADP 150, QB around 120). Captures positional scarcity: a mid-tier RB is worth more than a mid-tier QB because the QB floor is deeper.

Formula + thresholds: league_format.py.

4. Consistency (9.2%)

Coefficient of variation on last season's weekly PPR:

CoV = stdev(weekly_ppr) / mean(weekly_ppr)
consistency_score = 10 ร— (1 โˆ’ min(CoV / 1.5, 1.0))

Lower weekly variance โ†’ higher score. A player averaging 18 PPG with a 4-PPG standard deviation scores higher than one averaging 18 PPG with a 12-PPG standard deviation.

Needs at least 8 games of data in the prior season; otherwise defaults to 5.0.

5. Boom Potential (9.2%)

Upside, measured as the ratio of top-quartile weeks to median:

boom_ratio = percentile_90(weekly_ppr) / median(weekly_ppr)
boom_score = min(10, (boom_ratio โˆ’ 1) ร— 5)

A player whose 90th-percentile week is 2ร— their median week scores 5/10. A 3ร— boomer scores 10/10. Same 8-game minimum as consistency.

6. Usage / Opportunity (8%) โ€” new in v1.1

How much of his team's offensive work does this player get? The single biggest blind spot in v1, where ascending TEs (Bowers, McBride) and high-snap WRs (ARSB) were under-valued because none of the seven prior components captured raw opportunity.

Composite per position:

  • WR/TE: target_share ร— 0.55 + snap_pct ร— 0.45
  • RB: rush_share ร— 0.45 + target_share ร— 0.25 + snap_pct ร— 0.30
  • QB: snap_pct ร— 0.70 + rush_share ร— 0.30 (zeroed in final composite โ€” see ML Projection note above)

The composite is then percentile-ranked within position to produce the 0โ€“10 score. So a WR with 25% target share + 90% snap share lands above one with 18% + 80%.

Sources: nflverse_weekly_stats.target_share (precomputed), nflverse_snap_counts.offense_pct, and rush_share derived in SQL as carries / team_total_carries.

Rookie fallback: players with <12 career NFL games AND drafted in the last 2 seasons substitute a draft-capital proxy: clamp(10 โˆ’ draft_ovr/27, 1, 10). Pick 1 โ†’ 9.96, Pick 32 โ†’ 8.8, UDFA โ†’ 2. Tagged in the components JSONB as source: rookie_proxy so the trading card can display a "~" indicator.

7. Situation (2.3%)

Offseason movement signal โ€” coaching changes, team switches, depth-chart shuffles. Pulled from situation_scores (populated daily by the situation-tracking CronJob from Firecrawl-scraped offseason intel).

Only ~260 players carry a non-zero situation score at any given time (the ones actually mentioned in recent news). Contribution is small by design โ€” it nudges, doesn't rule.

8. News Impact (2.3%)

Recent player news, extracted structured-ly by Claude Haiku from nightly Firecrawl scrapes of ESPN/RotoWire/NFL.com/FantasyPros. For each mentioned player, Claude emits {direction: positive|negative|neutral, magnitude: 0-1, confidence: 0-1} and we aggregate the last 7 days weighted by recency:

news_score = 5.0 + clamp(weighted_impact ร— 2.5, -5, 5)

5.0 = neutral (no recent news). Max +5 or โˆ’5 based on news flow.

Tiers โ€” USDA-Grade Labels

Single-word labels using the USDA-grade quality vocabulary. Universal scale (every fantasy player has seen these on a steak menu), fits the pigskin/agricultural brand without being cute, and every label is positively framed:

Tier Score Label Meaning
1 โ‰ฅ 7.5 PRIME Top quality โ€” first 2-3 rounds, must-have when available
2 5.5 โ€“ 7.5 CHOICE Reliable starter โ€” draft inside ADP range
3 3.5 โ€“ 5.5 SELECT Lean depth โ€” K, DEF, RB3/4, late WR; draftable, not avoid
4 < 3.5 RESERVE Waiver pool / final-round flier

The tier label is also the recommendation field on the trading card and /players table โ€” no separate action verb. Same string in both columns of pigpro_score_history so downstream consumers reading either field see the same value.

Aligned across pigpro/scorer.py, ml_pipeline/pigpro_v1_5.py, pigpro_v1_1_hybrid.py, and projection_writer.py.

Threshold history: T1 cutoff was 8.0 (now 7.5 so legitimate elites stop landing in T2), T3 cutoff was 4.0 / 3.0 in different scorers (now uniformly 3.5). Earlier label sets included TARGET / CONSIDER / AVOID / SLEEPER and GRAND CHAMPION / BLUE RIBBON / STOCKYARD / STRAY โ€” both retired in favor of the clean USDA terms.

Rookie cards โ€” three implicit transitions

Brand-new rookies (drafted within the last ~2 seasons, fewer than 12 NFL games) get proxy values for the four components that depend on NFL history: Usage, Boom Potential, Consistency, Durability. The proxies are derived from draft capital โ€” Pick 1 โ†’ Usage 9.96, Pick 32 โ†’ 8.8, UDFA โ†’ 2.0. Components flagged with source: 'rookie_proxy' show a gold ~ PROXY pip on the trading card.

The transitions are automatic โ€” three triggers baked into ml_pipeline/pigpro_v1_5.py:

Trigger When What changes
Day 0โ€“2 post-draft nflverse-backfill + Sleeper updates ingest NFL Draft slot, combine measurements, college, age, height/weight populate. Score remains proxy-driven.
Career games โ‰ฅ 12 Mid rookie season (typically Week 12 of the rookie year) Usage / Boom / Consistency / Durability switch from proxies to measured signal. Gold ~ PROXY pip disappears.
draft_year < season โˆ’ 2 Two calendar years post-draft Forced exit from the rookie regime even if the player was injured all year. Components fall back to v1 defaults if no measured signal exists.

The trading card itself shows a banner โ€” ๐Ÿฃ ROOKIE CARD โ€” Usage / Boom / Durability inferred from draft capital. Real signal arrives once the player accumulates 12 NFL games (~Week 12 of the season). โ€” whenever years_exp == 0 so users know what's measured vs. inferred.

Defense / Special-Teams cards

Team defenses don't fit the player composite โ€” no target_share, no ml_projection, no situation. They get a simplified 0.70 ร— ADP value + 0.30 ร— prior PPR rank scoring path (see ValueScorer.score_kicker_defense). Currently the prior-PPR-rank component defaults to 5.0 because nflverse import_weekly_data() ships zero team-defense aggregation; the math collapses to ADP-driven.

Cards for DEFs render with: - Sleeper team logo (via sleepercdn.com/images/team_logos/nfl/{abbr}.png) instead of a player headshot - "TEAM DEFENSE" label in the meta row (no age/height/weight) - Banner: ๐Ÿ›ก๏ธ TEAM DEFENSE โ€” scored on a 70% ADP / 30% prior-rank composite. Player-level components don't apply. - Six of the eight skill-position components show 0.0 (genuinely don't apply); only adp_value and prior_ppr_rank are populated

When we wire up a real DEF season-rank source (FantasyPros team-defense rankings or nflverse_pbp aggregation), the second component activates and DEF differentiation tightens. Until then, ADP is the only signal.

Not a projection โ€” a value ranking

PigPro is a ranking signal, not a projection. The Projected / Ceiling / Floor numbers on the card come from the ML Projection component and are in PPR points; the 0โ€“10 score itself is a percentile-like value number. A player projected for 180 PPR with a great context can score higher than a player projected for 220 with red flags.

When you want the raw points prediction, read the Projected tile on the trading card. When you want "should I draft this person," read the PigPro score.