Sob o capô

Como funciona o LotteryCortex

LotteryCortex combina quatro técnicas normalmente usadas só por quants e data scientists. Tornamo-las acessíveis a quem compra um bilhete.

Esta página descreve cada algoritmo que o LotteryCortex executa — da ingestão de dados à seleção de bilhetes. Tudo é abertamente documentável: para cada passo nomeamos o ficheiro-fonte na base de código. Não é uma solução milagrosa, mas a pilha de análise de lotaria mais completamente documentada publicamente que conseguimos construir.

A pipeline em 7 passos

Cada sorteio percorre esta cadeia. Detalhes por algoritmo mais abaixo.

  1. 01

    Ingestão de dados e deteção de anomalias

    Por lotaria fazemos scrape dos arquivos oficiais (fetch HTML direto com fallback Firecrawl). Cada novo sorteio passa por detectDrawAnomaly(): verificação de forma, intervalo, duplicados e comparação qui-quadrado contra os últimos 200 sorteios. Sorteios suspeitos são bloqueados antes de tocar o histórico.

  2. 02

    Engine-core: seis sinais base

    engine-core.ts calcula seis pontuações independentes por número (Bayes, EMA × 3 meias-vidas, PMI, Markov), normaliza-as em postos e combina-as com pesos afinados numa pontuação de ensemble.

  3. 03

    Meta-learner e stacking GBM

    As seis pontuações do motor + 6 features derivadas (interações, recência, hot/cold streaks) alimentam uma regressão logística por lotaria (meta-learner.ts) e um modelo gradient boosted stumps (gbm.ts). O blender empilhado combina ambos out-of-fold sem fuga.

  4. 04

    Geração de bilhetes com anti-clustering

    Amostramos milhares de bilhetes candidatos entre os números melhor pontuados, mas rejeitamos tudo o que tenha demasiado clustering: máx 3 da mesma década, paridade dentro da banda histórica, soma dentro da banda IQR de sorteios reais.

  5. 05

    Otimização de portefólio

    Seleção greedy submodular (portfolio-optimizer.ts) + seletor Markowitz de mínima variância (markowitz.ts) escolhem K bilhetes que juntos cobrem a maior massa de probabilidade com sobreposição mínima.

  6. 06

    Correção de popularidade e EV gating

    popularity.ts penaliza combinações 'bonitas' (aniversários ≤ 31, sequências consecutivas, metade inferior) porque ao acertar é preciso partilhar. ev-gating calcula EV ciente do jackpot por 1 € apostado e dá conselho 'jogar' ou 'saltar'.

  7. 07

    Backtest walk-forward e auto-tuning

    Todos os domingos corre uma validação cruzada k-fold aninhada (nested-cv.ts) mais uma grid-search sobre pesos do motor e presets de meias-vidas. Um bandido Thompson-sampling (bandit.ts) seleciona online o preset de melhor desempenho com base no ROI realizado.

Índice — 20 algoritmos

Algoritmos em detalhe

1. Encolhimento bayesiano de frequências

Quantas vezes um número foi sorteado, corrigido pela quantidade de dados disponíveis.

  • A frequência bruta sobrestima números 'quentes' quando o histórico é curto. Sobrepomos um prior de Dirichlet com estimação automática de alfa (dirichletAlphaAuto, método dos momentos sobre a variância empírica).
  • O resultado é uma probabilidade a posteriori por número que encolhe para 1/N (uniforme) enquanto o histórico é pequeno e segue a verdadeira frequência quando há sorteios suficientes.
p̂(n) = (count(n) + α) / (totalDraws · k + α · N)

src/lib/engine-core.ts · src/lib/advanced-stats.ts

2-4. EMA ponderado no tempo com múltiplas meias-vidas

Sorteios recentes pesam mais que antigos — três horizontes em paralelo.

  • Para cada sorteio i a partir do fim atribuímos peso w_i = 0.5^(i / H). Corremos três EMAs em paralelo com H = 8, 26 e 78 sorteios (curto/médio/longo).
  • Meia-vida curta capta 'hot streaks'. Meia-vida longa evita reagir em excesso a um sorteio estranho. O blender de ensemble decide quanto peso cada horizonte tem por lotaria — afinado por grid-search.
