diff --git a/main.py b/main.py index bd541f55..cd9df16f 100644 --- a/main.py +++ b/main.py @@ -124,22 +124,30 @@ def main(): time.sleep(300) continue - # 2. 批量同步盘口价格 (优化:一次请求获取所有市场的 Buy Yes/No) + # 2. 批量同步盘口价格 (优化:为每个档位获取其对应的真实 Token 价格) token_price_map = {} price_requests = [] for m in all_weather_markets: ts = m.get("tokens", []) if isinstance(ts, str): - try: - ts = json.loads(ts) - except: - ts = [] - if ts and len(ts) >= 2: + try: ts = json.loads(ts) + except: ts = [] + + active_tid = m.get("active_token_id") + + # 如果是多选一市场(比如 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[1], "side": "ask"}) # Buy No if price_requests: - logger.info(f"正在同步 {len(price_requests)} 个档位的盘口价格...") + logger.info(f"正在同步 {len(price_requests)} 个档位的真实盘口价格...") token_price_map = polymarket.get_multiple_prices(price_requests) logger.info(f"价格同步完成,成功获取 {len(token_price_map)} 个实时报价") @@ -156,11 +164,19 @@ def main(): # 注入实时批量价格 ts = m.get("tokens", []) if isinstance(ts, str): - try: - ts = json.loads(ts) - except: - ts = [] - if ts and len(ts) >= 2: + try: ts = json.loads(ts) + except: ts = [] + + active_tid = m.get("active_token_id") + + # 多选一市场逻辑 + 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_no_live"] = token_price_map.get(ts[1]) @@ -243,37 +259,40 @@ def main(): ref_temp = max_temps[idx] break - # --- 价格获取逻辑 --- + # --- 价格获取逻辑 (增强版) --- buy_yes_price = market.get("buy_yes_live") buy_no_price = market.get("buy_no_live") - - # 如果批量接口没拿到,尝试单独查询 orderbook + ts = market.get("tokens", []) + 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: - ts = market.get("tokens", []) - if isinstance(ts, str): - try: - ts = json.loads(ts) - except: - ts = [] if ts and len(ts) >= 2: prices = polymarket.get_buy_prices(ts[0], ts[1]) if prices: buy_yes_price = prices.get("buy_yes") buy_no_price = prices.get("buy_no") - # 最后回退:使用 gamma 概率 + # 最后的回退:使用原始 Gamma 概率 if buy_yes_price is None or buy_no_price is None: gamma_prices = market.get("prices", []) - if isinstance(gamma_prices, str): - try: - gamma_prices = json.loads(gamma_prices) - except: - 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 + if isinstance(gamma_prices, str): gamma_prices = json.loads(gamma_prices) + prob = float(gamma_prices[0]) if gamma_prices else 0.5 + if buy_yes_price is None: buy_yes_price = prob + if buy_no_price is None: buy_no_price = 1.0 - prob # C. 准备缓存 temp_unit = weather_data.get("open-meteo", {}).get( @@ -286,6 +305,8 @@ def main(): .get("local_time") ) + current_price = buy_yes_price if buy_yes_price else 0.5 + # 计算价格趋势 prev_data = price_history.get(market_id, {}) prev_price = prev_data.get("price", current_price) diff --git a/src/data_collection/polymarket_api.py b/src/data_collection/polymarket_api.py index d994c9fb..419108a0 100644 --- a/src/data_collection/polymarket_api.py +++ b/src/data_collection/polymarket_api.py @@ -375,7 +375,9 @@ class PolymarketClient: continue 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( { "condition_id": c_id, @@ -387,7 +389,7 @@ class PolymarketClient: "slug": event_slug, } ) - seen_condition_ids.add(c_id) + seen_condition_ids.add(unique_key) new_markets_count += 1 if new_markets_count > 0: logger.debug(f"[{source_label}] 发现 {new_markets_count} 个新市场合约") diff --git a/src/data_collection/weather_sources.py b/src/data_collection/weather_sources.py index 977e6c50..748a5ee4 100644 --- a/src/data_collection/weather_sources.py +++ b/src/data_collection/weather_sources.py @@ -17,9 +17,7 @@ class WeatherDataCollector: def __init__(self, config: dict): self.config = config - self.openweather_key = config.get("openweather_api_key") self.wunderground_key = config.get("wunderground_api_key") - self.visualcrossing_key = config.get("visualcrossing_api_key") self.timeout = 10 self.session = requests.Session() @@ -45,8 +43,7 @@ class WeatherDataCollector: Returns: dict: Weather data """ - if not self.openweather_key: - logger.warning("OpenWeatherMap API key not configured") + if not getattr(self, "openweather_key", None): return None query = f"{city},{country}" if country else city @@ -123,8 +120,7 @@ class WeatherDataCollector: Returns: dict: Historical weather data """ - if not self.visualcrossing_key: - logger.warning("Visual Crossing API key not configured") + if not getattr(self, "visualcrossing_key", None): return None # Default to last 30 days if no dates provided @@ -391,16 +387,6 @@ class WeatherDataCollector: if 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 def check_consensus(self, forecasts: Dict) -> Dict: