python案例如何利用历史同赔数据预测?

wen python案例 2

本文目录导读:

python案例如何利用历史同赔数据预测?

  1. 核心思路:什么是同赔数据预测?
  2. 完整Python案例
  3. 扩展:从真实数据源获取赔率
  4. 实际应用建议
  5. 注意事项

这是一个关于利用历史同赔数据进行预测的Python案例教程,我会提供一个完整的、可运行的示例,涵盖数据获取、特征工程、模型训练和预测。

核心思路:什么是同赔数据预测?

同赔数据是指不同博彩公司(如威廉希尔、立博、Bet365等)对同一场比赛开出的赔率组合,核心逻辑是:

  • 如果当前比赛的赔率组合与历史某些比赛的赔率高度相似,那么历史比赛的结果分布可作为预测参考。
  • 通过统计相似赔率组合下的历史胜平负概率,来预测当前比赛。

完整Python案例

下面是一个完整的预测流程,以足球比赛预测为例:

import pandas as pd
import numpy as np
from sklearn.neighbors import NearestNeighbors
from sklearn.preprocessing import StandardScaler
from collections import Counter
import warnings
warnings.filterwarnings('ignore')
# =====================================================
# 第一部分:模拟历史赔率数据(实际使用时替换为真实数据)
# =====================================================
np.random.seed(42)
def generate_historical_data(n_samples=5000):
    """生成模拟历史比赛数据"""
    historical_data = []
    for _ in range(n_samples):
        # 模拟三条主赔率(如威廉希尔、立博、Bet365)
        # 主胜、平局、客胜赔率
        home_win = np.random.uniform(1.1, 5.0)
        # 平局赔率通常介于主胜和客胜之间
        draw = np.random.uniform(2.5, 4.5)
        away_win = np.random.uniform(1.5, 6.0)
        # 根据赔率计算隐含概率(归一化)
        probs = [1/home_win, 1/draw, 1/away_win]
        probs = np.array(probs) / sum(probs)
        # 根据概率随机生成比赛结果:0=主胜, 1=平局, 2=客胜
        outcome = np.random.choice([0, 1, 2], p=probs)
        historical_data.append({
            'home_win_odds_EU': home_win,
            'draw_odds_EU': draw,
            'away_win_odds_EU': away_win,
            'outcome': outcome
        })
    return pd.DataFrame(historical_data)
# =====================================================
# 第二部分:同赔匹配与预测核心类
# =====================================================
class SameOddsPredictor:
    """
    同赔预测模型:
    1. 使用KNN寻找最相似的K场比赛
    2. 统计这些比赛的胜负平概率
    3. 输出预测概率
    """
    def __init__(self, k=20, similarity_threshold=0.0001):
        self.k = k
        self.scaler = StandardScaler()
        self.knn = None
        self.historical_data = None
    def prepare_features(self, df):
        """标准化赔率特征"""
        feature_cols = ['home_win_odds_EU', 'draw_odds_EU', 'away_win_odds_EU']
        features = df[feature_cols].values
        return self.scaler.transform(features)
    def fit(self, historical_df):
        """训练模型:拟合KNN"""
        self.historical_data = historical_df.copy()
        features = self.prepare_features(historical_df)
        self.knn = NearestNeighbors(n_neighbors=self.k, metric='euclidean')
        self.knn.fit(features)
        return self
    def predict(self, current_odds, return_top=True):
        """
        预测当前比赛结果概率
        参数:
        - current_odds: 当前赔率,格式为 [主胜, 平局, 客胜]
        - return_top: 是否返回最相似的前几场比赛详情
        返回:
        - 预测概率字典
        """
        # 转换输入为DataFrame
        current_df = pd.DataFrame([current_odds], 
                                 columns=['home_win_odds_EU', 'draw_odds_EU', 'away_win_odds_EU'])
        # 标准化当前赔率
        current_features = self.scaler.transform(current_df.values)
        # 找出K个最近邻
        distances, indices = self.knn.kneighbors(current_features)
        indices = indices[0]
        distances = distances[0]
        # 提取最近邻的比赛结果
        nearest_outcomes = self.historical_data.iloc[indices]['outcome'].values
        # 统计频率作为概率
        counts = Counter(nearest_outcomes)
        total = len(nearest_outcomes)
        probs = {
            'home_win': counts.get(0, 0) / total,
            'draw': counts.get(1, 0) / total,
            'away_win': counts.get(2, 0) / total,
            'most_likely': np.argmax([counts.get(0,0), counts.get(1,0), counts.get(2,0)])
        }
        # 附加相似度信息
        probs['similarity'] = {
            'max_similarity': 1 - max(distances),
            'avg_similarity': 1 - np.mean(distances),
            'similar_targets': len(indices)
        }
        if return_top:
            # 返回最相似的几场比赛详细信息
            top_matches = self.historical_data.iloc[indices].copy()
            top_matches['distance'] = distances
            top_matches = top_matches.sort_values('distance').head(self.k)
            probs['top_matches'] = top_matches
        return probs
