This scanner compares Polymarket temperature prediction markets against real weather forecasts to find mispriced markets. If the weather forecast says it'll be 75 degrees tomorrow but the market is pricing it at 70 degrees, that's a potential opportunity.
Polymarket has markets for tomorrow's temperature in major cities. Each market asks a question like "Will NYC be above 75 degrees tomorrow?" with YES/NO binary outcomes.
These markets have "temperature buckets" - ranges like 70-75, 75-80, 80-85 degrees. Each bucket has its own market with its own price.
The scanner uses the Open-Meteo API, which is completely free and doesn't require an API key. It provides:
Instead of trusting one weather forecast, the scanner looks at 51 different weather models. If 45 out of 51 models agree it'll be above 75 degrees, that's an 88% probability. If the market price only implies a 60% chance, there might be an edge.
Weather forecasts don't change every second. The scanner caches forecasts for 1 hour to avoid hammering the API and to keep things fast.
# Scrape Polymarket's weather page for market URLs
# Returns a list of "slugs" like "nyc-temperature-tomorrow"
tomorrow_slugs = scrape_temperature_slugs()
What it does: Scrapes the Polymarket weather/temperature page to find all available temperature markets for tomorrow. A "slug" is the URL-friendly name of a market.
# Uses Open-Meteo API to get forecast for a city
forecast = fetch_forecast(city)
# Returns: { predicted_temp: 76.3, high: 79, low: 72, ... }
What it does: Calls the free Open-Meteo API with the city's coordinates to get tomorrow's predicted temperature. No API key needed.
# Gets 51 weather model runs for probability estimation
ensemble = fetch_ensemble(city)
# If 40/51 models say temp > 75 -> ~78% probability
What it does: Fetches 51 different weather model predictions. By counting how many models predict each temperature range, you can estimate the true probability of each outcome.
def run_scan():
# Step 1: Find tomorrow's temperature markets
tomorrow_slugs = scrape_temperature_slugs()
# Step 2: For each city, fetch market data + weather forecast
for slug in tomorrow_slugs:
city = extract_city_from_slug(slug)
market = parse_market_data(fetch_event_data(slug))
forecast = fetch_forecast(city)
ensemble = fetch_ensemble(city)
# Step 3: Sort by volume and display
city_data.sort(key=lambda x: x["market"]["volume_24h"])
print_summary_table(city_data)
What it does: Runs the full scan cycle: find markets, fetch weather data, compare forecasts to market prices, display results sorted by trading volume. Loops every 30 minutes.
This scanner is unique because it requires zero API keys:
This makes it the easiest tutorial to run - just install dependencies and go!
pip install requests termcolor
That's it! Only two packages needed.
| Term | Meaning |
|---|---|
| Slug | A URL-friendly identifier for a Polymarket market |
| Ensemble Model | Multiple weather prediction models run together for better accuracy |
| Temperature Bucket | A temperature range that has its own market (e.g., 70-75 degrees) |
| Mispriced Market | A market where the price doesn't reflect the true probability |
| Cache | Temporarily storing data to avoid re-fetching it |
| Scraping | Extracting data from a webpage automatically |
# --- PYTHON ---
def run_scan():
tomorrow_slugs = scrape_temperature_slugs()
if not tomorrow_slugs:
print("No temperature markets for tomorrow found!")
return []
_load_cache()
city_data = []
for slug in tomorrow_slugs:
city = extract_city_from_slug(slug)
event_data = fetch_event_data(slug)
market = parse_market_data(event_data)
forecast = fetch_forecast(city)
ensemble = fetch_ensemble(city) if city in CITY_COORDS else None
city_data.append({"city": city, "market": market,
"forecast": forecast, "ensemble": ensemble})
time.sleep(0.3)
city_data.sort(key=lambda x: x["market"]["volume_24h"], reverse=True)
print_summary_table(city_data)
print_detailed_city(city_data[:TOP_N])
return city_data
# --- PSEUDO-CODE ---
FUNCTION run_scan():
STEP 1 - Find tomorrow's temperature markets:
SCRAPE the Polymarket weather page for market URLs (slugs)
IF no markets found: PRINT "not posted yet" and STOP
STEP 2 - Load any cached weather data from previous runs
STEP 3 - For each city market:
EXTRACT the city name from the market URL slug
FETCH the Polymarket market data (prices, volumes, buckets)
PARSE the market data into structured format
FETCH the weather forecast from Open-Meteo (free API)
FETCH ensemble data (51 weather models) if coordinates are known
SAVE all this data together
WAIT 0.3 seconds (be polite to the API)
STEP 4 - Sort results:
SORT all cities by 24-hour trading volume (most popular first)
STEP 5 - Display results:
PRINT a summary table showing all cities side by side
PRINT detailed analysis for the top N cities
PRINT full deep-dive for the #1 city
# --- PYTHON ---
def fetch_forecast(city):
coords = CITY_COORDS.get(city)
if not coords: return None
lat, lon = coords
url = f"https://api.open-meteo.com/v1/forecast?latitude={lat}&longitude={lon}&daily=temperature_2m_max,temperature_2m_min"
response = requests.get(url, timeout=10)
return response.json()
# --- PSEUDO-CODE ---
FUNCTION fetch_forecast(city name):
LOOK UP the latitude and longitude for this city
IF coordinates not found: RETURN nothing
CALL the Open-Meteo free weather API:
"Give me tomorrow's high and low temperature for these coordinates"
WAIT up to 10 seconds for a response
PARSE and RETURN the forecast data
(No API key needed - this is a free public service)
# --- PSEUDO-CODE ---
FUNCTION fetch_ensemble(city name):
LOOK UP the latitude and longitude for this city
CALL the Open-Meteo ensemble API:
"Give me the predictions from ALL 51 weather models"
FOR each temperature bucket (e.g., 70-75, 75-80 degrees):
COUNT how many of the 51 models predict this range
CALCULATE the probability = count / 51
EXAMPLE: If 40 out of 51 models say temperature > 75:
Probability of >75 degrees = 40/51 = 78.4%
RETURN all the probability estimates
# --- PYTHON ---
def main():
while True:
run_scan()
remaining = REFRESH_MINUTES * 60
while remaining > 0:
time.sleep(COUNTDOWN_INTERVAL)
remaining -= COUNTDOWN_INTERVAL
# --- PSEUDO-CODE ---
FUNCTION main():
LOOP FOREVER:
RUN a complete scan (find markets, get forecasts, compare, display)
WAIT 30 minutes
THEN RUN another scan