- Création POD/Intelligence/ML_Predictions_SaaS.md : architecture ML complète, flow ml_predictions_cache → ml_feedback_saas → paris → ROI dashboard, schéma données/jointures, décision duplication vs modification turf_scraper, documentation des 4 stratégies XGBoost, idempotence, usage CLI - Mise à jour DOCUMENTATION.md : ajout section Turf SaaS API v1 complète avec tous les endpoints documentés dont /api/v1/roi/* et /api/v1/ml/feedback/* (HRT-92 ROI backend + HRT-93 ML feedback loop) Co-Authored-By: Paperclip <noreply@paperclip.ing>
11 KiB
Note Intelligence — Système ML Prédictions dans turf_saas
Date de création : 2026-04-30
Auteur : IngenieurDev (H3R7Tech)
Ticket de référence : HRT-96 (sprint ML SaaS — HRT-90)
Scope : /home/h3r7/turf_saas/ — AUCUNE modification de /home/h3r7/turf_scraper/
1. Contexte & Décision architecturale
1.1 Deux systèmes, deux DB
H3R7Tech exploite deux dépôts séparés :
| Dépôt | Rôle | Base de données |
|---|---|---|
/home/h3r7/turf_scraper/ |
Scraping PMU + entraînement XGBoost | turf.db |
/home/h3r7/turf_saas/ |
SaaS utilisateurs + API v1 + dashboard | turf_saas.db |
1.2 Décision de duplication (vs modification turf_scraper)
Choix : dupliquer les tables et scripts ML dans turf_saas.db, sans toucher à turf_scraper.
Justification :
turf_scraperest la source de vérité du scraping PMU et des modèles XGBoost — toute modification risque de casser la chaîne de collecte de données.turf_saasdoit fonctionner de manière autonome, avec ses propres utilisateurs, subscriptions et données.- La table
ml_predictions_cacheest pré-peuplée dansturf_saas.dbpar un processus de synchronisation (scheduler ou copie périodique depuisturf.db). - Le feedback loop (
ml_feedback_saas.py) écrit dansparisdeturf_saas.dbuniquement.
2. Architecture du système ML dans turf_saas
2.1 Vue d'ensemble du flow
[turf_scraper/turf.db]
└── ml_predictions_cache (XGBoost v1)
│
│ [sync périodique / scheduler]
▼
[turf_saas/turf_saas.db]
├── ml_predictions_cache ← prédictions XGBoost importées
├── pmu_partants ← données courses PMU
├── pmu_rapports ← dividendes réels PMU
├── paris ← paris virtuels ML (ml_feedback_saas.py)
│
└── API v1 ──┬── GET /api/v1/predictions/* (lecture ml_predictions_cache)
├── GET /api/v1/roi/by-model (jointure paris + rapports)
├── POST /api/v1/ml/feedback/run (déclenche ml_feedback_saas)
└── GET /api/v1/ml/feedback/stats (stats par stratégie)
│
▼
[dashboard_saas.html]
Section "Performance & ROI"
Chart.js — ROI par modèle / évolution
2.2 Table ml_predictions_cache (turf_saas.db)
Table centrale du système ML. Contient les prédictions XGBoost pour chaque cheval/course.
| Colonne | Type | Description |
|---|---|---|
date |
TEXT | Date de la course (YYYY-MM-DD) |
num_reunion |
INTEGER | Numéro de réunion |
num_course |
INTEGER | Numéro de course |
horse_name |
TEXT | Nom du cheval |
horse_number |
INTEGER | Numéro du cheval |
odds |
REAL | Cote au moment de la prédiction |
prob_top1 |
REAL | Probabilité XGBoost de finir 1er |
prob_top3 |
REAL | Probabilité XGBoost de finir top 3 |
ml_score |
REAL | Score ML composite (0–100) |
recommendation |
TEXT | top1 / top3 / value_bet |
is_value_bet |
INTEGER | 1 si value bet détecté |
is_outlier |
INTEGER | 1 si outlier de cote |
race_label |
TEXT | Ex: R1C3 |
model_version |
TEXT | Version du modèle (ex: xgboost_v1) |
risque_label |
TEXT | Niveau de risque (low/neutral/high) |
risque_score |
INTEGER | Score risque (0–100) |
Contrainte d'unicité : (date, num_reunion, num_course, horse_name) — garantit l'idempotence des imports.
Volume actuel : ~1 000 entrées (2 dates de courses).
3. Feedback Loop ML — ml_feedback_saas.py
3.1 Rôle
Script Python autonome qui :
- Lit les prédictions XGBoost dans
ml_predictions_cachedeturf_saas.db - Génère des paris virtuels selon 4 stratégies XGBoost
- Insère les paris dans la table
parisdeturf_saas.db - Est idempotent : ne duplique pas les paris existants
3.2 Stratégies supportées
| Stratégie | Type pari | Condition sélection | Mise |
|---|---|---|---|
xgboost_sg |
simple_gagnant |
top 1 ML par course, ml_score >= 70 |
1€ |
xgboost_value |
simple_gagnant |
is_value_bet = 1 |
1€ |
xgboost_sp |
simple_place |
top 1 ML par course, ml_score >= 50 |
1€ |
xgboost_2sur4 |
deux_sur_quatre |
top 4 ML par course, 6 combos générés | 6€ (1€/combo) |
3.3 Schéma d'idempotence
# Vérifie avant insertion
SELECT id FROM paris
WHERE date_course = ?
AND source_reco = ? # ex: 'xgboost_sg'
AND type_pari = ?
AND numero1 = ?
AND race_label = ?
Si le pari existe déjà → skip (aucune duplication).
3.4 Table paris — colonnes clés pour le ML
| Colonne | Valeur ML |
|---|---|
source_reco |
xgboost_sg / xgboost_value / xgboost_sp / xgboost_2sur4 |
model_source |
xgboost_v1 (héritée de ml_predictions_cache) |
type_pari |
simple_gagnant / simple_place / deux_sur_quatre |
statut |
EN_ATTENTE → GAGNE / PERDU (mise à jour par update_paris_results.py) |
gain |
Dividende réel × mise (depuis pmu_rapports) |
3.5 Usage CLI
# Traitement du jour
python3 ml_feedback_saas.py
# Date spécifique
python3 ml_feedback_saas.py --date 2026-04-29
# Backfill
python3 ml_feedback_saas.py --backfill 2026-04-20
Différence avec turf_scraper/ml_feedback.py :
DB_PATH=/home/h3r7/turf_saas/turf_saas.db(PAS/home/h3r7/turf_scraper/turf.db)- Logs dans
/home/h3r7/turf_saas/logs/ - AUCUNE référence à
turf_scraper
4. API ROI — /api/v1/roi/*
4.1 Route principale
GET /api/v1/roi/by-model — Calcul du ROI par modèle/stratégie
Jointures SQL :
-- paris ←→ pmu_partants (via race_label + date + numero)
-- paris ←→ pmu_rapports (dividendes réels)
SELECT
p.source_reco AS model_source,
COUNT(p.id) AS nb_paris,
SUM(p.mise) AS mise_totale,
SUM(p.gain) AS gain_total,
(SUM(p.gain) - SUM(p.mise)) / SUM(p.mise) * 100 AS roi_pct,
COUNT(CASE WHEN p.statut='GAGNE' THEN 1 END) * 100.0 / COUNT(p.id) AS win_rate
FROM paris p
WHERE p.date_course BETWEEN :start AND :end
AND (:strategy IS NULL OR p.source_reco = :strategy)
GROUP BY p.source_reco
Paramètres query :
?strategy=xgboost_sg— filtrer par stratégie (optionnel)?days=30— fenêtre temporelle en jours (défaut : 30, max : 365)
Réponse JSON :
{
"period": {"start": "2026-04-01", "end": "2026-04-30", "days": 30},
"models": [
{
"model_source": "xgboost_sg",
"nb_paris": 42,
"mise": 42.0,
"gain": 51.3,
"roi_pct": 22.1,
"win_rate": 28.6
}
]
}
Accès plan :
free: 1 stratégie maxpremium: completpro: complet + historique illimité
4.2 Blueprint api_v1/routes/roi.py
Enregistré dans api_v1/__init__.py avec :
from .routes.roi import roi_bp
app.register_blueprint(roi_bp)
5. API ML Feedback — /api/v1/ml/feedback/*
5.1 Routes
| Méthode | Path | Auth | Description |
|---|---|---|---|
POST |
/api/v1/ml/feedback/run |
Admin | Déclenche ml_feedback_saas.py manuellement |
GET |
/api/v1/ml/feedback/stats |
Premium+ | Stats paris par stratégie XGBoost |
5.2 POST /api/v1/ml/feedback/run
- Réservé aux admins (token admin requis)
- Déclenche le script
ml_feedback_saas.pyen subprocess - Corps optionnel :
{"date": "2026-04-29"}ou{"backfill": "2026-04-20"}
5.3 GET /api/v1/ml/feedback/stats
Retourne les statistiques agrégées par stratégie :
{
"stats": [
{
"source_reco": "xgboost_sg",
"nb_paris": 42,
"nb_gagnes": 12,
"win_rate_pct": 28.6,
"mise_totale": 42.0,
"gain_total": 51.3,
"roi_pct": 22.1
}
],
"last_run": "2026-04-29T18:30:00"
}
5.4 Blueprint api_v1/routes/ml_feedback.py
Enregistré dans api_v1/__init__.py avec :
from .routes.ml_feedback import ml_feedback_bp
app.register_blueprint(ml_feedback_bp)
6. Jointures de données — Schéma complet
ml_predictions_cache
date, num_reunion, num_course, horse_name, horse_number
ml_score, recommendation, is_value_bet
race_label, model_version
│
│ [ml_feedback_saas.py]
▼
paris
date_course, race_label, numero1
source_reco (= stratégie XGBoost)
model_source (= xgboost_v1)
type_pari, mise, statut, gain
│
├──── JOIN pmu_partants ──── date_programme + num_reunion + num_course + num_pmu
│ ordre_arrivee (résultat réel)
│
└──── JOIN pmu_rapports ──── date_programme + num_reunion + num_course + type_pari
dividende_euro (gain réel calculé)
7. Dashboard SaaS — Section ROI
Le dashboard dashboard_saas.html intègre une section "Performance & ROI" (implémentée dans HRT-94) :
- Graphique ROI par
model_source(histogramme Chart.js) - Évolution ROI dans le temps (line chart, 7j/30j/90j)
- Tableau :
model_source | nb paris | mise | gain | ROI% | win_rate% - Filtre dropdown par stratégie
- Gating plan : Free = 1 stratégie, Premium/Pro = complet
Appel API dashboard :
fetch('/api/v1/roi/by-model?days=30')
8. Points d'attention & limites
-
Données ML limitées : actuellement 1 000 prédictions sur 2 dates (2026-04-24 et 2026-04-25). La pertinence du ROI augmentera avec le volume de données.
-
Pas de paris XGBoost actifs : la table
pariscontient des parismanual,scoring_v2,canalturfmais pas encore de parisxgboost_*. HRT-93 (ml_feedback_saas.py) doit être complété et exécuté. -
Modèle unique :
model_version = 'xgboost_v1'. L'évolution vers des versions de modèle multiples est prévue dans la roadmap. -
Sync turf_scraper → turf_saas : le mécanisme de synchronisation de
ml_predictions_cachen'est pas encore documenté formellement. À documenter dans une prochaine Note Intelligence. -
update_paris_results.py : script de mise à jour des statuts paris (
EN_ATTENTE → GAGNE/PERDU) à partir depmu_rapports— dépendance critique pour le calcul du ROI réel.
9. Fichiers clés
| Fichier | Rôle |
|---|---|
turf_saas.db |
Base de données principale SaaS |
ml_feedback_saas.py |
Feedback loop ML (à créer — HRT-93) |
api_v1/routes/roi.py |
Routes API ROI (à créer — HRT-92) |
api_v1/routes/ml_feedback.py |
Routes API feedback (à créer — HRT-93) |
api_v1/__init__.py |
Enregistrement des blueprints |
dashboard_saas.html |
Dashboard SaaS avec section ROI |
update_paris_results.py |
MAJ statuts paris depuis résultats PMU |
scoring_v2.py |
Scoring engine (stratégie scoring_v2) |
10. Références tickets
| Ticket | Description | Statut |
|---|---|---|
| HRT-90 | Orchestration ML SaaS (parent) | blocked |
| HRT-92 | Backend: API ROI par modèle | in_progress |
| HRT-93 | ML feedback loop ml_feedback_saas | in_progress |
| HRT-94 | Frontend: Dashboard ROI | in_progress |
| HRT-95 | QA: Tests end-to-end ML + ROI | in_progress |
| HRT-96 | Note Intelligence ML + documentation (ce ticket) | in_progress |