ALGORITHM

Laoma Predictor v0.5 - Technical Documentation

← BACK

OVERVIEW

Core Algorithm

Based on Chinese Sports Lottery official odds analysis. Converts decimal odds to implied probabilities, removes overround (bookmaker margin), and generates score predictions.

Input/Output

  • • Input: Match odds (Home/Draw/Away + Correct Score odds)
  • • Output: Top 5 score predictions with probabilities
  • • Reasoning chain included

DATA SOURCE

# Chinese Sports Lottery Official API
GET https://webapi.sporttery.cn/gateway/jc/football/getMatchCalculatorV1.qry

# Parameters
poolCode=HAD,HHAD,CRS,TTG,HAFU

# Response fields:
- matchInfoList[].subMatchList[]
  - matchNumStr: "周一001"
  - leagueAbbName: "芬超"
  - homeTeamAbbName / awayTeamAbbName
  - had.h/d/a: Home/Draw/Away odds (decimal)
  - crs: Correct Score odds (all score combinations)

CORE ALGORITHM

1. Decimal Odds → Implied Probability

def decimal_to_implied_probability(decimal_odds):
    """Convert decimal odds to implied probability"""
    if decimal_odds is None or decimal_odds <= 1:
        return 0
    return 1 / decimal_odds

# Example: odds 2.24 → probability 44.6%

2. Remove Overround (Bookmaker Margin)

def remove_overround(probabilities):
    """Normalize probabilities to remove bookmaker margin"""
    total = sum(probabilities.values())
    return {k: v / total for k, v in probabilities.items()}

# Example: Home 44.6% + Draw 29.9% + Away 38.5% = 113%
# After normalization: Home 39.5% + Draw 26.4% + Away 34.1% = 100%

3. Score Prediction

# Parse correct score odds from API
# Format: s01s02 → 1-2 (home 1, away 2)

for key, value in crs.items():
    if key.startswith("s") and "s" in key[1:]:
        parts = key[1:].split("s")
        home_goals = int(parts[0])
        away_goals = int(parts[1])
        score = f"{home_goals}-{away_goals}"
        
        # Convert odds to probability
        prob = decimal_to_implied_probability(float(value))
        score_probs[score] = prob

# Remove overround and sort
score_probs = remove_overround(score_probs)
sorted_scores = sorted(score_probs.items(), key=lambda x: x[1], reverse=True)
top5 = sorted_scores[:5]

FULL SOURCE CODE

File: laoma_v05.py

#!/usr/bin/env python3
"""
老马足彩算法 v0.5 — 基于体彩赔率分析
输入:体彩赔率数据(独赢+比分赔率)
输出:5个比分预测 + 概率 + 推理链
"""
import json
import math


def decimal_to_implied_probability(decimal_odds):
    """欧赔转隐含概率"""
    if decimal_odds is None or decimal_odds <= 1:
        return 0
    return 1 / decimal_odds


def remove_overround(probabilities):
    """去除抽水,归一化概率"""
    total = sum(probabilities.values())
    if total == 0:
        return probabilities
    return {k: v / total for k, v in probabilities.items()}


def predict_from_odds(match_data):
    """
    基于体彩赔率生成预测
    
    match_data格式:
    {
        "home_team": "赫尔火花",
        "away_team": "坦山猫",
        "odds_h": 2.24,  # 主胜赔率
        "odds_d": 3.35,  # 平局赔率
        "odds_a": 2.60,  # 客胜赔率
        "odds_crs": {    # 比分赔率(体彩格式)
            "s00s00": "18.00",  # 0-0
            "s01s00": "11.00",  # 1-0
            "s00s01": "12.00",  # 0-1
            ...
        }
    }
    """
    home = match_data.get("home_team", "主队")
    away = match_data.get("away_team", "客队")
    odds_h = match_data.get("odds_h")
    odds_d = match_data.get("odds_d")
    odds_a = match_data.get("odds_a")
    crs = match_data.get("odds_crs", {})
    
    # 1. 分析独赢赔率
    if odds_h and odds_d and odds_a:
        prob_h = decimal_to_implied_probability(odds_h)
        prob_d = decimal_to_implied_probability(odds_d)
        prob_a = decimal_to_implied_probability(odds_a)
        
        # 去除抽水
        total = prob_h + prob_d + prob_a
        prob_h_norm = prob_h / total
        prob_d_norm = prob_d / total
        prob_a_norm = prob_a / total
        
        result_1x2 = {
            "home_win": round(prob_h_norm * 100, 1),
            "draw": round(prob_d_norm * 100, 1),
            "away_win": round(prob_a_norm * 100, 1)
        }
    else:
        result_1x2 = {"home_win": 33.3, "draw": 33.3, "away_win": 33.4}
    
    # 2. 分析比分赔率
    score_probs = {}
    if crs:
        for key, value in crs.items():
            # 解析比分:s01s02 -> 1-2
            if key.startswith("s") and "s" in key[1:]:
                try:
                    parts = key[1:].split("s")
                    home_goals = int(parts[0])
                    away_goals = int(parts[1])
                    score = f"{home_goals}-{away_goals}"
                    
                    if isinstance(value, str):
                        decimal_odds = float(value)
                    else:
                        decimal_odds = value
                    
                    prob = decimal_to_implied_probability(decimal_odds)
                    score_probs[score] = prob
                except:
                    pass
        
        # 去除抽水
        if score_probs:
            score_probs = remove_overround(score_probs)
            score_probs_pct = {k: round(v * 100, 1) for k, v in score_probs.items()}
            
            # 排序取前5
            sorted_scores = sorted(score_probs_pct.items(), key=lambda x: x[1], reverse=True)
            top5 = sorted_scores[:5]
            
            predictions = [
                {"score": score, "probability": prob}
                for score, prob in top5
            ]
        else:
            predictions = _fallback_predictions(result_1x2)
    else:
        predictions = _fallback_predictions(result_1x2)
    
    # 3. 生成推理链
    reasoning = _generate_reasoning(home, away, result_1x2, predictions)
    
    return {
        "agent": "老马v0.5",
        "match": f"{home} vs {away}",
        "result_1x2": result_1x2,
        "predictions": predictions,
        "reasoning": reasoning
    }


