writings · 2026-10-15 · 5 min
A Betting Agent on Live Data
A side project to see if an AI-built agent can run on live sports data every day without me. The pipeline works. The edge does not, yet. So it's open source.

This is a fun side project, not a business. I wanted to know if I could build something with AI that runs on its own, on live data, every day, and keeps an honest record of whether it is any good. Sports betting is the test case because the feedback is fast and the market is brutal. Every pick gets graded within a day, and the closing line tells you whether you were smart or lucky.
It has been running since July. Here is what it looks like, where it struggles, and why it's now on GitHub.
What runs every morning
At 7:30 a launchd job runs 20 steps. It pulls schedules for the NFL, college football and the NHL from ESPN. It pulls one paid odds snapshot from The Odds API (six US books, three credits a pull, so it rotates which league gets the paid pull each day). It pulls weather for every outdoor venue from api.weather.gov, injuries, college ratings from CollegeFootballData, and a small ration of player props. Then it settles yesterday's picks from box scores, grades every logged pick against the closing line, and writes the dashboard.
A second job runs every 30 minutes and pulls free lines: Pinnacle's guest API for the sharp reference price, and DraftKings as ESPN embeds it, because that is the line I can actually bet. Rows captured inside 75 minutes of kickoff become the close, so closing line value is graded against a real number instead of a guess.

All of it is plain files. The odds log is 97,000 rows and 180MB on disk, and when it hit 66MB in one file the agent started archiving rows older than 30 days. The ledger is a CSV. The rules it has learned are a JSON file. I did this on purpose: when the output looks wrong I want to read the input, not query it.
The whole thing is about 14,700 lines of Python across 50 scripts, all written with Claude Code. One file, football.py, is the agent. Everything else is a fetcher, a settler, or research that lost.
The loop
The agent does not predict games. That was the first thing that failed. I backtested an Elo model against 715 college games with real closing lines and it landed at 52.5, 49.3, 44.2 and 56.4 percent against the spread by confidence bucket. That is noise around the 52.4 percent you need to break even. An NFL SRS model on 208 games had the same shape. The closing line predicted the margin better than either model on every axis.
So the agent is price-first now. It takes both sides of every market, works out Pinnacle's no-vig fair probability at the same line, and asks one question: is DraftKings offering a better price than fair? Ratings, weather and situational flags are context for me, never a gate.

The gate is the part I care about most. A pick has to clear an edge floor, the price cannot be stale, and the slice it belongs to cannot be blocked. For parlays the rules are hard: three legs at most, every leg at 56 percent or better, half a unit, one a day. If a ticket breaks a rule it prints BLOCKED and the place command refuses to log it.

Where it struggles
The honest part. The record since the price-first rewrite is 49-40-3, up about four units flat, beating the close 41 percent of the time. That is not under water, and it is nowhere near proof of an edge. The old model-driven totals went 11-21 and lost nearly 11 units, which is why totals are price-only now.
The specific things that keep eating time:
Team names. Every feed spells teams differently. Pinnacle says Ole Miss, The Odds API says Ole Miss Rebels, ESPN says Mississippi. There is an alias table and an unmatched log, and this week it still mislabeled a Missouri game as Mississippi State.
Credits. The paid odds pull is rationed, so most of the day the agent is comparing a fresh DraftKings line to a morning snapshot of the other books. That gap hides edges and invents them.
The parlay pool. The builder only admits games where DraftKings disagrees with Pinnacle, so a plain 70 percent favorite with a fair price never gets in. On a Saturday with twenty favorites it found one leg.
The hold. Every favorite at a fair price loses about four percent to the book's margin. The agent knows this and sizes those at zero units, which is correct and also means most days it tells me not to bet.
It's open source
The whole thing is on GitHub: github.com/skeltonmedia/football-picks-agent. Not because it wins, because it doesn't yet. Because the plumbing is the interesting part: keyless sources, flat files, a settle step that grades itself against the close, and a learn step that writes rules the gate enforces the next day. I suspect other people are building the same thing and hitting the same walls, and I'd rather compare notes than keep guessing alone.
What's in the repo is every script and doc, the launchd schedules with paths blanked, the example config, and the alias table. What's not: my keys, my ledgers, and the 180MB odds log, which regenerates itself after a day of running. Standard library only, so it runs on a plain Python 3.9.
If that is you, the questions I have are simple. Where does a price-first agent find edge the market prices slowly? What would you log that I'm not? And how are you handling team names, because I refuse to believe the alias table is the answer. Open an issue.