Build a custom OpenClaw skill that calculates the vig (juice/overround) on any sportsbook market. One SKILL.md file, basic math, and your agent can rank every book by how much they charge you.

Why Your Agent Needs a Vig Calculator

Every sportsbook takes a cut. That cut — the vig, juice, or overround — is the difference between winning long-term and slowly bleeding money. A -110/-110 market has ~4.5% vig. A -105/-105 market has ~2.4%. Over thousands of bets, that gap is the difference between profitability and ruin.

Most bettors never calculate vig. They eyeball odds and pick the best number. An autonomous betting agent can do better — it can quantify the exact cost of every market at every book, every time, and always trade at the sharpest price.

The vig-calculator skill is the quality layer of the Agent Betting Stack. While odds-scanner handles price discovery (what are the odds?), vig-calculator handles price evaluation (how good are these odds?). Together, they give your agent the foundation for intelligent line shopping.

What You’re Building

The vig-calculator skill teaches your OpenClaw agent four operations:

  1. Calculate vig for a market — Input two-way or multi-way odds, get hold percentage
  2. Convert odds to no-vig fair odds — Remove the vig to find the true implied probability
  3. Rank sportsbooks by hold — Compare efficiency across books for a given sport
  4. Generate vig index snapshot — Aggregate hold data across markets for daily tracking

The skill uses Python 3 for math — no external packages, no API keys required. It optionally chains with odds-scanner for live data.

Prerequisites

  • OpenClaw installed and running — Any channel (WhatsApp, Telegram, Discord, WebChat)
  • Python 3 — Pre-installed on macOS and most Linux distributions
  • odds-scanner skill (optional) — For live odds ingestion. See the odds-scanner guide

The Math Behind Vig

Before building the skill, here’s the math it implements:

American Odds → Implied Probability

For negative odds (favorites):

implied_prob = |odds| / (|odds| + 100)

Example: -150 → 150 / 250 = 0.600 (60.0%)

For positive odds (underdogs):

implied_prob = 100 / (odds + 100)

Example: +130 → 100 / 230 = 0.4348 (43.48%)

Vig Calculation

Sum the implied probabilities for all outcomes in a market:

vig = (sum of implied probabilities) - 1

Example: -150/+130 → 0.600 + 0.4348 = 1.0348 → 3.48% vig

No-Vig Fair Odds

Remove the overround proportionally:

fair_prob = implied_prob / sum_of_implied_probs

Example: -150 side → 0.600 / 1.0348 = 0.5799 → fair odds of -138

Hold Percentage

Hold is vig expressed as a percentage of total handle:

hold% = 1 - (1 / sum_of_implied_probs) × 100

A 3.48% vig translates to approximately 3.36% hold — the percentage of every dollar wagered the book expects to keep.

The Complete SKILL.md

Create the skill directory:

mkdir -p ~/.openclaw/skills/vig-calculator

Create ~/.openclaw/skills/vig-calculator/SKILL.md with this content:

---
name: vig-calculator
description: "Calculate vig (juice/overround/hold) for any sportsbook market. Convert odds to no-vig fair lines. Rank books by efficiency. Use when asked about vig, juice, hold percentage, overround, or sportsbook sharpness."
metadata:
  openclaw:
    emoji: "🧮"
    requires:
      bins: ["python3"]
---

# Vig Calculator

Calculate the vig (juice/overround) for any sportsbook market and rank books by efficiency.

# When to Use

Use this skill when the user asks about:
- Vig, juice, or overround on a market
- Hold percentage for a sportsbook
- Removing vig to find fair/true odds
- Ranking sportsbooks by efficiency or sharpness
- Which book has the lowest juice
- The AgentBets Vig Index

# Operations

### 1. Calculate Vig for a Two-Way Market

Given American odds for both sides:

```python
python3 -c "
import sys
odds = [int(x) for x in sys.argv[1:]]
probs = []
for o in odds:
    if o < 0:
        probs.append(abs(o) / (abs(o) + 100))
    else:
        probs.append(100 / (o + 100))
total = sum(probs)
vig = (total - 1) * 100
hold = (1 - 1/total) * 100
print(f'Implied probs: {[round(p*100,2) for p in probs]}%')
print(f'Sum: {round(total*100,2)}%')
print(f'Vig (overround): {round(vig,2)}%')
print(f'Hold: {round(hold,2)}%')
fair = [round(p/total*100,2) for p in probs]
print(f'No-vig fair probs: {fair}%')
" ODDS_SIDE_A ODDS_SIDE_B
```

