71 lines
1.6 KiB
Python
71 lines
1.6 KiB
Python
"""反向盤口移動(RLM)偵測模組。"""
|
||
|
||
from __future__ import annotations
|
||
|
||
from dataclasses import dataclass
|
||
from datetime import datetime
|
||
|
||
|
||
@dataclass(frozen=True)
|
||
class ReverseLineMovementAlert:
|
||
match_id: str
|
||
market_type: str
|
||
selection: str
|
||
opening_odds: float
|
||
current_odds: float
|
||
ticket_pct: float
|
||
handle_pct: float
|
||
odds_change_pct: float
|
||
smart_money_to: str
|
||
is_triggered: bool
|
||
triggered_at: datetime
|
||
rationale: str
|
||
|
||
|
||
def evaluate_reverse_line_movement(
|
||
match_id: str,
|
||
market_type: str,
|
||
selection: str,
|
||
*,
|
||
opening_odds: float,
|
||
current_odds: float,
|
||
ticket_pct: float,
|
||
handle_pct: float,
|
||
ticket_threshold: float = 70.0,
|
||
odds_change_threshold: float = 0.05,
|
||
) -> ReverseLineMovementAlert:
|
||
"""依條件判斷是否出現反向盤口。"""
|
||
|
||
if opening_odds <= 0:
|
||
odds_pct = 0.0
|
||
else:
|
||
odds_pct = round((current_odds - opening_odds) / opening_odds, 6)
|
||
|
||
is_triggered = (
|
||
ticket_pct > ticket_threshold
|
||
and odds_pct > odds_change_threshold
|
||
and handle_pct < ticket_pct
|
||
)
|
||
|
||
smart_money_to = selection if handle_pct > ticket_pct else '對側'
|
||
rationale = (
|
||
f'散戶 {ticket_pct:.1f}% 追捧卻資金 {handle_pct:.1f}%,\n'
|
||
f'盤口由 {opening_odds:.2f} 上升到 {current_odds:.2f}'
|
||
)
|
||
|
||
return ReverseLineMovementAlert(
|
||
match_id=match_id,
|
||
market_type=market_type,
|
||
selection=selection,
|
||
opening_odds=opening_odds,
|
||
current_odds=current_odds,
|
||
ticket_pct=ticket_pct,
|
||
handle_pct=handle_pct,
|
||
odds_change_pct=round(odds_pct * 100, 4),
|
||
smart_money_to=smart_money_to,
|
||
is_triggered=is_triggered,
|
||
triggered_at=datetime.utcnow(),
|
||
rationale=rationale,
|
||
)
|
||
|