From 97589531e8039e5ce2a9f62a8d5531714c464f90 Mon Sep 17 00:00:00 2001 From: "2569718930@qq.com" <2569718930@qq.com> Date: Sun, 8 Feb 2026 03:11:22 +0800 Subject: [PATCH] feat: Add city risk profile data collection. --- bot_listener.py | 30 ++- src/data_collection/city_risk_profiles.py | 225 ++++++++++++++++++++++ 2 files changed, 250 insertions(+), 5 deletions(-) create mode 100644 src/data_collection/city_risk_profiles.py diff --git a/bot_listener.py b/bot_listener.py index b72f1766..dd5f23dd 100644 --- a/bot_listener.py +++ b/bot_listener.py @@ -11,6 +11,7 @@ if project_root not in sys.path: from src.utils.config_loader import load_config from src.data_collection.weather_sources import WeatherDataCollector +from src.data_collection.city_risk_profiles import get_city_risk_profile, format_risk_warning def analyze_weather_trend(weather_data, temp_symbol): """根据实测与预测分析气温态势,增加峰值时刻预测""" @@ -23,6 +24,7 @@ def analyze_weather_trend(weather_data, temp_symbol): return "" curr_temp = metar.get("current", {}).get("temp") + max_so_far = metar.get("current", {}).get("max_temp_so_far") # 今日实测最高 daily = open_meteo.get("daily", {}) forecast_high = daily.get("temperature_2m_max", [None])[0] wind_speed = metar.get("current", {}).get("wind_speed_kt", 0) @@ -36,17 +38,27 @@ def analyze_weather_trend(weather_data, temp_symbol): local_date_str = datetime.now().strftime("%Y-%m-%d") local_hour = datetime.now().hour - # --- 增加:峰值时刻预测逻辑 --- + # === 核心判断:实测是否已超预报 === + if max_so_far is not None and forecast_high is not None: + if max_so_far > forecast_high + 0.5: + # 实测已超预报! + exceed_by = max_so_far - forecast_high + insights.append(f"🚨 预报已被击穿:实测最高 {max_so_far}{temp_symbol} 已超预报 {forecast_high}{temp_symbol} 约 {exceed_by:.1f}°!") + insights.append(f"💡 博弈建议:市场需重新评估,关注更高温度区间。") + # 直接返回,不再显示过时的建议 + if wind_speed >= 10: + insights.append(f"🍃 清劲风:空气流动快,可能伴随阵风引起微小波动。") + return "\n💡 态势分析\n" + "\n".join(insights) + + # --- 峰值时刻预测逻辑 --- hourly = open_meteo.get("hourly", {}) times = hourly.get("time", []) - # 优先寻找高精模型的逐小时数据 - temps = hourly.get("temperature_2m_hrrr_conus") or hourly.get("temperature_2m_ecmwf_ifs") or hourly.get("temperature_2m", []) + temps = hourly.get("temperature_2m", []) peak_hours = [] if times and temps and forecast_high is not None: for t_str, temp in zip(times, temps): if t_str.startswith(local_date_str): - # 记录所有接近最高温的小时 (容差 0.2) if abs(temp - forecast_high) <= 0.2: hour = t_str.split("T")[1][:5] peak_hours.append(hour) @@ -54,7 +66,8 @@ def analyze_weather_trend(weather_data, temp_symbol): if peak_hours: window = f"{peak_hours[0]} - {peak_hours[-1]}" if len(peak_hours) > 1 else peak_hours[0] insights.append(f"⏱️ 预计峰值时刻:今天 {window} 之间。") - if local_hour < int(peak_hours[0].split(":")[0]): + # 只有在实测还没超预报时才给这个建议 + if local_hour < int(peak_hours[0].split(":")[0]) and (max_so_far is None or max_so_far < forecast_high): insights.append(f"🎯 博弈建议:关注该时段实测能否站稳 {forecast_high}{temp_symbol}。") if curr_temp is not None and forecast_high is not None: @@ -177,6 +190,13 @@ def start_bot(): time_only = local_time.split(" ")[1] if " " in local_time else local_time msg_lines.append(f"🕐 当地时间: {time_only}") + # 显示城市风险档案 + risk_profile = get_city_risk_profile(city_name) + if risk_profile: + risk_warning = format_risk_warning(risk_profile, temp_symbol) + if risk_warning: + msg_lines.append(f"\n{risk_warning}") + daily = open_meteo.get("daily", {}) dates = daily.get("time", []) max_temps = daily.get("temperature_2m_max", []) diff --git a/src/data_collection/city_risk_profiles.py b/src/data_collection/city_risk_profiles.py new file mode 100644 index 00000000..d3ab4846 --- /dev/null +++ b/src/data_collection/city_risk_profiles.py @@ -0,0 +1,225 @@ +# Polymarket 城市温度市场 - 数据偏差风险档案 +# 基于 METAR 机场站与市区实际温度的系统性差异 + +CITY_RISK_PROFILES = { + # 🔴 高危城市 - 数据偏差大,容易误判 + "seoul": { + "risk_level": "high", + "risk_emoji": "🔴", + "icao": "RKSI", + "airport_name": "仁川国际机场", + "distance_km": 48.8, + "elevation_diff_m": 0, + "typical_bias_f": 5.8, + "bias_direction": "机场靠海偏暖,市区内陆更冷", + "warning": "距离太远,根本不是同一个天气区", + "season_notes": None, + }, + "chicago": { + "risk_level": "high", + "risk_emoji": "🔴", + "icao": "KORD", + "airport_name": "O'Hare 国际机场", + "distance_km": 25.3, + "elevation_diff_m": 42, + "typical_bias_f": 4.0, + "bias_direction": "密歇根湖效应:风向变化时湖边vs内陆可差10°F+", + "warning": "冬天温差最不稳定", + "season_notes": "冬季", + }, + + # 🟡 中危城市 - 存在系统偏差,需注意 + "ankara": { + "risk_level": "medium", + "risk_emoji": "🟡", + "icao": "LTAC", + "airport_name": "Esenboğa 机场", + "distance_km": 24.5, + "elevation_diff_m": 65, + "typical_bias_f": 2.0, + "bias_direction": "机场海拔更高", + "warning": "内陆高原城市,昼夜温差大(可达15°C+)", + "season_notes": "下午最高温时偏差会放大", + }, + "london": { + "risk_level": "low", + "risk_emoji": "🟢", + "icao": "EGLC", + "airport_name": "London City 机场", + "distance_km": 12.7, + "elevation_diff_m": 4, + "typical_bias_f": 0.5, + "bias_direction": "河水调节效应:泰晤士河 Royal Docks 使得夏天偏凉,冬天偏暖", + "warning": "极端天气日(热浪/寒潮)偏差会显著放大", + "season_notes": None, + }, + "dallas": { + "risk_level": "medium", + "risk_emoji": "🟡", + "icao": "KDAL", + "airport_name": "Dallas Love Field 机场", + "distance_km": 11.2, + "elevation_diff_m": 0, + "typical_bias_f": 1.1, + "bias_direction": "比 DFW 更接近市中心,数据更准", + "warning": "城市热岛效应在夏季午后会使温度略高于郊区", + "season_notes": None, + }, + "buenos aires": { + "risk_level": "medium", + "risk_emoji": "🟡", + "icao": "SAEZ", + "airport_name": "Ezeiza 国际机场", + "distance_km": 28.1, + "elevation_diff_m": 0, + "typical_bias_f": 1.2, + "bias_direction": "夏天城区可比郊区高2-3°C", + "warning": "距离远但地形平坦,偏差稳定可预测", + "season_notes": "夏季", + }, + + # 🟢 低危城市 - 数据相对靠谱 + "toronto": { + "risk_level": "low", + "risk_emoji": "🟢", + "icao": "CYYZ", + "airport_name": "Pearson 国际机场", + "distance_km": 19.6, + "elevation_diff_m": 0, + "typical_bias_f": 0.3, + "bias_direction": None, + "warning": "冬季湖效应偶尔炸裂", + "season_notes": "冬季", + }, + "new york": { + "risk_level": "low", + "risk_emoji": "🟢", + "icao": "KLGA", + "airport_name": "LaGuardia 机场", + "distance_km": 14.5, + "elevation_diff_m": 0, + "typical_bias_f": 0.7, + "bias_direction": "相比 JFK 更靠近曼哈顿", + "warning": "东河水汽可能在春季产生微小的降温效果", + "season_notes": None, + }, + "seattle": { + "risk_level": "low", + "risk_emoji": "🟢", + "icao": "KSEA", + "airport_name": "Sea-Tac 国际机场", + "distance_km": 17.4, + "elevation_diff_m": 0, + "typical_bias_f": 0.6, + "bias_direction": "微气候差异存在但较小", + "warning": None, + "season_notes": None, + }, + "atlanta": { + "risk_level": "low", + "risk_emoji": "🟢", + "icao": "KATL", + "airport_name": "Hartsfield-Jackson 机场", + "distance_km": 12.6, + "elevation_diff_m": 0, + "typical_bias_f": 0.5, + "bias_direction": None, + "warning": None, + "season_notes": None, + }, + "miami": { + "risk_level": "low", + "risk_emoji": "🟢", + "icao": "KMIA", + "airport_name": "Miami 国际机场", + "distance_km": 10.3, + "elevation_diff_m": 0, + "typical_bias_f": 0.3, + "bias_direction": None, + "warning": None, + "season_notes": None, + }, + "wellington": { + "risk_level": "low", + "risk_emoji": "🟢", + "icao": "NZWN", + "airport_name": "Wellington 机场", + "distance_km": 5.1, + "elevation_diff_m": 0, + "typical_bias_f": 0.2, + "bias_direction": None, + "warning": "12城最近,数据最靠谱", + "season_notes": None, + }, +} + + +def get_city_risk_profile(city_name: str) -> dict: + """获取城市的风险档案""" + city_lower = city_name.lower().strip() + + # 别名映射 + aliases = { + "nyc": "new york", + "ny": "new york", + "chi": "chicago", + "atl": "atlanta", + "sea": "seattle", + "dal": "dallas", + "mia": "miami", + "tor": "toronto", + "ank": "ankara", + "sel": "seoul", + "wel": "wellington", + "ba": "buenos aires", + "首尔": "seoul", + "芝加哥": "chicago", + "纽约": "new york", + "伦敦": "london", + "达拉斯": "dallas", + "迈阿密": "miami", + "亚特兰大": "atlanta", + "西雅图": "seattle", + "多伦多": "toronto", + "惠灵顿": "wellington", + "安卡拉": "ankara", + "布宜诺斯艾利斯": "buenos aires", + } + + city_key = aliases.get(city_lower, city_lower) + return CITY_RISK_PROFILES.get(city_key) + + +def format_risk_warning(profile: dict, temp_symbol: str) -> str: + """格式化风险警告信息""" + if not profile: + return "" + + lines = [] + + # 风险等级标题 + risk_labels = { + "high": "高危", + "medium": "中危", + "low": "低危" + } + risk_label = risk_labels.get(profile["risk_level"], "未知") + lines.append(f"⚠️ 数据偏差风险: {profile['risk_emoji']} {risk_label}") + + # 机场信息 + lines.append(f" 📍 机场: {profile['airport_name']} ({profile['icao']})") + lines.append(f" 📏 距市区: {profile['distance_km']}km") + + # 典型偏差 + if profile["typical_bias_f"] >= 1.0: + lines.append(f" 📊 典型偏差: ±{profile['typical_bias_f']}{temp_symbol}") + + # 偏差方向说明 + if profile["bias_direction"]: + lines.append(f" 💡 {profile['bias_direction']}") + + # 特别警告 + if profile["warning"]: + lines.append(f" 🚨 {profile['warning']}") + + return "\n".join(lines)