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):
| Grade | Vig Range | Description |
|---|---|---|
| A+ | < 2.00% | Exchange-level pricing. Pinnacle territory. |
| A | 2.00% – 2.99% | Very competitive. Sharp-friendly lines. |
| B+ | 3.00% – 3.99% | Above average. Better than most US retail books. |
| B | 4.00% – 4.99% | Standard US sportsbook pricing. The −110/−110 baseline (4.76%). |
| C+ | 5.00% – 5.99% | Slightly below average. |
| C | 6.00% – 6.99% | Below average. You’re paying a noticeable premium. |
| D | 7.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:
| Delta | Direction |
|---|---|
| < −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
| File | Role |
|---|---|
workers/shared/src/vig.ts | impliedProb, vig2Way, vig3Way, grade — the mathematical core |
workers/odds-ingest/src/compute.ts | computeMarketVig, computeVigBySport, computeVigOverall — the aggregation engine |
workers/shared/src/trends.ts | computeTrends, computeWeeklyTrend — 24h and 7-day trend calculations |
workers/odds-ingest/src/write.ts | KV write layer — overall, per-sport, per-market, per-book, and daily history |
workers/odds-ingest/src/index.ts | Orchestrator — cron handler that ties fetch → compute → write |
proxy/src/index.js | API routing — maps /api/odds/vig to KV keys |
themes/agentbets/static/js/vig-index.js | Frontend 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:
- Pick a sport and a sportsbook (e.g., NFL, DraftKings)
- Pull the same odds from The Odds API or the sportsbook directly
- For each event, convert both sides to implied probability and compute overround
- Average all event-level overrounds — this should match our per-sport
avgVigfor that book - Check the grade against the threshold table in Step 5 above
- 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
| Date | Change |
|---|---|
| 2026-03-24 | Initial methodology document published |
