Pular para o conteúdo
Backend

Como Criar um Bot de Apostas Esportivas

Um guia direto, sem enrolação, pra quem quer construir um bot de apostas esportivas do zero usando Python e modelos de IA. Arquitetura, APIs, código funcional e —

Aviso antes de tudo

Como Criar um Bot de Apostas Esportivas. Um guia direto, sem enrolação, pra quem quer construir um bot de apostas esportivas do zero usando Python e modelos de IA. Arquitetura, APIs, código funcional e — claro — os riscos que ninguém conta.

Galera, vou ser direto: construir um bot de apostas esportivas não é tão difícil quanto parece do ponto de vista técnico. Python resolve bem. O difícil mesmo é fazer um bot que não perca dinheiro — e aí a IA entra na jogada.

Nos últimos meses, construí três versões diferentes de bots de apostas. Duas perderam dinheiro. Uma ficou levemente positiva depois de 4 meses de operação. Vou mostrar o caminho completo aqui — incluindo os erros — porque na internet todo mundo finge que deu certo de primeira.

O que é um betting bot e como funciona a arquitetura

Um betting bot é um programa que coleta odds em tempo real, analisa se existe valor nelas e, quando encontra, faz a aposta automaticamente. Tipo um robô trader, só que pra esportes. A diferença é que o mercado de apostas esportivas tem ineficiências enormes — especialmente em ligas menores.

A arquitetura básica tem três camadas. A primeira coleta dados: odds de várias casas, estatísticas dos times, lesões, clima, tudo. A segunda camada é o cérebro — um modelo de IA que pega esses dados e calcula a probabilidade real de cada resultado. A terceira camada compara essa probabilidade com as odds oferecidas e decide se aposta ou não.

  1. Coletor de Dados: puxa odds de APIs como The Odds API, Betfair Exchange e Pinnacle em intervalos de 30-60 segundos
  2. Pipeline de Features: limpa, transforma e enriquece os dados com estatísticas históricas dos times
  3. Modelo Preditivo: recebe as features e cospe probabilidades pra cada outcome (vitória casa, empate, vitória fora)
  4. Motor de Decisão: compara as probabilidades do modelo com as odds do mercado e identifica value bets
  5. Executor: quando o motor aprova, faz a aposta via API da casa ou interface automatizada
  6. Logger: registra tudo — cada aposta, cada decisão, cada centavo. Sem isso, você não consegue melhorar nada

Setup Python — bibliotecas e configuração das APIs

Vamos ao que interessa. O setup em Python é surpreendentemente simples. Você precisa de poucas bibliotecas pra ter algo rodando. O grosso do trabalho não é o código em si — é a qualidade dos dados e do modelo.

bash
pip install requests pandas scikit-learn xgboost schedule python-dotenv loguru

Requests pra consumir APIs, Pandas pra manipular dados, scikit-learn e XGBoost pro modelo de IA, schedule pra rodar tarefas em intervalos e loguru porque o logging padrão do Python é sofrível. O python-dotenv é pra guardar suas API keys fora do código.

python
import os
import requests
import pandas as pd
from dotenv import load_dotenv
from loguru import logger

load_dotenv()

API_KEY = os.getenv("ODDS_API_KEY")
BASE_URL = "https://api.the-odds-api.com/v4"

def get_odds(sport="soccer_brazil_serie_a", regions="eu", markets="h2h"):
    """Puxa odds ao vivo de múltiplas casas."""
    url = f"{BASE_URL}/sports/{sport}/odds"
    params = {
        "apiKey": API_KEY,
        "regions": regions,
        "markets": markets,
        "oddsFormat": "decimal"
    }
    resp = requests.get(url, params=params, timeout=10)
    resp.raise_for_status()
    logger.info(f"Coletadas odds de {len(resp.json())} jogos")
    return resp.json()

Organizando o projeto

Não jogue tudo em um arquivo só. Mesmo pra um projeto pessoal, separe pelo menos em quatro módulos: collector, features, model e executor. Quando algo quebrar às 3 da manhã (e vai quebrar), você precisa saber exatamente onde olhar.

plaintext
betting-bot/
├── collector/
│   ├── odds_api.py
│   ├── stats_api.py
│   └── cache.py
├── features/
│   ├── builder.py
│   └── historical.py
├── model/
│   ├── train.py
│   ├── predict.py
│   └── backtest.py
├── executor/
│   ├── decision.py
│   └── placer.py
├── config.py
├── main.py
└── .env

Coletando odds — scraping vs APIs

Aqui tem uma decisão importante. Dá pra coletar odds de duas formas: scraping direto dos sites das casas ou usando APIs oficiais/terceirizadas. Cada caminho tem tradeoffs.

Web Scraping

Extrai dados diretamente dos sites das casas de apostas.

+ Prós

  • • Grátis — sem custo de API
  • • Acesso a qualquer casa e mercado
  • • Dados em tempo real se bem implementado

