B∑

Sassamaru Docs

Documentação técnica do modelo

← Voltar ao índice
Sassamaru: Um modelo probabilístico de previsão de partidas de futebol
Arquitectura, parâmetros e decisões de design para previsão de partidas e simulação de competições usando regressão de Poisson bivariada, ratings Elo e correção Dixon-Coles.
Poisson bivariado Ratings Elo Dixon-Coles Temporal decay Monte Carlo Brasileirão 2003–2026 International 1872–2026
Março 2026 · Versão 1.0
Índice
  1. Motivação e escopo
  2. Dados
  3. Arquitetura do modelo
  4. Da λ às probabilidades e odds
  5. Validação e backtesting
  6. Simulação Monte Carlo
  7. Diferenças entre os apps
  8. Limitações conhecidas
  9. ToDo e próximos passos
§ 1

Motivação e escopo

O Sassamaru nasceu de uma pergunta simples: é possível construir um modelo de previsão de futebol robusto que rode inteiramente no navegador, sem servidor, sem API externa, usando apenas dados históricos públicos?

A resposta é sim — com ressalvas. Modelos probabilísticos de futebol têm longa tradição acadêmica, começando com Maher (1982) e Dixon & Coles (1997). O Sassamaru reimplementa essa tradição em JavaScript client-side, priorizando transparência, reprodutibilidade e facilidade de uso.

O escopo é deliberadamente limitado: previsão de resultados 1X2 (mandante vence / empate / visitante vence) com probabilidades calibradas e odds derivadas. Não há previsão de escanteios, cartões, marcadores individuais ou mercados asiáticos.

O que o modelo não é: Uma ferramenta de apostas. As odds geradas são odds justas derivadas das probabilidades do próprio modelo, com overround fixo. Elas não refletem o mercado real e não incorporam informação de escalações, lesões ou condição de campo.
§ 2

Dados

Brasileirão

Dataset customizado campeonatobrasileirolimpo.csv com 9 200+ partidas do Campeonato Brasileiro Série A entre 2003 e 2026 (rodadas 1–8 da edição atual). As colunas usadas são: rodada (rodata), mandante, visitante e placar.

Atenção — estrutura temporal: O dataset não contém datas — apenas o número da rodada. O modelo usa a rodada como unidade de tempo, o que exige detecção automática de temporadas via clustering de "rodada 1" (mínimo de 5 ocorrências consecutivas). Isso torna o decay temporal independente de calendário, mas também significa que jogos fora de rodada (amistosos, copas) não estão presentes.

Internacional

Dataset público de Mart Jürisoo disponível no Kaggle (international football results), com 49 000+ partidas entre seleções desde 1872. Colunas: date, home_team, away_team, home_score, away_score, neutral. O campo neutral é booleano e indica se o jogo foi disputado em campo neutro.

Para o modelo de seleções, o decay é date-based (dias desde cada partida), não round-based. O treinamento usa dados a partir de 1990 por padrão, já que o futebol pré-Copa do Mundo moderna tem distribuições estatísticas bastante distintas das atuais.

Jogos (Brasileirão)
9 200+
2003 – 2026
Clubes ativos
~50
ao longo dos anos
Jogos (internacional)
49 000+
1872 – 2026
Seleções
~270
com dados históricos
§ 3

Arquitetura do modelo

O pipeline de previsão segue cinco etapas principais:

1. Pesos temporais 2. Poisson (λ) 3. Elo (ajuste λ) 4. Forma (ajuste λ) 5. Dixon-Coles P(H/D/A)
Figura 1 — Pipeline de previsão. Cada etapa refina a estimativa de gols esperados (λ) ou a distribuição de placares resultante.

3.1 Pesos temporais (temporal decay)

Partidas mais antigas são menos informativas do que partidas recentes. Para capturar isso, cada jogo histórico recebe um peso calculado com uma função de half-life exponencial:

w(age) = max(w_min, 0.5 ^ (age / half_life))
ageIdade do jogo em rodadas (Brasileirão) ou dias (seleções)
half_lifeMeia-vida — quando age = half_life, o peso cai para 0.5
w_minPeso mínimo — evita que jogos muito antigos sejam zerados