Replace ODDS_SIDE_A and ODDS_SIDE_B with the American odds (e.g., -110 -110, or -150 +130).

For multi-way markets (3+ outcomes like moneyline in a 3-way soccer match), add more odds as arguments.

### 2. Convert to No-Vig Fair Odds

Remove the vig and return fair American odds:

```python
python3 -c "
import sys
odds = [int(x) for x in sys.argv[1:]]
probs = []
for o in odds:
    if o < 0:
        probs.append(abs(o) / (abs(o) + 100))
    else:
        probs.append(100 / (o + 100))
total = sum(probs)
print('Fair odds (vig removed):')
for i, p in enumerate(probs):
    fair_p = p / total
    if fair_p >= 0.5:
        fair_american = round(-100 * fair_p / (1 - fair_p))
    else:
        fair_american = round(100 * (1 - fair_p) / fair_p)
    print(f'  Side {i+1}: {odds[i]} -> fair {fair_american:+d} (true prob {round(fair_p*100,1)}%)')
" ODDS_SIDE_A ODDS_SIDE_B
```

### 3. Rank Sportsbooks by Hold (Requires odds-scanner)

When the user has odds data from odds-scanner, calculate hold per book:

```python
python3 -c "
import json, sys
data = json.loads(sys.stdin.read())
book_vigs = {}
for game in data:
    for book in game.get('books', []):
        name = book['name']
        h2h = book.get('h2h', {})
        if h2h and len(h2h) >= 2:
            odds = list(h2h.values())
            probs = []
            for o in odds:
                if o < 0:
                    probs.append(abs(o) / (abs(o) + 100))
                else:
                    probs.append(100 / (o + 100))
            total = sum(probs)
            hold = (1 - 1/total) * 100
            book_vigs.setdefault(name, []).append(hold)
print('Sportsbook Rankings (lowest hold = sharpest):')
print('-' * 50)
rankings = []
for name, holds in book_vigs.items():
    avg = sum(holds) / len(holds)
    rankings.append((name, avg, len(holds)))
for rank, (name, avg, n) in enumerate(sorted(rankings, key=lambda x: x[1]), 1):
    print(f'{rank}. {name}: {avg:.2f}% avg hold ({n} markets)')
"
```

Pipe odds-scanner JSON output into this command.

### 4. Generate Vig Index Snapshot

Summarize the current vig landscape for a sport:

```python
python3 -c "
import json, sys
from datetime import datetime
data = json.loads(sys.stdin.read())
book_vigs = {}
for game in data:
    for book in game.get('books', []):
        name = book['name']
        h2h = book.get('h2h', {})
        if h2h and len(h2h) >= 2:
            odds = list(h2h.values())
            probs = [abs(o)/(abs(o)+100) if o < 0 else 100/(o+100) for o in odds]
            total = sum(probs)
            hold = (1 - 1/total) * 100
            book_vigs.setdefault(name, []).append(hold)
print(f'=== VIG INDEX SNAPSHOT — {datetime.now().strftime(\"%Y-%m-%d %H:%M\")} ===')
print()
rankings = sorted(
    [(n, sum(h)/len(h), min(h), max(h), len(h)) for n, h in book_vigs.items()],
    key=lambda x: x[1]
)
for name, avg, low, high, n in rankings:
    bar = '█' * int(avg * 4)
    print(f'{name:<20} avg {avg:5.2f}%  range [{low:.1f}%-{high:.1f}%]  {bar}  ({n} mkts)')
print()
market_avg = sum(r[1] for r in rankings) / len(rankings) if rankings else 0
print(f'Market average hold: {market_avg:.2f}%')
sharpest = rankings[0][0] if rankings else 'N/A'
print(f'Sharpest book today: {sharpest}')
"
```

# Output Rules