EMA_H(n) = Σ_i  0.5^(i/H) · 1[n ∈ draw_i]

src/lib/engine-core.ts (HALF_LIVES = [8, 26, 78])

5. Boost de co-ocorrência PMI

Que números aparecem juntos mais vezes do que o acaso preveria?

  • Calculamos Pointwise Mutual Information: PMI(a,b) = log [P(a,b) / (P(a)·P(b))]. PMI positivo = o par aparece junto mais do que sob independência.
  • Por número candidato somamos o PMI com todos os números do sorteio mais recente. Isso dá um sinal 'segue o padrão' sem overfit: PMI é limitado e cortado a ±3.
score_PMI(n) = Σ_{m ∈ lastDraw}  clip(log[P(n,m) / (P(n)P(m))], ±3)

src/lib/engine-core.ts

6. Probabilidades de transição de Markov

A partir do último sorteio estimamos a probabilidade de transição para o próximo número.

  • Construímos uma matriz de transição N×N T com T[a,b] = P(b em draw_t+1 | a em draw_t), estimada com suavização de Laplace.
  • Para a pontuação live tomamos a média de T[a, n] sobre todos os a do último sorteio. Números frios não ficam frios para sempre; este passo modela memória finita.

src/lib/engine-core.ts

7. Ensemble e pesos afinados

Os seis sinais são fundidos via agregação ponderada de postos.

  • Cada sinal é convertido em percentis de posto [0, 1]. Depois combinado linearmente com pesos w = (w_bayes, w_ema1, w_ema2, w_ema3, w_pmi, w_markov). Uniforme por defeito; afinado por lotaria via grid-search + validação de ROI.
  • A agregação de postos é robusta a diferenças de escala entre sinais e evita que uma pontuação atípica domine o ensemble.
score(n) = Σ_s  w_s · rank_s(n) / N

src/lib/engine-core.ts · src/lib/engine-tuning.functions.ts

8. Meta-learner (regressão logística, 12 features)

Uma logreg por lotaria aprende a combinação não-linear ótima dos sinais do motor.

  • Vetor de features (12 dim): os 6 postos do motor + interações (EMA1·PMI, Bayes·Markov, EMA3·Bayes) + recência + hot streak (aparições nos últimos 10) + cold streak (ausências consecutivas).
  • Treino: logreg regularizada L2 com SGD sobre todo o histórico. Perda = log-loss sobre o rótulo '1 se o número apareceu no sorteio seguinte'. Retrocompatível: modelos antigos de 9 dim continuam a funcionar.
P(n no próximo sorteio) = σ(w · features(n) + b)

src/lib/meta-learner.ts

9. Gradient Boosted Decision Stumps

Segundo meta-learner que capta interações não-lineares sem rede neural.

  • Mini-clone do LightGBM: decision stumps iterativos sobre os resíduos. Cada stump escolhe 1 feature + threshold que reduz a perda ao máximo; adicionamos lr · stump.output à predição atual.
  • A saída é serializável em JSON e é guardada ao lado da logreg em engine_config.meta_weights.gbm. O blender empilhado combina os dois.

src/lib/gbm.ts

10. Blender de stacking out-of-fold

Logreg + GBM são combinados sem fuga de dados.

  • Dividimos o histórico em 5 folds estratificados. Por fold treinamos logreg + GBM em 4 folds e prevemos no fold retido. Essas predições OOF tornam-se as entradas de uma segunda camada meta-logreg que aprende a ponderação ótima.
  • Resultado: probabilidades calibráveis sem que o meta-learner reveja os seus próprios dados de treino.

src/lib/advanced-stats-v3.ts · src/lib/nested-cv.ts

11. Calibração isotónica e de Platt

As probabilidades brutas do modelo são monotonizadas para serem verdadeiramente probabilidades.

  • Platt scaling ajusta um sigmoide de 1 parâmetro sobre as pontuações brutas em dados de validação. A regressão isotónica (Pool-Adjacent-Violators) é uma versão não-paramétrica que apenas impõe monotonia.
  • Medimos o Expected Calibration Error (ECE) nas predições out-of-fold; o calibrador com menor ECE vai para produção.

src/lib/advanced-stats-v2.ts · src/lib/advanced-stats-v3.ts

12. Gerador de bilhetes anti-clustering