Três pesos independentes são calculados, com half-lives diferentes, para os três componentes que os usam:

ComponenteHalf-life (Brasileirão)Half-life (Seleções)Peso mínimo
Elo76 rodadas (~2 temporadas)730 dias (~2 anos)0.10
Poisson (λ)152 rodadas (~4 temporadas)1 460 dias (~4 anos)0.25
Forma recente19 rodadas (~½ temporada)120 dias (~4 meses)0.35

A intuição por trás dos diferentes half-lives: a força de ataque e defesa (Poisson) muda devagar — um time bom geralmente permanece bom por anos. A força relativa (Elo) muda um pouco mais rápido. A forma é por definição transitória — o que importa são os últimos meses, não os últimos anos.

3.2 Regressão de Poisson

O modelo central assume que o número de gols marcados por cada time em uma partida segue distribuições de Poisson independentes com parâmetros λH (gols esperados do mandante) e λA (gols esperados do visitante).

λ_H = atk_H × def_A × league_home_avgλ_A = atk_A × def_H × league_away_avg
atk_HForça de ataque do mandante jogando em casa (razão sobre a média da liga)
def_AVulnerabilidade defensiva do visitante fora de casa
league_home_avgMédia ponderada de gols por jogo em casa na liga

As quatro forças por time (ataque em casa, defesa em casa, ataque fora, defesa fora) são calculadas como razões em relação à média da liga, usando médias ponderadas pelos pesos temporais de Poisson:

atk_H(t) = Σ(gols_marcados_em_casa × w_poi) / Σ(w_poi) ÷ league_home_avgdef_H(t) = Σ(gols_sofridos_em_casa × w_poi) / Σ(w_poi) ÷ league_away_avg
Valores > 1 indicam força acima da média da liga · valores < 1 indicam abaixo. w_poi = peso temporal de Poisson de cada partida.

Essa estrutura é a base do modelo de Dixon & Coles (1997) e equivale a uma regressão log-linear de Poisson com efeitos fixos por time e por lado (casa/fora).

3.3 Ratings Elo

O sistema Elo, desenvolvido originalmente para xadrez por Arpad Elo, foi adaptado para futebol pela FIFA e por pesquisadores como Hvattum & Arntzen (2010). A ideia central é que cada time tem um rating que sobe quando vence e cai quando perde, proporcionalmente à surpresa do resultado.

R_novo = R_antigo + K × (resultado − E[resultado])E[resultado] = 1 / (1 + 10 ^ ((R_adversário − R_time) / 400))
resultado1 se vitória · 0.5 se empate · 0 se derrota
E[resultado]Probabilidade esperada de vitória via escala logística
KK_base × (1 + 0.5 × max(0, diff_gols − 1)) × w_elo
w_eloPeso temporal — reduz o impacto de jogos antigos no rating

O fator K é dinâmico: vitórias por maior placar geram atualização mais forte. O peso temporal w_elo reduz o impacto de jogos antigos no rating atual.

No início de cada temporada do Brasileirão, os ratings sofrem um reset parcial em direção ao valor inicial (1500), controlado pelo parâmetro SEASON_RESET_ALPHA = 0.20. Isso captura o fato de que, com mudanças de elenco, um time forte tende a regredir um pouco à média.

R_resetado = R_anterior × (1 − α) + R_inicial × α
αSEASON_RESET_ALPHA = 0.20 — fração de regressão à média a cada temporada

O Elo entra no modelo como um ajuste multiplicativo em λ, usando o parâmetro γ (gamma):

λ_H_ajustado = λ_H × exp( +γ × ΔElo / 400 )λ_A_ajustado = λ_A × exp( −γ × ΔElo / 400 )
ΔEloR_mandante − R_visitante
γ0.06 — controla o quanto o Elo pesa sobre λ
clampO ajuste é limitado a [−0.18, +0.18] para evitar distorções extremas
Por que não usar Elo diretamente? Seria possível derivar probabilidades direto do Elo via fórmula logística, mas isso perderia a informação de gols esperados — fundamental para calcular distribuição de placares. A abordagem híbrida (Poisson + Elo como ajuste) captura tanto a intensidade ofensiva/defensiva quanto a força relativa histórica.

