Build a custom OpenClaw skill that tracks Closing Line Value — the single best predictor of long-term betting edge. One SKILL.md file, a SQLite database, and your agent can tell you whether your edge is real or you’re just running hot.

Why Your Agent Needs a CLV Tracker

Win rate lies. A bettor can run 60% over 50 bets on pure variance. ROI fluctuates wildly at small sample sizes. But Closing Line Value — the difference between your placement odds and the closing odds — is the one metric that separates sharp bettors from lucky ones.

Here’s why: the closing line at a sharp book like Pinnacle is the market’s final consensus on the true probability of an outcome. Thousands of sharp bettors, syndicates, and algorithms have hammered that line into shape. If you consistently get better odds than the close, you’re systematically seeing something the market hasn’t priced in yet. That’s edge.

Without CLV tracking, your agent is flying blind. It might win 55% of its bets this month and lose 55% next month, and you’d have no idea whether the strategy is fundamentally sound. The clv-tracker skill gives your agent a memory — it logs every bet, captures the closing line, and computes whether your agent is genuinely beating the market over time.

This is a Layer 4 (Intelligence) skill in the Agent Betting Stack. It sits downstream of odds-scanner and kelly-sizer, consuming their data to produce the ultimate performance metric.

What You’re Building

The clv-tracker skill teaches your OpenClaw agent five operations:

  1. Log a bet — Record placement odds, sport, selection, and game identifier
  2. Fetch closing odds — Pull the closing line from The Odds API for pending bets
  3. Compute CLV — Calculate CLV percentage for individual bets
  4. Generate CLV report — Aggregate CLV metrics: average, hit rate, per-sport breakdown
  5. Export data — Dump CLV records to CSV for external analysis

The skill uses sqlite3, curl, jq, and inline python3 math — no external Python packages needed.

Prerequisites

  • OpenClaw installed and running — Any channel (WhatsApp, Telegram, Discord, WebChat)
  • The Odds API key — Free at the-odds-api.com (500 requests/month)
  • sqlite3 — Pre-installed on macOS and most Linux distributions
  • curl, jq, python3 — Standard on macOS and most Linux distributions

The Math Behind CLV

Closing Line Value measures the percentage edge you captured at bet placement versus where the line closed.

For American odds, the CLV formula converts both placement and closing odds to implied probability, then computes the relative difference:

Implied probability:
  If odds < 0:  prob = |odds| / (|odds| + 100)
  If odds > 0:  prob = 100 / (odds + 100)

CLV % = ((closing_prob - placement_prob) / placement_prob) * 100

A positive CLV means you got a better price than the market’s final assessment. For example, if you bet a team at +150 (40% implied) and it closed at +130 (43.5% implied), your CLV is +8.6% — you paid 40 cents for something the market ultimately valued at 43.5 cents.

Over hundreds of bets, a positive average CLV of even 2-3% indicates a genuine, sustainable edge.

The Complete SKILL.md

Create the skill directory and data folder:

mkdir -p ~/.openclaw/skills/clv-tracker ~/.openclaw/data

Initialize the SQLite database:

sqlite3 ~/.openclaw/data/clv.db "CREATE TABLE IF NOT EXISTS clv_bets (
  id INTEGER PRIMARY KEY AUTOINCREMENT,
  game_id TEXT NOT NULL,
  sport TEXT NOT NULL,
  selection TEXT NOT NULL,
  bet_type TEXT DEFAULT 'h2h',
  placement_odds INTEGER NOT NULL,
  closing_odds INTEGER,
  placement_prob REAL,
  closing_prob REAL,
  clv_percent REAL,
  result TEXT,
  logged_at TEXT DEFAULT (datetime('now')),
  closed_at TEXT
);"

Create ~/.openclaw/skills/clv-tracker/SKILL.md with this content:

---
name: clv-tracker
description: "Track Closing Line Value — the gold standard for measuring betting edge. Log placement odds, fetch closing lines, compute CLV, and generate performance reports. Use when asked about CLV, closing line, edge tracking, or bet performance."
metadata:
  openclaw:
    emoji: "📈"
    requires:
      bins: ["sqlite3", "curl", "jq", "python3"]
    credentials:
      - id: "odds-api-key"
        name: "The Odds API Key"
        description: "Free API key from https://the-odds-api.com/"
        env: "ODDS_API_KEY"
---

# CLV Tracker — Closing Line Value

Track and measure your betting edge by comparing placement odds against closing lines.

# When to Use

Use this skill when the user asks about:
- Logging a bet with placement odds for CLV tracking
- Checking closing line value for recent bets
- Generating a CLV performance report
- Whether their betting edge is real or variance
- Exporting bet history for analysis
- "Am I beating the close?"

# Database Location

All bet data is stored in: `~/.openclaw/data/clv.db`

# Operations

### 1. Log a Bet