Os bilhetes candidatos são amostrados, estruturas más rejeitadas.

  • Sorteamos números proporcionalmente à pontuação do ensemble (softmax com temperatura τ afinada por lotaria). Cada candidato é validado:
  • • máx 3 da mesma década (anti-clustering),
  • • soma dentro da banda IQR histórica (sem bilhetes só baixos ou só altos),
  • • split de paridade dentro da distribuição empírica,
  • • sem sequência aritmética de ≥4 números consecutivos.
  • Candidatos rejeitados são reamostrados até termos N bilhetes válidos.

src/lib/engine-core.ts (generateAntiClusterTickets)

13. Seleção greedy submodular de portefólio

Entre milhares de candidatos escolhemos K bilhetes que juntos cobrem o máximo.

  • Problema: escolher K bilhetes que maximizem a soma de P(número sorteado) entre os bilhetes selecionados, com retornos decrescentes para sobreposição.
  • Funções submodulares trazem uma garantia de otimalidade de 1 − 1/e ≈ 63 % para a solução greedy. Por iteração escolhemos o bilhete com o maior ganho marginal e aplicamos fator 0.4 aos números já cobertos.
f(S ∪ {t}) − f(S) = Σ_{n ∈ t}  remain[n] · (½ se bónus)

src/lib/portfolio-optimizer.ts

14. Portefólio Markowitz de mínima variância

Além da cobertura minimizamos a correlação mútua entre bilhetes.

  • Correlação entre bilhetes i e j ≈ |overlap| / √(|t_i|·|t_j|). Greedy: escolher sempre o bilhete com maior EV menos λ · Σ corr(atual, escolhidos).
  • A aversão ao risco λ é um parâmetro afinável (defeito 1.2). λ mais alto = mais diversificação, menor pontuação esperada por bilhete mas menor variância do pagamento total do portefólio.
util(t) = EV(t) − λ · Σ_{j ∈ picked}  overlap(t, j)

src/lib/markowitz.ts

15. Correção de popularidade de jackpot partilhado

Um acerto numa combinação 'popular' é partilhado com mais vencedores.

  • popularityScore() conta: % de números ≤ 31 (aniversários), % na metade inferior, sequências consecutivas ≥3, progressões aritméticas, todos com o mesmo dígito final, cruzes/diagonais no boletim.
  • Com base nisso estimamos o número de co-vencedores e aplicamos adjustJackpotForSharing(): o EV no passo de gating usa o pagamento esperado após partilha, não o jackpot bruto.

src/lib/popularity.ts

16. EV gating ciente do jackpot

Aconselhar 'jogar' apenas quando o valor esperado é positivo.

  • EV = Σ_tier P(tier) · payout(tier) − ticketCost. P(tier) é exato a partir de odds combinatórios (coeficientes binomiais por fórmula de lotaria). Payout(tier) vem de PRIZE_TIERS — para a classe jackpot substituído pelo jackpot atual (após partilha por popularidade).
  • Também calculamos o break-even jackpot: o valor onde EV = 0. Abaixo disso, a UI mostra '⏸ saltar'.
  • O critério de Kelly determina o tamanho opcional da aposta para assinantes que planeiam várias semanas.
EV = Σ_t  P(t) · payout(t) − cost   |   break-even = (cost − Σ_{t≠jp} P(t)·payout(t)) / P(jp)

src/lib/ev-gating.ts

17. Deteção de regime (CUSUM + HMM 2 estados + filtro de partículas)

Detetar se o próprio gerador de sorteios se deslocou.

  • CUSUM segue por número um desvio cumulativo da frequência esperada e alarma ao ultrapassar o limiar h.
  • Um HMM de 2 estados (hot/cold) é treinado com Baum-Welch sobre a série temporal de frequência; transições são descodificadas por Viterbi.
  • Um filtro de partículas Sequential Monte Carlo mantém 200 partículas sobre o estado oculto de regime e dá uma probabilidade suave por sorteio.
  • Em mudança de regime a EMA curta recebe temporariamente mais peso (adaptação online).

src/lib/advanced-stats-v2.ts · src/lib/advanced-stats-v3.ts

18. Extensões estatísticas de nível 3

