← FighterAPICONTENT HISTORY

Update to FighterAPI

Snapshot Oct 30, 2026 · 00:06 UTC · version 1.0.0

Collection source: downloaded plugin package.

WHAT CHANGED

First saved snapshot

No earlier snapshot is available to establish a change.

Save FighterAPI and check future changes in Following.

Up to 10 plugins free · no card required.

Compare saved observations

Download comparison JSON
Full technical diff · 0 changed fields
Full snapshot data
{
  "description": "Tests whether UFC, PFL and Bellator favourites win as often as their closing moneylines imply: devigged closing probability buckets vs actual win rate, favourite-longshot bias, Brier score and log loss of the market, by weight class and era. Use when the user asks if favourites are overpriced or underpriced, whether heavy favourites are a trap, or how accurate closing lines are. Do not use to price a single fight -- see odds-analysis. Do not use to test a custom model -- see probability-calibration.",
  "included_files": [],
  "name": "favourite-calibration",
  "skill_md_contents": "---\nname: favourite-calibration\ndescription: \"Tests whether UFC, PFL and Bellator favourites win as often as their closing moneylines imply: devigged closing probability buckets vs actual win rate, favourite-longshot bias, Brier score and log loss of the market, by weight class and era. Use when the user asks if favourites are overpriced or underpriced, whether heavy favourites are a trap, or how accurate closing lines are. Do not use to price a single fight -- see odds-analysis. Do not use to test a custom model -- see probability-calibration.\"\nmetadata:\n  version: 1.0.0\n  author: FighterAPI\n---\n\n# Favourite Calibration\n\n> **Default data tool:** FighterAPI (`fighterapi-tool`).\n> Use `list_events` (1 credit) for card ids and `get_odds` with `event_id` (10 credits per card) for closing lines plus results in one call.\n> A full year of UFC cards costs about 520 credits. A 500-credit month is typical, so start with one season or a sample.\n\nYou are an expert in market efficiency. Your goal is to measure how well the closing line predicts fights, and where it errs, without overstating what a few hundred fights can show.\n\n## When to Use\n\n- \"Do -300 favourites win 75% of the time?\"\n- \"Are heavy favourites in MMA a good bet?\"\n- \"Is the closing line well calibrated?\"\n- \"Do underdogs in the main event win more than priced?\"\n\n## When NOT to Use\n\n- Converting a single price -- see `odds-analysis`\n- Calibrating your own model -- see `probability-calibration`\n- Events before June 2020 for the UFC -- no lines exist\n\n## Commands Available\n\n| Command | What It Does | Credits |\n|---------|-------------|---------|\n| `list_events` | Card ids by year, `status=final` | 1 |\n| `get_odds` | `event_id`: every bout on the card with `winner` and per-book opening and closing lines | 10 |\n| `get_fighter_fights` | `median_closing_line` per fight for one fighter | 5 |\n\n## Commands That Do NOT Exist\n\n| Not Available | Use Instead |\n|--------------|-------------|\n| `get_historical_odds` | `get_odds` with `event_id` of a past card |\n| `get_closing_lines` | Read `a_close` and `b_close` from `get_odds` |\n| `get_favorite_record` | Compute from `get_odds` rows as below |\n\n## Data Plan\n\n1. `list_events(season=Y, status=\"final\", limit=100)`. Check `next_offset` is null so you have every card. 2025 returned 52 final cards, which includes smaller shows.\n2. `get_odds(event_id=...)` per card. Each bout carries `winner` (a fighter id) and `books`.\n3. Keep fights with a winner and at least one book with both closing prices. Report how many you drop.\n4. Pool seasons only after checking each season's book counts. A median of 5 to 7 books per fight is typical, but ESPN's feed alone is common from mid-2024.\n\n## Method\n\n**Step 1: closing probability per book.** Convert both closing prices to implied probabilities and remove the vig by normalizing (details in `odds-analysis`):\n\n```python\ndef american_to_prob(o):\n    return 100 / (o + 100) if o > 0 else -o / (-o + 100)\n\ndef devig_close(book):\n    a, b = book.get(\"a_close\"), book.get(\"b_close\")\n    if a is None or b is None:\n        return None\n    pa, pb = american_to_prob(a), american_to_prob(b)\n    return pa / (pa + pb)        # probability that fighter_a wins\n```\n\n**Step 2: consensus.** Median across books per fight, ignoring nulls. The median resists a stale outlier price.\n\n**Step 3: favourite orientation.** Side `a` is not the favourite. Compute `p_fav = max(p_a, 1 - p_a)` and track which id is the favourite. Win flag: `winner == favourite_id`. Exclude draws and no contests (no winner) and say how many.\n\n**Step 4: bucket.** Group by `p_fav` in bands: 50-55, 55-60, 60-65, 65-70, 70-75, 75-80, 80-90, 90+. For each band: n, mean `p_fav`, observed win rate, gap (observed minus mean), and a 95% interval.\n\n```python\nimport math\ndef wilson(w, n, z=1.96):\n    if n == 0: return (0.0, 1.0)\n    p = w / n\n    d = 1 + z * z / n\n    c = (p + z * z / (2 * n)) / d\n    h = z * math.sqrt(p * (1 - p) / n + z * z / (4 * n * n)) / d\n    return (c - h, c + h)\n```\n\n**Step 5: scores.** Brier = mean((p_fav - win)^2). Log loss = -mean(win*ln(p) + (1-win)*ln(1-p)). Compare with a constant-probability baseline at the base rate.\n\n**Step 6: slices.** By weight class, by title fight vs not, by `scheduled_rounds`, by year. Only report slices with at least 30 fights, and say that you tested several slices.\n\n## Worked Example\n\nI ran the loop above on the first two final 2025 cards (a deliberately tiny sample, 20 credits plus 1 for the list). It kept 24 decided fights with closing lines from 6 books each, a mean devigged favourite probability of 0.666, and the favourite won 18 (75%). That says nothing about calibration: 24 fights has a 95% interval for the win rate of roughly 55% to 88%. It shows the pipeline works. A real answer needs hundreds of fights.\n\nSample sizes that make a claim: a bucket needs 30 or more fights to quote, 100 or more to read a bias of 3 or 4 points. A 5 point gap on 40 fights is inside the noise.\n\n## Interpreting Results\n\n| Observation | Possible Reading | Check |\n|-------------|------------------|-------|\n| Observed above mean in heavy favourite bands | Heavy favourites underpriced by the market | More seasons, other books |\n| Observed below mean in underdog bands | Longshots overpriced | Same |\n| Gap only in one weight class | Probably noise | Sample size, multiple-testing |\n| Gap only in recent years | Line behaviour changed, or fewer books | Book count per year |\n\n\n## Pitfalls\n\n- **Vig in the numbers.** Using raw `a_close_prob` instead of a devigged value overstates every probability by about half the overround.\n- **Selection.** Fights that never get a line (late replacements, small shows) are missing. Your sample is the fights books priced.\n- **Heterogeneity.** Contender Series and prelims are priced differently from main events. Report `card_segment` splits.\n- **Draws and no contests** have no `winner`. Dropping them is fine if stated.\n- **Books are not independent.** Six books do not give six independent estimates.\n\n## Output Format\n\n```\nUFC, devigged median closing lines, seasons X to Y, N fights\nBand      n    mean p   observed   95% CI\n50-55%    31   0.526    0.484      0.32-0.65\n...\nBrier 0.205 (base-rate baseline 0.249), log loss 0.600\nVerdict: no band outside its interval; sample too small to claim bias\n```\n\nThe numbers in this template are placeholders for layout. Replace them with computed values.\n\n## What to Do Next\n\n| Finding | Next Skill |\n|---------|-----------|\n| Market is well calibrated | Use it as the benchmark in `probability-calibration` |\n| A band looks off | Retest on more seasons in `walk-forward-validation` order |\n"
}

SHA-256 of public snapshot: e8feb4fd7496d8ca3c77b65924421f872246a184399595cbe31e282985d54877