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

wen java案例 2

本文目录导读:

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

  1. 整体架构设计
  2. 核心数据模型
  3. 同赔匹配引擎(核心算法)
  4. 概率统计分析器
  5. 权重加权模型(综合多家赔率)
  6. 完整预测系统实现
  7. 数据存储与加载(SQL示例)
  8. 使用示例
  9. 优化与注意事项

整体架构设计

flowchart TD
    A[历史赔率数据库] --> B[数据预处理模块]
    B --> C[同赔匹配引擎]
    C --> D[概率统计模型]
    D --> E[预测结果输出]
    F[实时赔率输入] --> C

核心数据模型

// 赔率数据实体类
public class OddsData {
    private String matchId;          // 比赛ID
    private String league;           // 联赛名称
    private double homeWinOdds;     // 主胜赔率
    private double drawOdds;         // 平局赔率
    private double awayWinOdds;     // 客胜赔率
    private String result;           // 实际结果: H(主胜)/D(平局)/A(客胜)
    private LocalDate matchDate;     // 比赛日期
    // 计算赔率组合唯一标识(用于同赔匹配)
    public String getOddsKey() {
        return String.format("%.2f_%.2f_%.2f", 
            homeWinOdds, drawOdds, awayWinOdds);
    }
}

同赔匹配引擎(核心算法)

public class OddsMatcher {
    // 容差范围,用于模糊匹配(如3%误差)
    private static final double TOLERANCE = 0.03;
    /**
     * 查找历史同赔数据
     */
    public List<HistoricalMatch> findSimilarOdds(
            List<OddsData> historyData, 
            double targetHomeOdds, 
            double targetDrawOdds, 
            double targetAwayOdds) {
        return historyData.stream()
            .filter(data -> isSimilar(data.getHomeWinOdds(), targetHomeOdds)
                && isSimilar(data.getDrawOdds(), targetDrawOdds)
                && isSimilar(data.getAwayWinOdds(), targetAwayOdds))
            .collect(Collectors.toList());
    }
    /**
     * 判断两个赔率是否在容差范围内相同
     */
    private boolean isSimilar(double odds1, double odds2) {
        if (odds2 == 0) return false;
        return Math.abs(odds1 - odds2) / odds2 <= TOLERANCE;
    }
}

概率统计分析器

public class ProbabilityAnalyzer {
    /**
     * 根据历史同赔数据计算三种结果的概率
     */
    public PredictionResult analyze(List<HistoricalMatch> similarMatches) {
        if (similarMatches.isEmpty()) {
            return new PredictionResult(0.33, 0.33, 0.33, 0);
        }
        long totalMatches = similarMatches.size();
        long homeWins = similarMatches.stream()
            .filter(m -> "H".equals(m.getResult())).count();
        long draws = similarMatches.stream()
            .filter(m -> "D".equals(m.getResult())).count();
        long awayWins = similarMatches.stream()
            .filter(m -> "A".equals(m.getResult())).count();
        // 计算概率
        double homeWinProb = (double) homeWins / totalMatches;
        double drawProb = (double) draws / totalMatches;
        double awayWinProb = (double) awayWins / totalMatches;
        // 使用正态分布置信区间修正
        double confidence = calculateConfidence(totalMatches);
        return new PredictionResult(homeWinProb, drawProb, 
                                  awayWinProb, confidence, totalMatches);
    }
    /**
     * 计算置信度(样本量越大越可信)
     */
    private double calculateConfidence(int sampleSize) {
        if (sampleSize < 5) return 0.3;
        if (sampleSize < 20) return 0.5;
        if (sampleSize < 50) return 0.7;
        if (sampleSize < 100) return 0.85;
        return 0.95;
    }
    /**
     * 贝叶斯平滑(处理小样本情况)
     */
    public double[] bayesianSmooth(double[] probs, double alpha) {
        int n = probs.length;
        double[] smoothed = new double[n];
        double sum = 0;
        for (double p : probs) {
            sum += p;
        }
        for (int i = 0; i < n; i++) {
            smoothed[i] = (probs[i] + alpha) / (sum + n * alpha);
        }
        return smoothed;
    }
}

权重加权模型(综合多家赔率)

public class WeightedPredictionModel {
    // 不同博彩公司的权重(信誉越高权重越大)
    private static final Map<String, Double> COMPANY_WEIGHTS = Map.of(
        "WilliamHill", 0.25,
        "Bet365", 0.30,
        "Pinnacle", 0.35,
        "Ladbrokes", 0.10
    );
    /**
     * 综合多公司赔率预测
     */
    public PredictionResult combinedPrediction(
            Map<String, OddsData> companyOdds) {
        double totalWeight = companyOdds.entrySet().stream()
            .mapToDouble(e -> COMPANY_WEIGHTS
                .getOrDefault(e.getKey(), 0.2))
            .sum();
        double weightedHome = 0, weightedDraw = 0, weightedAway = 0;
        for (Map.Entry<String, OddsData> entry : companyOdds.entrySet()) {
            double weight = COMPANY_WEIGHTS
                .getOrDefault(entry.getKey(), 0.2) / totalWeight;
            // 赔率转概率(去除抽水)
            double[] probs = convertOddsToProb(
                entry.getValue().getHomeWinOdds(),
                entry.getValue().getDrawOdds(),
                entry.getValue().getAwayWinOdds());
            weightedHome += probs[0] * weight;
            weightedDraw += probs[1] * weight;
            weightedAway += probs[2] * weight;
        }
        return new PredictionResult(weightedHome, weightedDraw, 
                                  weightedAway, 0.8);
    }
    /**
     * 赔率转换为概率(去除博彩公司抽水利润)
     */
    private double[] convertOddsToProb(double homeOdds, double drawOdds, 
                                     double awayOdds) {
        double sum = 1/homeOdds + 1/drawOdds + 1/awayOdds;
        return new double[]{
            (1/homeOdds) / sum,
            (1/drawOdds) / sum,
            (1/awayOdds) / sum
        };
    }
}

