From d2a9e9a9b35e5d510a263b4b8ad0d67f95879f23 Mon Sep 17 00:00:00 2001 From: "2569718930@qq.com" <2569718930@qq.com> Date: Mon, 25 May 2026 02:51:58 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E6=A6=82=E7=8E=87=E5=BC=95?= =?UTF-8?q?=E6=93=8E=E4=B8=8E=20Polymarket=20=E6=A1=B6=E4=B8=8D=E5=8C=B9?= =?UTF-8?q?=E9=85=8D=E5=AF=BC=E8=87=B4=20model=5Fp=20=E5=A7=8B=E7=BB=88?= =?UTF-8?q?=E4=B8=BA=20None=20=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit _distribution_probability_for_market 新增高斯 CDF fallback: 当离散概率分布桶与 Polymarket 市场桶无精确重叠时(如 NYC 分布 56-59F 但市场桶为 51-70F),用分布的 mu/sigma 构建正态分布计算 CDF 概率, 不再直接返回 None,保证 scan_rows 正常生成。 --- src/data_collection/polymarket_readonly.py | 55 +++++++++++++++++++++- 1 file changed, 53 insertions(+), 2 deletions(-) diff --git a/src/data_collection/polymarket_readonly.py b/src/data_collection/polymarket_readonly.py index b7161111..9749a01d 100644 --- a/src/data_collection/polymarket_readonly.py +++ b/src/data_collection/polymarket_readonly.py @@ -1628,9 +1628,60 @@ class PolymarketReadOnlyLayer: probability = max(0.0, min(1.0, probability)) total += probability matched += 1 - if matched <= 0: + if matched > 0: + return max(0.0, min(1.0, total)) + + # Fallback: use Gaussian CDF when no distribution bucket matches the + # market bucket exactly. Compute mu/sigma from the distribution, then + # integrate the Gaussian tail or band that corresponds to the market. + values = [] + weights = [] + for row in probability_distribution: + v = _safe_float(row.get("value")) + p = _safe_float(row.get("probability")) + if v is not None and p is not None: + prob = p / 100.0 if p > 1.0 else p + values.append(v) + weights.append(max(0.0, prob)) + if len(values) < 2: return None - return max(0.0, min(1.0, total)) + + total_weight = sum(weights) + if total_weight <= 0: + return None + mu = sum(v * w for v, w in zip(values, weights)) / total_weight + variance = sum(w * (v - mu) ** 2 for v, w in zip(values, weights)) / total_weight + sigma = math.sqrt(max(variance, 0.01)) + + unit = str(temp_symbol or "C").upper() + bucket_range = self._extract_market_bucket_range(market) + lower = bucket_range[0] if bucket_range else None + upper = bucket_range[1] if bucket_range else None + direction = self._extract_market_bucket_direction(market) + if lower is not None: + lower = self._convert_temp_to_market_unit( + lower, source_symbol=None, market_unit=(bucket_range[2] if bucket_range else unit), + ) or lower + if upper is not None: + upper = self._convert_temp_to_market_unit( + upper, source_symbol=None, market_unit=(bucket_range[2] if bucket_range else unit), + ) or upper + + def _norm_cdf(x: float) -> float: + return 0.5 * (1.0 + math.erf((x - mu) / (sigma * math.sqrt(2.0)))) + + if lower is not None and upper is not None: + prob = _norm_cdf(upper + 0.5) - _norm_cdf(lower - 0.5) + elif lower is not None and direction == "above": + prob = 1.0 - _norm_cdf(lower - 0.5) + elif lower is not None and direction == "below": + prob = _norm_cdf(lower + 0.5) + elif lower is not None: + prob = _norm_cdf(lower + 1.5) - _norm_cdf(lower - 0.5) + else: + return None + + return max(0.0, min(1.0, prob)) def _load_markets(self, active_only: bool = True) -> List[Dict[str, Any]]: now = time.time()