3.4 Forma recente

A forma recente captura a tendência de curto prazo de um time — um time que está em crise tende a continuar sofrendo gols, independentemente da sua força histórica.

Para cada time, os últimos FORM_N jogos são usados para calcular médias ponderadas de gols marcados (GF) e sofridos (GA), com o peso temporal de forma (half-life curto):

form_GF(t) = Σ(gols_marcados_i × w_form_i) / Σ(w_form_i)form_GA(t) = Σ(gols_sofridos_i × w_form_i) / Σ(w_form_i)
iÍndice dos últimos FORM_N jogos do time
w_form_iPeso temporal de forma: 0.5 ^ (age_i / HL_FORM), mínimo MIN_W_FORM

Esses valores são convertidos em fatores relativos à média da liga e aplicados a λ:

fator_atk_H = clamp( form_GF(mandante) / média_liga, 0.75, 1.25 )fator_def_A = clamp( form_GA(visitante) / média_liga, 0.75, 1.25 )λ_H = λ_H_base × fator_atk_H × fator_def_Aλ_A = λ_A_base × fator_atk_A × fator_def_H
FORM_MIN = 0.75 · FORM_MAX = 1.25 — clamp evita over-fitting a sequências curtas de resultados.

3.5 Correção Dixon-Coles

Dixon & Coles (1997) identificaram que o modelo de Poisson puro superestima a probabilidade de placares altos e subestima os placares baixos, particularmente 0-0, 1-0, 0-1 e 1-1. A correção introduz um parâmetro ρ (rho) que ajusta essas quatro células da matriz de placares:

P(g_H, g_A) ∝ Poisson(λ_H, g_H) × Poisson(λ_A, g_A) × τ(g_H, g_A) ⎧ 1 − λ_H·λ_A·ρ se g_H=0, g_A=0 ⎪ 1 + λ_A·ρ se g_H=1, g_A=0τ(g,g) = ⎨ 1 + λ_H·ρ se g_H=0, g_A=1 ⎪ 1 − ρ se g_H=1, g_A=1 ⎩ 1 caso contrário
ρEstimado via MLE — tipicamente entre −0.05 e −0.15 para o Brasileirão
ρ < 0Aumenta P(0-0) e P(1-1) · reduz P(1-0) e P(0-1)

O valor de ρ é estimado por máxima verossimilhança (MLE) via busca em grade de -0.50 a +0.50 em passos de 0.01, maximizando a log-verossimilhança do conjunto de treinamento. O valor empírico encontrado para o Brasileirão é aproximadamente ρ ≈ −0.01 — significativamente menos negativo do que o −0.10 assumido inicialmente, o que demonstra a importância de estimar o parâmetro em vez de fixá-lo por hipótese.

Normalização: Após aplicar a correção, a matriz de placares é renormalizada para que a soma de todas as probabilidades seja 1.0. Isso é necessário porque τ pode distorcer ligeiramente a soma total.

3.6 Fator de descanso (Brasileirão)

No Brasileirão, times com mais rodadas de descanso desde o último jogo jogam melhor. O modelo aplica um ajuste em λ baseado no gap (em rodadas) desde a última partida disputada:

rest_factor = clamp( 1 + REST_COEF × (gap − REST_BASELINE), REST_MIN, REST_MAX )
gapNúmero de rodadas desde o último jogo do time
REST_BASELINE1 rodada — gap neutro, sem ajuste
REST_COEF0.03 → +3% de λ por rodada extra de descanso
REST_MIN0.92 → penalidade máxima de 8%
REST_MAX1.06 → bônus máximo de 6%

3.7 Campo neutro (seleções)

Aproximadamente 30% das partidas internacionais são disputadas em campo neutro (Copas do Mundo, torneios continentais, amistosos em terceiro país). Nesses casos, a vantagem de jogar em casa não existe, então λ é calculado usando a média entre league_home_avg e league_away_avg para ambos os lados:

