feat: Implement multi-source weather data collection from various APIs and integrate Polymarket API functionality.

This commit is contained in:
2569718930@qq.com
2026-02-06 22:15:42 +08:00
parent 1b4ffe2d10
commit bc4eb6445b
3 changed files with 59 additions and 50 deletions
+53 -32
View File
@@ -124,22 +124,30 @@ def main():
time.sleep(300) time.sleep(300)
continue continue
# 2. 批量同步盘口价格 (优化:一次请求获取所有市场的 Buy Yes/No) # 2. 批量同步盘口价格 (优化:为每个档位获取其对应的真实 Token 价格)
token_price_map = {} token_price_map = {}
price_requests = [] price_requests = []
for m in all_weather_markets: for m in all_weather_markets:
ts = m.get("tokens", []) ts = m.get("tokens", [])
if isinstance(ts, str): if isinstance(ts, str):
try: try: ts = json.loads(ts)
ts = json.loads(ts) except: ts = []
except:
ts = [] active_tid = m.get("active_token_id")
if ts and len(ts) >= 2:
# 如果是多选一市场(比如 Dallas 76-77°F
if len(ts) > 2 and active_tid:
# 获取该档位的买入价 (Ask)
price_requests.append({"token_id": active_tid, "side": "ask"})
# 获取该档位的买入“否”价所需的 Bid 价
price_requests.append({"token_id": active_tid, "side": "bid"})
# 如果是传统的 Yes/No 二选一市场
elif len(ts) == 2:
price_requests.append({"token_id": ts[0], "side": "ask"}) # Buy Yes price_requests.append({"token_id": ts[0], "side": "ask"}) # Buy Yes
price_requests.append({"token_id": ts[1], "side": "ask"}) # Buy No price_requests.append({"token_id": ts[1], "side": "ask"}) # Buy No
if price_requests: if price_requests:
logger.info(f"正在同步 {len(price_requests)} 个档位的盘口价格...") logger.info(f"正在同步 {len(price_requests)} 个档位的真实盘口价格...")
token_price_map = polymarket.get_multiple_prices(price_requests) token_price_map = polymarket.get_multiple_prices(price_requests)
logger.info(f"价格同步完成,成功获取 {len(token_price_map)} 个实时报价") logger.info(f"价格同步完成,成功获取 {len(token_price_map)} 个实时报价")
@@ -156,11 +164,19 @@ def main():
# 注入实时批量价格 # 注入实时批量价格
ts = m.get("tokens", []) ts = m.get("tokens", [])
if isinstance(ts, str): if isinstance(ts, str):
try: try: ts = json.loads(ts)
ts = json.loads(ts) except: ts = []
except:
ts = [] active_tid = m.get("active_token_id")
if ts and len(ts) >= 2:
# 多选一市场逻辑
if len(ts) > 2 and active_tid:
m["buy_yes_live"] = token_price_map.get(active_tid) # 该档位的 Ask
# 买入“否”的价格 = 1 - 该档位的 Bid (别人愿意买 Yes 的最高价)
bid_price = token_price_map.get(active_tid) # 这里逻辑稍后在下文 fallback 处理中增强
# 我们这里只是注入,真正复杂的 fallback 逻辑在循环内部
# 二选一市场逻辑
elif len(ts) == 2:
m["buy_yes_live"] = token_price_map.get(ts[0]) m["buy_yes_live"] = token_price_map.get(ts[0])
m["buy_no_live"] = token_price_map.get(ts[1]) m["buy_no_live"] = token_price_map.get(ts[1])
@@ -243,37 +259,40 @@ def main():
ref_temp = max_temps[idx] ref_temp = max_temps[idx]
break break
# --- 价格获取逻辑 --- # --- 价格获取逻辑 (增强版) ---
buy_yes_price = market.get("buy_yes_live") buy_yes_price = market.get("buy_yes_live")
buy_no_price = market.get("buy_no_live") buy_no_price = market.get("buy_no_live")
ts = market.get("tokens", [])
# 如果批量接口没拿到,尝试单独查询 orderbook if isinstance(ts, str): ts = json.loads(ts)
active_tid = market.get("active_token_id")
# 特殊逻辑:针对多选一型市场(Dallas/Chicago 等温度段)
if len(ts) > 2 and active_tid:
# 1. 尝试从批量映射中重新获取该档位的精确买入价(Ask)
buy_yes_price = polymarket.get_price(active_tid, side="ask") if buy_yes_price is None else buy_yes_price
# 2. 计算“否”的价格(在多选一里是 1 - 最高买入意愿价Bid)
bid_price = polymarket.get_price(active_tid, side="bid")
if bid_price:
buy_no_price = 1.0 - bid_price
if i < 3: # 仅对前几个档位打印调试
logger.debug(f"Categorical价格匹配 [{market.get('city')}-{market.get('question')}]: Yes={buy_yes_price}, No={buy_no_price} (from Bid={bid_price})")
# 通用回退逻辑 (二选一)
if buy_yes_price is None or buy_no_price is None: if buy_yes_price is None or buy_no_price is None:
ts = market.get("tokens", [])
if isinstance(ts, str):
try:
ts = json.loads(ts)
except:
ts = []
if ts and len(ts) >= 2: if ts and len(ts) >= 2:
prices = polymarket.get_buy_prices(ts[0], ts[1]) prices = polymarket.get_buy_prices(ts[0], ts[1])
if prices: if prices:
buy_yes_price = prices.get("buy_yes") buy_yes_price = prices.get("buy_yes")
buy_no_price = prices.get("buy_no") buy_no_price = prices.get("buy_no")
# 最后回退:使用 gamma 概率 # 最后回退:使用原始 Gamma 概率
if buy_yes_price is None or buy_no_price is None: if buy_yes_price is None or buy_no_price is None:
gamma_prices = market.get("prices", []) gamma_prices = market.get("prices", [])
if isinstance(gamma_prices, str): if isinstance(gamma_prices, str): gamma_prices = json.loads(gamma_prices)
try: prob = float(gamma_prices[0]) if gamma_prices else 0.5
gamma_prices = json.loads(gamma_prices) if buy_yes_price is None: buy_yes_price = prob
except: if buy_no_price is None: buy_no_price = 1.0 - prob
gamma_prices = []
current_price = float(gamma_prices[0]) if gamma_prices else 0.5
if buy_yes_price is None:
buy_yes_price = current_price
if buy_no_price is None:
buy_no_price = 1.0 - current_price
# C. 准备缓存 # C. 准备缓存
temp_unit = weather_data.get("open-meteo", {}).get( temp_unit = weather_data.get("open-meteo", {}).get(
@@ -286,6 +305,8 @@ def main():
.get("local_time") .get("local_time")
) )
current_price = buy_yes_price if buy_yes_price else 0.5
# 计算价格趋势 # 计算价格趋势
prev_data = price_history.get(market_id, {}) prev_data = price_history.get(market_id, {})
prev_price = prev_data.get("price", current_price) prev_price = prev_data.get("price", current_price)
+4 -2
View File
@@ -375,7 +375,9 @@ class PolymarketClient:
continue continue
c_id = m.get("conditionId") c_id = m.get("conditionId")
if c_id and c_id not in seen_condition_ids: # 对于多选一市场,不同档位共享 conditionId,但 tokenId 不同
unique_key = f"{c_id}_{m.get('activeTokenId')}"
if c_id and unique_key not in seen_condition_ids:
all_weather_markets.append( all_weather_markets.append(
{ {
"condition_id": c_id, "condition_id": c_id,
@@ -387,7 +389,7 @@ class PolymarketClient:
"slug": event_slug, "slug": event_slug,
} }
) )
seen_condition_ids.add(c_id) seen_condition_ids.add(unique_key)
new_markets_count += 1 new_markets_count += 1
if new_markets_count > 0: if new_markets_count > 0:
logger.debug(f"[{source_label}] 发现 {new_markets_count} 个新市场合约") logger.debug(f"[{source_label}] 发现 {new_markets_count} 个新市场合约")
+2 -16
View File
@@ -17,9 +17,7 @@ class WeatherDataCollector:
def __init__(self, config: dict): def __init__(self, config: dict):
self.config = config self.config = config
self.openweather_key = config.get("openweather_api_key")
self.wunderground_key = config.get("wunderground_api_key") self.wunderground_key = config.get("wunderground_api_key")
self.visualcrossing_key = config.get("visualcrossing_api_key")
self.timeout = 10 self.timeout = 10
self.session = requests.Session() self.session = requests.Session()
@@ -45,8 +43,7 @@ class WeatherDataCollector:
Returns: Returns:
dict: Weather data dict: Weather data
""" """
if not self.openweather_key: if not getattr(self, "openweather_key", None):
logger.warning("OpenWeatherMap API key not configured")
return None return None
query = f"{city},{country}" if country else city query = f"{city},{country}" if country else city
@@ -123,8 +120,7 @@ class WeatherDataCollector:
Returns: Returns:
dict: Historical weather data dict: Historical weather data
""" """
if not self.visualcrossing_key: if not getattr(self, "visualcrossing_key", None):
logger.warning("Visual Crossing API key not configured")
return None return None
# Default to last 30 days if no dates provided # Default to last 30 days if no dates provided
@@ -391,16 +387,6 @@ class WeatherDataCollector:
if open_meteo: if open_meteo:
results["open-meteo"] = open_meteo results["open-meteo"] = open_meteo
# OpenWeatherMap (Requires Key)
openweather = self.fetch_from_openweather(city, country)
if openweather:
results["openweathermap"] = openweather
# Visual Crossing (Requires Key)
visualcrossing = self.fetch_from_visualcrossing(city)
if visualcrossing:
results["visualcrossing"] = visualcrossing
return results return results
def check_consensus(self, forecasts: Dict) -> Dict: def check_consensus(self, forecasts: Dict) -> Dict: