Purpose

This document provides full transparency into how the AgentBets Vig Index calculates vigorish (vig), assigns letter grades, and ranks sportsbooks. Every formula, threshold, and design decision is documented here so bettors, researchers, and competing platforms can verify our work.


1. Data Source

All odds data comes from The Odds API, which aggregates real-time lines from licensed sportsbooks. We request data from two regions:

  • us — US-regulated books (DraftKings, FanDuel, BetMGM, Caesars, etc.)
  • us2 — Offshore/international books (Bovada, BetOnline, Pinnacle, MyBookie, etc.)

We track three market types per sport:

  • Moneyline (h2h) — Win/lose (2-way) or win/draw/lose (3-way for soccer)
  • Point spreads — Handicap lines
  • Totals (over/under) — Combined score lines

Sports, books, and markets are discovered dynamically from the API — there are no hardcoded allowlists for what gets tracked. When a new sport comes in season or a new book appears, it’s included automatically.

Update Frequency

A Cloudflare Worker cron job runs 3x daily at 06:00, 14:00, and 22:00 UTC. Each run fetches a fresh snapshot of every tracked market from every available sportsbook.


2. Step-by-Step Calculation

Step 1: Convert American Odds to Implied Probability

Every odds price is in American format. We convert to implied probability using the standard formula:

For positive odds (underdog):

$$ \text{impliedProb} = \frac{100}{\text{odds} + 100} $$

Example: +200 → 100 / 300 = 0.3333 (33.33%)

For negative odds (favorite):

$$ \text{impliedProb} = \frac{|\text{odds}|}{|\text{odds}| + 100} $$

Example: -150 → 150 / 250 = 0.60 (60.00%)

Step 2: Calculate Vig Per Event Per Market

For each event and each sportsbook, we sum the implied probabilities of all outcomes in a market and subtract 1. The result is the overround (vig) for that specific line.

Two-way markets (moneyline for most sports, spreads, totals):

$$ \text{vig} = \bigl(\text{impliedProb}(A) + \text{impliedProb}(B) - 1\bigr) \times 100 $$

Example: A line of -110 / -110 has implied probabilities of 52.381% each:

$$ (0.52381 + 0.52381 - 1) \times 100 = 4.76% $$

Three-way markets (soccer moneyline with draw):

$$ \text{vig} = \bigl(\text{impliedProb}(A) + \text{impliedProb}(B) + \text{impliedProb}(C) - 1\bigr) \times 100 $$

Three-way markets naturally have higher overround because the bookmaker has three outcomes to price.

Step 3: Filter Invalid Values

Any event where the computed vig is negative (which would indicate an arbitrage opportunity or a data error) is silently excluded from averaging. This prevents anomalous data from distorting the rankings. A negative vig means the sum of implied probabilities is below 100%, which shouldn’t occur in a correctly priced market.

Step 4: Average Vig Per Book Per Sport

For each sportsbook in each sport, we collect every valid vig measurement across all events and all market types into a single pool, then take the arithmetic mean.

Concretely: if a book has vig measurements from 80 moneyline events, 75 spread events, and 75 totals events, all 230 measurements are averaged together to produce that book’s average vig for that sport.

This is an event-count-weighted average, not an equal-weight-per-market average. Markets with more events naturally carry more weight. In practice, all three markets are usually available for the same events, so the weighting is roughly equal. But for sports like MMA (moneyline only, no spreads/totals), the average only reflects moneyline vig.

We also compute separate per-market averages (moneyline average, spreads average, totals average) that are displayed in the sport-specific breakdown tables.

All averages are rounded to two decimal places.

Step 5: Assign a Letter Grade

The grade is assigned based on the per-sport average vig (for sport-level grades) or the cross-sport average (for overall grades):

GradeVig RangeDescription
A+< 2.00%Exchange-level pricing. Pinnacle territory.
A2.00% – 2.99%Very competitive. Sharp-friendly lines.
B+3.00% – 3.99%Above average. Better than most US retail books.
B4.00% – 4.99%Standard US sportsbook pricing. The −110/−110 baseline (4.76%).
C+5.00% – 5.99%Slightly below average.
C6.00% – 6.99%Below average. You’re paying a noticeable premium.
D7.00% – 7.99%High vig. Consider alternatives.
D−8.00% – 9.99%Very high vig.
F≥ 10.00%Predatory pricing. Avoid.

