feat: Add city risk profile data collection.

This commit is contained in:
2569718930@qq.com
2026-02-08 03:11:22 +08:00
parent dda62e2c90
commit 97589531e8
2 changed files with 250 additions and 5 deletions
+25 -5
View File
@@ -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"🚨 <b>预报已被击穿</b>:实测最高 {max_so_far}{temp_symbol} 已超预报 {forecast_high}{temp_symbol}{exceed_by:.1f}°!")
insights.append(f"💡 <b>博弈建议</b>:市场需重新评估,关注更高温度区间。")
# 直接返回,不再显示过时的建议
if wind_speed >= 10:
insights.append(f"🍃 <b>清劲风</b>:空气流动快,可能伴随阵风引起微小波动。")
return "\n💡 <b>态势分析</b>\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"⏱️ <b>预计峰值时刻</b>:今天 <b>{window}</b> 之间。")
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"🎯 <b>博弈建议</b>:关注该时段实测能否站稳 {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", [])
+225
View File
@@ -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"⚠️ <b>数据偏差风险</b>: {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)