− Contras

  • • Quebra quando o site muda o layout
  • • Sites usam anti-bot pesado (Cloudflare, etc.)
  • • Pode violar os termos de uso
  • • Precisa de proxies e headless browsers

APIs de Odds

Consome dados estruturados de provedores como The Odds API ou Betfair.

+ Prós

  • • Dados limpos e estruturados
  • • Uptime confiável
  • • Legal e dentro dos termos de uso
  • • Atualizações automáticas de novos mercados

− Contras

  • • Custo mensal (de $0 a $200+)
  • • Rate limits podem ser restritivos
  • • Nem todas as casas/mercados disponíveis

Minha recomendação? Começa com API. The Odds API tem um plano grátis de 500 requests por mês — dá pra prototipar tranquilo. Só vai pra scraping se precisar de dados que nenhuma API oferece. E se for por esse caminho, leia nosso guia de web scraping de sites de apostas.

Limpeza e normalização dos dados

Odds brutas de diferentes fontes vêm bagunçadas. Nomes de times diferentes, formatos de odds inconsistentes, mercados faltando. Você precisa normalizar tudo antes de alimentar o modelo.

python
def normalize_odds(raw_games: list) -> pd.DataFrame:
    """Transforma dados brutos da API em DataFrame limpo."""
    rows = []
    for game in raw_games:
        for bookmaker in game.get("bookmakers", []):
            for market in bookmaker.get("markets", []):
                if market["key"] == "h2h":
                    outcomes = {o["name"]: o["price"] for o in market["outcomes"]}
                    rows.append({
                        "game_id": game["id"],
                        "home": game["home_team"],
                        "away": game["away_team"],
                        "bookmaker": bookmaker["key"],
                        "home_odds": outcomes.get(game["home_team"]),
                        "draw_odds": outcomes.get("Draw"),
                        "away_odds": outcomes.get(game["away_team"]),
                        "timestamp": bookmaker["last_update"]
                    })
    return pd.DataFrame(rows)

Modelo de IA — features, treinamento e backtesting

Agora a parte mais interessante. O modelo de IA precisa responder uma pergunta simples: qual a probabilidade real de cada resultado? Se o modelo diz que o time da casa tem 60% de chance de vencer e a casa tá pagando odds de 2.0 (que implica 50%), você tem um value bet.

Escolhendo as features certas

As features são o combustível do modelo. Coloque lixo, sai lixo. Pra futebol, as features que mais funcionam na prática são estas.

Features que realmente importam

  • Expected Goals (xG) dos últimos 10 jogos de cada time
  • Forma recente: pontos ganhos nos últimos 5 jogos em casa/fora
  • Confronto direto: histórico dos últimos 5 jogos entre os times
  • Odds de mercado (consenso): média das odds de 5+ casas de apostas
  • Posição na tabela e diferença de gols
  • Dias de descanso desde o último jogo
  • Jogadores chave ausentes (se conseguir o dado de forma automatizada)

Um detalhe que quase ninguém fala: as odds de mercado são, elas mesmas, uma feature poderosa. Casas de apostas gastam milhões em modelos. Usar as odds delas como input no seu modelo é tipo ter a resposta parcial de graça.

Treinando o modelo com XGBoost

python
import xgboost as xgb
from sklearn.model_selection import TimeSeriesSplit
from sklearn.calibration import CalibratedClassifierCV
from sklearn.metrics import log_loss

def train_model(df: pd.DataFrame):
    """Treina modelo calibrado pra prever probabilidades."""
    features = ["home_xg_avg", "away_xg_avg", "home_form", "away_form",
                "h2h_home_wins", "market_home_prob", "market_draw_prob",
                "rest_days_diff", "table_position_diff"]
    
    X = df[features]
    y = df["result"]  # 0=home, 1=draw, 2=away
    
    tscv = TimeSeriesSplit(n_splits=5)
    base_model = xgb.XGBClassifier(
        n_estimators=300,
        max_depth=5,
        learning_rate=0.05,
        objective="multi:softprob",
        eval_metric="mlogloss"
    )
    
    # Calibração é obrigatória — XGBoost puro cospe probabilidades ruins
    model = CalibratedClassifierCV(base_model, cv=tscv, method="isotonic")
    model.fit(X, y)
    
    logger.info(f"Log loss: {log_loss(y, model.predict_proba(X)):.4f}")
    return model

Por que calibração é obrigatória

Um modelo que diz '70% de chance de vitória' precisa acertar perto de 70% das vezes quando faz essa previsão. Isso se chama calibração. Sem isso, suas decisões de value betting vão ser baseadas em probabilidades distorcidas.

Use CalibratedClassifierCV do scikit-learn com o método isotonic. Sempre. Não confie nos scores brutos de nenhum classificador pra betting.

Backtesting — antes de gastar um centavo

Backtesting é rodar o modelo em dados históricos pra ver como ele teria performado. Parece óbvio, mas a maioria dos caras pula direto pro dinheiro real. Não faça isso.