The grade function evaluates strict less-than boundaries. A vig of exactly 2.00% receives an A (not A+). A vig of exactly 5.00% receives a C+ (not B).

Step 6: Compute Overall Cross-Sport Ranking

The overall ranking averages a book’s per-sport average vig across all sports where that book has data, with equal weight per sport.

If a book has data in NFL (avg vig 4.2%), NBA (avg vig 4.5%), and MMA (avg vig 5.1%), the overall is:

$$ \frac{4.2 + 4.5 + 5.1}{3} = 4.60% $$

This means each sport counts equally regardless of how many events it has. A book covering 200 NFL events and 5 MMA events gives both sports the same influence on the overall grade.


3. Trend Tracking

We store daily vig snapshots in KV with a 30-day TTL, enabling two trend windows:

  • 24-hour trend: Compares today’s vig averages against yesterday’s
  • 7-day trend: Compares today’s market-wide average against 7 days ago

A change is classified using a ±0.05 percentage-point dead zone:

DeltaDirection
< −0.05 pp↓ Improving (vig decreased)
−0.05 to +0.05 pp→ Flat
> +0.05 pp↑ Worsening (vig increased)

Positive delta = vig went up = worse for bettors. Negative delta = vig went down = better for bettors.


4. How Data Flows Through the System

The Odds API (external)
    │
    ▼
odds-ingest Worker (Cloudflare cron, 3x daily)
    │  Fetches odds for all active sports
    │  Computes per-event vig
    │  Averages per book per sport
    │  Computes cross-sport overall
    │  Assigns grades
    │  Writes to KV
    │
    ▼
Cloudflare KV (ODDS_DATA namespace)
    │  Key: vig:overall → VigOverallEntry[]
    │  Key: vig:{sport} → BookVigEntry[]
    │  Key: vig:{sport}:{market} → BookVigEntry[]
    │  Key: vig:book:{slug} → per-book breakdown
    │  Key: history:{date}:{sport} → daily snapshots
    │
    ▼
Proxy Worker (api.agentbets.ai)
    │  GET /api/odds/vig → vig:overall
    │  GET /api/odds/vig/{sport} → vig:{sport}
    │
    ▼
Frontend JS (vig-index.js on agentbets.ai/vig-index/)
    │  Fetches JSON, renders tables with grades
    │
    ▼
User sees ranked tables with letter grades

5. Known Limitations & Design Decisions

5a. Equal Sport Weighting in Overall Rankings

The overall ranking weights every sport equally. A book that covers NFL (200 events) and MMA (5 events) treats both sports equally in its overall grade. This was a deliberate choice: it prevents high-volume sports from completely dominating the ranking and ensures niche-market pricing is visible. However, it means a book with unusually high or low vig on a low-volume sport may see its overall grade shift more than expected.

Alternative considered: Event-count weighting across sports. Rejected because it would make the overall grade almost entirely an NFL/NBA grade for most US books, hiding poor pricing in smaller markets.

5b. Negative Vig Exclusion

Events with negative vig (implied arbitrage) are excluded from averaging. This is a data-quality safeguard — negative vig usually indicates stale lines, API lag, or a data error. In rare cases it could be a real arbitrage window, but including it would artificially deflate vig averages.

5c. “Events” Column Is Per-Book

The “Events” column in per-sport tables shows the number of events where that specific sportsbook had odds — specifically, the maximum of the three market-level counts (moneyline, spreads, totals). This accurately represents the sample size backing each book’s grade.

5d. Snapshot Timing

Odds are captured at fixed cron times (06:00, 14:00, 22:00 UTC), not continuously. Vig can fluctuate throughout the day as lines move. Our grades represent the market’s state at snapshot time, not the 24-hour average.

5e. Three-Way Market Vig Is Naturally Higher

Soccer (and any sport with a draw outcome) uses 3-way pricing, which inherently carries higher overround than 2-way markets. When comparing grades across sports, keep in mind that a soccer “B” represents a different absolute vig than an NFL “B” — but the same percentile ranking within that sport.

The per-sport grade still accurately ranks books within that sport. Cross-sport comparison should use the per-sport tables, not the overall grade.

