# 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_scraper` est 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_saas` doit fonctionner de manière autonome, avec ses propres utilisateurs, subscriptions et données.
- La table `ml_predictions_cache` est *pré-peuplée* dans `turf_saas.db` par un processus de synchronisation (scheduler ou copie périodique depuis `turf.db`).
- Le feedback loop (`ml_feedback_saas.py`) écrit dans `paris` de `turf_saas.db` uniquement.

---

## 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 :
1. Lit les prédictions XGBoost dans `ml_predictions_cache` de `turf_saas.db`
2. Génère des paris virtuels selon 4 stratégies XGBoost
3. Insère les paris dans la table `paris` de `turf_saas.db`
4. 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

```python
# 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

```bash
# 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 :

```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** :
```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 max
- `premium` : complet
- `pro` : complet + historique illimité

### 4.2 Blueprint `api_v1/routes/roi.py`

Enregistré dans `api_v1/__init__.py` avec :
```python
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.py` en 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 :

```json
{
  "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 :
```python
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 :
```javascript
fetch('/api/v1/roi/by-model?days=30')
```

---

## 8. Points d'attention & limites

1. **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.

2. **Pas de paris XGBoost actifs** : la table `paris` contient des paris `manual`, `scoring_v2`, `canalturf` mais pas encore de paris `xgboost_*`. HRT-93 (ml_feedback_saas.py) doit être complété et exécuté.

3. **Modèle unique** : `model_version = 'xgboost_v1'`. L'évolution vers des versions de modèle multiples est prévue dans la roadmap.

4. **Sync turf_scraper → turf_saas** : le mécanisme de synchronisation de `ml_predictions_cache` n'est pas encore documenté formellement. À documenter dans une prochaine Note Intelligence.

5. **update_paris_results.py** : script de mise à jour des statuts paris (`EN_ATTENTE → GAGNE/PERDU`) à partir de `pmu_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 |