O truque é usar TimeSeriesSplit, nunca KFold aleatório. Dados de apostas são temporais — se você treinar com dados de dezembro pra prever outubro, tá trapaceando. O modelo precisa sempre treinar no passado e prever o futuro, exatamente como vai funcionar na prática.

python
def backtest(df: pd.DataFrame, model, min_edge=0.05):
    """Simula operação real com dados históricos."""
    bankroll = 1000.0
    bets = []
    
    for _, row in df.iterrows():
        probs = model.predict_proba([row[features]])[0]
        for outcome_idx, prob in enumerate(probs):
            implied_prob = 1 / row[f"odds_{outcome_idx}"]
            edge = prob - implied_prob
            
            if edge > min_edge:
                # Kelly criterion pra sizing
                kelly_fraction = edge / (row[f"odds_{outcome_idx}"] - 1)
                stake = bankroll * kelly_fraction * 0.25  # quarter Kelly
                stake = min(stake, bankroll * 0.03)  # max 3% por aposta
                
                won = row["result"] == outcome_idx
                pnl = stake * (row[f"odds_{outcome_idx}"] - 1) if won else -stake
                bankroll += pnl
                bets.append({"edge": edge, "stake": stake, "pnl": pnl, "bankroll": bankroll})
    
    results = pd.DataFrame(bets)
    logger.info(f"Apostas: {len(results)}, ROI: {results['pnl'].sum()/results['stake'].sum()*100:.1f}%")
    return results

Automação e execução em tempo real

Se o backtest mostrar resultados positivos (e consistentes, não só um mês bom), dá pra partir pra automação. Aqui o bot roda sozinho: coleta odds, gera previsão, avalia valor e executa.

python
import schedule
import time

def run_cycle():
    """Um ciclo completo: coleta -> análise -> decisão."""
    try:
        odds = get_odds()
        df = normalize_odds(odds)
        df = enrich_with_features(df)  # adiciona xG, forma, etc.
        
        for _, game in df.iterrows():
            probs = model.predict_proba([game[features]])[0]
            decision = evaluate_value(probs, game)
            
            if decision["should_bet"]:
                logger.info(f"VALUE encontrado: {game['home']} vs {game['away']}")
                logger.info(f"  Edge: {decision['edge']:.1%}, Stake: R${decision['stake']:.2f}")
                # place_bet(decision)  # descomente quando estiver pronto
    except Exception as e:
        logger.error(f"Erro no ciclo: {e}")

schedule.every(2).minutes.do(run_cycle)

while True:
    schedule.run_pending()
    time.sleep(1)

Repara que o place_bet tá comentado. Roda assim por pelo menos duas semanas, só logando as decisões. Compara o que o bot teria apostado com os resultados reais. Se os números baterem com o backtest, aí sim liga o executor. Paciência aqui salva dinheiro.

Riscos e disclaimer legal

Riscos que você precisa aceitar antes de começar

Overfitting: seu modelo pode parecer incrível no backtest e perder dinheiro no real. Sempre use dados out-of-sample.

Casas limitam contas vencedoras: se você lucrar consistentemente, sua conta será restringida. Isso acontece em semanas, não meses.

Latência mata: a diferença entre uma odd de 2.05 e 1.98 é a diferença entre lucro e prejuízo. Seu bot precisa ser rápido.

Regulação: em muitos países, bots de apostas violam os termos de serviço das casas. No Brasil, a regulação de apostas esportivas ainda está evoluindo.

Risco financeiro: mesmo com edge positivo, sequências de perdas longas acontecem. Kelly criterion ajuda, mas não elimina o risco.

Na real, a maioria das pessoas que constroem bots de apostas acaba aprendendo mais sobre data science e Python do que ganhando dinheiro com apostas. E sinceramente? Esse aprendizado já vale muito.

Perguntas frequentes

Preciso de quanto dinheiro pra começar?

Pra desenvolver e testar, zero. Você pode backtestear com dados gratuitos por meses. Pra operar de verdade, o mínimo razoável gira em torno de R$ 1.000-2.000 como bankroll, pra aguentar as oscilações naturais sem quebrar.

Qual linguagem é melhor pra bots de apostas?

Python, sem dúvida. O ecossistema de data science (pandas, scikit-learn, XGBoost) é imbatível. Se precisar de latência ultra-baixa, partes críticas podem ir pra Rust ou C++, mas 99% dos projetos pessoais ficam bem só com Python.

Dá pra viver de bot de apostas?

Na teoria, sim. Na prática, é extremamente difícil. Casas limitam contas, edges diminuem com o tempo, e o estresse emocional é real. A maioria dos profissionais que conheço diversifica entre apostas, trading e consultoria. Não coloque todos os ovos nessa cesta.

O bot funciona pra qualquer esporte?

A arquitetura funciona pra qualquer esporte, mas as features mudam bastante. Futebol e basquete têm mais dados públicos disponíveis. Esportes menores (tipo handball ou vôlei de ligas regionais) podem ter menos dados mas também menos eficiência nas odds — o que é bom pra encontrar valor.