HK Weather Prediction Market Pipeline: WeatherNext + HKO + Polymarket

- Open-Meteo WeatherNext API client for HK forecasts
- HKO public data client (current conditions, 9-day forecast, typhoon warnings)
- HK-specific weather extraction and calibration
- Polymarket market scanning, price discovery, and market creation proposals
- Trading strategy engine: edge detection, Kelly criterion sizing, probability calibration
- End-to-end pipeline with dry-run mode and scheduled runner
- Interactive dashboard with live HK weather + forecasts + trading signals

Dependencies: Python 3.10+, openmeteo-requests, pandas
No API keys needed for dry-run mode.
Polymarket trading requires private key in .env.
This commit is contained in:
ramseshk
2026-08-10 12:48:05 +08:00
commit c93af97059
21 changed files with 2497 additions and 0 deletions
+177
View File
@@ -0,0 +1,177 @@
"""Hong Kong Observatory data client.
Fetches real-time weather observations, warnings, and forecasts from HKO.
Uses public RSS/JSON feeds where available.
"""
import json
from datetime import datetime
from typing import Optional, List
import requests
class HKOClient:
"""Fetch data from Hong Kong Observatory's public data feeds."""
BASE_URL = "https://data.weather.gov.hk"
ENDPOINTS = {
"current_weather": f"{BASE_URL}/weatherAPI/opendata/weather.php?dataType=rhrread&lang=en",
"9day_forecast": f"{BASE_URL}/weatherAPI/opendata/weather.php?dataType=fnd&lang=en",
"warnings": f"{BASE_URL}/weatherAPI/opendata/weather.php?dataType=warnsum&lang=en",
"local_forecast": f"{BASE_URL}/weatherAPI/opendata/weather.php?dataType=flw&lang=en",
"swt": f"{BASE_URL}/weatherAPI/opendata/weather.php?dataType=swt&lang=en",
"rainfall": f"{BASE_URL}/weatherAPI/opendata/weather.php?dataType=rfmap",
"lightning": f"{BASE_URL}/weatherAPI/opendata/weather.php?dataType=lmap",
"uv_index": f"{BASE_URL}/weatherAPI/opendata/weather.php?dataType=uvi",
"earthquake": f"{BASE_URL}/weatherAPI/opendata/weather.php?dataType=eq",
}
def __init__(self):
self.session = requests.Session()
self.session.headers.update({
"User-Agent": "HK-Weather-Market/1.0",
"Accept": "application/json",
})
def get_current_weather(self) -> Optional[dict]:
"""Get current weather observations for Hong Kong."""
try:
resp = self.session.get(self.ENDPOINTS["current_weather"], timeout=15)
resp.raise_for_status()
data = resp.json()
result = {
"update_time": data.get("updateTime", ""),
"temperature": [],
"humidity": [],
"rainfall": [],
"warning_message": data.get("warningMessage", ""),
"rainstorm_reminder": data.get("rainstormReminder", ""),
"mintemp_from00to09": data.get("mintempFrom00To09", ""),
}
# Parse temperature data
for record in data.get("temperature", {}).get("data", []):
result["temperature"].append({
"place": record.get("place"),
"value": record.get("value"),
"unit": record.get("unit", "C"),
})
for record in data.get("humidity", {}).get("data", []):
result["humidity"].append({
"place": record.get("place"),
"value": record.get("value"),
"unit": record.get("unit", "%"),
})
# Parse rainfall
for record in data.get("rainfall", {}).get("data", []):
result["rainfall"].append({
"place": record.get("place"),
"max": record.get("max"),
"min": record.get("min"),
"unit": record.get("unit", "mm"),
})
return result
except Exception as e:
print(f"HKO current weather error: {e}")
return None
def get_forecast(self) -> Optional[List[dict]]:
"""Get 9-day weather forecast from HKO."""
try:
resp = self.session.get(self.ENDPOINTS["9day_forecast"], timeout=15)
resp.raise_for_status()
data = resp.json()
forecasts = []
for day in data.get("weatherForecast", []):
forecasts.append({
"date": day.get("forecastDate"),
"week": day.get("week"),
"forecast_wind": day.get("forecastWind"),
"forecast_weather": day.get("forecastWeather"),
"forecast_temp_max": day.get("forecastMaxtemp", {}).get("value"),
"forecast_temp_min": day.get("forecastMintemp", {}).get("value"),
"forecast_humidity_max": day.get("forecastMaxrh", {}).get("value"),
"forecast_humidity_min": day.get("forecastMinrh", {}).get("value"),
"forecast_rain_probability": self._parse_probability(
day.get("forecastRainProbability", {})
),
"psr": day.get("PSR"),
})
return forecasts
except Exception as e:
print(f"HKO forecast error: {e}")
return None
def get_warnings(self) -> Optional[dict]:
"""Get current weather warnings and signals."""
try:
resp = self.session.get(self.ENDPOINTS["warnings"], timeout=15)
resp.raise_for_status()
return resp.json()
except Exception as e:
print(f"HKO warnings error: {e}")
return None
def get_typhoon_info(self) -> Optional[dict]:
"""Get tropical cyclone warnings and tracks from HKO."""
try:
resp = self.session.get(self.ENDPOINTS["swt"], timeout=15)
resp.raise_for_status()
return resp.json()
except Exception as e:
print(f"HKO typhoon info error: {e}")
return None
def get_rainfall_map(self) -> Optional[dict]:
"""Get recent rainfall distribution."""
try:
resp = self.session.get(self.ENDPOINTS["rainfall"], timeout=15)
resp.raise_for_status()
return resp.json()
except Exception as e:
print(f"HKO rainfall map error: {e}")
return None
def is_t8_active(self) -> bool:
"""Check if T8 or higher typhoon signal is active."""
warnings = self.get_warnings()
if not warnings:
return False
for detail in warnings.get("details", []):
name = detail.get("name", "")
if any(signal in name for signal in ["8", "9", "10"]):
if "tropical cyclone" in name.lower() or "typhoon" in name.lower():
return True
return False
def get_current_signal_level(self) -> int:
"""Get current typhoon signal level (0, 1, 3, 8, 9, 10)."""
typhoon = self.get_typhoon_info()
if not typhoon:
return 0
for signal_text in ["T1", "T3", "T8", "T9", "T10"]:
if signal_text in str(typhoon):
return int(signal_text[1:])
return 0
@staticmethod
def _parse_probability(prob_data: dict) -> Optional[str]:
"""Parse HKO rainfall probability data."""
values = []
for period in prob_data.get("probability", []):
perc = period.get("value")
if perc:
values.append(perc)
return " / ".join(values) if values else None