Application de prédictions NHL, sur le web et sur iPhone. Un modèle de score XGBoost prédit les buts de chaque équipe, d'où la probabilité de plus de 40 marchés, cohérents entre eux et recalculés pendant le match. Ces probabilités sont comparées aux cotes pour isoler les écarts exploitables, et une communauté suit ses coupons en temps réel. Conçue, développée et déployée seule — de la collecte de données jusqu'à l'app iOS.
vs 54,0 % pour la baseline « le domicile gagne » — backtest walk-forward sur 9 saisons (9 823 matchs).
Projet personnel mené seule, de bout en bout. Product, design, frontend, backend, base de données, pipeline de données et modèles ML, application iOS, déploiement, facturation et SEO. En production sur arakice.com. L'app iOS est terminée et tourne sur iPhone via TestFlight, mais Apple réserve les apps de paris simulés aux comptes d'entreprise : sa publication attend, et le web est commercialisé d'abord.
Le modèle ne prédit pas « qui gagne » : il prédit combien de buts chaque équipe devrait marquer. De ces deux nombres découle la matrice de tous les scores possibles, donc la probabilité de chaque marché — victoire, puck line, totaux, score exact — sans contradiction entre eux. Les cotes n'y entrent jamais : elles servent d'étalon, pas d'entrée.
Log5, Poisson et Skellam pour plus de 40 marchés — vainqueur, handicaps, totaux, props joueur et gardien. Chaque famille de pari a ses propres facteurs, dont l'utilisateur ajuste les poids ; décocher un facteur redistribue son poids au prorata.
L'écart se calcule contre une cote débarrassée de la marge du bookmaker (DraftKings et BET99, collectées en parallèle), puis se filtre dans la seule bande dont le backtest valide le rendement. Au-delà, l'écart trahit une information manquante plutôt qu'une occasion — il est écarté.
Pendant le match, chaque probabilité est recalculée à partir de l'avant-match, du score et du temps restant, avec des effets de score mesurés sur de vrais matchs. Règle tenue : rien n'est activé sans gain mesuré au rejeu. L'effet de l'avantage numérique, calibré sur 9 673 matchs puis rejoué sur 3 890 autres, n'apportait rien — il n'est donc pas en service.
Un modèle par statistique — buts, passes, points, tirs, mises en échec, tirs bloqués, minutes de pénalité, points en avantage numérique — avec une dispersion ajustée à chacune. L'historique contre l'adversaire du soir n'est gardé que là où il tient en validation et en test : les mises en échec et les pénalités, auxquelles s'ajoute l'effet des arbitres du match.
Chaque jour, les sélections dont l'écart au marché tombe dans la bande validée sont classées, et des combinés sont assemblés en écartant les jambes incompatibles entre elles. Leur règlement, jambe par jambe, alimente un suivi interne du rendement réel.
Construction de combinés avec probabilité recalculée à chaque ajout, sauvegarde locale, puis règlement automatique dès qu'un match devient final — un job événementiel qui n'interroge l'API que lorsqu'un coupon attend. Le résultat est conservé jambe par jambe.
Salons publics et privés en WebSocket : réponses, réactions, suppression de ses propres messages, reprise propre du socket après une coupure ou un retour au premier plan. Côté modération : filtre de langage, blocage en direct et file d'attente d'administration.
Rendu canvas 1080 × 1080 aux couleurs de l'app, densité adaptée au nombre de sélections, partageable en salon ou vers l'extérieur — sur iPhone, par la feuille de partage native. L'instantané est construit par le serveur à partir du seul identifiant du coupon, puis figé.
Podium par expérience, recherche de joueur et consultation des coupons des autres — encadrée par trois garde-fous indépendants : le consentement de l'auteur, une date de coupure et le masquage des sélections jusqu'à la mise au jeu.
Le proxy LLM n'accepte qu'un type de tâche défini côté serveur, jamais une consigne libre venue du client — la surface d'abus et le coût restent bornés. Deux usages : une analyse rédigée pour chaque pari, et un coupon construit à partir d'une phrase (« Suzuki 3+ tirs »). Quota mensuel compté et affiché dans les réglages.
Stripe sur le web, achat intégré StoreKit 2 sur iOS, et un seul droit d'accès côté serveur : un abonné web est Pro dans l'app, et inversement. Les transactions Apple sont vérifiées sans dépendance externe — signature JWS, chaîne de certificats épinglée sur la racine d'Apple — puis dédupliquées et appliquées dans l'ordre.
Plusieurs centaines de pages statiques générées à partir des données — dont 129 affiches tête-à-tête et une page par match, régénérée chaque jour — avec sitemap, redirections permanentes pour les URLs retirées et suivi dans Search Console.
Chaîne de génération vidéo verticale pour les réseaux : quatre récapitulatifs d'après-match par soirée, construits à partir des données du jour, sans appel à un modèle de langage — donc à coût nul et reproductible.
Le client React est enveloppé avec Capacitor, et ce qu'un site ne peut pas faire est écrit en natif. Sans Mac : chaque build est compilé, signé et envoyé sur TestFlight par une CI macOS dans le cloud. L'app est complète et testée sur iPhone ; sa sortie sur l'App Store attend un compte développeur d'entreprise, seul type de compte qu'Apple accepte pour cette catégorie.
Dans le WebView, le cookie de rafraîchissement aurait été bloqué — déconnexion toutes les 15 minutes. Sur iOS, le jeton passe donc dans le corps de la réponse et vit dans le Keychain ; le web garde son cookie httpOnly, inchangé. La connexion Google, refusée par Google dans un WebView, est retirée du build iOS.
Un pari réglé déclenche une notification dans la langue de l'utilisateur ; la toucher ouvre le pari. L'envoi passe en HTTP/2 avec un jeton ES256, écrit sans bibliothèque. Les alertes de value bets sont réservées aux abonnés Pro.
SwiftUI et WidgetKit, en trois tailles : les paris en cours avec leur probabilité en direct. L'app confie au widget un jeton en lecture seule par un App Group, et le widget interroge une route qui ne sert que lui.
Un lien arakice.com — page d'équipe, coupons d'un joueur, calendrier — ouvre l'écran correspondant dans l'app plutôt que Safari. Les pages SEO, elles, restent sur le web.
Le build iOS échoue si un lien de bookmaker se glisse dans le bundle, et adapte les textes aux règles d'Apple. Le web a été gelé à une étiquette avant le début du travail iOS : depuis, chaque changement est partagé et testé des deux côtés.
GitHub Actions sur un runner macOS : certificat et profils de l'app et du widget décodés depuis des secrets, build signé, envoi sur TestFlight. Le premier build a été téléversé le 9 octobre et tournait sur un iPhone le lendemain.
Le service Python est volontairement séparé de l'API Node : il se déploie, redémarre et se réentraîne sans toucher au reste, et si jamais il tombe, le moteur de règles continue de servir des probabilités. Le même principe vaut partout — chaque appel externe a un repli.
La partie la plus délicate n'est pas l'entraînement, c'est de garantir qu'aucune information postérieure au match ne se glisse dans les features. Tout est calculé à la veille du match, et le découpage train/test est chronologique — jamais aléatoire.
Calendrier officiel des matchs NHL depuis 2008, statistiques avancées MoneyPuck (buts attendus, Corsi, occasions dangereuses, gardiens et patineurs match par match), cotes historiques et journal de cotes en direct capturé par un job maison.
Moyennes exponentielles à deux horizons, ELO avec avantage de la glace et régression entre saisons, forme du gardien partant, puissance offensive et absences pondérées par le rôle réel du joueur, repos et matchs rapprochés.
XGBoost en régression de Poisson, une ligne par équipe et par match, arrêt anticipé sur un jeu de validation distinct. Par-dessus : une correction pour les filets déserts, une couche qui recale la probabilité de victoire, et un recalage du niveau de buts recalculé chaque lundi pendant la saison.
Backtest walk-forward sur neuf saisons cotées, avec des baselines honnêtes — le domicile, l'ELO seul, le marché — et un rendement simulé par seuil d'écart. C'est ce qui a fixé la bande de valeur retenue en production. Même méthode pour chercher des patterns cachés : 3 517 conditions (calendrier, repos, équipe contre équipe) testées contre la chaîne servie — aucune ne survit à la correction de Benjamini-Hochberg.