base = (league_home_avg + league_away_avg) / 2λ_H = atk_H × def_A × baseλ_A = atk_A × def_H × base
Com vantagem de casa: λ_H usa league_home_avg (maior) e λ_A usa league_away_avg. No campo neutro, ambos usam a mesma base — eliminando o benefício do mandante.

Ao construir o modelo, jogos em campo neutro são tratados como "jogos fora" para ambos os times nos cálculos de força de ataque e defesa, evitando inflar o parâmetro de vantagem de casa com dados de jogos que não têm tal vantagem.

§ 4

Da λ às probabilidades e odds

Com λH e λA determinados, a probabilidade de cada placar possível (gH, gA) é calculada como produto das probabilidades de Poisson, corrigida por Dixon-Coles:

P(g_H, g_A) ∝ Pois(λ_H, g_H) × Pois(λ_A, g_A) × τ(g_H, g_A)Pois(λ, k) = λ^k × e^(−λ) / k!
g_H, g_AGols do mandante e visitante ∈ {0, 1, 2, …, 8} (MAX_GOALS = 8)
τ(g_H, g_A)Fator de correção Dixon-Coles (ver §3.5)

As probabilidades marginais de resultado (1X2) são obtidas pela soma das células da matriz:

P(Mandante) = Σ P(g_H, g_A) ∀ g_H > g_AP(Empate) = Σ P(g_H, g_A) ∀ g_H = g_AP(Visitante) = Σ P(g_H, g_A) ∀ g_H < g_A

As odds são derivadas dessas probabilidades adicionando um overround (margem da casa), que representa o lucro esperado do bookmaker. Com overround de 6%:

odd_H = 1 / (P(H) × (1 + overround))odd_D = 1 / (P(D) × (1 + overround))odd_A = 1 / (P(A) × (1 + overround))
overround0.06 — soma das probabilidades implícitas = 1.06
odd justa1 / P(X) sem overround — odd que um mercado eficiente pagaria
Log-space para evitar underflow: Para λ elevados, λ^k / k! pode causar underflow em ponto flutuante. O modelo calcula Poisson em log-space: exp(k × log(λ) − λ − logfact(k)), com log-fatoriais pré-computados em cache.
§ 5

Validação e backtesting

O modelo inclui um backtest walk-forward: para cada uma das últimas BACKTEST_SEASONS temporadas do Brasileirão, o modelo é treinado em todas as temporadas anteriores e avaliado nos jogos da temporada corrente. As métricas calculadas são:

Brier Score (multi-classe)

BS = (1/N) × Σ [(p_H − o_H)² + (p_D − o_D)² + (p_A − o_A)²]
o_X1 se X ocorreu, 0 caso contrário (variável indicadora)
range0 = perfeito · 2 = totalmente errado
benchmark~0.667 para modelo uniforme (1/3, 1/3, 1/3)

Log-Loss

LL = −(1/N) × Σ log( p_resultado )
p_resultadoProbabilidade atribuída pelo modelo ao resultado que de fato ocorreu
penalidadeConfiança errada é punida mais do que incerteza — log(0.1) >> log(0.4)
benchmarklog(3) ≈ 1.099 para modelo uniforme

Acurácia 1X2

Percentual de jogos em que o palpite do modelo (resultado mais provável) coincidiu com o resultado real. Sassamarumark de referência: ~46% (proporção de vitórias do mandante). O modelo historicamente atinge ~52–55% no Brasileirão.

Calibração

O modelo é considerado calibrado se, entre todos os jogos para os quais atribuiu probabilidade ~30%, aproximadamente 30% desses jogos ocorreram. A calibração é visualizada em um gráfico de confiabilidade com 10 bins de 10% cada.

Nota metodológica: O backtest usa o modelo treinado em dados históricos completos (até a temporada de teste), não um modelo re-treinado a cada rodada. Isso é uma simplificação que pode introduzir leve viés otimista, mas é computacionalmente viável para execução no navegador.
§ 6

Simulação Monte Carlo

Para os apps de simulação de temporada (Brasileirão e Copa do Mundo), o modelo é usado para gerar distribuições de resultados possíveis via Monte Carlo: o torneio inteiro é simulado N vezes (tipicamente 5 000), e as frequências de cada evento (campeão, classificado, rebaixado, etc.) são estimadas empiricamente.

