lyndonkl/claude

matchup-win-probability-sim

Computes P(we win at least K of N categories) for a head-to-head categorical matchup via Monte-Carlo simulation or Poisson-binomial approximation. Domain-neutral — works for any fantasy sport with H2H Categories scoring (MLB, NBA, NHL) or any zero-sum per-category competition. Use when you need matchup_win_probability, per_cat_win_probability, expected_cats_won, or variance_estimate; or when user mentions "matchup win probability", "head to head simulation", "Monte Carlo matchup", "Poisson bino…

First seen Apr 19, 2026

Installation

$ npx skills add lyndonkl/claude --skill matchup-win-probability-sim

Summary

  • Computes P(we win at least K of N categories) for a head-to-head categorical matchup via Monte-Carlo simulation or Poisson-binomial approximation.
  • Domain-neutral — works for any fantasy sport with H2H Categories scoring (MLB, NBA, NHL) or any zero-sum per-category competition.
  • Use when you need matchup_win_probability, per_cat_win_probability, expected_cats_won, or variance_estimate; or when user mentions "matchup win probability", "head to head simulation", "Monte Carlo matchup", "Poisson binomial matchup", "P win 6 of 10", "category matchup simulation", or "weekly win probability".

Also in this package

Other skills from lyndonkl/claude · top by installs.

npx skills add lyndonkl/claude

Browse all from lyndonkl/claude

More details

Agent compatibility

Declared targets from SKILL.md / docs. Unmarked agents are not listed — the skill may still install via the CLI.

Claude Code Not declared
Cursor Not declared
Codex Not declared
GitHub Copilot Not declared
Windsurf Not declared
Gemini CLI Not declared
Cline Not declared
OpenCode Not declared

Repository health

Stars 155
Default branch main
Open issues 0
Status Active

Package contents

Files included with this skill beyond the listing page.

  • skill md SKILL.md 15,747 B
  • docs SUMMARY.md 627 B

History

  1. First seen on skills.sh
  2. First recorded snapshot · 41 installs

SKILL.md

Matchup Win Probability Simulator