Conformal prediction, modelo de gap binomial negativo, cópulas, Shapley e mais.

  • • Split-conformal prediction → banda sobre meta-probs com cobertura garantida.
  • • A posteriori Dirichlet-Multinomial sobre o sorteio completo (não só por número).
  • • Modelo de gap binomial negativo para sobredispersão entre aparições.
  • • Cópula gaussiana para dependências de pares que o PMI perde.
  • • Teste de permutação por sinal (label-shuffle) → p-valor para 'melhor que o aleatório'.
  • • Atribuição SHAP por bilhete (aproximação linear) → explica porque este bilhete foi escolhido.
  • • CVaR / Expected Shortfall sobre pagamentos do portefólio.
  • • Goodness-of-fit Anderson-Darling sobre uniformidade.

src/lib/advanced-stats-v2.ts · src/lib/advanced-stats-v3.ts

19. Auto-tuner: CV aninhada + grid-search

Um pg_cron semanal revê pesos do motor e meta-modelos por lotaria.

  • CV estratificada 5-fold externa → estimativa de ROI fora de amostra. CV interna → seleção de preset.
  • Grid-search sobre simplex de pesos 6-dim (ponderado BMA) + presets de meias-vidas (HALF_LIFE_GRID). Para cada config: simular K bilhetes por fold, pontuar com tiers de prémios, agregar ROI com IC bootstrap 95 %.
  • Um novo preset só é aceite se CI-low > ROI baseline (não um golpe de sorte).
  • Os resultados vão para engine_config (DB) e estão imediatamente live para todos os assinantes.

src/lib/engine-tuning.functions.ts · src/routes/api/public/hooks/auto-tune.ts

20. Bandido Thompson-sampling (seleção online de preset)

Entre auto-tunes, um bandido alterna live entre presets com base no ROI atual.

  • Por preset mantemos um a posteriori Beta(α, β): α = vitórias (ROI > 0), β = derrotas. Em cada escolha de produção amostramos de cada Beta e escolhemos a amostra mais alta.
  • As atualizações vêm do tracker: assim que um sorteio termina, os presets escolhidos somam +1 em α ou β.
  • Resultado: mesmo sem re-tuning completo o sistema converge online para o melhor preset.
pick = argmax_p  Beta(α_p, β_p).sample()

src/lib/bandit.ts

21. Backtest walk-forward

Cada estratégia é testada sem conhecimento do futuro (sem fuga).

  • A partir de MIN_HISTORY iteramos sorteio a sorteio. Por sorteio construímos o modelo APENAS a partir de sorteios anteriores, geramos N bilhetes e pontuamos contra o sorteio real.
  • Agregamos acertos por tier, ROI médio, P&L cumulativo e uma baseline aleatória de controlo. Uma estratégia só 'ganha' se a sua banda CI estiver acima do aleatório.

src/lib/backtest.functions.ts

22. Sistemas de wheeling (covering designs)

Garantia matemática: com M números corretos no pool, sempre um prémio Y-de-X.

  • Implementamos full wheels, key wheels e covering designs abreviados. Para cada (tamanho de pool, tamanho de bilhete, garantia) escolhemos o conjunto combinatoriamente ótimo.
  • O gerador de wheels combina com o otimizador de portefólio: candidatos são primeiro wheeled e depois escolhidos submodularmente.

src/lib/wheel.functions.ts

Honestamente

As loterias são e continuarão jogos de azar. Nenhuma análise pode prever um sorteio. O que o LotteryCortex faz: ajudá-lo a tomar melhores decisões sobre que combinações jogar, quando apostar mais e quando saltar uma semana.

Os sorteios de lotaria são desenhados para ser aleatórios. Nenhum algoritmo — aqui ou noutro sítio — pode prever o resultado. O que fazemos de forma mensurável: escolher melhores distribuições de números, evitar combinações populares (para partilhar menos quando ganhar) e aconselhar honestamente a saltar quando o valor esperado é negativo. O auto-tuner valida semanalmente que a nossa curva de ROI está significativamente acima do aleatório — se isso deixar de ser verdade, lê-lo-á aqui primeiro.

Porquê LotteryCortex?

A maioria das ferramentas de loteria tem mais de 10 anos, são só em inglês e feitas para desktop. LotteryCortex é moderna, mobile-first e orientada para a UE — com um motor de IA que aprende continuamente.