def _fallback_predictions(result_1x2):
    """如果没有比分赔率,基于独赢赔率生成默认预测"""
    home_prob = result_1x2["home_win"]
    away_prob = result_1x2["away_win"]
    
    if home_prob > 50:
        return [
            {"score": "2-0", "probability": 25},
            {"score": "2-1", "probability": 20},
            {"score": "1-0", "probability": 18},
            {"score": "3-1", "probability": 12},
            {"score": "3-0", "probability": 10}
        ]
    elif away_prob > 50:
        return [
            {"score": "0-2", "probability": 25},
            {"score": "1-2", "probability": 20},
            {"score": "0-1", "probability": 18},
            {"score": "1-3", "probability": 12},
            {"score": "0-3", "probability": 10}
        ]
    else:
        return [
            {"score": "1-1", "probability": 22},
            {"score": "1-0", "probability": 18},
            {"score": "0-1", "probability": 18},
            {"score": "2-1", "probability": 15},
            {"score": "1-2", "probability": 15}
        ]


def _generate_reasoning(home, away, result_1x2, predictions):
    """生成推理链"""
    home_prob = result_1x2["home_win"]
    draw_prob = result_1x2["draw"]
    away_prob = result_1x2["away_win"]
    
    if home_prob > 50:
        advantage = f"{home}明显占优(胜率{home_prob}%)"
    elif away_prob > 50:
        advantage = f"{away}明显占优(胜率{away_prob}%)"
    elif home_prob > away_prob:
        advantage = f"{home}略占优势({home_prob}% vs {away_prob}%)"
    elif away_prob > home_prob:
        advantage = f"{away}略占优势({away_prob}% vs {home_prob}%)"
    else:
        advantage = "双方势均力敌"
    
    top_score = predictions[0]["score"] if predictions else "未知"
    top_prob = predictions[0]["probability"] if predictions else 0
    
    reasoning = f"""基于体彩赔率分析:

1. 独赢概率:{home}胜{home_prob}% | 平局{draw_prob}% | {away}胜{away_prob}%
2. {advantage}
3. 最可能比分:{top_score}(概率{top_prob}%)
4. 推荐投注:前3个比分覆盖率高,建议4串2容错

注意:赔率已去除抽水,概率已归一化。实际投注请结合近期战绩和伤停信息。"""
    
    return reasoning

API ENDPOINTS

GET /api/matches

# Get all matches
curl https://predict.houyuanbar.com/api/matches

# Response:
{
  "matches": [
    {
      "id": 1,
      "match_num": "周一001",
      "league": "芬超",
      "home_team": "赫尔火花",
      "away_team": "坦山猫",
      "odds_h": 2.24,
      "odds_d": 3.35,
      "odds_a": 2.60
    }
  ]
}

POST /api/predict/{match_id}

# Generate prediction (Laoma v0.5 + GPT prompt)
curl -X POST https://predict.houyuanbar.com/api/predict/1

# Response:
{
  "success": true,
  "laoma": {
    "agent": "老马v0.5",
    "predictions": [
      {"score": "1-1", "probability": 19.2},
      {"score": "2-1", "probability": 16.8}
    ],
    "reasoning": "..."
  },
  "gpt_prompt": "..."
}

IMPROVEMENT IDEAS