Em cada simulação, para cada partida:

Para cada partida em cada simulação: 1. Calcular λ_H, λ_A via pipeline completo 2. Construir matriz P(g_H, g_A) com correção Dixon-Coles 3. Amostrar (g_H, g_A) ~ P(g_H, g_A) 4. Atualizar pontuação e saldo de gols
5 000 simulações × 380 partidas = ~1.9 milhões de placares amostrados por execução.

Para jogos eliminatórios (Copa do Mundo), o empate não existe como resultado final. Se a distribuição Poisson + DC gera empate, o vencedor é determinado por um sorteio ponderado pela razão P(H) / (P(H) + P(A)), emulando pênaltis.

Escalonamento e responsividade

Para evitar travar o navegador, o loop Monte Carlo cede o controle ao event loop do JavaScript a cada 50–100 iterações via await new Promise(r => setTimeout(r, 0)). Isso permite atualizar a barra de progresso e manter a interface responsiva durante o cálculo.

Interpretação das probabilidades

Com N = 5 000 simulações, o erro padrão de uma probabilidade de 50% é √(0.5 × 0.5 / 5000) ≈ 0.7%. Para probabilidades extremas (ex.: rebaixamento do favorito, ~2%), o erro padrão é ≈ 0.2%, suficiente para diferenciar candidatos ao rebaixamento com confiança razoável.

§ 7

Diferenças entre os apps

Característica Sassamaru Rodada Sassamaru Brasileirão Sassamaru Seleções Sassamaru Copa
Dataset Brasileirão 2003–26 Brasileirão 2003–26 Internacional 1872–26 Internacional 1872–26
Unidade de tempo Rodadas Rodadas Dias Dias
Campo neutro Não Não Sim (flag CSV) Sim (grupo/fase)
Fator descanso Sim Não (MC) Não Não
Detecção de temporadas Auto (clusters) Auto (clusters) N/A N/A
Modo de saída Por partida Classificação Monte Carlo Por partida Torneio Monte Carlo
§ 8

Limitações conhecidas

Nenhum modelo é completo. As limitações mais relevantes do Sassamaru são:

Informação não modelada

O modelo não sabe que Neymar está lesionado, que um técnico foi demitido ontem, que o jogo é disputado em altitude de 3 600m, que chove forte ou que um time está concentrado em outra competição. Toda essa informação contextual é real e relevante — o modelo ignora completamente.

Empates

O modelo de Poisson bivariado tende a subestimar a probabilidade de empate em comparação com o mercado real. A correção Dixon-Coles mitiga isso parcialmente (via aumento de P(0-0) e P(1-1)), mas o problema persiste para placares maiores (2-2, 3-3).

Times com poucos dados

Times recém-promovidos ou seleções com histórico limitado no dataset têm estimativas de força pouco confiáveis. O modelo usa fallback para médias da liga, o que pode super ou subestimar esses times.

Não-estacionaridade

O futebol muda: estilos de jogo, volume de transferências, intensidade física. O modelo trata toda a história (desde 2003 / 1990) como amostras do mesmo processo, ponderando pelo decay. Isso é uma aproximação — o futebol de 2026 não é estatisticamente idêntico ao de 2003, mesmo com decay.

Independência entre gols

O modelo assume que os gols do mandante e do visitante são independentes. Na prática, o estado do jogo influencia o comportamento: um time que abre 3-0 não joga da mesma forma que em 0-0. Essa correlação dinâmica não é capturada.

Monte Carlo e o bracket da Copa

O chaveamento da Copa do Mundo 2026 para a fase eliminatória é complexo — o chaveamento real depende de quais grupos os 8 melhores terceiros colocados vieram. O simulador usa uma simplificação de pareamento sequencial, o que pode gerar matches que não seriam possíveis no torneio real.

§ 9

ToDo e próximos passos

O modelo atual é funcional e produz previsões razoáveis, mas há espaço significativo para melhoria. Os itens abaixo estão ordenados por impacto esperado na qualidade das previsões.

Modelo

Dados

Interface

Infraestrutura