AI-система для фентезі-спорту та ставок

Проектуємо та впроваджуємо системи штучного інтелекту: від прототипу до production-ready рішення. Наша команда поєднує експертизу в машинному навчанні, дата-інжинірингу та MLOps, щоб AI працював не в лабораторії, а в реальному бізнесі.
Показано 1 з 1Усі 1566 послуг
AI-система для фентезі-спорту та ставок
Складний
від 1 тижня до 3 місяців
Часті запитання

Напрямки AI-розробки

Етапи розробки AI-рішення

Останні роботи

  • image_website-b2b-advance_0.webp
    Розробка сайту компанії B2B ADVANCE
    1285
  • image_web-applications_feedme_466_0.webp
    Розробка веб-додатків для компанії FEEDME
    1198
  • image_websites_belfingroup_462_0.webp
    Розробка веб-сайту для компанії БЕЛФІНГРУП
    902
  • image_ecommerce_furnoro_435_0.webp
    Розробка інтернет магазину для компанії FURNORO
    1122
  • image_logo-advance_0.webp
    Розробка логотипу компанії B2B Advance
    589
  • image_crm_enviok_479_0.webp
    Розробка веб-додатків для компанії Enviok
    859

AI-системи для фантазійного спорту та ставок

Спортивна аналітика з ML означає прогнозування результатів шляхом моделювання реальних процесів: фізичний стан гравця, форма команди, історичні матчапи, фактор місця, погода. Платформи фантазійного спорту використовують ці самі моделі для скорингу, підбору складів та оцінки гравців.

Прогнозування результатів матчів

import numpy as np
import pandas as pd
from sklearn.ensemble import GradientBoostingClassifier, GradientBoostingRegressor
from sklearn.calibration import CalibratedClassifierCV
import json

class MatchOutcomePredictor:
    """
    Прогнозування результату матчу: Перемога/Нічия/Поразка + xG (Expected Goals).
    На основі ELO, форми команди, переваги дома, матчапів.
    """

    ELO_K = 32
    HOME_ADVANTAGE = 100  # ELO points

    def __init__(self):
        # Модель для ймовірностей результатів
        self.outcome_model = CalibratedClassifierCV(
            GradientBoostingClassifier(n_estimators=300, learning_rate=0.05, random_state=42),
            method='isotonic', cv=5
        )
        # Регресійна модель для xG
        self.xg_model = GradientBoostingRegressor(
            n_estimators=200, learning_rate=0.05, random_state=42
        )
        self.elo_ratings = {}

    def update_elo(self, home_team: str, away_team: str,
                    outcome: str) -> tuple:
        """Оновлення рейтингу ELO після матчу"""
        home_elo = self.elo_ratings.get(home_team, 1500)
        away_elo = self.elo_ratings.get(away_team, 1500)

        home_elo_adj = home_elo + self.HOME_ADVANTAGE

        p_home_win = 1 / (1 + 10 ** ((away_elo - home_elo_adj) / 400))
        p_away_win = 1 - p_home_win

        if outcome == 'home_win':
            home_result, away_result = 1.0, 0.0
        elif outcome == 'away_win':
            home_result, away_result = 0.0, 1.0
        else:  # draw
            home_result, away_result = 0.5, 0.5

        new_home_elo = home_elo + self.ELO_K * (home_result - p_home_win)
        new_away_elo = away_elo + self.ELO_K * (away_result - p_away_win)

        self.elo_ratings[home_team] = new_home_elo
        self.elo_ratings[away_team] = new_away_elo

        return new_home_elo, new_away_elo

    def build_match_features(self, home_team: str, away_team: str,
                              match_context: dict,
                              team_stats: dict) -> np.ndarray:
        """Вектор ознак для матчу"""
        home_elo = self.elo_ratings.get(home_team, 1500)
        away_elo = self.elo_ratings.get(away_team, 1500)

        home_stats = team_stats.get(home_team, {})
        away_stats = team_stats.get(away_team, {})

        return np.array([
            # ELO та форма
            home_elo - away_elo,                          # Різниця ELO
            home_elo + self.HOME_ADVANTAGE - away_elo,    # Коригована різниця
            home_stats.get('form_5_games', 0.5),          # Форма: очки/можливі останні 5
            away_stats.get('form_5_games', 0.5),
            home_stats.get('form_trend', 0),              # Тренд форми

            # Атака/захист
            home_stats.get('goals_scored_avg', 1.5),
            home_stats.get('goals_conceded_avg', 1.2),
            away_stats.get('goals_scored_avg', 1.5),
            away_stats.get('goals_conceded_avg', 1.2),
            home_stats.get('xg_for_avg', 1.4),
            home_stats.get('xg_against_avg', 1.1),

            # Контекст
            int(match_context.get('is_cup', False)),
            int(match_context.get('is_derby', False)),
            match_context.get('home_fatigue_days', 7),    # Днів з попереднього матчу
            away_stats.get('travel_distance_km', 0) / 1000,

            # Історичні матчапи
            match_context.get('h2h_home_win_rate', 0.4),
            match_context.get('h2h_goals_avg', 2.5),
        ])

    def predict_outcome(self, home_team: str, away_team: str,
                         match_context: dict, team_stats: dict) -> dict:
        """Ймовірності результатів матчу"""
        features = self.build_match_features(home_team, away_team, match_context, team_stats)
        probs = self.outcome_model.predict_proba(features.reshape(1, -1))[0]

        return {
            'home_win': round(float(probs[0]), 3),
            'draw': round(float(probs[1]), 3),
            'away_win': round(float(probs[2]), 3),
            'expected_goals_home': round(float(self.xg_model.predict(features.reshape(1, -1))[0]), 2),
            'home_elo': self.elo_ratings.get(home_team, 1500),
            'away_elo': self.elo_ratings.get(away_team, 1500)
        }


