Weather Bot — Episode 5: Building a Polymarket Weather Bot in Python — Code Architecture and API Integration

By this point I had a strategy I was reasonably confident in — model maximum, city bias, dynamic exits, confidence filters. But all of that lived in my head and a messy collection of test scripts. Turning it into a bot that could run unattended, scan markets every five minutes, and send me updates on Telegram was a different challenge entirely.

This episode walks through what I actually built. Not a tutorial — just the architecture, the key decisions, and the parts that tripped me up.

What This Post Covers

The full structure of weather_bot.py — a single Python file that handles forecast fetching, market scanning, position tracking, dynamic exits, and Telegram reporting. Plus the APIs that make it all work: Open-Meteo for weather data, Polymarket's Gamma API for market discovery, and the CLOB API for placing orders.

One File, Five Jobs

I considered splitting the bot into separate modules — one for forecasts, one for trading, one for reporting. But I'm not a developer, and every time I tried to organize it "properly," something broke. So it's all in one file. About 350 lines. Claude wrote most of it, I debugged the parts that didn't work, and we went back and forth until it ran without crashing.

The bot does five things on every scan cycle:

Forecast Engine pulls hourly temperature data from GFS, ECMWF, and ICON through Open-Meteo's combined endpoint. One API call per city, all three models at once. Results get cached for 15 minutes — I learned that the hard way when I burned through 8,000 API calls in a single day before adding the cache.

Market Scanner queries Polymarket's Gamma API to find active weather markets. The API uses a slug format:

slug = f"highest-temperature-in-{city}-on-{month}-{day}-{year}"
resp = requests.get(
    f"{gamma_host}/events?slug={slug}"
)

Basically: it constructs a URL based on the city and date, and asks Polymarket "does this market exist and what are the current prices?" For each temperature bucket in the response, the bot checks whether the adjusted forecast matches and whether YES is below $0.20.

Position Tracker saves everything to a local positions.json file. Every buy, every sell, every open position with its entry price, share count, and timestamp. Nothing fancy — just a JSON file that persists between restarts.

Dynamic Exit Engine checks every open position against the exit tiers from Episode 4. If the current price hits the threshold for how long the position's been held, it triggers a sell signal.

Telegram Reports send a summary after every scan. Here's what an actual report from my bot looks like:

🌤️ WeatherBot v2.1
📅 03/13 20:03 KST
💰 Balance: $189.85 | Realized P&L: $+1.35
────────────────────────────

🎯 Buy Opportunities: 1

🇹🇷 ANKARA (3/15) 🟢
  🌡️ 13.6°C (13.1°/13.0°/12.4°)
  14°C @ $0.145 ($1.0)

📦 Holding: 1 position
  🇦🇷 BUENOS-AIRES 3/14 @$0.100 → exit target $0.115

My bot actually reports in Korean, but the structure is the same. Balance, realized gains, buy opportunities with confidence level and model data, open positions with entry price and exit target. Every five minutes, this shows up on my phone. It's the single most useful feature in the whole project — I can check what the bot's doing while eating dinner or lying in bed.

The APIs

Three APIs power the bot. All free.

Open-Meteo provides the weather forecasts. No API key, no signup. Up to 10,000 calls per day on the free tier. The combined endpoint was a discovery I made after hitting rate limits — instead of three separate calls per city (one per model), one call returns all three:

https://api.open-meteo.com/v1/forecast
  ?latitude=40.13
  &longitude=33.00
  &hourly=temperature_2m
  &models=gfs_seamless,ecmwf_ifs025,icon_seamless
  &forecast_days=3

Polymarket Gamma API discovers active markets and returns current prices. Also free, no key required. The slug-based lookup means you need to know the exact format Polymarket uses for city names — I found out the hard way that it's "buenos-aires" not "buenosaires."

Polymarket CLOB API places actual orders via py-clob-client. This one needs your wallet's private key and funder address. It only runs in LIVE mode — in DRY RUN, the bot skips order placement entirely and just reports what it would have bought.

Running the Bot

Setup is minimal:

# Install dependencies
pip install requests python-dotenv py-clob-client

# Environment variables (.env)
POLYMARKET_PRIVATE_KEY=your-key
POLYMARKET_FUNDER_ADDRESS=your-address
TELEGRAM_BOT_TOKEN=your-token
TELEGRAM_CHAT_ID=your-chat-id
DRY_RUN=true

I ran DRY RUN for a full week before even thinking about real money. It scans the same markets, applies the same filters, sends the same Telegram reports — it just doesn't touch the CLOB API. Every bug I found during that week would have cost me real USDC if I'd gone live immediately.

# Start in background
nohup python3 weather_bot.py > bot.log 2>&1 &

# Check logs
tail -50 bot.log

# Stop
pkill -f weather_bot.py

On my MacBook, it runs in a Terminal tab. Later I moved it to Railway for 24/7 operation at $5/month — but for the first few weeks, my laptop was the server.

Things I Learned Building This

The 15-minute cache wasn't in the first version. I was fetching fresh forecasts every 5-minute cycle, which makes no sense — weather models update every 6-12 hours. I only added caching after noticing my API call count was climbing past 8,000 on day two. With 15 cities now, the cache keeps me comfortably under 10,000.

Position tracking sounds simple until you realize the bot can crash mid-trade. If it buys shares but crashes before writing to positions.json, the position exists on Polymarket but not in the bot's memory. I added a reconciliation check on startup, but honestly, I still manually verify the portfolio every morning.

And Telegram reporting turned out to be more important than the trading logic itself. There were several nights where I woke up, checked my phone, saw a Telegram alert about a position hitting exit price, and knew exactly what the bot was doing. Without that, I'd have been staring at a Terminal all day.

Key Takeaways

  • The entire bot is one Python file, ~350 lines. Claude wrote most of it. I debugged and iterated.
  • Three free APIs: Open-Meteo (forecasts), Gamma (market discovery), CLOB (order placement). Total API cost: $0.
  • DRY RUN mode is not optional. Run it for at least a week. Every bug I caught would have been a real loss in LIVE mode.
  • Telegram reporting matters more than you'd think. Being able to check the bot from your phone changes everything.

What's Next

The bot was built and running. DRY RUN data was accumulating. In Episode 6, I'll lay out the results from 10 days of paper trading across 5 cities — which cities actually performed, which ones I should've dropped sooner, and what the numbers looked like before I switched to real money.

← Previous: Episode 4: How the ACP Job Lifecycle Works       Next: Episode 6: DRY RUN Results →


More updates on the way. If you're working on something similar or found a smarter way to do it, drop it in the comments — the more we share, the faster we all move.

Disclaimer: This blog documents my personal learning journey. Nothing here is financial advice.

Comments