Record a new bet with placement odds for future CLV calculation. Replace the values in caps:

```bash
python3 -c "
odds = PLACEMENT_ODDS
prob = abs(odds)/(abs(odds)+100) if odds < 0 else 100/(odds+100)
print(f'{prob:.6f}')
" | xargs -I{} sqlite3 ~/.openclaw/data/clv.db \
  "INSERT INTO clv_bets (game_id, sport, selection, bet_type, placement_odds, placement_prob)
   VALUES ('GAME_ID', 'SPORT_KEY', 'TEAM_OR_SELECTION', 'BET_TYPE', PLACEMENT_ODDS, {});"

Parameters to substitute:

  • GAME_ID — The Odds API event ID or a descriptive slug like “nba-lakers-celtics-2026-03-21”
  • SPORT_KEY — e.g., basketball_nba, americanfootball_nfl
  • TEAM_OR_SELECTION — e.g., “Los Angeles Lakers”, “Over 215.5”
  • BET_TYPE — h2h, spreads, or totals
  • PLACEMENT_ODDS — American odds integer, e.g., -150 or +130

After logging, confirm with:

sqlite3 ~/.openclaw/data/clv.db "SELECT id, game_id, selection, placement_odds, logged_at FROM clv_bets ORDER BY id DESC LIMIT 5;"

2. Fetch Closing Odds

Pull the latest (closing) odds for a pending bet from The Odds API. This should be run as close to game time as possible:

curl -s "https://api.the-odds-api.com/v4/sports/SPORT_KEY/odds?apiKey=$ODDS_API_KEY&regions=us&markets=BET_TYPE&oddsFormat=american" \

  | jq '[.[] | select(.id == "GAME_ID") | {

    game: "\(.away_team) @ \(.home_team)",
    start: .commence_time,
    closing_lines: [.bookmakers[] | select(.title == "Pinnacle" or .title == "Circa Sports" or .title == "FanDuel") | {
      book: .title,
      odds: [.markets[0].outcomes[] | {(.name): .price}] | add
    }]
  }]'

Use Pinnacle as the primary closing line source. If Pinnacle is unavailable, fall back to Circa Sports or the consensus (average) across all books.

3. Record Closing Odds and Compute CLV

Once you have the closing odds, update the bet record and compute CLV:

python3 -c "
placement = PLACEMENT_ODDS
closing = CLOSING_ODDS
p_prob = abs(placement)/(abs(placement)+100) if placement < 0 else 100/(placement+100)
c_prob = abs(closing)/(abs(closing)+100) if closing < 0 else 100/(closing+100)
clv = ((c_prob - p_prob) / p_prob) * 100
print(f'{c_prob:.6f}|{clv:.4f}')
" | IFS='|' read c_prob clv && \
sqlite3 ~/.openclaw/data/clv.db \
  "UPDATE clv_bets SET
    closing_odds = CLOSING_ODDS,
    closing_prob = $c_prob,
    clv_percent = $clv,
    closed_at = datetime('now')
   WHERE id = BET_ID;"

Replace BET_ID with the row ID from the log, PLACEMENT_ODDS and CLOSING_ODDS with the American odds values.

4. Generate CLV Report

Produce an aggregate CLV performance report:

python3 -c "
import sqlite3, json
conn = sqlite3.connect('$HOME/.openclaw/data/clv.db')
c = conn.cursor()

## Overall stats
c.execute('SELECT COUNT(*), AVG(clv_percent), SUM(CASE WHEN clv_percent > 0 THEN 1 ELSE 0 END) FROM clv_bets WHERE clv_percent IS NOT NULL')
total, avg_clv, positive = c.fetchone()
positive = positive or 0

## Per-sport breakdown
c.execute('SELECT sport, COUNT(*), AVG(clv_percent), SUM(CASE WHEN clv_percent > 0 THEN 1 ELSE 0 END) FROM clv_bets WHERE clv_percent IS NOT NULL GROUP BY sport ORDER BY AVG(clv_percent) DESC')
sports = c.fetchall()

print('=== CLV Performance Report ===')
print(f'Total bets tracked: {total}')
if total and total > 0:
    print(f'Average CLV: {avg_clv:+.2f}%')
    print(f'CLV hit rate: {positive}/{total} ({100*positive/total:.1f}%)')
    if total < 100:
        print(f'⚠ Sample size ({total}) is below 100 — results may not be statistically significant')
    print()
    print('Per-sport breakdown:')
    for sport, cnt, avg, pos in sports:
        pos = pos or 0
        print(f'  {sport}: {avg:+.2f}% avg CLV | {pos}/{cnt} positive ({100*pos/cnt:.1f}%) | n={cnt}')
else:
    print('No closed bets with CLV data yet.')
conn.close()
"

5. Export to CSV

Export all CLV data for external analysis:

sqlite3 -header -csv ~/.openclaw/data/clv.db \
  "SELECT id, game_id, sport, selection, bet_type, placement_odds, closing_odds, placement_prob, closing_prob, clv_percent, result, logged_at, closed_at FROM clv_bets ORDER BY logged_at DESC;" \
  > ~/.openclaw/data/clv_export.csv && echo "Exported to ~/.openclaw/data/clv_export.csv"

Output Rules

  1. When logging a bet, confirm the entry with the bet ID, selection, and placement odds
  2. When showing CLV for a single bet, display: selection, placement odds, closing odds, CLV %, and whether it was +CLV or -CLV
  3. For reports, always include total sample size, average CLV, hit rate, and per-sport breakdown
  4. Flag sample sizes below 100 as “not yet statistically significant”
  5. Use +/- notation for CLV percentages (e.g., +3.2% CLV, -1.5% CLV)
  6. When fetching closing odds, prefer Pinnacle’s line as the benchmark — it’s the sharpest market

Error Handling

  • If sqlite3 is not available, tell the user to install it (apt install sqlite3 or brew install sqlite3)
  • If the database doesn’t exist, run the CREATE TABLE command from the setup section
  • If ODDS_API_KEY is not set, tell the user to get a free key at https://the-odds-api.com/
  • If no closing odds are available (game not yet started or API doesn’t have the event), skip CLV calculation and note it as pending
  • If a bet ID doesn’t exist when updating, warn and list recent bet IDs

## Set Your API Key

```bash
export ODDS_API_KEY=your_key_here

