Deep-dive analysis of a single MLB player (hitter or pitcher) for the Yahoo Fantasy Baseball 2K25 league. Web-searches FanGraphs (ATC projections), Baseball Savant (xwOBA/xBA/xERA), MLB.com (lineups, probables), RotoWire (weather, injuries), and RotoBaller (closer depth) to produce the full set of structured player signals defined in the signal framework. Emits form_score, matchup_score, opportunity_score, daily_quality, regression_index, obp_contribution, sb_opportunity, role_certainty for hitters and qs_probability, k_ceiling, era_whip_risk, streamability_score, two_start_bonus, save_role_certainty for pitchers. Use when you need to analyze player, compute daily_quality, compute regression index, produce player signals, run a hitter analysis, run a pitcher analysis, or prep start/sit inputs for the lineup optimizer.
Deep-dive analysis of a single MLB player (hitter or pitcher) for the Yahoo Fantasy Baseball 2K25 league. Web-searches FanGraphs (ATC projections), Baseball Savant (xwOBA/xBA/xERA), MLB.com (lineups, probables), RotoWire (weather, injuries), and RotoBaller (closer depth) to produce the full set of structured player signals defined in the signal framework. Emits form_score, matchup_score, opportunity_score, daily_quality, regression_index, obp_contribution, sb_opportunity, role_certainty for hitters and qs_probability, k_ceiling, era_whip_risk, streamability_score, two_start_bonus, save_role_certainty for pitchers. Use when you need to analyze player, compute daily_quality, compute regression index, produce player signals, run a hitter analysis, run a pitcher analysis, or prep start/sit inputs for the lineup optimizer.
Recommendation to lineup-optimizer: daily_quality = 66 -> START. regression_index = +15 suggests no need to sit on any recent cold-streak noise.
Pitcher counter-example (pitcher start): Bowden Francis (TOR) at COL. daily_quality replaced by streamability_score. Coors kills streamability_score regardless of raw stuff; skill would emit qs_probability ~28, k_ceiling ~40, era_whip_risk ~82 -> streamability_score ~32 (sub-70 threshold -> SIT / DO NOT STREAM).
Workflow
Copy this checklist and track progress:
MLB Player Analysis Progress:
- [ ] Step 1: Classify player (hitter vs pitcher; SP vs RP)
- [ ] Step 2: Collect season + 15-day performance (Savant, FanGraphs)
- [ ] Step 3: Collect today's context (opp SP/hitters, park, weather, lineup)
- [ ] Step 4: Compute normalized component scores
- [ ] Step 5: Compute composite signals (daily_quality or streamability_score)
- [ ] Step 6: Check regression_index and role_certainty
- [ ] Step 7: Validate against rubric and emit signal file
Step 1: Classify player
Determine role: hitter (any position player), SP (starter), RP (reliever, closer or setup). The signal set is different per role. See resources/methodology.md for role determination rules when a player has dual eligibility (two-way player, opener + bulk).
Confirm today's role: is the player in today's lineup? Is the SP on the probable-pitcher chart today?
If RP: is this a save-role RP or middle-relief RP? Closer depth lookup required.
Step 2: Collect performance data
Web-search the primary sources. Every URL goes in the signal file's source_urls: list.
Every numeric signal has confidence and at least one source_url
Call mlb-signal-emitter (validation); on failure, log to tracker/decisions-log.md
Common Patterns
Pattern 1: Hot Streak Hitter (Sell-the-News)
Profile: Rolling 15-day wOBA well above xwOBA (actual outperforming expected)
Signal signature: form_score high (>=70), regression_index negative (e.g., -25)
Read: Production is BABIP-aided and not backed by Statcast quality. Do not overweight recent numbers.
Action feed to lineup-optimizer: trim daily_quality by ~5 points mentally; flag for the waiver-analyst if the user is considering selling high
Pattern 2: Cold Hitter with Loud Contact (Buy-Window)
Profile: Rolling 15-day wOBA below season average but xwOBA still strong (>=season xwOBA)
Signal signature: form_score depressed, regression_index positive (>=+20), barrel% still good
Read: Bad-luck stretch. Underlying contact quality intact. Start through it.
Action: keep daily_quality weight as computed; flag positive regression to category-strategist (this is the guy who will pop next week)
Pattern 3: Two-Start Pitcher in a Bad Park
Profile: SP with two starts this scoring week, one of which is at COL / CIN / BOS
Signal signature: two_start_bonus = true, but one start has era_whip_risk >= 70
Read: Volume pays in K and QS, but a blowup in Coors could torch ERA/WHIP for the week
Action: Emit both starts as separate pitcher signals, each with its own streamability_score; streaming-strategist decides whether to eat the bad park for the volume
Pattern 4: Closer in Committee / Role Uncertainty
Profile: RP with save opportunities but manager has said "mix-and-match" or "matchup based"
Signal signature: save_role_certainty <= 50, k_ceiling decent, era_whip_risk low
Read: Rostering pays only if saves materialize. Great ratios but the fantasy cat (SV) is unreliable.
Action: Note explicitly in the signal body. Waiver-analyst uses this to decide FAAB willingness.
Guardrails
Cite every fact. Every numeric input (xwOBA, projected PAs, park factor, CS%) must trace to a URL in source_urls:. Unsourced claims fail the rubric's Source Citation criterion.
OBP matters more than AVG for this league. Our batting cats are R/HR/RBI/SB/OBP (not AVG). When computing obp_contribution and when choosing which rate stat to weight in form_score, use OBP or wOBA (which is walk-inclusive), never AVG alone. Walk rate is a feature, not a footnote.
QS matters more than W for this league. For qs_probability, compute the probability of 6+ IP and <=3 ER, not the probability of a win. Ignore bullpen-game starters and openers -- they score zero QS points by definition.
Use ATC projections, not Steamer alone. FanGraphs ATC is the consensus ensemble and is the most accurate single source. Steamer and ZiPS can be consulted for triangulation but do not substitute ATC without noting it.
Degrade gracefully on search failure. If a source is unreachable, do not invent numbers. Set that component's confidence to 0.3 and record the gap in the red-team note field. The red-team pass will escalate if confidence < 0.4.
Do not re-derive matchup-analyzer signals. If signals/YYYY-MM-DD-matchup.md exists for today's game, consume opp_sp_quality, park_hitter_factor, park_pitcher_factor, weather_risk, bullpen_state directly. Re-deriving wastes runtime and risks inconsistency across agents.
Timestamp every signal.computed_at: YYYY-MM-DDTHH:MMZ. Morning-brief calls are fresh; afternoon re-checks (once lineups post) supersede the morning signal with higher role_certainty.
Range-check every number. 0-100 signals never exceed 100 or go negative. +/-100 signals (regression_index) are clamped. The mlb-signal-emitter validator rejects out-of-range values -- check before calling.
Plain-English body. The frontmatter is for machines; the body must be jargon-free or translate jargon inline for the end user. "xwOBA" -> "expected offensive output based on how hard and at what angle he hit the ball, regardless of whether balls found gloves."