Table of Contents

  • [Example](#example)
  • [Workflow](#workflow)
  • [Common Patterns](#common-patterns)
  • [Guardrails](#guardrails)
  • [Quick Reference](#quick-reference)

Example

Scenario: Yahoo MLB 10-category H2H matchup, Week 8, threshold = 6 of 10. Categories: R, HR, RBI, SB, OBP (hitting); K, ERA, WHIP, QS, SV (pitching). ERA and WHIP are inverse (lower is better).

Inputs (remaining-week projections for both teams; mean = expected output, stddev = uncertainty):

Cat Our mean Our stddev Opp mean Opp stddev Inverse?
R 42 8 38 7 no
HR 12 3.5 14 4 no
RBI 40 9 41 8 no
SB 6 2.5 4 2 no
OBP 0.335 0.015 0.328 0.014 no
K 55 10 50 9 no
ERA 3.85 0.45 4.10 0.50 yes
WHIP 1.22 0.08 1.28 0.09 yes
QS 4 1.5 3 1.4 no
SV 2 1.2 5 1.5 no

Run both modes with randomseed=42, catwinthreshold=6, nsimulations=10000:

Monte Carlo output:

  • matchupwinprobability = 0.612
  • expectedcatswon = 6.18
  • variance_estimate = 2.42 (variance of cats-won count)
  • percatwin_probability: R 0.64, HR 0.35, RBI 0.47, SB 0.72, OBP 0.64, K 0.64, ERA 0.65, WHIP 0.68, QS 0.69, SV 0.08

Poisson-binomial output (for comparison, uses the same per-cat win probs as inputs to the PB recurrence):

  • matchupwinprobability = 0.605
  • expectedcatswon = 6.18 (exact — sum of per-cat probs)
  • variance_estimate = 2.11 (variance of sum of independent Bernoullis)

Interpretation: we are a modest favorite (~61%). SV is a hard-punt cat (8% win). HR is contested but we lean losing. The six most defensible pushes are SB, QS, WHIP, ERA, R, K (and OBP). Downstream mlb-lineup-optimizer uses win_prob = 0.612 to classify us as a favorite → damp variance.

Workflow

Copy this checklist and track progress:

Matchup Win Probability Simulation Progress:
- [ ] Step 1: Validate inputs and cat_list coverage
- [ ] Step 2: Choose sim_mode (monte_carlo or poisson_binomial)
- [ ] Step 3: Apply inverse-cat handling
- [ ] Step 4: Run simulation with seeded RNG
- [ ] Step 5: Compute per-cat and overall win probabilities
- [ ] Step 6: Emit outputs with variance and optional sim_trace

Step 1: Validate inputs

Confirm every cat in catlist has an entry in both ourpercatprojection and opppercatprojection, each with {mean, stddev}. Confirm catwinthreshold <= len(catlist). Confirm catinverselist ⊆ cat_list. See [resources/template.md](resources/template.md#input-schema).

  • Every cat in cat_list has both sides' projections
  • stddev > 0 for all cats (zero stddev blocks correct simulation; use small floor if unknown)
  • catwinthreshold in [1, len(cat_list)]
  • All catinverselist entries are valid cat names
  • If simmode == "montecarlo", n_simulations >= 1000 (10k default; 100k for tight confidence)

Step 2: Choose sim_mode

Default to montecarlo for operational decisions (full distribution of cats-won). Use poissonbinomial when you need a deterministic, sub-millisecond answer and are willing to assume per-cat independence. See [resources/methodology.md](resources/methodology.md#when-to-use-which-mode).

  • montecarlo: when you need simtrace for audit, when distributions are non-normal, or when per-cat correlations are passed
  • poisson_binomial: when you need a fast closed-form approximation, or when calling this skill inside an inner optimization loop

Step 3: Apply inverse-cat handling

For every cat in catinverselist, flip the comparison: our team wins the cat when our draw is less than the opponent's draw (ERA 3.50 beats ERA 4.20). The cleanest implementation is to negate the margin (ourdraw - oppdraw) → (oppdraw - ourdraw) for inverse cats before counting the win. See [resources/methodology.md](resources/methodology.md#inverse-category-handling).

  • Identify inverse cats from catinverselist
  • Negate margin (or flip comparison) per cat
  • Verify the per-cat win prob for a known-inverse cat aligns with intuition

Step 4: Run simulation

With random_seed set, draw paired outcomes (one per team per cat per sim) from the configured distribution (normal default) and score each sim.

  • Seed the RNG deterministically from random_seed
  • For each of n_simulations:

- Draw ourdraw[cat] ~ Normal(ourmean, ourstddev) for every cat - Draw oppdraw[cat] ~ Normal(oppmean, oppstddev) for every cat - For each cat, compute margin (negated for inverse) - Count catswon = sum(margin > 0) (tie-break rule: exact tie counts as 0.5 or 0; see guardrail #4) - Record matchupwin = (catswon >= catwin_threshold)

For Poisson-binomial mode instead: compute per-cat win prob pi = Φ(marginmeani / combinedstddev_i) analytically (negate margin for inverse cats), then apply the PB recurrence to get P(sum >= threshold).

Step 5: Compute outputs

  • matchupwinprobability = mean of matchup_win across sims (MC) or closed-form PB result.
  • percatwin_probability[cat] = mean of margin > 0 per cat (MC) or Φ(...) per cat (PB).
  • expectedcatswon = mean of catswon (MC) or Σ pi (PB).
  • varianceestimate = variance of catswon across sims (MC) or Σ pi(1-pi) (PB, since cats are modeled as independent Bernoullis).

See [resources/methodology.md](resources/methodology.md#variance-estimation) for derivation.

Step 6: Emit outputs and audit trace

Return the full output dict. If returnsimtrace=true, include the first 100 sims as simtrace for caller-side audit (showing per-cat draws and the final catswon vector).

  • All outputs present: matchupwinprobability, percatwinprobability, expectedcatswon, varianceestimate
  • Optional: sim_trace (first 100 sims only — keep payload small)
  • Cite random_seed used so the caller can reproduce
  • Validate using [resources/evaluators/rubricmatchupwinprobabilitysim.json](resources/evaluators/rubricmatchupwinprobabilitysim.json). Minimum standard: average score 3.5 or above.

Common Patterns

Pattern 1: MLB Yahoo 5x5 (10 cats)

  • cat_list: [R, HR, RBI, SB, OBP, K, ERA, WHIP, QS, SV]
  • catwinthreshold: 6
  • catinverselist: [ERA, WHIP]
  • Ratio cats needing volume weighting: OBP (weight by PA), ERA (weight by IP), WHIP (weight by IP). Caller should pre-compute stddev that reflects volume — a half-week with 20 IP has larger ratio variance than a full week with 55 IP.
  • Typical runtime: 10k sims < 200ms in plain Python

Pattern 2: NBA 9-cat H2H

  • cat_list: [PTS, REB, AST, STL, BLK, 3PM, FG%, FT%, TO]
  • catwinthreshold: 5
  • catinverselist: [TO] (turnovers — lower is better)
  • Ratio cats: FG%, FT% (volume-weight by FGA, FTA)
  • Special: NBA has higher cat-to-cat correlation than MLB (team usage patterns link PTS-AST-3PM); pass correlation matrix if available

Pattern 3: NHL 10-cat or similar

  • cat_list example: [G, A, +/-, PIM, PPP, SOG, W, GAA, SV%, SO]
  • catwinthreshold: 6
  • catinverselist: [GAA] (goals against average)
  • Ratio cats: SV% (volume-weight by SA), GAA (volume-weight by games played)

Pattern 4: Mid-week live matchup (partial week elapsed)

  • Caller should pass {mean, stddev} reflecting remaining-week output plus current running total. That is, mean = runningtotal + expectedremaining; stddev shrinks as less time remains.
  • cat_position from mlb-category-state-analyzer feeds directly: locked-in cats should have near-zero stddev and a mean far outside the opponent's distribution.

Guardrails

  1. Reproducibility requires a seed. Without random_seed, two calls with identical inputs will return slightly different probabilities (Monte Carlo error). For audit logs and unit tests, always pass a seed. The Poisson-binomial mode is deterministic regardless.
  1. Monte Carlo standard error. With n_simulations = N and true probability p, the standard error is sqrt(p(1-p)/N). For N=10000 and p=0.5, SE ≈ 0.005. If the caller needs 3-decimal precision, use N >= 100000.
  1. Inverse cats: negate the margin, not the mean. A common bug is to negate mean at input time, which causes the Poisson-binomial Φ computation to flip sign but breaks the stddev interpretation. Preferred: keep inputs in their natural units (ERA = 3.85 stays 3.85) and negate the computed margin at comparison time. See [resources/methodology.md](resources/methodology.md#inverse-category-handling).
  1. Tie-break convention must be stated. If ourdraw == oppdraw for a cat in a given sim, the convention is catswon += 0.5 for both sides (H2H Cats "ties count as half wins" style) OR catswon += 0 (strict majority). Default: tie_rule = "half" to match Yahoo H2H behavior. Document which rule is in effect.
  1. Normal distribution assumption breaks for extreme counting cats. Saves and Home Runs are low-count discrete quantities; a normal approximation puts non-trivial mass on negative values. For low-mean counting cats (mean < 5), the caller can specify distribution_family = "poisson" per cat; Monte Carlo handles this, Poisson-binomial does not (since PB needs a Φ-based per-cat prob — compute it from the Poisson-normal approximation with continuity correction).
  1. Ratio cats need volume weighting. OBP, ERA, WHIP are ratios (weighted aggregates over PAs or IP). The stddev of the ratio depends on the volume of observations: few PAs → wide stddev. The caller is responsible for supplying a volume-adjusted stddev (see methodology for the formula stddevratio ≈ σperobs / sqrt(nobs)). This skill treats the supplied stddev as truth.
  1. Independence assumption is a simplification. OBP and R are correlated (on-base runners generate runs). The default Monte Carlo assumes independence across cats. If the caller passes a catcorrelationmatrix (positive semi-definite, dimension equal to len(cat_list)), Monte Carlo uses it via Cholesky decomposition of the combined covariance. Poisson-binomial cannot accept correlation (the whole point of PB is independent Bernoullis).
  1. Threshold must match the league format. catwinthreshold = 6 for 10-cat MLB (strict majority), 5 for 9-cat NBA, etc. Passing the wrong threshold silently produces a meaningful but wrong matchupwinprobability. Always confirm the league's tie-break rules for the overall matchup too (some leagues award ties for half-wins in the aggregate count).
  1. Don't aggregate across distinct matchups. A single-call output answers "this week vs this opponent." Weighting a season-long playoff-probability from weekly win probs is a downstream caller's job (mlb-playoff-planner).
  1. Document the variance estimate's meaning. varianceestimate is the variance of the cats-won count (range 0..N). It is NOT the variance of matchupwin_probability. The latter is the MC standard-error variance p(1-p)/N. Both are useful; label them clearly if returning both.

Quick Reference

Core formulas:

Monte Carlo (per sim):
  For each cat c:
    our_draw[c]  ~ Normal(our_mean[c],  our_stddev[c])
    opp_draw[c]  ~ Normal(opp_mean[c],  opp_stddev[c])
    margin[c]    = our_draw[c] - opp_draw[c]
    if c in cat_inverse_list: margin[c] *= -1
    cat_won[c]   = (margin[c] > 0)     # or 0.5 if exact tie and tie_rule="half"
  cats_won       = sum(cat_won across cats)
  matchup_won    = (cats_won >= cat_win_threshold)

Monte Carlo aggregate (over N sims):
  matchup_win_probability    = mean(matchup_won)
  per_cat_win_probability[c] = mean(cat_won[c])
  expected_cats_won          = mean(cats_won)
  variance_estimate          = var(cats_won)

Poisson-Binomial (closed form):
  combined_stddev[c] = sqrt(our_stddev[c]^2 + opp_stddev[c]^2)
  margin_mean[c]     = our_mean[c] - opp_mean[c]    # negated for inverse cats
  per_cat_win_prob[c] = Φ(margin_mean[c] / combined_stddev[c])
  P(exactly k of N wins) via PB recurrence:
    P_0(0) = 1
    P_i(k) = P_{i-1}(k) * (1 - p_i) + P_{i-1}(k-1) * p_i
  matchup_win_probability = Σ_{k >= threshold} P_N(k)
  expected_cats_won       = Σ p_i
  variance_estimate       = Σ p_i (1 - p_i)

When to use which mode:

Need Use
Full distribution of cats-won, audit trace monte_carlo
Sub-millisecond, deterministic, exact PB result poisson_binomial
Per-cat correlations (e.g., OBP-R) monte_carlo (with correlation matrix)
Low-mean counting cats (SV, HR for a short week) montecarlo with distributionfamily="poisson"
Inside an inner optimization loop (thousands of calls) poisson_binomial
Default for weekly strategy montecarlo with nsimulations=10000

Inputs required:

  • ourpercat_projection: dict[cat, {mean: float, stddev: float}]
  • opppercat_projection: dict[cat, {mean: float, stddev: float}]
  • cat_list: list[str] — category names in canonical order
  • catwinthreshold: int (6 for 10-cat, 5 for 9-cat)
  • catinverselist: list[str] — cats where lower is better
  • n_simulations: int (default 10000)
  • random_seed: int (optional but recommended)
  • simmode: "montecarlo" (default) or "poisson_binomial"
  • tie_rule: "half" (default) or "strict"
  • catcorrelationmatrix: optional float[N][N]
  • distribution_family: optional per-cat dict[cat, "normal"|"poisson"]
  • returnsimtrace: bool (default false)

Outputs produced:

  • matchupwinprobability: float in [0, 1]
  • percatwin_probability: dict[cat, float]
  • expectedcatswon: float
  • variance_estimate: float (variance of cats-won count)
  • simtrace: optional list[dict] (first 100 sims; present only if returnsim_trace=true)
  • meta: {simmode, nsimulations, randomseed, tierule} for audit

Key resources:

  • [resources/template.md](resources/template.md): Input schema, output schema, worked MLB 5x5 example with both modes
  • [resources/methodology.md](resources/methodology.md): Monte Carlo formalization, Poisson-binomial recurrence, variance derivation, inverse & ratio cat handling, mode-selection criteria
  • [resources/evaluators/rubricmatchupwinprobabilitysim.json](resources/evaluators/rubricmatchupwinprobabilitysim.json): 10 criteria for input-spec correctness, MC accuracy, PB accuracy, inverse-cat handling, ratio-cat volume weighting, threshold application, reproducibility, output completeness, variance estimation, citations

Upstream callers (examples):

  • mlb-category-state-analyzer — passes remaining-week projections per cat (principles #1, #5, #6 in frameworks/game-theory-principles.md)
  • Any fantasy *-category-state-analyzer equivalent for NBA/NHL
  • Any caller that has per-cat {mean, stddev} and wants matchup-level win probability