Add to your shell profile (~/.zshrc, ~/.bashrc) so it persists across sessions:

echo 'export ODDS_API_KEY=your_key_here' >> ~/.zshrc

Test It

Restart OpenClaw (or wait for hot-reload if your version supports it), then try these prompts:

PromptExpected Behavior
“Log a bet on Lakers ML at -150 for tonight’s game”Inserts a row into clv_bets with placement_odds=-150, computes implied prob
“What are the closing odds for tonight’s NBA games?”Fetches current odds from The Odds API (run close to tip-off)
“Compute CLV for bet #12 — it closed at -170”Updates the record with closing_odds=-170, calculates CLV %
“Show my CLV report”Generates aggregate stats: avg CLV, hit rate, per-sport breakdown
“Am I beating the close?”Same as CLV report — answers whether your edge is real
“Export my CLV data to CSV”Dumps clv_bets table to ~/.openclaw/data/clv_export.csv

Your agent reads the SKILL.md, identifies the relevant operation, substitutes the correct values, and runs the sqlite3/curl/python3 pipeline.

How It Works Under the Hood

OpenClaw skills are not plugins that execute code directly. The SKILL.md is a set of instructions that the LLM (Claude, GPT, etc.) reads and follows at runtime.

When you ask “show my CLV report,” this is what happens:

User message → OpenClaw Gateway
  → LLM reads system prompt + active skills
  → LLM matches "CLV report" to clv-tracker skill
  → LLM generates python3 command with SQLite query
  → OpenClaw executes command via shell tool
  → Python reads database, computes aggregates
  → LLM formats output for user with +/- CLV notation

The CLV tracker depends on two data points per bet: placement odds (logged when you take the position) and closing odds (fetched from The Odds API near game time). For best results, pair this skill with a cron job that auto-fetches closing lines, so you don’t have to remember to capture them manually.

Extending the Skill

Auto-Fetch Closing Lines on a Schedule

Set up a cron job that fetches closing odds for all pending bets 5 minutes before scheduled game times:

{
  "cron": "*/5 * * * *",
  "prompt": "Check for any logged bets starting in the next 10 minutes. If found, fetch closing odds from Pinnacle and compute CLV automatically."
}

This eliminates the manual step of capturing closing lines — your agent does it autonomously.

Chain with Other Skills

The clv-tracker consumes data from and feeds insights to other skills in the stack:

  • odds-scanner — Provides the initial placement odds that get logged here
  • kelly-sizer — CLV data over time helps calibrate your edge estimates, which Kelly uses for position sizing
  • vig-calculator — Low-vig books produce sharper closing lines, improving CLV benchmark accuracy
  • sharp-line-detector — Steam moves often precede significant closing line shifts — cross-referencing with CLV data reveals which line movements your agent is capturing

Add Win/Loss Tracking

Extend the schema to track actual results alongside CLV:

sqlite3 ~/.openclaw/data/clv.db "UPDATE clv_bets SET result = 'win' WHERE id = BET_ID;"

This lets you correlate CLV with actual outcomes — confirming that +CLV bets produce positive ROI over time, which is the entire thesis of sharp betting.

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, Polymarket monitoring, Kalshi tracking, arbitrage detection
  • Layer 4 — Intelligence: EV calculation, Kelly sizing, CLV tracking (this guide), news sentiment

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

What’s Next