# =====================================================
# 第三部分:执行预测
# =====================================================
if __name__ == "__main__":
    # 1. 生成模拟历史数据 (5000场历史比赛)
    print("生成模拟历史数据中...")
    historical_df = generate_historical_data(5000)
    print(f"历史数据形状: {historical_df.shape}")
    print(f"历史结果分布:\n{historical_df['outcome'].value_counts(normalize=True)}")
    # 2. 训练模型
    print("\n训练同赔预测模型...")
    predictor = SameOddsPredictor(k=30)  # 取30场最相似的比赛
    predictor.fit(historical_df)
    print("模型训练完成!")
    # 3. 模拟一场当前比赛(需要预测的比赛)
    current_match_odds = [1.75, 3.40, 4.80]  # 主胜1.75,平局3.40,客胜4.80
    print(f"\n当前比赛赔率: 主胜={current_match_odds[0]},平局={current_match_odds[1]},客胜={current_match_odds[2]}")
    # 4. 进行预测
    prediction = predictor.predict(current_match_odds, return_top=True)
    print("\n===== 预测结果 =====")
    print(f"主胜概率: {prediction['home_win']:.2%}")
    print(f"平局概率: {prediction['draw']:.2%}")
    print(f"客胜概率: {prediction['away_win']:.2%}")
    # 解读最有倾向的结果
    outcome_labels = ['主胜', '平局', '客胜']
    predicted_outcome = outcome_labels[prediction['most_likely']]
    print(f"\n最可能的结果: {predicted_outcome}")
    print(f"\n相似度信息:")
    print(f"  - 平均相似度: {prediction['similarity']['avg_similarity']:.4f}")
    print(f"  - 最相似匹配度: {prediction['similarity']['max_similarity']:.4f}")
    # 5. 查看最相似的历史比赛
    print("\n===== 最相似的10场比赛 =====")
    top_10 = prediction['top_matches'].head(10)
    for i, row in top_10.iterrows():
        result_labels = ['主胜', '平局', '客胜']
        print(f"赔率[{row['home_win_odds_EU']:.2f}, {row['draw_odds_EU']:.2f}, "
              f"{row['away_win_odds_EU']:.2f}] -> 实际结果: {result_labels[row['outcome']]} "
              f"(相似度: {1-row['distance']:.3f})")

扩展:从真实数据源获取赔率

import requests
import time
def fetch_real_odds_from_api(start_date, end_date):
    """
    从公开API获取历史赔率数据
    例如使用 football-data 或 odds-api 等免费接口
    """
    # 这里以the-odds-api为例(需要注册获取API key)
    API_KEY = "YOUR_API_KEY_HERE"
    base_url = "https://api.the-odds-api.com/v4/sports/soccer/odds/"
    params = {
        'apiKey': API_KEY,
        'regions': 'eu',
        'markets': 'h2h',
        'oddsFormat': 'decimal',
        'dateFormat': 'iso'
    }
    all_data = []
    # 示例:获取最近30天的数据
    for i in range(30):
        date = (pd.Timestamp.today() - pd.Timedelta(days=i+1)).strftime('%Y-%m-%d')
        params['commenceTimeFrom'] = f"{date}T00:00:00Z"
        params['commenceTimeTo'] = f"{date}T23:59:59Z"
        try:
            response = requests.get(base_url, params=params)
            if response.status_code == 200:
                games = response.json()
                for game in games:
                    if 'bookmakers' in game:
                        for bookmaker in game['bookmakers']:
                            for market in bookmaker['markets']:
                                if market['key'] == 'h2h':
                                    outcomes = {outcome['name']: outcome['price'] 
                                              for outcome in market['outcomes']}
                                    all_data.append({
                                        'home_win_odds': outcomes.get('Home', None),
                                        'draw_odds': outcomes.get('Draw', None),
                                        'away_win_odds': outcomes.get('Away', None),
                                        'bookmaker': bookmaker['key'],
                                        'game_id': game['id']
                                    })
        except Exception as e:
            print(f"获取日期 {date} 数据失败: {e}")
        time.sleep(1)  # 避免API限流
    return pd.DataFrame(all_data)

实际应用建议

  1. 数据清洗:真实数据通常需要处理赔率波动(取平均值或中位数)、缺失值填充
  2. 特征工程
    • 增加变量:欧赔、初盘/终盘差异、多家公司赔率差值
    • 增加历史战绩特征
  3. 模型优化
    • 调整K值(用交叉验证寻找最优K)
    • 使用加权投票(距离越近权重越高)
  4. 验证方法:用时间序列交叉验证(前70%训练,后30%验证)
# 加权投票改进示例
def weighted_prediction(self, current_odds, k=20, power=2):
    """使用距离倒数作为权重的加权预测"""
    features = self.scaler.transform(pd.DataFrame([current_odds]).values)
    distances, indices = self.knn.kneighbors(features, n_neighbors=k)
    outcomes = self.historical_data.iloc[indices[0]]['outcome'].values
    weights = 1 / (distances[0] ** power + 1e-10)  # 距离越近权重越大
    # 加权概率计算
    prob_home = np.sum(weights[outcomes == 0]) / np.sum(weights)
    prob_draw = np.sum(weights[outcomes == 1]) / np.sum(weights)
    prob_away = np.sum(weights[outcomes == 2]) / np.sum(weights)
    return {'home_win': prob_home, 'draw': prob_draw, 'away_win': prob_away}

注意事项

  • 样本量:同赔数据预测效果依赖历史样本,一般建议至少500-1000场有效记录
  • 赔率时效性:赔率会随时间变化,建议使用开赛前最终赔率或平均值
  • 市场差异:不同联赛/赛事特征不同,建议区分比赛级别建模
  • 风险提示:任何预测模型都有局限性,建议设置止损,切不可盲目重注

这个案例是一个完整的基础框架,你可以根据自己的数据格式调整特征列名和结果编码,真实应用时建议用历史数据进行回测,验证模型的稳定性和有效性。

抱歉,评论功能暂时关闭!