5f. Per-Market Grades Are Market-Specific

When we write per-market breakdowns to KV (e.g., vig:nfl:h2h), each book entry has its grade recomputed from the market-specific vig value — not the overall sport average. A book might earn an A on moneyline but a B on totals within the same sport.


6. Code References

The vig calculation lives in a shared TypeScript module used by the odds-ingest worker.

Core Math

The implied probability conversion:

impliedProb = (american: number): number =>
  american > 0 ? 100 / (american + 100) : Math.abs(american) / (Math.abs(american) + 100);

Two-way and three-way vig:

vig2Way = (a: number, b: number): number =>
  ((impliedProb(a) + impliedProb(b)) - 1) * 100;

vig3Way = (a: number, b: number, c: number): number =>
  ((impliedProb(a) + impliedProb(b) + impliedProb(c)) - 1) * 100;

Grade assignment:

grade = (vig: number): string =>
  vig < 2 ? 'A+' : vig < 3 ? 'A' : vig < 4 ? 'B+' : vig < 5 ? 'B' :
  vig < 6 ? 'C+' : vig < 7 ? 'C' : vig < 8 ? 'D' : vig < 10 ? 'D-' : 'F';

Key Files

FileRole
workers/shared/src/vig.tsimpliedProb, vig2Way, vig3Way, grade — the mathematical core
workers/odds-ingest/src/compute.tscomputeMarketVig, computeVigBySport, computeVigOverall — the aggregation engine
workers/shared/src/trends.tscomputeTrends, computeWeeklyTrend — 24h and 7-day trend calculations
workers/odds-ingest/src/write.tsKV write layer — overall, per-sport, per-market, per-book, and daily history
workers/odds-ingest/src/index.tsOrchestrator — cron handler that ties fetch → compute → write
proxy/src/index.jsAPI routing — maps /api/odds/vig to KV keys
themes/agentbets/static/js/vig-index.jsFrontend rendering — fetches API, builds HTML tables

7. Verification Examples

You can verify our math against any sportsbook’s posted odds.

Example 1: Standard -110/-110 Spread

Home: -110  →  impliedProb = 110 / 210 = 0.52381
Away: -110  →  impliedProb = 110 / 210 = 0.52381
Sum  = 1.04762
Vig  = (1.04762 - 1) × 100 = 4.76%
Grade = B  (4.76 < 5)

Example 2: Reduced Juice -105/-105

Home: -105  →  impliedProb = 105 / 205 = 0.51220
Away: -105  →  impliedProb = 105 / 205 = 0.51220
Sum  = 1.02439
Vig  = (1.02439 - 1) × 100 = 2.44%
Grade = A  (2.44 < 3)

Example 3: Heavy Favorite Moneyline

Home: -300  →  impliedProb = 300 / 400 = 0.75000
Away: +240  →  impliedProb = 100 / 340 = 0.29412
Sum  = 1.04412
Vig  = (1.04412 - 1) × 100 = 4.41%
Grade = B  (4.00 ≤ 4.41 < 5.00)

Example 4: Soccer 3-Way

Home: +130  →  impliedProb = 100 / 230 = 0.43478
Draw: +230  →  impliedProb = 100 / 330 = 0.30303
Away: +180  →  impliedProb = 100 / 280 = 0.35714
Sum  = 1.09495
Vig  = (1.09495 - 1) × 100 = 9.50%
Grade = D-  (9.50 < 10)

8. Audit Checklist

If you want to independently verify the Vig Index:

  1. Pick a sport and a sportsbook (e.g., NFL, DraftKings)
  2. Pull the same odds from The Odds API or the sportsbook directly
  3. For each event, convert both sides to implied probability and compute overround
  4. Average all event-level overrounds — this should match our per-sport avgVig for that book
  5. Check the grade against the threshold table in Step 5 above
  6. Compare against the value shown at api.agentbets.ai/api/odds/vig/{sport}

The API is publicly accessible. You can fetch real data right now:

  • Overall rankings: https://api.agentbets.ai/api/odds/vig
  • Per-sport (e.g., NFL): https://api.agentbets.ai/api/odds/vig/nfl
  • Last updated timestamp: https://api.agentbets.ai/api/odds/generated-at

Changelog

DateChange
2026-03-24Initial methodology document published