← Back to blog

Running a Trading Bot for $13 a Month

By the ZenHodl team — we run the trading bots this blog writes about, and the qualifying live-position record, including losses, is public with its admission rules at /results.

Correction — 6 August 2026

This post was published under the title "Running a Profitable Trading Bot for $13 a Month." The bot is not profitable.

Reconciled against our canonical trade ledger, live trades only (shadow mode and backfill excluded):

claimed here actual
bot P&L $60–80/month −$217.75 cumulative, over 2,214 resolved live trades
per trade −$0.098 mean
win rate 45.9%
settlement CLV −1.87c mean, beats the close 47.6% of the time

The account's best cumulative position at any point in its history is +$38.34. The entire lifetime gain of the bot, at its peak, is smaller than one month at the low end of the rate this post claimed. The paragraph that made the claim is corrected in place below rather than deleted, and the title now describes what the post actually is: an infrastructure cost breakdown.

Two further claims in the opening line were wrong and are also corrected below. "8 sports" — four sports have taken a live fill in the last 30 days; the rest run as paper trades only. "On-chain trade verification" — trades do settle on Polymarket's on-chain CLOB, but our published ledger is not on-chain verification: about 60% of resolved trades carry an execution identifier and only 0.5% a direct on-chain transaction hash.

The infrastructure content — the bill, what runs where, the stack — is the genuinely useful part of this post and stands. The one exception is the hosting section, which had gone stale in a way that mattered: the real-money bot now runs on the VPS, not on a local Mac. That is corrected in place too.

The live ledger, including every loss, is at /results; the honest edge aggregate is at /clv.

The retail narrative says you need expensive infrastructure to compete with quant funds. You don't. Our entire trading operation — one supervised bot process, a public website, real-time edge detection, a public trade ledger — runs on $13 a month total.

Being precise about what that operation is: the bot evaluates eleven sports, but only four have taken a live fill in the last 30 days (LoL, soccer, WNBA, WTA). The rest — MLB, tennis, NFL, CFB, NBA, NCAAMB, NCAAWB, and as of August 2026 soccer as well — are force-shadowed. They run the full model and log paper trades; they place no live orders. And the operation loses money: −$217.75 over 2,214 resolved live trades.

This post is about what the infrastructure costs, not about what it returns. The two are unrelated, and the cheap bill does not make the strategy work.

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 Runs Where

Corrected August 2026. This section originally said "the actual trading bots run on a local Mac, not the VPS," that the bots held the Polymarket private key locally, and that "we never put private keys on remote servers" — so a compromised VPS could at worst deface the website. That describes an earlier architecture. It is not how the system runs today, and leaving it up was a security claim we no longer meet. We are stating that plainly instead of quietly swapping the paragraph.

Today the real-money bot is a single systemd service on the same VPS (zenhodl-unified, Restart=always), and its Polymarket credentials live on that server. The trade-off went the other way: a web-layer compromise is now a wallet compromise, not just a defaced homepage. What we bought for it is uptime — the bot no longer stops trading when a laptop sleeps — and one machine to deploy instead of two.

The mitigations are ordinary rather than clever. The origin sits behind Cloudflare with direct access to ports 443 and 8000 closed, secrets are mode 0600 and pulled to an offline backup nightly, and trading can be halted with a single sentinel file (touch KILL_SWITCH) from a phone. None of that is as strong as simply not having the key on the box.

The canonical trades.jsonl is the VPS copy. The public results page reads it directly; local copies go stale.

What genuinely is not on the VPS is the market-data capture rig. The Polymarket and Kalshi depth recorders, the sports snapshot logger, and the nightly backup jobs in both directions run on a local Mac. That captured data is the part that cannot be rebuilt if it is lost, so it is kept in two places by design.

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:

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 - Machine for the bot: none, if you run it on the same VPS as we now do. A separate machine that holds the key is the safer arrangement and costs whatever you already own. - 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

Thirteen dollars a month is a low bar to clear. We have not cleared it.

This post originally claimed "$60-80/month in bot P&L on small position sizes, which means infrastructure is a 17-23% expense." The real figure is −$217.75 cumulative across 2,214 resolved live trades, a mean of −$0.098 per trade, at a 45.9% win rate. Infrastructure is not a 17-23% expense against profit; there is no profit for it to be an expense against. It is a $13/month cost, paid regardless of what the bot does.

The honest version of the lesson is narrower but still worth something: cheap infrastructure means a losing strategy costs you the strategy's losses and very little else. We ran 2,214 live trades to establish that the edge was not there. The finding-out was almost free. That is not the same as making money, and a post that conflated the two — which this one did, in its title — was selling the wrong thing.

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. We moved the VPS nearer to Polymarket's matching engine rather than leaving it where we started, in Europe. Throughput was never the constraint. This post previously put a "~400ms to ~150ms" figure on that move; we no longer have a measurement we trust behind it, so it is removed rather than restated. What we do measure today is price-relevant latency of roughly 556ms.

Security through architecture. This lesson originally read that splitting trading from web hosting "eliminates the most catastrophic failure mode (key theft from a compromised server)." We no longer run that split — see the corrected hosting section above — so we are not entitled to the lesson. The general point survives: decide deliberately which machine holds the key, and know what you gave up when that changes. We gave up the strongest version of this protection in exchange for uptime.

You don't need a quant fund to run the stack. You need a $13/month plan, a working pipeline, and the discipline to run it. Whether the strategy sitting on top of that stack makes money is a completely separate question, and in our case, over 2,214 live trades, the answer so far is no.


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.

Related reading

Get ZenHodl Weekly

One weekly email with live results, one model insight, and product updates.

Tuesday mornings. No spam.

Want to build this yourself?

The ZenHodl course teaches you to build a complete prediction market bot in 6 notebooks.

Join the community

Discuss strategies, share results, get help.

Join Discord