LotteryCortex combineert vier technieken die normaal alleen door quants en data-scientists worden gebruikt. We maken ze toegankelijk voor iedereen die een lot koopt.
Deze pagina beschrijft elk algoritme dat LotteryCortex draait — van data-ingest tot ticket-selectie. Alles is open documenteerbaar: voor elke stap noemen we de bronfile in de codebase. Geen wondermiddel, wél de meest complete publiek gedocumenteerde stack voor loterij-analyse die we konden bouwen.
Algoritmes in detail
1. Bayesiaanse frequentie-shrinkage
Hoe vaak een nummer is getrokken, gecorrigeerd voor de hoeveelheid data die we hebben.
- Pure ruwe frequentie overschat 'hot' nummers wanneer de geschiedenis kort is. We leggen er een Dirichlet-prior overheen met automatische alpha-schatting (dirichletAlphaAuto, method-of-moments op de empirische variantie).
- Het resultaat is een posterior-kans per nummer die naar 1/N (uniform) krimpt zolang de geschiedenis klein is, en de echte frequentie gaat volgen zodra we genoeg trekkingen hebben.
p̂(n) = (count(n) + α) / (totalDraws · k + α · N)
src/lib/engine-core.ts · src/lib/advanced-stats.ts
2-4. Tijd-gewogen EMA met meerdere half-lifes
Recente trekkingen tellen zwaarder dan oude — drie horizonnen tegelijk.
- Voor elke trekking i vanaf het einde geeft we gewicht w_i = 0.5^(i / H). We draaien drie EMA's parallel met H = 8, 26 en 78 trekkingen (kort/middel/lang).
- Korte half-life vangt 'hot streaks' op. Lange half-life voorkomt over-reageren op één rare trekking. De ensemble-blender kiest hoeveel gewicht elke horizon krijgt per loterij — getuneed met 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. PMI co-occurrence boost
Welke nummers samen verschijnen vaker dan het toeval zou voorspellen?
- We berekenen Pointwise Mutual Information: PMI(a,b) = log [P(a,b) / (P(a)·P(b))]. Positieve PMI = paar verschijnt vaker samen dan onafhankelijk verwacht.
- Per kandidaat-nummer sommeren we de PMI met alle nummers van de meest recente trekking. Dat geeft een 'follow-the-pattern' signaal zonder over-fitten: PMI is bounded en wordt geclipt op ±3.
score_PMI(n) = Σ_{m ∈ lastDraw} clip(log[P(n,m) / (P(n)P(m))], ±3)
src/lib/engine-core.ts
6. Markov-overgangskansen
Vanuit de laatste trekking schatten we de transitie-kans naar het volgende nummer.
- We bouwen een N×N transitiematrix T waarin T[a,b] = P(b in trekking_t+1 | a in trekking_t), geschat met Laplace-smoothing.
- Voor de live-score nemen we het gemiddelde van T[a, n] over alle a in de laatste trekking. Cold nummers blijven niet altijd cold; deze stap modelleert eindige geheugen.
src/lib/engine-core.ts
7. Ensemble & getuneede gewichten
De zes signalen worden via gewogen rank-aggregatie samengevoegd.
- Elk signaal wordt naar rank-percentielen [0, 1] omgezet. Daarna lineair gecombineerd met gewichten w = (w_bayes, w_ema1, w_ema2, w_ema3, w_pmi, w_markov). Default uniform; per loterij getuneed via grid-search + ROI-validatie.
- Rank-aggregatie is robuust tegen scale-verschillen tussen signalen en voorkomt dat één outlier-score het ensemble domineert.
score(n) = Σ_s w_s · rank_s(n) / N
src/lib/engine-core.ts · src/lib/engine-tuning.functions.ts
8. Meta-learner (logistic regression, 12 features)
Een per-loterij logreg leert de optimale non-lineaire combinatie van engine-signalen.
- Feature-vector (12 dims): de 6 engine-ranks + interacties (EMA1·PMI, Bayes·Markov, EMA3·Bayes) + recency + hot streak (verschijningen laatste 10) + cold streak (consecutive absences).
- Training: L2-geregulariseerde logreg met SGD over de hele historie. Verlies = log-loss op label '1 als nummer in de volgende trekking voorkwam'. Backward-compatible: oude 9-dim modellen blijven werken.
P(n in volgende trekking) = σ(w · features(n) + b)
src/lib/meta-learner.ts
9. Gradient Boosted Decision Stumps
Tweede meta-learner die non-lineaire interacties opvangt zonder neuraal net.
- Mini-LightGBM clone: itereratief decision stumps op de residuen. Elke stump kiest 1 feature + threshold die de loss maximaal reduceert; we voegen lr · stump.output toe aan de huidige predictie.
- Output is JSON-serializable en wordt naast de logreg gestored in engine_config.meta_weights.gbm. De stacked blender combineert beide.
src/lib/gbm.ts
10. Out-of-fold stacking blender
Logreg + GBM worden gecombineerd zonder data-leakage.
- We splitsen de historie in 5 stratified folds. Per fold trainen we logreg + GBM op 4 folds en voorspellen op de overgebleven fold. Die OOF-predicties worden de inputs voor een tweede-laag logreg-meta die de optimale weging leert.
- Resultaat: kalibreerbare kansen zonder dat de meta-learner zichzelf zijn eigen trainingsdata terug ziet.
src/lib/advanced-stats-v3.ts · src/lib/nested-cv.ts
11. Isotonische & Platt-kalibratie
Ruwe model-kansen worden gemonotoniseerd zodat ze écht kansen zijn.
- Platt scaling fit een 1-parameter sigmoid over de raw scores op validatiedata. Isotonische regressie (Pool-Adjacent-Violators) is een non-parametrische versie die alleen monotonie afdwingt.
- We meten Expected Calibration Error (ECE) op out-of-fold predicties; de calibrator met laagste ECE wordt productie.
src/lib/advanced-stats-v2.ts · src/lib/advanced-stats-v3.ts
12. Anti-clustering ticket-generator
Kandidaat-tickets worden gesampled, slechte structuren afgewezen.
- We trekken nummers proportioneel aan ensemble-score (softmax met temperatuur τ getuneed per loterij). Elke kandidaat wordt gevalideerd:
- • max 3 uit dezelfde decade (anti-clustering),
- • som binnen historische IQR-band (geen alleen-laag of alleen-hoog tickets),
- • pariteits-split binnen empirische distributie,
- • geen rekenkundige reeks van ≥4 opeenvolgende getallen.
- Afgewezen kandidaten worden hergesampled tot we N geldige tickets hebben.
src/lib/engine-core.ts (generateAntiClusterTickets)
13. Greedy submodular portfolio selection
Uit duizenden kandidaten kiezen we K tickets die samen maximaal dekken.
- Probleem: kies K tickets die de som van P(nummer komt) over de geselecteerde tickets maximaliseren, met diminishing returns voor overlap.
- Submodulaire functies hebben een 1 − 1/e ≈ 63% optimaliteits-garantie voor de greedy oplossing. Per iteratie kiezen we het ticket met de grootste marginal gain en discounten gedekte nummers met factor 0.4.
f(S ∪ {t}) − f(S) = Σ_{n ∈ t} remain[n] · (½ als bonus)
src/lib/portfolio-optimizer.ts
14. Markowitz min-variance portfolio
Naast dekking minimaliseren we onderlinge correlatie tussen tickets.
- Correlatie tussen ticket i en j ≈ |overlap| / √(|t_i|·|t_j|). Greedy: kies steeds het ticket met hoogste EV minus λ · Σ corr(huidig, gekozenen).
- Risico-aversie λ is een tuneable parameter (default 1.2). Hogere λ = meer diversificatie, lagere verwachte score per ticket maar lagere variantie van de totale portfolio-uitbetaling.
util(t) = EV(t) − λ · Σ_{j ∈ picked} overlap(t, j)
src/lib/markowitz.ts
15. Shared-jackpot popularity correctie
Een hit op een 'populaire' combinatie wordt met meer winnaars gedeeld.
- popularityScore() telt: % nummers ≤ 31 (verjaardagen), % in onderste helft, opeenvolgende reeksen ≥3, rekenkundige progressies, allemaal-zelfde-eindcijfer, kruisjes/diagonalen op de speelkaart.
- Op basis daarvan schatten we het aantal mede-winnaars en passen we adjustJackpotForSharing() toe: de EV in de gating-stap gebruikt de verwachte uitbetaling ná deling, niet de bruto jackpot.
src/lib/popularity.ts
16. Jackpot-aware EV-gating
Adviseer 'spelen' alleen als de verwachte waarde positief is.
- EV = Σ_tier P(tier) · payout(tier) − ticketCost. P(tier) is exact uit combinatorische kansen (binomial coefficients per loterij-formule). Payout(tier) komt uit PRIZE_TIERS — voor de jackpot-klasse vervangen door de huidige jackpot (na popularity-deling).
- We berekenen ook de break-even jackpot: het bedrag waarvoor EV = 0. Onder dat bedrag toont de UI '⏸ overslaan'.
- Kelly-criterion bepaalt optionele bet-sizing voor abonnees die meerdere weken plannen.
EV = Σ_t P(t) · payout(t) − cost | break-even = (cost − Σ_{t≠jp} P(t)·payout(t)) / P(jp)
src/lib/ev-gating.ts
17. Regime-detectie (CUSUM + 2-state HMM + particle filter)
Detecteren of de generator van trekkingen zelf is verschoven.
- CUSUM houdt per nummer een cumulatieve afwijking van de verwachte frequentie bij en alarmeert bij overschrijding van de drempel h.
- Een 2-state HMM (hot/cold) wordt met Baum-Welch op de frequentie-tijdreeks getraind; transities worden Viterbi-gedecodeerd.
- Een Sequential Monte-Carlo particle filter onderhoudt 200 deeltjes over de verborgen regime-state en geeft een soft probability per draw.
- Bij regime-shift krijgt de korte-EMA tijdelijk meer gewicht (online adaptatie).
src/lib/advanced-stats-v2.ts · src/lib/advanced-stats-v3.ts
18. Niveau-3 statistische uitbreidingen
Conformal prediction, Negative-Binomial gap-model, copulas, Shapley, en meer.
- • Split-conformal prediction → bandbreedte op meta-probs met gegarandeerde coverage.
- • Dirichlet-Multinomial posterior over de volledige trekking (niet alleen per nummer).
- • Negative-Binomial gap-model voor over-dispersie tussen verschijningen.
- • Gaussian-copula voor pair-dependencies die PMI mist.
- • Permutatie-test per signaal (label-shuffle) → p-waarde voor 'beter dan random'.
- • SHAP-attributie per ticket (lineaire benadering) → uitleg waarom dit ticket gekozen is.
- • CVaR / Expected Shortfall over portfolio-uitbetalingen.
- • Anderson-Darling goodness-of-fit op uniformiteit.
src/lib/advanced-stats-v2.ts · src/lib/advanced-stats-v3.ts
19. Auto-tuner: nested CV + grid-search
Wekelijkse pg_cron herziet engine-gewichten en meta-modellen per loterij.
- Outer 5-fold stratified CV → out-of-sample ROI-schatting. Inner CV → preset selectie.
- Grid-search over 6-dim weight-simplex (BMA-weighted) + half-life presets (HALF_LIFE_GRID). Voor elke configuratie: simuleer K tickets per fold, scoor met prize-tiers, aggregeer ROI met 95% bootstrap CI.
- Een nieuwe preset wordt alleen geaccepteerd als CI-low > baseline-ROI (geen geluksshot).
- Resultaten gaan in engine_config (DB) en zijn meteen live voor alle abonnees.
src/lib/engine-tuning.functions.ts · src/routes/api/public/hooks/auto-tune.ts
20. Thompson-sampling bandit (online preset-selectie)
Tussen auto-tunes door schakelt een bandit live tussen presets op basis van actuele ROI.
- Per preset houden we een Beta(α, β) posterior: α = winnen (ROI > 0), β = verliezen. Bij elke productie-pick samplen we uit elke Beta en kiezen de hoogste sample.
- Updates komen uit de tracker: zodra een trekking afgelopen is, krijgen de gekozen presets hun α of β +1.
- Resultaat: zelfs zonder volledige her-tuning convergeert het systeem online naar de beste preset.
pick = argmax_p Beta(α_p, β_p).sample()
src/lib/bandit.ts
21. Walk-forward backtest
Iedere strategie wordt getoetst zonder toekomstkennis (geen leakage).
- Vanaf MIN_HISTORY itereren we trekking voor trekking. Per trekking bouwen we het model op uit ENKEL trekkingen daarvoor, genereren N tickets, en scoren tegen de werkelijke trekking.
- We aggregeren hits per tier, gemiddelde ROI, cumulatieve P&L én een random-baseline ter controle. Een strategie 'wint' alleen als het CI-band boven random ligt.
src/lib/backtest.functions.ts
22. Wheeling-systemen (covering designs)
Wiskundige garantie: bij M juiste nummers in de pool altijd een Y-uit-X win.
- We implementeren full wheels, key wheels en abbreviated covering designs. Voor elke (pool-size, ticket-size, garantie) kiezen we de combinatorisch optimale set.
- De wheel-generator combineert met de portfolio-optimizer: kandidaten worden eerst gewheel'd, daarna submodulair gekozen.
src/lib/wheel.functions.ts
Eerlijk gezegd
Loterijen zijn en blijven kansspelen. Geen enkele analyse kan een trekking voorspellen. Wat LotteryCortex wel doet: je helpen betere beslissingen te nemen over welke combinaties je speelt, wanneer je meer inzet en wanneer je een week overslaat.
Loterij-trekkingen zijn ontworpen om willekeurig te zijn. Geen enkel algoritme — hier of elders — kan de uitkomst voorspellen. Wat we wél meetbaar doen: betere nummer-spreiding kiezen, populaire combinaties vermijden (zodat je minder hoeft te delen bij een hit), en eerlijk advies geven om over te slaan wanneer de verwachte waarde negatief is. De auto-tuner valideert wekelijks dat onze ROI-curve significant boven random ligt — als dat ooit niet meer zo is, lees je dat hier eerst.
Waarom LotteryCortex?
De meeste loterij-tools zijn 10+ jaar oud, alleen Engelstalig en desktop-first. LotteryCortex is modern, mobile-first en EU-gericht — met een AI-motor die continu leert van nieuwe trekkingen.