Analytique de paris · NHL · Web et iOS · Projet solo

ARAKICE

arakice.com

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.

React 18 TypeScript Node.js · Express PostgreSQL Python · FastAPI XGBoost Socket.io Stripe Capacitor · Swift
58,6 %
Précision du modèle

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.

Ce qui a été construit

Modèle de score XGBoost

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.

Moteur de règles explicable

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.

Détection d'écarts de valeur

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é.

Prédictions en direct

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.

Modèle joueur

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.

Meilleurs paris et combinés du jour

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.

Coupons et règlement automatique

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.

Chat temps réel et modération

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.

Partage d'un coupon en image

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é.

Classement et profils publics

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.

Assistant IA encadré

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.

Abonnement Pro, deux rails de paiement

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.

SEO programmatique

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.

Fabrique de contenu

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.

Application iOS

Le même produit, natif sur iPhone

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.

Session dans le Keychain

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.

Notifications push (APNs)

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.

Widget d'écran d'accueil

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.

Liens universels

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.

Un code, deux cibles

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.

CI macOS, sans Mac

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.

Architecture

Trois services, une base de données

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.

Sources
NHL · Odds · MoneyPuck
API Node.js
Express · Railway
Service ML
FastAPI · XGBoost
PostgreSQL
Neon · migrations
Client React
Vite · Vercel
App iOS
Capacitor · SwiftUI
Socket.io + Redis — chat et présence Cloudflare R2 — avatars Stripe et StoreKit 2 — abonnements APNs — notifications iOS Sentry — erreurs et santé
Pipeline de données

Du match brut au modèle validé

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.

1

Collecte

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.

2

Construction des features

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.

3

Entraînement

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.

4

Validation

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.

Sécurité et exploitation

Ce qui tient l'application debout

Sessions — jeton d'accès court en mémoire, jeton de rafraîchissement en cookie httpOnly avec rotation atomique et période de grâce.
Connexion Google — refus par défaut si l'adresse n'est pas vérifiée ; le jeton ne transite jamais par l'URL visible.
Validation — schémas stricts sur chaque entrée, y compris le chemin WebSocket qui ne passe pas par les middlewares HTTP.
Limitation de débit et délais maximaux sur toutes les routes coûteuses et tous les appels externes.
Chiffrement des données sensibles, vérifié par une sonde au démarrage, et procédure de rotation des secrets documentée.
Migrations SQL numérotées — quarante-cinq à ce jour, appliquées une par une, jamais de schéma modifié à la main.
Bilingue FR / EN sur toute l'interface, les conditions et la politique de confidentialité.
Vie privée — publication des coupons en retrait volontaire plutôt qu'en adhésion forcée, conformément à la Loi 25.
Suppression du compte depuis l'app, comme l'exige Apple, et exercices de restauration de la base et de retour arrière des déploiements.
Aperçu
Web
ArakIce — accueil
Accueil — matchs du jour
ArakIce — prédiction
Page de prédiction d'un match
ArakIce — coupon
Coupon et probabilité combinée
ArakIce — chat
Salon de discussion
ArakIce — podium
Podium des joueurs
iPhone
ArakIce — widget iOS
Widget — paris en cours
ArakIce — notification
Notification « pari réglé »
ArakIce — match en direct
Match en direct
ArakIce — combinés du jour
Combinés du jour
Stack technique
React 18 · ViteClient mobile-first
TypeScriptStrict, client et serveur
Tailwind CSSThème patinoire maison
Node.js · ExpressAPI REST
PostgreSQL · NeonBase de données gérée
Socket.ioChat et présence
Python · FastAPIService d'inférence
XGBoost · pandasEntraînement et backtest
Stripe · StoreKit 2Abonnements web et iOS
Vercel · RailwayHébergement
Cloudflare R2Stockage des médias
The Odds APICotes des bookmakers
Capacitor 8Coque iOS
Swift · SwiftUIWidget et plugins natifs
RedisSocket.io multi-instance
GitHub ActionsBuilds iOS signés