Add market creation engine + ML dashboard panel
Market creator (ml/create_markets.py): - Generates Polymarket-compliant BinaryOption question specs - Temperature threshold markets (30°C, 33°C, 35°C) per day - Rain probability and heavy rain (10mm/25mm) markets per day - Typhoon T8 weekly + monthly markets - Wind gust threshold markets (>50 km/h at Chek Lap Kok) - Calendar spread markets (rainy days per week) - Resolution criteria tied to verifiable HKO public API sources - Submission-ready JSON output with dispute windows + metadata - Human-readable summary with probability bars + edge estimates Web dashboard: - /api/ml endpoint: per-model raw/calibrated probabilities - ML Predictions card: color-coded, sorted by confidence - Typhoon Probabilities card: T1/T3/T8 now + 72h/120h - Auto-refreshes independently (avoids blocking chart refresh) Usage: python ml/create_markets.py # View proposals python ml/create_markets.py --submit # JSON for Polymarket
This commit is contained in:
@@ -0,0 +1,436 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
Market Creation Engine for HK Weather Prediction Markets.
|
||||||
|
|
||||||
|
Generates Polymarket-compliant market proposals with:
|
||||||
|
- Clear binary questions (verifiable via HKO public API)
|
||||||
|
- Resolution criteria with specific data sources
|
||||||
|
- Appropriate end dates + resolution delays
|
||||||
|
- Category tags and metadata
|
||||||
|
|
||||||
|
Output:
|
||||||
|
- JSON market specs (for Polymarket curation submission)
|
||||||
|
- Human-readable summary (for review before submission)
|
||||||
|
|
||||||
|
Polymarket curation contact: partnerships@polymarket.com
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
python ml/create_markets.py # Generate proposals
|
||||||
|
python ml/create_markets.py --submit # Output submission-ready JSON
|
||||||
|
python ml/create_markets.py --next 7 # Proposals for next 7 days
|
||||||
|
"""
|
||||||
|
|
||||||
|
import sys
|
||||||
|
import json
|
||||||
|
import argparse
|
||||||
|
from pathlib import Path
|
||||||
|
from datetime import datetime, timedelta
|
||||||
|
from typing import Dict, List, Optional
|
||||||
|
|
||||||
|
import numpy as np
|
||||||
|
|
||||||
|
sys.path.insert(0, str(Path(__file__).parent.parent))
|
||||||
|
|
||||||
|
from ml.predictor import MLPredictor
|
||||||
|
from ml.model import TARGET_DEFINITIONS
|
||||||
|
from weather.openmeteo_client import OpenMeteoClient
|
||||||
|
from weather.hko_client import HKOClient
|
||||||
|
|
||||||
|
|
||||||
|
# Resolution sources — must be publicly verifiable
|
||||||
|
RESOLUTION_SOURCES = {
|
||||||
|
"hko_temperature": {
|
||||||
|
"name": "Hong Kong Observatory Daily Temperature",
|
||||||
|
"url": "https://data.weather.gov.hk/weatherAPI/opendata/weather.php?dataType=rhrread&lang=en",
|
||||||
|
"description": "Maximum temperature recorded at HKO Headquarters (King's Park) on the target date, as published on the HKO Open Data API. Value in degrees Celsius.",
|
||||||
|
"verification_field": "temperature.data[place='Hong Kong Observatory'].value",
|
||||||
|
"verification_window": "00:00-23:59 HKT on target date",
|
||||||
|
"dispute_window_hours": 24,
|
||||||
|
},
|
||||||
|
"hko_rainfall": {
|
||||||
|
"name": "Hong Kong Observatory Daily Rainfall",
|
||||||
|
"url": "https://data.weather.gov.hk/weatherAPI/opendata/weather.php?dataType=rhrread&lang=en",
|
||||||
|
"description": "Total rainfall (mm) recorded at HKO Headquarters on the target date. If any rainfall > 0mm is recorded, the market resolves YES.",
|
||||||
|
"verification_field": "rainfall.data.max",
|
||||||
|
"verification_window": "00:00-23:59 HKT on target date",
|
||||||
|
"dispute_window_hours": 24,
|
||||||
|
},
|
||||||
|
"hko_signal": {
|
||||||
|
"name": "Hong Kong Observatory Tropical Cyclone Warning Signals",
|
||||||
|
"url": "https://www.hko.gov.hk/en/wxinfo/currwx/tc_gis.htm",
|
||||||
|
"description": "Official HKO tropical cyclone warning signal level. If signal T8 or above is hoisted at any time during the window, resolves YES.",
|
||||||
|
"verification_field": "signal_level >= 8",
|
||||||
|
"verification_window": "target_date 00:00 to end_date 23:59 HKT",
|
||||||
|
"dispute_window_hours": 24,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
MARKET_CATEGORIES = {
|
||||||
|
"temperature": {"category": "Weather", "tags": ["weather", "temperature", "hong-kong"]},
|
||||||
|
"rainfall": {"category": "Weather", "tags": ["weather", "rain", "hong-kong"]},
|
||||||
|
"typhoon": {"category": "Weather", "tags": ["weather", "typhoon", "hong-kong"]},
|
||||||
|
"wind": {"category": "Weather", "tags": ["weather", "wind", "hong-kong"]},
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class MarketCreator:
|
||||||
|
"""Generate Polymarket-ready market proposals from ML forecasts."""
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.predictor = MLPredictor()
|
||||||
|
self.openmeteo = OpenMeteoClient()
|
||||||
|
self.hko = HKOClient()
|
||||||
|
|
||||||
|
def generate_proposals(self, days_ahead: int = 7) -> List[Dict]:
|
||||||
|
"""Generate market proposals for the next N days."""
|
||||||
|
self.predictor.fetch_and_predict()
|
||||||
|
forecasts = self.predictor._last_predictions or {}
|
||||||
|
fc = self.openmeteo.get_forecast(lead_days=days_ahead)
|
||||||
|
|
||||||
|
proposals = []
|
||||||
|
|
||||||
|
# Temperature markets
|
||||||
|
proposals.extend(self._temperature_markets(fc, forecasts, days_ahead))
|
||||||
|
|
||||||
|
# Rain markets
|
||||||
|
proposals.extend(self._rain_markets(fc, forecasts, days_ahead))
|
||||||
|
|
||||||
|
# Typhoon markets
|
||||||
|
proposals.extend(self._typhoon_markets(forecasts))
|
||||||
|
|
||||||
|
# Wind markets
|
||||||
|
proposals.extend(self._wind_markets(fc, forecasts, days_ahead))
|
||||||
|
|
||||||
|
# Calendar spread / multi-day markets
|
||||||
|
proposals.extend(self._calendar_spread_markets(fc, forecasts))
|
||||||
|
|
||||||
|
return proposals
|
||||||
|
|
||||||
|
def _temperature_markets(self, fc, forecasts, days):
|
||||||
|
"""Generate temperature threshold markets."""
|
||||||
|
proposals = []
|
||||||
|
|
||||||
|
for day_offset in range(1, min(days + 1, len(fc) if fc is not None else 1)):
|
||||||
|
if fc is None or day_offset >= len(fc):
|
||||||
|
continue
|
||||||
|
row = fc.iloc[day_offset]
|
||||||
|
target_date = row.name.strftime("%Y-%m-%d") if hasattr(row.name, 'strftime') else str(row.name)
|
||||||
|
tmax = float(row.get("temperature_2m_max", 0))
|
||||||
|
|
||||||
|
display_date = (datetime.strptime(target_date, "%Y-%m-%d") if "-" in target_date else datetime.now()).strftime("%b %d")
|
||||||
|
|
||||||
|
for threshold in [30, 33, 35]:
|
||||||
|
target = f"temp_gt_{threshold}c_24h"
|
||||||
|
model_prob = forecasts.get(f"{target}_{target_date}", forecasts.get(target, 50.0))
|
||||||
|
|
||||||
|
# Only propose if model has a meaningful signal
|
||||||
|
if 5 < model_prob < 95:
|
||||||
|
proposals.append({
|
||||||
|
"question": f"Will the maximum temperature in Hong Kong exceed {threshold}°C on {display_date}?",
|
||||||
|
"outcomes": ["Yes", "No"],
|
||||||
|
"description": (
|
||||||
|
f"Market resolves YES if the maximum temperature recorded at the "
|
||||||
|
f"Hong Kong Observatory (King's Park) exceeds {threshold}°C on "
|
||||||
|
f"{datetime.strptime(target_date, '%Y-%m-%d').strftime('%B %d, %Y')}.\n\n"
|
||||||
|
f"Resolution source: HKO Open Data API (temperature.data)."
|
||||||
|
),
|
||||||
|
"end_date": (datetime.strptime(target_date, "%Y-%m-%d") + timedelta(days=1)).strftime("%Y-%m-%dT12:00:00"),
|
||||||
|
"resolution_source": "hko_temperature",
|
||||||
|
"category": "temperature",
|
||||||
|
"model_probability": round(model_prob, 1),
|
||||||
|
"target_date": target_date,
|
||||||
|
"threshold": threshold,
|
||||||
|
"forecast_value": round(tmax, 1),
|
||||||
|
"tag": f"Hong Kong temperature {threshold}C {display_date}",
|
||||||
|
})
|
||||||
|
|
||||||
|
return proposals
|
||||||
|
|
||||||
|
def _rain_markets(self, fc, forecasts, days):
|
||||||
|
"""Generate rain probability/amount markets."""
|
||||||
|
proposals = []
|
||||||
|
|
||||||
|
for day_offset in range(1, min(days + 1, len(fc) if fc is not None else 1)):
|
||||||
|
if fc is None or day_offset >= len(fc):
|
||||||
|
continue
|
||||||
|
row = fc.iloc[day_offset]
|
||||||
|
target_date = row.name.strftime("%Y-%m-%d") if hasattr(row.name, 'strftime') else str(row.name)
|
||||||
|
precip = float(row.get("precipitation_sum", 0))
|
||||||
|
prob = float(row.get("precipitation_probability_max", 0))
|
||||||
|
display_date = (datetime.strptime(target_date, "%Y-%m-%d") if "-" in target_date else datetime.now()).strftime("%b %d")
|
||||||
|
|
||||||
|
# Rain yes/no
|
||||||
|
model_p = forecasts.get("rain_gt_0mm_24h", 50.0)
|
||||||
|
if 5 < model_p < 95:
|
||||||
|
proposals.append({
|
||||||
|
"question": f"Will measurable rain (≥0.5mm) fall in Hong Kong on {display_date}?",
|
||||||
|
"outcomes": ["Yes", "No"],
|
||||||
|
"description": (
|
||||||
|
f"Market resolves YES if the HKO records ≥0.5mm of rainfall "
|
||||||
|
f"at the Observatory station on {datetime.strptime(target_date, '%Y-%m-%d').strftime('%B %d, %Y')}.\n\n"
|
||||||
|
f"Forecast: {prob:.0f}% probability, {precip:.1f}mm expected.\n\n"
|
||||||
|
f"Resolution: HKO Open Data API (rainfall.data)."
|
||||||
|
),
|
||||||
|
"end_date": (datetime.strptime(target_date, "%Y-%m-%d") + timedelta(days=1)).strftime("%Y-%m-%dT12:00:00"),
|
||||||
|
"resolution_source": "hko_rainfall",
|
||||||
|
"category": "rainfall",
|
||||||
|
"model_probability": round(model_p, 1),
|
||||||
|
"target_date": target_date,
|
||||||
|
"forecast_value": round(precip, 1),
|
||||||
|
"tag": f"Hong Kong rain {display_date}",
|
||||||
|
})
|
||||||
|
|
||||||
|
# Heavy rain threshold
|
||||||
|
for mm in [10, 25]:
|
||||||
|
target = f"rain_gt_{mm}mm_24h"
|
||||||
|
model_p = forecasts.get(target, 50.0)
|
||||||
|
if 1 < model_p < 50:
|
||||||
|
proposals.append({
|
||||||
|
"question": f"Will Hong Kong receive more than {mm}mm of rain on {display_date}?",
|
||||||
|
"outcomes": ["Yes", "No"],
|
||||||
|
"description": (
|
||||||
|
f"Market resolves YES if total rainfall at HKO Observatory exceeds {mm}mm "
|
||||||
|
f"on {datetime.strptime(target_date, '%Y-%m-%d').strftime('%B %d, %Y')}.\n\n"
|
||||||
|
f"Forecast: {precip:.1f}mm expected.\n\n"
|
||||||
|
f"Resolution: HKO Open Data API (rainfall.data)."
|
||||||
|
),
|
||||||
|
"end_date": (datetime.strptime(target_date, "%Y-%m-%d") + timedelta(days=1)).strftime("%Y-%m-%dT12:00:00"),
|
||||||
|
"resolution_source": "hko_rainfall",
|
||||||
|
"category": "rainfall",
|
||||||
|
"model_probability": round(model_p, 1),
|
||||||
|
"target_date": target_date,
|
||||||
|
"threshold": mm,
|
||||||
|
"forecast_value": round(precip, 1),
|
||||||
|
"tag": f"Hong Kong rain {mm}mm {display_date}",
|
||||||
|
})
|
||||||
|
|
||||||
|
return proposals
|
||||||
|
|
||||||
|
def _typhoon_markets(self, forecasts):
|
||||||
|
"""Generate typhoon signal markets."""
|
||||||
|
proposals = []
|
||||||
|
typhoon = self.predictor._last_typhoon or {}
|
||||||
|
today = datetime.now()
|
||||||
|
|
||||||
|
# T8 in 7 days (weekly market)
|
||||||
|
t8_7d = typhoon.get("typhoon_T8_120h", 0.5)
|
||||||
|
end_date = (today + timedelta(days=7)).strftime("%Y-%m-%dT12:00:00")
|
||||||
|
display_end = (today + timedelta(days=7)).strftime("%b %d")
|
||||||
|
|
||||||
|
if t8_7d > 0.5: # Only propose when there's any signal
|
||||||
|
proposals.append({
|
||||||
|
"question": f"Will the T8 typhoon signal be hoisted in Hong Kong by {display_end}?",
|
||||||
|
"outcomes": ["Yes", "No"],
|
||||||
|
"description": (
|
||||||
|
f"Market resolves YES if the Hong Kong Observatory hoists the "
|
||||||
|
f"Tropical Cyclone Warning Signal No. 8 (or higher) at any time "
|
||||||
|
f"between now and {display_end} 23:59 HKT.\n\n"
|
||||||
|
f"Climatological probability for this period: {t8_7d:.1f}%.\n\n"
|
||||||
|
f"Resolution: HKO Tropical Cyclone Warning System."
|
||||||
|
),
|
||||||
|
"end_date": end_date,
|
||||||
|
"resolution_source": "hko_signal",
|
||||||
|
"category": "typhoon",
|
||||||
|
"model_probability": round(t8_7d, 1),
|
||||||
|
"target_date": display_end,
|
||||||
|
"tag": "Hong Kong typhoon T8 weekly",
|
||||||
|
})
|
||||||
|
|
||||||
|
# Monthly: any T8 this month
|
||||||
|
month_end = today.replace(day=1) + timedelta(days=32)
|
||||||
|
month_end = month_end.replace(day=1)
|
||||||
|
remaining_days = (month_end - today).days
|
||||||
|
month_name = today.strftime("%B")
|
||||||
|
|
||||||
|
t8_month = typhoon.get("typhoon_T8_120h", 0.5)
|
||||||
|
if t8_month > 0.5:
|
||||||
|
proposals.append({
|
||||||
|
"question": f"Will a T8 or higher typhoon signal be hoisted in Hong Kong in {month_name} {today.year}?",
|
||||||
|
"outcomes": ["Yes", "No"],
|
||||||
|
"description": (
|
||||||
|
f"Market resolves YES if HKO hoists Tropical Cyclone Warning Signal "
|
||||||
|
f"No. 8 or higher at any time during {month_name} {today.year}.\n\n"
|
||||||
|
f"Historical frequency: ~{3/12*100:.0f}% of months have T8+ events.\n\n"
|
||||||
|
f"Resolution: HKO Tropical Cyclone Warning System."
|
||||||
|
),
|
||||||
|
"end_date": month_end.strftime("%Y-%m-%dT12:00:00"),
|
||||||
|
"resolution_source": "hko_signal",
|
||||||
|
"category": "typhoon",
|
||||||
|
"model_probability": round(t8_month, 1),
|
||||||
|
"target_date": month_end.strftime("%Y-%m-%d"),
|
||||||
|
"tag": f"Hong Kong typhoon T8 {month_name} {today.year}",
|
||||||
|
})
|
||||||
|
|
||||||
|
return proposals
|
||||||
|
|
||||||
|
def _wind_markets(self, fc, forecasts, days):
|
||||||
|
"""Generate wind speed threshold markets."""
|
||||||
|
proposals = []
|
||||||
|
|
||||||
|
for day_offset in range(1, min(days + 1, len(fc) if fc is not None else 1)):
|
||||||
|
if fc is None or day_offset >= len(fc):
|
||||||
|
continue
|
||||||
|
row = fc.iloc[day_offset]
|
||||||
|
target_date = row.name.strftime("%Y-%m-%d") if hasattr(row.name, 'strftime') else str(row.name)
|
||||||
|
wind = float(row.get("wind_speed_10m_max", 0))
|
||||||
|
gust = float(row.get("wind_gusts_10m_max", 0))
|
||||||
|
display_date = (datetime.strptime(target_date, "%Y-%m-%d") if "-" in target_date else datetime.now()).strftime("%b %d")
|
||||||
|
|
||||||
|
if gust > 35: # Only propose when windy
|
||||||
|
proposals.append({
|
||||||
|
"question": f"Will wind gusts exceed 50 km/h in Hong Kong on {display_date}?",
|
||||||
|
"outcomes": ["Yes", "No"],
|
||||||
|
"description": (
|
||||||
|
f"Market resolves YES if wind gusts ≥50 km/h are recorded at "
|
||||||
|
f"Chek Lap Kok (airport) on {datetime.strptime(target_date, '%Y-%m-%d').strftime('%B %d, %Y')}.\n\n"
|
||||||
|
f"Forecast wind: {wind:.0f} km/h, gusts: {gust:.0f} km/h.\n\n"
|
||||||
|
f"Resolution: HKO wind gust data from Chek Lap Kok station."
|
||||||
|
),
|
||||||
|
"end_date": (datetime.strptime(target_date, "%Y-%m-%d") + timedelta(days=1)).strftime("%Y-%m-%dT12:00:00"),
|
||||||
|
"resolution_source": "hko_temperature",
|
||||||
|
"category": "wind",
|
||||||
|
"model_probability": round(forecasts.get("wind_gt_30kmh_24h", 50), 1),
|
||||||
|
"target_date": target_date,
|
||||||
|
"threshold": 50,
|
||||||
|
"forecast_value": round(gust, 1),
|
||||||
|
"tag": f"Hong Kong wind gusts {display_date}",
|
||||||
|
})
|
||||||
|
|
||||||
|
return proposals
|
||||||
|
|
||||||
|
def _calendar_spread_markets(self, fc, forecasts):
|
||||||
|
"""Generate calendar spread / multi-day aggregate markets."""
|
||||||
|
proposals = []
|
||||||
|
if fc is None or len(fc) < 4:
|
||||||
|
return proposals
|
||||||
|
|
||||||
|
today = datetime.now()
|
||||||
|
next_7_days = [(today + timedelta(days=i)) for i in range(1, 8)]
|
||||||
|
days_with_rain = 0
|
||||||
|
|
||||||
|
for i in range(min(7, len(fc) - 1)):
|
||||||
|
row = fc.iloc[i + 1]
|
||||||
|
prob = float(row.get("precipitation_probability_max", 0))
|
||||||
|
if prob > 35:
|
||||||
|
days_with_rain += 1
|
||||||
|
|
||||||
|
if days_with_rain > 0:
|
||||||
|
proposals.append({
|
||||||
|
"question": f"Will it rain on at least 3 days in Hong Kong from {next_7_days[0].strftime('%b %d')} to {next_7_days[-1].strftime('%b %d')}?",
|
||||||
|
"outcomes": ["Yes", "No"],
|
||||||
|
"description": (
|
||||||
|
f"Counts the number of days with measurable rain (≥0.5mm) at HKO "
|
||||||
|
f"during the 7-day period. Resolves YES if ≥3 days have rain.\n\n"
|
||||||
|
f"Forecast suggests ~{days_with_rain} days with rain probability >35%."
|
||||||
|
),
|
||||||
|
"end_date": next_7_days[-1].strftime("%Y-%m-%dT12:00:00"),
|
||||||
|
"resolution_source": "hko_rainfall",
|
||||||
|
"category": "rainfall",
|
||||||
|
"model_probability": round(min(95, max(5, days_with_rain * 20)), 1),
|
||||||
|
"target_date": next_7_days[-1].strftime("%Y-%m-%d"),
|
||||||
|
"threshold": 3,
|
||||||
|
"tag": "Hong Kong rainy days weekly",
|
||||||
|
})
|
||||||
|
|
||||||
|
return proposals
|
||||||
|
|
||||||
|
def format_proposals(self, proposals: List[Dict]) -> str:
|
||||||
|
"""Format proposals as a human-readable summary."""
|
||||||
|
if not proposals:
|
||||||
|
return "No market proposals generated."
|
||||||
|
|
||||||
|
lines = [
|
||||||
|
f"=== Polymarket HK Weather Market Proposals ===",
|
||||||
|
f"Generated: {datetime.now():%Y-%m-%d %H:%M HKT}",
|
||||||
|
f"Total proposals: {len(proposals)}",
|
||||||
|
f"",
|
||||||
|
]
|
||||||
|
|
||||||
|
by_category = {}
|
||||||
|
for p in proposals:
|
||||||
|
cat = p.get("category", "other")
|
||||||
|
by_category.setdefault(cat, []).append(p)
|
||||||
|
|
||||||
|
for cat, ps in by_category.items():
|
||||||
|
lines.append(f"─── {cat.title()} ({len(ps)}) ───")
|
||||||
|
for p in ps:
|
||||||
|
model_p = p.get("model_probability", 50)
|
||||||
|
fv = p.get("forecast_value", "-")
|
||||||
|
threshold = p.get("threshold", "-")
|
||||||
|
prob_bar = "█" * int(model_p / 5) + "░" * (20 - int(model_p / 5))
|
||||||
|
lines.append(f" [{model_p:>5.1f}% {prob_bar}] {p['question']}")
|
||||||
|
if threshold != "-" and fv != "-":
|
||||||
|
lines.append(f" threshold={threshold}, forecast={fv}")
|
||||||
|
lines.append("")
|
||||||
|
|
||||||
|
lines.append(f"═══ Submission Instructions ═══")
|
||||||
|
lines.append(f"1. Review proposals above")
|
||||||
|
lines.append(f"2. Submit via: python ml/create_markets.py --submit > proposals.json")
|
||||||
|
lines.append(f"3. Contact: partnerships@polymarket.com with proposals.json")
|
||||||
|
lines.append(f"4. Reference resolution sources are all HKO public APIs")
|
||||||
|
lines.append(f"")
|
||||||
|
lines.append(f"Rough edge estimate (ML model vs naive 50% prior):")
|
||||||
|
for p in proposals[:5]:
|
||||||
|
model_p = p.get("model_probability", 50)
|
||||||
|
edge = abs(model_p - 50)
|
||||||
|
if edge > 15:
|
||||||
|
lines.append(f" {p['tag']:<45s} edge={edge:.1f}pp")
|
||||||
|
|
||||||
|
return "\n".join(lines)
|
||||||
|
|
||||||
|
def to_submission_json(self, proposals: List[Dict]) -> List[Dict]:
|
||||||
|
"""Convert proposals to Polymarket submission format."""
|
||||||
|
submission = []
|
||||||
|
for p in proposals:
|
||||||
|
source = RESOLUTION_SOURCES.get(
|
||||||
|
p.get("resolution_source", ""),
|
||||||
|
RESOLUTION_SOURCES["hko_temperature"],
|
||||||
|
)
|
||||||
|
cats = MARKET_CATEGORIES.get(p.get("category", ""), MARKET_CATEGORIES["temperature"])
|
||||||
|
|
||||||
|
submission.append({
|
||||||
|
"question": p["question"],
|
||||||
|
"outcomes": p["outcomes"],
|
||||||
|
"description": p["description"],
|
||||||
|
"endDateIso": p["end_date"],
|
||||||
|
"resolutionSource": {
|
||||||
|
"name": source["name"],
|
||||||
|
"url": source["url"],
|
||||||
|
"verificationDescription": source["description"],
|
||||||
|
"disputeWindowHours": source["dispute_window_hours"],
|
||||||
|
},
|
||||||
|
"category": cats["category"],
|
||||||
|
"tags": cats["tags"],
|
||||||
|
"metadata": {
|
||||||
|
"target_date": p.get("target_date", ""),
|
||||||
|
"threshold": p.get("threshold", ""),
|
||||||
|
"forecast_value": p.get("forecast_value", ""),
|
||||||
|
"model_probability": p.get("model_probability", ""),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
return submission
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
parser = argparse.ArgumentParser(description="Generate Polymarket weather market proposals")
|
||||||
|
parser.add_argument("--next", type=int, default=5, help="Days ahead for daily markets")
|
||||||
|
parser.add_argument("--submit", action="store_true", help="Output submission-ready JSON")
|
||||||
|
parser.add_argument("--min-edge", type=float, default=10, help="Minimum edge (pp) to include")
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
creator = MarketCreator()
|
||||||
|
proposals = creator.generate_proposals(days_ahead=args.next)
|
||||||
|
|
||||||
|
# Filter by edge
|
||||||
|
proposals = [p for p in proposals if abs(p.get("model_probability", 50) - 50) >= args.min_edge]
|
||||||
|
|
||||||
|
if args.submit:
|
||||||
|
submission = creator.to_submission_json(proposals)
|
||||||
|
print(json.dumps(submission, indent=2))
|
||||||
|
else:
|
||||||
|
print(creator.format_proposals(proposals))
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
Reference in New Issue
Block a user