54 lines
1.8 KiB
Python
54 lines
1.8 KiB
Python
"""裁判尺度分析器。"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from typing import Dict
|
|
|
|
from .ev_calculator import calculate_expected_value
|
|
|
|
|
|
def calculate_cards_ev(
|
|
referee_stats: dict,
|
|
match_tension_index: float,
|
|
bookmaker_card_line: float,
|
|
bookmaker_odds: float,
|
|
) -> dict[str, float | bool | str]:
|
|
"""判斷裁判/對手張力對紅黃牌盤口的偏差與價值。
|
|
|
|
依據裁判最近場次平均黃牌數與比賽張力(衝突度)估算
|
|
本場真實牌數,並與莊家 O/U 盤口比較。
|
|
"""
|
|
|
|
if bookmaker_odds <= 1:
|
|
raise ValueError('bookmaker_odds 必須大於 1')
|
|
if bookmaker_card_line <= 0:
|
|
raise ValueError('bookmaker_card_line 必須大於 0')
|
|
if not 0 <= match_tension_index <= 1:
|
|
raise ValueError('match_tension_index 必須在 0~1')
|
|
|
|
avg_cards = float(referee_stats.get('avg_yellow_cards', 0.0) or 0.0)
|
|
penalties_per_game = float(referee_stats.get('penalties_per_game', 0.0) or 0.0)
|
|
|
|
strictness_index = 20.0 + avg_cards * 1.9 + penalties_per_game * 2.5
|
|
# 綜合壓力補正,將裁判嚴厲度與球隊/賽事張力轉為預測牌數。
|
|
expected_cards = max(
|
|
0.5,
|
|
strictness_index * (0.45 + 0.55 * max(0.0, min(match_tension_index, 1.0))),
|
|
)
|
|
|
|
true_prob = min(1.0, max(0.0, expected_cards / (bookmaker_card_line * 1.4)))
|
|
implied_prob = 1.0 / bookmaker_odds
|
|
edge = true_prob - implied_prob
|
|
|
|
ev = calculate_expected_value(true_prob, bookmaker_odds, stake=100.0)
|
|
|
|
return {
|
|
'strictness_index': round(strictness_index, 3),
|
|
'expected_total_cards': round(expected_cards, 3),
|
|
'true_prob': round(true_prob, 4),
|
|
'implied_prob': round(implied_prob, 4),
|
|
'edge_percent': round(edge * 100, 3),
|
|
'is_value_bet': ev['is_value_bet'],
|
|
'ev_percentage': ev['ev_percentage'],
|
|
}
|