1. Always show vig as a percentage with two decimal places
2. Show both vig (overround) and hold percentage — they differ slightly
3. When ranking books, sort from lowest hold (sharpest) to highest
4. Include the number of markets sampled per book
5. For no-vig fair odds, show original → fair with true probability
6. Flag any market with vig > 8% as "high juice — consider another book"

# Error Handling

- If the user provides only one side of odds, ask for the other side
- If odds are in decimal or fractional format, convert to American first
- If hold comes out negative (which shouldn't happen with real odds), flag as likely data error
- If chaining with odds-scanner and no data is returned, the sport may be out of season

Test It

Restart OpenClaw and try these prompts:

PromptExpected Behavior
“What’s the vig on -110/-110?”Calculates 4.55% vig, 4.35% hold, shows fair odds
“Remove the vig from -150/+130”Shows fair probabilities and no-vig American odds
“Rank NBA sportsbooks by vig”Chains with odds-scanner, ranks books by avg hold
“Generate a vig index for NFL”Produces vig index snapshot with all books ranked
“Is -105/-105 better than -110/-110?”Compares vig: 2.38% vs 4.55%, recommends the lower

How It Works Under the Hood

The vig-calculator follows the same pattern as every OpenClaw skill — it’s a set of instructions the LLM reads and follows at runtime:

User message → OpenClaw Gateway
  → LLM reads system prompt + active skills
  → LLM matches "vig" / "juice" / "hold" to vig-calculator skill
  → LLM generates python3 command with user's odds
  → OpenClaw executes command via shell tool
  → Output returned to LLM
  → LLM formats response with context

The skill uses inline Python (python3 -c "...") rather than a standalone script. This keeps it portable — no files to manage, no imports beyond the standard library, and it runs on any system with Python 3.

Why Vig Matters More Than You Think

Here’s a quick table showing how vig compounds over 1,000 bets at $100/bet with a 53% win rate:

Vig LevelOddsProfit/1,000 betsAnnual Edge
4.55%-110/-110+$6000.6% ROI
2.38%-105/-105+$2,8502.85% ROI
1.00%-102/-102+$4,1004.1% ROI

The difference between trading at -110 and -105 is $2,250 per thousand bets. For an autonomous agent placing bets 24/7, vig is the single biggest determinant of profitability.

This is why sharp bettors obsess over line shopping — and why your agent needs a vig calculator as a core skill.

Extending the Skill

Historical Vig Tracking

Add a logging layer that tracks vig over time:

{
  "cron": "0 */6 * * *",
  "prompt": "Run the vig index snapshot for NFL, NBA, and MLB. Append results to ~/openclaw-data/vig-history.jsonl with today's date."
}

Over weeks, this builds a dataset showing which books tighten lines for primetime games and which maintain consistent hold. That data feeds directly into the AgentBets Vig Index.

Low-Vig Alerts

Set up threshold alerts:

{
  "cron": "*/15 * * * *",
  "prompt": "Check NBA odds. If any sportsbook's average hold drops below 2%, alert me on Telegram with the book name and current hold."
}

When a book drops its hold (often during promotional periods), your agent catches it in real time.

Chain with Other Skills

The vig-calculator outputs feed directly into other Agent Betting Stack skills:

  • odds-scanner → vig-calculator — Fetch odds, then evaluate their quality
  • vig-calculator → arb-finder — Low-vig books are more likely to offer arb opportunities
  • vig-calculator → kelly-sizer — Factor vig into edge estimates before sizing positions
  • vig-calculator → clv-tracker — Track whether you consistently beat the vig-adjusted closing line

Full Skill Series

This guide is part of the AgentBets OpenClaw Skills series. We’re building a complete library of betting-specific OpenClaw skills that map to the four layers of the Agent Betting Stack:

  • Layer 1 — Identity: Agent reputation tracking via Moltbook
  • Layer 2 — Wallet: Bankroll management, balance checking across platforms
  • Layer 3 — Trading: Odds scanning, vig calculation (this guide), Polymarket monitoring, Kalshi tracking, arbitrage detection
  • Layer 4 — Intelligence: EV calculation, Kelly sizing, CLV tracking, news sentiment

Each skill is a standalone SKILL.md you can install independently or compose into a full autonomous betting pipeline.

What’s Next