Qu'est-ce que la pool phase
La pool phase est une phase dédiée du cycle de vie d'un event, qui s'intercale entre la fermeture du placement de nouveaux paris et la résolution finale. Concrètement, c'est une fenêtre de quelques minutes juste avant la fermeture, pendant laquelle :
- Plus aucun nouveau pari ne peut être placé.
- Plus aucune annulation côté user n'est possible (verrou temporaire).
- Le système rassemble tous les résidus non matchés et calcule un pool mutualisé.
Elle commence à un timestamp poolPhaseStartsAt = closesAt − Δ, où Δ dépend de la durée de l'event. Tu vois ce timestamp dans le détail de l'event sous « Phase pool commence à ».
Combien de temps dure la pool phase
Δ est calculé automatiquement selon la durée totale de l'event :
- Event < 1h : Δ = max(5min, 10% de la durée). Exemple : event de 30min → Δ = 5min.
- Event entre 1h et 24h : Δ = 15min fixes.
- Event entre 1 et 7 jours : Δ = 30min.
- Event > 7 jours : Δ = 60min.
L'admin de l'event peut surcharger cette valeur (entre 5 et 120 minutes) à la création, par exemple pour un event sensible qui nécessite plus de temps de calcul.
Ce qui rentre dans le pool
Au démarrage de la pool phase, le système liste tous les bets dont une partie n'a pas trouvé d'adversaire en phase 1 (matching live). Ces résidus tombent dans deux camps :
- Camp A (par exemple « Argentine gagne ») : somme des montants résiduels = masse A.
- Camp B (« France gagne ») : somme des montants résiduels = masse B.
Une cote pool indicative est calculée pour chaque camp : cote camp X = (masse A + masse B) / masse X, ajustée pour la commission. Plus une masse est faible relativement à l'autre, plus la cote de ce camp est élevée.
Le calcul itératif (et pourquoi)
Chaque participant a une préférence personnelle de cote minimale pool (entre 1.00 et 5.00, par pas de 0.10, défaut 1.20). Si la cote calculée pour son camp est inférieure à son seuil, il refuse le pool et doit être remboursé.
Mais retirer un participant change les masses, donc change les cotes, donc peut faire passer un autre participant sous son seuil. D'où le calcul itératif :
- Calcul des cotes A et B avec tous les résidus.
- Identifier les participants sous leur seuil.
- S'il y en a, refunder le plus exigeant (celui dont le seuil est le plus haut). En cas d'égalité, FIFO (le plus ancien d'abord).
- Re-calcul des cotes avec les participants restants.
- Répéter jusqu'à convergence (tout le monde au-dessus de son seuil) ou pool dégénéré (un camp entièrement vide).
Quand on converge, tous les restants rentrent en pool à la cote calculée finale. Les refundés reçoivent 100% de leur résidu sans aucune commission.
Atomicité et garanties
Tout le calcul est exécuté en mémoire, puis appliqué en une seule transaction atomique :
- Mise à jour des statuts des bets (POOLED pour les acceptés, REFUNDED pour les autres).
- Création des entrées MutualPool + PoolParticipation.
- Crédit des balances utilisateur pour les refunds.
- Insertion des transactions BET_REFUNDED.
- Audit complet de chaque itération (debug + support).
- Transition de l'event vers le statut CLOSED.
Si le moindre élément échoue, tout est annulé et l'event reste en POOL_PHASE ; le système réessaie (jusqu'à 3 fois avec backoff). Garantie : aucun drift financier possible, pas un centime ne peut se perdre ou se créer.
Le cas du pool dégénéré
Si au démarrage de la pool phase une des deux masses est à zéro (par exemple personne sur le camp opposé au tien parmi les résidus), le pool est dit dégénéré. Aucune cote ne peut être calculée. Dans ce cas :
- Tous les résidus sont remboursés à 100%, sans commission.
- Tu reçois une notification « Ton pari a été remboursé (aucune contrepartie en pool) ».
- L'event passe directement à CLOSED.
Aussi : si la pool phase démarre et qu'il y a zéro résidu total (tout a matché en phase 1), la pool phase est skip et l'event passe directement à CLOSED.
Les paris hybrides (partiellement matchés P2P)
Un bet peut être partiellement matché en phase 1 et avoir un résidu qui rentre en pool phase. Exemple :
- Tu mises 100K. La cascade matche 70K en P2P pendant l'event.
- 30K restent en résidu à l'entrée de la pool phase.
- Le pool accepte ton résidu à la cote 1.40 → ton bet est désormais « 70K matchés en P2P + 30K en pool à 1.40 ».
- Si tu gagnes, tu touches : les gains P2P (70K × cote P2P) + les gains pool (30K × 1.40), moins les commissions correspondantes.
Le pool refuse ton résidu ? Tu gardes les 70K P2P matchés, et tu récupères les 30K refundés. Tu vois dans le détail du pari les deux composantes clairement.