feat: Add a multi-source weather data collection module with integrations for OpenWeatherMap, Visual Crossing, and Open-Meteo.
This commit is contained in:
@@ -240,7 +240,7 @@ def main():
|
|||||||
)
|
)
|
||||||
temp_symbol = "°F" if temp_unit == "fahrenheit" else "°C"
|
temp_symbol = "°F" if temp_unit == "fahrenheit" else "°C"
|
||||||
logger.info(
|
logger.info(
|
||||||
f"☁️ {city} 当前气温: {consensus['average_temp']}{temp_symbol} | 监控合约: {len(city_markets)}"
|
f"☁️ {city} 当前气温: {consensus['average_temp']}{temp_symbol} (unit={temp_unit}) | 监控合约: {len(city_markets)}"
|
||||||
)
|
)
|
||||||
|
|
||||||
# --- 本城市汇总预警缓存 ---
|
# --- 本城市汇总预警缓存 ---
|
||||||
@@ -364,6 +364,10 @@ def main():
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# 获取温度符号(在此处定义以便后续使用)
|
||||||
|
temp_unit = weather_data.get("open-meteo", {}).get("unit", "celsius")
|
||||||
|
temp_symbol = "°F" if temp_unit == "fahrenheit" else "°C"
|
||||||
|
|
||||||
# 预测偏差分析
|
# 预测偏差分析
|
||||||
if ref_temp:
|
if ref_temp:
|
||||||
city_pred_high = ref_temp # 记录到城市概览
|
city_pred_high = ref_temp # 记录到城市概览
|
||||||
@@ -378,7 +382,7 @@ def main():
|
|||||||
else low_b
|
else low_b
|
||||||
)
|
)
|
||||||
diff = ref_temp - ((low_b + high_b) / 2)
|
diff = ref_temp - ((low_b + high_b) / 2)
|
||||||
msg += f"\n📐 预测偏差: {diff:+.1f}{temp_symbol} (预测 {ref_temp}{temp_symbol})"
|
# 偏差信息将在后面构建 msg 时统一添加
|
||||||
|
|
||||||
# 生成策略建议:仅保留模型一致提示
|
# 生成策略建议:仅保留模型一致提示
|
||||||
if abs(diff) < 2 and current_prob > 0.7:
|
if abs(diff) < 2 and current_prob > 0.7:
|
||||||
@@ -386,6 +390,7 @@ def main():
|
|||||||
f"预测温度{ref_temp}{temp_symbol}落在{question}区间,市场与模型一致"
|
f"预测温度{ref_temp}{temp_symbol}落在{question}区间,市场与模型一致"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
# 模拟下单 - 使用 Ask 价格(实际可成交价格)
|
# 模拟下单 - 使用 Ask 价格(实际可成交价格)
|
||||||
if buy_yes_price and buy_yes_price > 0.5:
|
if buy_yes_price and buy_yes_price > 0.5:
|
||||||
trigger_side = "Buy Yes"
|
trigger_side = "Buy Yes"
|
||||||
@@ -398,6 +403,13 @@ def main():
|
|||||||
else int((1 - current_prob) * 100)
|
else int((1 - current_prob) * 100)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# 构建预测文本
|
||||||
|
forecast_text = f"{ref_temp}{temp_symbol}" if ref_temp else "N/A"
|
||||||
|
|
||||||
|
# 构建简约版消息
|
||||||
|
side_display = "Buy No" if trigger_side == "Buy No" else "Buy Yes"
|
||||||
|
msg = f"⚡ {question} ({target_date}): {side_display} {trigger_price}¢ | 预测:{forecast_text}"
|
||||||
|
|
||||||
success = paper_trader.open_position(
|
success = paper_trader.open_position(
|
||||||
market_id=market_id,
|
market_id=market_id,
|
||||||
city=city,
|
city=city,
|
||||||
@@ -408,6 +420,10 @@ def main():
|
|||||||
target_date=target_date,
|
target_date=target_date,
|
||||||
predicted_temp=ref_temp,
|
predicted_temp=ref_temp,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# 添加模拟交易标签
|
||||||
|
if success:
|
||||||
|
msg += " [🛒 $5.0 💡试探]"
|
||||||
|
|
||||||
city_alerts.append(
|
city_alerts.append(
|
||||||
{
|
{
|
||||||
@@ -415,7 +431,7 @@ def main():
|
|||||||
"msg": msg,
|
"msg": msg,
|
||||||
"bought": success,
|
"bought": success,
|
||||||
"amount": 5.0,
|
"amount": 5.0,
|
||||||
"confidence": "动态",
|
"confidence": "💡试探",
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
pushed_signals[alert_key] = time.time()
|
pushed_signals[alert_key] = time.time()
|
||||||
@@ -476,8 +492,8 @@ def main():
|
|||||||
all_markets_cache[market_id] = cache_entry
|
all_markets_cache[market_id] = cache_entry
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# 2. 过滤已过期日期 (对比当前日期: 2026-02-06)
|
# 2. 过滤已过期日期 (动态获取当前日期)
|
||||||
current_today = "2026-02-06"
|
current_today = datetime.now().strftime("%Y-%m-%d")
|
||||||
if target_date and target_date < current_today:
|
if target_date and target_date < current_today:
|
||||||
cache_entry["rationale"] = "EXPIRED"
|
cache_entry["rationale"] = "EXPIRED"
|
||||||
all_markets_cache[market_id] = cache_entry
|
all_markets_cache[market_id] = cache_entry
|
||||||
|
|||||||
@@ -209,7 +209,7 @@ class WeatherDataCollector:
|
|||||||
current = data.get("current_weather", {})
|
current = data.get("current_weather", {})
|
||||||
utc_offset = data.get("utc_offset_seconds", 0)
|
utc_offset = data.get("utc_offset_seconds", 0)
|
||||||
timezone_name = data.get("timezone", "UTC")
|
timezone_name = data.get("timezone", "UTC")
|
||||||
|
|
||||||
# 计算精确的当地时间而不是气象站 bucket 时间
|
# 计算精确的当地时间而不是气象站 bucket 时间
|
||||||
now_utc = datetime.utcnow()
|
now_utc = datetime.utcnow()
|
||||||
local_now = now_utc + timedelta(seconds=utc_offset)
|
local_now = now_utc + timedelta(seconds=utc_offset)
|
||||||
@@ -287,7 +287,7 @@ class WeatherDataCollector:
|
|||||||
normalized_city = city.lower().strip()
|
normalized_city = city.lower().strip()
|
||||||
if normalized_city in static_coords:
|
if normalized_city in static_coords:
|
||||||
return static_coords[normalized_city]
|
return static_coords[normalized_city]
|
||||||
|
|
||||||
# 模糊匹配映射 (针对包含城市名的情况)
|
# 模糊匹配映射 (针对包含城市名的情况)
|
||||||
for key in static_coords:
|
for key in static_coords:
|
||||||
if key in normalized_city:
|
if key in normalized_city:
|
||||||
@@ -368,7 +368,7 @@ class WeatherDataCollector:
|
|||||||
results = {}
|
results = {}
|
||||||
|
|
||||||
# 判断是否为美国市场(使用华氏度)
|
# 判断是否为美国市场(使用华氏度)
|
||||||
us_cities = [
|
us_cities = {
|
||||||
"dallas",
|
"dallas",
|
||||||
"nyc",
|
"nyc",
|
||||||
"new york",
|
"new york",
|
||||||
@@ -383,9 +383,26 @@ class WeatherDataCollector:
|
|||||||
"houston",
|
"houston",
|
||||||
"phoenix",
|
"phoenix",
|
||||||
"philadelphia",
|
"philadelphia",
|
||||||
]
|
"new york's central park",
|
||||||
city_lower = city.lower()
|
"portland",
|
||||||
use_fahrenheit = any(uc in city_lower for uc in us_cities)
|
"denver",
|
||||||
|
"austin",
|
||||||
|
"san diego",
|
||||||
|
"detroit",
|
||||||
|
"cleveland",
|
||||||
|
"minneapolis",
|
||||||
|
"st. louis",
|
||||||
|
}
|
||||||
|
city_lower = city.lower().strip()
|
||||||
|
# 检查城市名是否在美国城市列表中(支持完全匹配或包含关系)
|
||||||
|
use_fahrenheit = city_lower in us_cities or any(
|
||||||
|
us_city in city_lower for us_city in us_cities
|
||||||
|
)
|
||||||
|
|
||||||
|
if use_fahrenheit:
|
||||||
|
logger.info(f"🌡️ {city} 使用华氏度 (°F)")
|
||||||
|
else:
|
||||||
|
logger.info(f"🌡️ {city} 使用摄氏度 (°C)")
|
||||||
|
|
||||||
# Open-Meteo (Primary Free Source - No Key)
|
# Open-Meteo (Primary Free Source - No Key)
|
||||||
if lat and lon:
|
if lat and lon:
|
||||||
|
|||||||
Reference in New Issue
Block a user