完整预测系统实现

public class OddsPredictionSystem {
    private OddsMatcher matcher = new OddsMatcher();
    private ProbabilityAnalyzer analyzer = new ProbabilityAnalyzer();
    private WeightedPredictionModel weightedModel = new WeightedPredictionModel();
    private List<OddsData> historicalData;  // 历史数据(从数据库加载)
    /**
     * 主预测流程
     */
    public PredictionResult predict(
            double homeOdds, double drawOdds, double awayOdds) {
        // 1. 查找历史同赔数据
        List<HistoricalMatch> similarMatches = matcher
            .findSimilarOdds(historicalData, homeOdds, drawOdds, awayOdds);
        // 2. 如果样本太少,扩大容差范围再试
        if (similarMatches.size() < 5) {
            matcher.setTolerance(0.05);
            similarMatches = matcher.findSimilarOdds(
                historicalData, homeOdds, drawOdds, awayOdds);
        }
        // 3. 批量分析
        PredictionResult histPred = analyzer
            .analyze(similarMatches);
        // 4. 结合实时赔率隐含概率
        double[] marketProbs = calculateMarketProb(
            homeOdds, drawOdds, awayOdds);
        // 5. 综合加权(历史数据60% + 市场数据40%)
        return fuseResults(histPred, marketProbs);
    }
    /**
     * 结果融合
     */
    private PredictionResult fuseResults(
            PredictionResult histPred, double[] marketProbs) {
        double histWeight = histPred.getConfidence() * 0.6;
        double marketWeight = 1 - histWeight;
        double finalHome = histPred.getHomeWinProb() * histWeight 
                         + marketProbs[0] * marketWeight;
        double finalDraw = histPred.getDrawProb() * histWeight 
                         + marketProbs[1] * marketWeight;
        double finalAway = histPred.getAwayWinProb() * histWeight 
                         + marketProbs[2] * marketWeight;
        return new PredictionResult(finalHome, finalDraw, 
                                  finalAway, histPred.getConfidence());
    }
    /**
     * 市场赔率转换为概率
     */
    private double[] calculateMarketProb(
            double homeOdds, double drawOdds, double awayOdds) {
        double margin = 1/homeOdds + 1/drawOdds + 1/awayOdds;
        return new double[]{
            (1/homeOdds) / margin,
            (1/drawOdds) / margin,
            (1/awayOdds) / margin
        };
    }
}

数据存储与加载(SQL示例)

-- 创建赔率历史表
CREATE TABLE odds_history (
    id BIGINT AUTO_INCREMENT PRIMARY KEY,
    match_date DATE NOT NULL,
    league VARCHAR(50),
    home_team VARCHAR(100),
    away_team VARCHAR(100),
    home_win_odds DECIMAL(6,2),
    draw_odds DECIMAL(6,2),
    away_win_odds DECIMAL(6,2),
    result CHAR(1),  -- H/D/A
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    INDEX idx_odds (home_win_odds, draw_odds, away_win_odds)
);
-- 查询同赔数据
SELECT * FROM odds_history 
WHERE ABS(home_win_odds - ?) <= ? * ?  -- 目标值,容差,目标值
  AND ABS(draw_odds - ?) <= ? * ?
  AND ABS(away_win_odds - ?) <= ? * ?
  AND match_date < CURDATE() - INTERVAL 1 DAY;  -- 排除当天数据避免未来数据

使用示例

public class Demo {
    public static void main(String[] args) {
        OddsPredictionSystem system = new OddsPredictionSystem();
        // 加载历史数据(假设从数据库)
        List<OddsData> history = loadHistoryFromDB();
        system.setHistoricalData(history);
        // 某场比赛实时赔率
        double homeOdds = 1.75;
        double drawOdds = 3.60;
        double awayOdds = 4.50;
        PredictionResult result = system.predict(homeOdds, drawOdds, awayOdds);
        System.out.printf("主胜概率: %.2f%%%n", result.getHomeWinProb()*100);
        System.out.printf("平局概率: %.2f%%%n", result.getDrawProb()*100);
        System.out.printf("客胜概率: %.2f%%%n", result.getAwayWinProb()*100);
        System.out.printf("推荐结果: %s%n", result.getRecommended());
        System.out.printf("置信度: %.2f%%%n", result.getConfidence()*100);
    }
}

优化与注意事项

  1. 数据质量:至少需要5000+条历史数据,且要过滤异常赔率
  2. 联赛细分:不同联赛的赔率规律差异大,最好按联赛分开建模
  3. 时间衰减:近期的比赛数据权重应该更高(例如最近3个月权重0.5,更早的0.3)
  4. 动态容差:根据样本量动态调整匹配精度
  5. 冷门检测:当预测概率与市场隐含概率差异过大时,提示可能冷门

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