class PlayerPerformancePredictor:
    """Прогнозування показників гравців для фантазійного спорту"""

    def predict_fantasy_points(self, player: dict,
                                match_context: dict,
                                season_stats: pd.DataFrame) -> dict:
        """
        Прогноз очок фантазійного спорту для гравця у конкретному матчі.
        Враховує позицію, форму, суперника, місце.
        """
        player_stats = season_stats[season_stats['player_id'] == player['id']]

        if player_stats.empty:
            return {'expected_points': 0, 'confidence': 'low'}

        # Ковзаючи середні показників
        recent = player_stats.sort_values('date').tail(5)
        avg_stats = {
            'goals_per_game': recent['goals'].mean(),
            'assists_per_game': recent['assists'].mean(),
            'shots_per_game': recent['shots'].mean(),
            'minutes_per_game': recent['minutes_played'].mean(),
            'key_passes': recent.get('key_passes', pd.Series([0])).mean(),
        }

        # Коригування на якість суперника
        opp_defensive_rank = match_context.get('opponent_defensive_rank', 10)  # 1=найкращий
        difficulty_multiplier = 1.0 + (opp_defensive_rank - 10) * 0.02  # ±0.2 від рангу

        # Коригування форми
        form_factor = recent['fantasy_points'].mean() / max(
            player_stats['fantasy_points'].mean(), 0.1
        )
        form_factor = float(np.clip(form_factor, 0.7, 1.3))

        # Множник позиції
        position_multiplier = {
            'GK': 0.8, 'DEF': 1.0, 'MID': 1.2, 'FWD': 1.1
        }.get(player.get('position', 'MID'), 1.0)

        # Базовий розрахунок очок
        expected_points = (
            avg_stats['goals_per_game'] * 4 +
            avg_stats['assists_per_game'] * 3 +
            avg_stats['shots_per_game'] * 0.3 +
            (avg_stats['minutes_per_game'] / 90) * 2
        ) * difficulty_multiplier * form_factor * position_multiplier

        # Ймовірність грати у стартовому складі
        starting_prob = player.get('starting_probability', 0.9)

        return {
            'player_id': player['id'],
            'player_name': player.get('name', ''),
            'position': player.get('position', ''),
            'expected_points': round(expected_points * starting_prob, 2),
            'expected_if_starts': round(expected_points, 2),
            'starting_probability': starting_prob,
            'form_factor': round(form_factor, 2),
            'difficulty_multiplier': round(difficulty_multiplier, 2),
            'confidence': 'high' if len(player_stats) >= 10 else 'medium'
        }


class FantasyTeamOptimizer:
    """Оптимізація складу фантазійної команди"""

    def optimize_lineup(self, player_predictions: list[dict],
                         budget: float,
                         formation: str = '4-3-3') -> dict:
        """
        Лінійне програмування для максимізації очікуваних очок
        під бюджетними та позиційними обмеженнями.
        """
        from scipy.optimize import linprog

        formation_requirements = {
            '4-3-3': {'GK': 1, 'DEF': 4, 'MID': 3, 'FWD': 3},
            '4-4-2': {'GK': 1, 'DEF': 4, 'MID': 4, 'FWD': 2},
            '3-5-2': {'GK': 1, 'DEF': 3, 'MID': 5, 'FWD': 2},
        }
        requirements = formation_requirements.get(formation, formation_requirements['4-3-3'])

        # Жадний підбір як швидкий heuristic
        selected = []
        remaining_budget = budget
        position_slots = dict(requirements)

        # Сортуємо за expected_points / price
        sorted_players = sorted(
            player_predictions,
            key=lambda p: p['expected_points'] / max(p.get('price', 5), 0.1),
            reverse=True
        )

        for player in sorted_players:
            pos = player.get('position', 'MID')
            if position_slots.get(pos, 0) <= 0:
                continue
            if player.get('price', 5) > remaining_budget:
                continue

            selected.append(player)
            position_slots[pos] -= 1
            remaining_budget -= player.get('price', 5)

            if all(v == 0 for v in position_slots.values()):
                break

        total_expected = sum(p['expected_points'] for p in selected)
        total_cost = budget - remaining_budget

        return {
            'lineup': selected,
            'total_expected_points': round(total_expected, 2),
            'total_cost': round(total_cost, 1),
            'remaining_budget': round(remaining_budget, 1),
            'formation': formation
        }

Відповідальний гейминг

Системи ставок та фантазійного спорту потребують інтеграції інструментів RG (Responsible Gambling): моніторинг патернів надмірних ставок (різкий зріст обсягу, ставки після великих програшів, ночні сесії), автоматичні обмеження, самовиключення. Це не опціонально — це нормативна вимога у більшості юрисдикцій.

Точність прогнозування результатів футбольних матчів: AUC 0.72-0.78 (ринки ефективні, абсолютної переваги немає). Цінність ML лежить у точній оцінці ймовірностей для виявлення переоцінених/недооцінених ставок проти лінії букмекера.