Ace.
An end-to-end machine-learning system that predicts professional tennis matches, compares its own probabilities against the live betting market to find mispriced games, and runs the whole loop — data, model, value detection, and bankroll tracking — automatically and unattended. Built solo, front to back.
What it is.
Ace is a full-stack ML system for professional tennis (both the ATP and WTA tours). At its core is a calibrated model that estimates each player's probability of winning a match — but a good prediction isn't the product. The product is finding the small number of matches where the betting market is priced wrong, sizing a position with disciplined risk management, and then measuring, honestly, whether that edge is real. Three systems work together: a data pipeline, a prediction + value-detection model, and a live service that prices matches, paper-trades, and tracks results — with no human in the loop.
What it does.
- Predicts match winners. for every upcoming ATP/WTA match at the tour-500 level and above, with calibrated win probabilities.
- Finds value bets. it removes the bookmaker's built-in margin to recover a “fair” market price, then flags only the matches where the model meaningfully disagrees with that fair price.
- Sizes and simulates bets. across multiple parallel strategies (flat staking and fractional Kelly), each with its own independently-tracked bankroll — including a per-sportsbook breakdown.
- Tracks itself honestly. every bet auto-resolves from the results feed, and closing-line value — the sharpest unbiased check of whether an edge is real — is logged against the market's final price.
- Runs unattended. daily data refresh, odds pricing, prediction, and bet resolution; weekly model retraining that hot-swaps with zero downtime.
How it's built.
DATA MODEL LIVE SERVICE
──── ───── ────────────
25y historical records ─┐ feature engineering price upcoming matches
live stats API (custom) ─┤ → gradient-boosted model → detect market mispricing
recent-match dataset ─┤ probability calibration multi-strategy bankrolls
live odds API ─┘ backtest + validation auto-resolve + track (CLV)
REST API + web dashboardIngestion runs on a scheduled machine (local home machine for now) and publishes a clean, versioned dataset that an always-on cloud service pulls and retrains on — a deliberate split that keeps the heavy data work off the live host and the live host lean.
The machine learning.
- Feature engineering. a custom Elo engine (overall + surface-specific, recency-weighted), rank signals, quality-adjusted rolling form, serve/return performance, fatigue, head-to-head, and playing-style features from shot-level data — all computed with strict time-ordering so a match can never see the future. Leakage is the silent killer, and guarding against it is most of the work.
- The model. gradient-boosted decision trees (XGBoost), trained per tour, with recency-weighted samples and a feature-selection pass that prunes redundant, low-signal inputs.
- Calibration. a calibration layer corrects probabilities so games the model calls “70%” actually win about 70% of the time — which matters more than raw accuracy, because the whole betting decision depends on the probability being honest.
- Value detection. live odds are stripped of the bookmaker's margin to recover a fair probability; a bet is flagged only when the model beats that fair price by a threshold. The question is never “who wins” — it's “where is the market wrong.”
- Risk & measurement. positions are sized with fractional Kelly against a tracked bankroll, and performance is judged by closing-line value, not just noisy short-run win/loss.
(The specific feature set, thresholds, and tuning are kept private.)
Data engineering.
The model is only as good as the data behind it, and clean, complete tennis data is genuinely hard to get — most of the real engineering lives here, not in the model.
- Multi-source reconciliation. ~115,000 matches over 25 years, unified from sources with totally different schemas. The seam is entity resolution — matching “Sinner J.” in a betting feed to “Jannik Sinner” in a stats feed — solved with surname + first-initial agreement, lifting recovery from near-zero to >90% while staying false-positive-safe.
- A reverse-engineered data source. the most predictive non-rating signal (detailed serve stats) has no clean public feed, so I integrate a live-scores provider's undocumented internal API with a browser-matched HTTP client. When the endpoint was silently retired, I diagnosed it by elimination, found the replacement in the site's own JS, and built a second independent discovery path so it can't be a single point of failure again.
- Decoupled ingest → exchange → serve. ingestion runs on a scheduled home machine and publishes a versioned dataset; the always-on cloud service pulls and retrains weekly, hot-swapping with zero downtime — keeping heavy scraping off the memory-constrained host.
- Idempotent, restartable backfills. multi-hour history fills are checkpointed and resumable — newest-first, dedup-safe, safe to interrupt and restart without corruption.
- Debugged a silent dev→prod data-loss bug. a whole field group validated in dev but was dropped before production, so the system “worked” while quietly training on incomplete data. I re-architected it so the data travels with the records. The lesson: verify the output, not the green checkmark.
Tech stack.
| ML / data | Python, pandas, NumPy, XGBoost, scikit-learn, SHAP, Parquet |
|---|---|
| Backend | FastAPI, SQLite, APScheduler |
| Frontend | Next.js, React, TypeScript, Tailwind CSS |
| Data sources | The Odds API, an undocumented live-stats API (custom client), Kaggle, 25y historical repos |
| Infra / ops | Railway, Vercel, Kaggle (versioned data exchange), scheduled jobs, curl_cffi, rapidfuzz |
What it demonstrates.
End-to-end ownership of a real ML product: data engineering (multi-source ingestion, entity resolution, an autonomous pipeline, integrating an undocumented API), applied ML (feature engineering, leakage prevention, calibration, model selection, backtesting), quantitative decision-making (market-relative value detection, Kelly sizing, CLV-based evaluation), and full-stack delivery (a cloud API + web dashboard running unattended in production). Built solo across every layer.
Why I built it.
I wanted to build a real ML product from scratch. I made it fun by picking a domain I enjoy which is tennis (well watching tennis, not playing it). This is an on going project, and I plan to keep improving it - adding features over time, add more analytics to the dashboard, and more.