from typing import List, Dict import math class SGPCorrelationEngine: """ 同場串關 (Same Game Parlay) 關聯性與價值探測引擎 """ @staticmethod def calculate_joint_probability(prob_A: float, prob_B: float, correlation_coeff: float) -> float: """ 計算兩個事件的聯合機率 (考慮相關係數)。 使用簡化的二元正態分佈/Copula近似邏輯。 :param prob_A: 事件 A 獨立發生的真實機率 :param prob_B: 事件 B 獨立發生的真實機率 :param correlation_coeff: 相關係數 (-1.0 到 1.0) """ if not (-1.0 <= correlation_coeff <= 1.0): raise ValueError("相關係數必須介於 -1.0 與 1.0 之間") # 獨立發生的聯合機率 independent_joint_prob = prob_A * prob_B # 理論最大與最小邊界 max_joint_prob = min(prob_A, prob_B) min_joint_prob = max(0.0, prob_A + prob_B - 1.0) if correlation_coeff == 0: return independent_joint_prob elif correlation_coeff > 0: # 正相關:聯合機率向 max_joint_prob 靠攏 return independent_joint_prob + correlation_coeff * (max_joint_prob - independent_joint_prob) else: # 負相關:聯合機率向 min_joint_prob 靠攏 return independent_joint_prob + abs(correlation_coeff) * (min_joint_prob - independent_joint_prob) @staticmethod def find_sgp_value(events: List[Dict], bookmaker_sgp_odds: float) -> Dict: """ 評估 SGP 注單是否具備正期望值。 events 範例: [{'prob': 0.6}, {'prob': 0.4}] 且需自帶兩兩相關係數矩陣 (此處簡化為平均相關性) """ if len(events) < 2: raise ValueError("SGP 必須至少包含兩個事件") # 假設外部特徵工程已經給出了這組事件的平均正相關係數 (例如 0.4) # 實務上會透過更複雜的 Monte Carlo 計算,此為展示核心邏輯 avg_correlation = events[0].get('correlation_with_others', 0.0) current_joint_prob = events[0]['prob'] for i in range(1, len(events)): current_joint_prob = SGPCorrelationEngine.calculate_joint_probability( current_joint_prob, events[i]['prob'], avg_correlation ) # 計算莊家隱含機率 implied_prob = 1.0 / bookmaker_sgp_odds # 計算 EV ev_percentage = (current_joint_prob * bookmaker_sgp_odds) - 1.0 is_profitable = ev_percentage > 0.05 # 設定 5% 的 EV 門檻 return { "true_joint_probability": round(current_joint_prob, 4), "bookmaker_implied_probability": round(implied_prob, 4), "ev_percentage": round(ev_percentage, 4), "is_profitable_sgp": is_profitable, "fair_odds": round(1.0 / current_joint_prob, 2) if current_joint_prob > 0 else 0 }