The retail narrative says you need expensive infrastructure to compete with quant funds. You don't. Our entire trading operation — 5 bots across 8 sports, a public website, real-time edge detection, on-chain trade verification — runs on $13 a month total.
This is the full breakdown.
The Bill
| Service | Cost | What it does |
|---|---|---|
| Hetzner CX22 VPS | $7/mo | Website, API, signal engine |
| The Odds API | $5/mo | DraftKings/FanDuel/BetMGM odds |
| Cloudflare | $0 | DNS, CDN, DDoS protection |
| Caddy SSL | $0 | HTTPS via Let's Encrypt (auto) |
| Domain | ~$1/mo | zenhodl.net |
| Total | ~$13/mo |
That's it. No managed databases. No paid analytics. No expensive APIs. Open-source software handling everything.
For comparison, the cheapest commercial sports data provider we evaluated was $500/month. The cheapest "trading bot platform" we found was $200/month. Building your own pipeline is 95%+ cheaper than buying one.
What's on the VPS
A single Hetzner CX22 ($7/mo, 2 vCPUs, 4GB RAM, Ashburn, Virginia) runs:
FastAPI + Uvicorn — The main application as one Python process under systemd. Handles the public website (course, results, pricing, blog, dashboard), user authentication (email + bcrypt passwords), the prediction API (real-time win probabilities, fair lines, edge signals), Stripe payment processing, and WebSocket connections.
Signal Engine — An async background task that polls ESPN every 5 seconds, receives Polymarket price updates via WebSocket, runs win probability models on every live game, and emits edge signals when our predictions disagree with market prices.
Multi-Venue Odds Collection — Polls The Odds API every 2 minutes for sportsbook prices from DraftKings, FanDuel, BetMGM, Caesars. Devigs them (removes bookmaker margin) and saves daily Parquet files for historical analysis.
Trade Resolution Cron — Runs every 15 minutes. Checks all pending trades against the Polymarket settlement API. Marks won/lost trades with correct P&L. Without this, the results page would show all trades as pending forever.
Caddy — HTTPS termination and reverse proxy. Auto-provisions Let's Encrypt SSL certificates. Configuration is 15 lines, plus a legacy redirect for the old subdomain:
zenhodl.net, www.zenhodl.net {
reverse_proxy localhost:8000
encode gzip
}
api.zenhodl.net {
redir https://zenhodl.net{uri} permanent
}
Cron Jobs — Cache refresh every 4 hours, ESPN data scraping daily at 6 AM UTC, health check every 5 minutes, database backup daily at 4 AM, trade resolution every 15 minutes, bot heartbeat every 30 minutes.
SQLite Databases — Users, course progress, ratings, usage tracking. WAL mode enabled for concurrent reads. SQLite handles our load fine — we get about 100 user requests per hour. PostgreSQL would be overkill until we 10x.
What's NOT on the VPS
The actual trading bots run on a local Mac, not the VPS. This is deliberate.
The bots hold the Polymarket private key, which controls the trading wallet. We never put private keys on remote servers. If the VPS gets compromised (someone finds an API vulnerability or zero-day in a dependency), the worst case is they deface the website. They can't steal funds because the keys aren't there.
The trading bots write their fills to a trades.jsonl file locally. The deploy script syncs this file to the VPS, which is how the public results page shows our live performance.
This split architecture has trade-offs. The bots stop trading when the Mac is offline (sleep mode, internet outage, etc.). For a production fund, you'd want a more resilient setup. For an indie operator, the security gain is worth the occasional downtime.
The Tech Stack
Language: Python 3.13 for everything. Jinja2 for templates. Alpine.js for frontend interactivity.
Web framework: FastAPI + Uvicorn. Async-native (important for WebSocket handling), fast for Python, self-documenting via type hints.
Database: SQLite with WAL mode. Free, embedded, sufficient for our scale.
Storage: JSONL for trade logs (append-only, human-readable). Parquet for training data and odds snapshots (columnar, compressed, fast queries).
Authentication: Email + bcrypt passwords, session cookies, HMAC-signed download tokens.
Payments: Stripe Checkout. 18 products configured. Webhooks for fulfillment.
Email: Resend SMTP. Free tier covers our volume.
Monitoring: Discord webhooks for alerts. Free.
CSS: Pre-built Tailwind (37KB, no CDN runtime).
Static assets: Cloudflare CDN (free tier). 1-year immutable cache headers.
Every component on this list is either free or open-source. The only paid services are Hetzner ($7), The Odds API ($5), and the domain (~$1).
Performance
Despite the minimal infrastructure, the website performs well:
- Server TTFB: 23ms (warm), <100ms (cold)
- Page size: 11KB gzipped
- Lighthouse Performance: 79/100 mobile, 95+ desktop
- Cloudflare cache hit rate: 92%
The single VPS handles thousands of page views per day, hundreds of API requests per hour, real-time WebSocket connections to Polymarket, ESPN polling every 5 seconds, and the daily cron jobs — all on 2 vCPUs and 4GB RAM. CPU usage averages around 12%. Memory is stable at 280MB.
Most of the performance comes from architectural choices, not hardware:
Single async process. FastAPI's async runtime handles thousands of concurrent connections without spawning threads or processes. Memory and CPU stay flat.
Aggressive caching. Static assets get 1-year immutable cache headers. Cloudflare serves them from edge nodes near the user. The origin server sees almost no static traffic.
Pre-built CSS. Tailwind compiled at build time, not runtime. The CDN version of Tailwind compiles CSS in the browser — terrible for performance. We ship a 37KB pre-built file instead.
WebP images. Dashboard mockups and screenshots are served as WebP, which is roughly half the size of equivalent PNG/JPG.
What This Costs to Replicate
If you wanted to build the same setup today:
One-time: - Domain registration: $12/year - Mac or Linux machine for trading bots: already owned - Time to set everything up: roughly a weekend if you know Python
Monthly: - Hetzner CX22: $7 - The Odds API: $5 - Cloudflare: $0 - Stripe: $0 (only pay per transaction) - Resend: $0 (free tier sufficient) - Discord webhook: $0 - Total: $12/month
The break-even is trivial. If your trading bot generates $13/month in profit, you cover the entire infrastructure. We're currently running at $60-80/month in bot P&L on small position sizes, which means infrastructure is a 17-23% expense — much less than a typical SaaS gross margin would absorb.
The Lessons
You don't need expensive tools. The "professional infrastructure" sold by sports betting platforms and quant fund vendors is overkill for retail-scale operations. SQLite, FastAPI, Caddy, and a $7 VPS handle the load.
Your bottleneck isn't infrastructure. It's data quality and execution discipline. We spent more time fixing model calibration bugs and tuning execution filters than we ever spent on infrastructure problems.
Open source is the cheat code. Every paid alternative we evaluated was 10-100x more expensive than the free open-source equivalent, and usually worse. Caddy beats nginx + manual SSL. FastAPI beats Flask + manual async. SQLite beats managed Postgres for any database under 10GB.
Latency matters more than throughput. Our VPS is in Ashburn, Virginia (close to Polymarket's matching engine) instead of Europe (where we started). This single move dropped our trade latency from ~400ms to ~150ms. Throughput was never the constraint — latency was.
Security through architecture. Splitting trading from web hosting eliminates the most catastrophic failure mode (key theft from a compromised server). It's not free — you give up uptime — but the security tradeoff is correct for our threat model.
You don't need a quant fund. You need a $13/month plan, a working pipeline, and the discipline to run it. That's the entire stack.
Module 6 of our course walks through the complete deployment process: setting up the VPS, configuring systemd services, automating SSL, and monitoring with Discord. Everything described here.