feat: Add city risk profile data collection.
This commit is contained in:
+25
-5
@@ -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", [])
|
||||
|
||||
@@ -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)
|
||||
Reference in New Issue
Block a user