Merge branch 'feature/HRT-96-note-intelligence-ml'
This commit is contained in:
281
DOCUMENTATION.md
281
DOCUMENTATION.md
@@ -155,3 +155,284 @@ python app.py
|
|||||||
---
|
---
|
||||||
|
|
||||||
*Document généré automatiquement - Dépenses Trello*
|
*Document généré automatiquement - Dépenses Trello*
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# Turf SaaS — Documentation API v1
|
||||||
|
|
||||||
|
**Mise à jour** : 2026-04-30 (HRT-96 — ML Predictions + ROI + Feedback)
|
||||||
|
**URL SaaS** : https://turf-saas-kolifee.duckdns.org
|
||||||
|
**Port local** : 8792
|
||||||
|
**Base de données** : `/home/h3r7/turf_saas/turf_saas.db`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Stack Technique Turf SaaS
|
||||||
|
|
||||||
|
| Composant | Technologie |
|
||||||
|
|---|---|
|
||||||
|
| Backend | Python Flask + Blueprints |
|
||||||
|
| Auth | JWT (access + refresh tokens) |
|
||||||
|
| Base de données | SQLite (`turf_saas.db`) |
|
||||||
|
| ML | XGBoost v1 (prédictions courses PMU) |
|
||||||
|
| Frontend | HTML5 + Chart.js |
|
||||||
|
| Hébergement | VPS Linux — https://turf-saas-kolifee.duckdns.org |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Plans d'accès
|
||||||
|
|
||||||
|
| Plan | Accès |
|
||||||
|
|---|---|
|
||||||
|
| `free` | health, auth, courses/today, predictions/top3 (1/jour) |
|
||||||
|
| `premium` | + predictions/all, valuebets, metrics, roi (complet), feedback/stats |
|
||||||
|
| `pro` | + backtest, export/csv, historique illimité, orgs |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Endpoints API v1
|
||||||
|
|
||||||
|
### Authentification
|
||||||
|
|
||||||
|
| Méthode | Path | Auth | Description |
|
||||||
|
|---|---|---|---|
|
||||||
|
| POST | `/api/v1/auth/register` | Non | Créer un compte (plan=free) |
|
||||||
|
| POST | `/api/v1/auth/login` | Non | Login — retourne access_token + refresh_token |
|
||||||
|
| POST | `/api/v1/auth/refresh` | Non | Renouveler l'access token |
|
||||||
|
| POST | `/api/v1/auth/logout` | Oui | Révoquer le refresh token |
|
||||||
|
|
||||||
|
### Système
|
||||||
|
|
||||||
|
| Méthode | Path | Auth | Description |
|
||||||
|
|---|---|---|---|
|
||||||
|
| GET | `/api/v1/health` | Non | Healthcheck public |
|
||||||
|
| GET | `/api/v1/docs` | Non | Swagger UI (Flasgger) |
|
||||||
|
|
||||||
|
### Courses
|
||||||
|
|
||||||
|
| Méthode | Path | Plan | Description |
|
||||||
|
|---|---|---|---|
|
||||||
|
| GET | `/api/v1/courses/today` | free+ | Courses du jour (paginé) |
|
||||||
|
| GET | `/api/v1/courses/{id}/predictions` | free+ | Prédictions ML pour une course |
|
||||||
|
|
||||||
|
`{id}` format : `{num_reunion}-{num_course}` ex: `1-3`
|
||||||
|
Query params `courses/today` : `filter=[all|quinte|trot|plat]`, `limit`, `offset`
|
||||||
|
|
||||||
|
### Prédictions ML
|
||||||
|
|
||||||
|
| Méthode | Path | Plan | Description |
|
||||||
|
|---|---|---|---|
|
||||||
|
| GET | `/api/v1/predictions/top3` | free+ | Top 3 chevaux du jour |
|
||||||
|
| GET | `/api/v1/predictions/all` | premium+ | Toutes les prédictions XGBoost |
|
||||||
|
|
||||||
|
Query params : `date=YYYY-MM-DD`, `limit`, `offset`
|
||||||
|
|
||||||
|
Source des données : table `ml_predictions_cache` (modèle `xgboost_v1`)
|
||||||
|
|
||||||
|
### Value Bets
|
||||||
|
|
||||||
|
| Méthode | Path | Plan | Description |
|
||||||
|
|---|---|---|---|
|
||||||
|
| GET | `/api/v1/valuebets` | premium+ | Value bets du jour (`is_value_bet=1`) |
|
||||||
|
|
||||||
|
Query params : `date`, `min_odds` (défaut 2.0), `limit`, `offset`
|
||||||
|
|
||||||
|
### Métriques ML
|
||||||
|
|
||||||
|
| Méthode | Path | Plan | Description |
|
||||||
|
|---|---|---|---|
|
||||||
|
| GET | `/api/v1/metrics` | premium+ | Métriques perf ML (precision, ROI, top-3 rate) |
|
||||||
|
|
||||||
|
Query params : `days` (int, défaut 30, max 365)
|
||||||
|
|
||||||
|
### ROI par Modèle/Stratégie (HRT-92)
|
||||||
|
|
||||||
|
| Méthode | Path | Plan | Description |
|
||||||
|
|---|---|---|---|
|
||||||
|
| GET | `/api/v1/roi/by-model` | premium+ | ROI calculé par stratégie ML XGBoost |
|
||||||
|
|
||||||
|
**Query params** :
|
||||||
|
- `strategy` : filtrer par stratégie (`xgboost_sg`, `xgboost_value`, `xgboost_sp`, `xgboost_2sur4`)
|
||||||
|
- `days` : période 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
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Jointures** : `paris` ← `pmu_partants` (résultats) ← `pmu_rapports` (dividendes)
|
||||||
|
|
||||||
|
**Accès plan** : Free = 1 stratégie max, Premium/Pro = complet + historique illimité
|
||||||
|
|
||||||
|
### ML Feedback Loop (HRT-93)
|
||||||
|
|
||||||
|
| Méthode | Path | Plan | Description |
|
||||||
|
|---|---|---|---|
|
||||||
|
| POST | `/api/v1/ml/feedback/run` | Admin | Déclencher ml_feedback_saas.py manuellement |
|
||||||
|
| GET | `/api/v1/ml/feedback/stats` | premium+ | Stats paris par stratégie XGBoost |
|
||||||
|
|
||||||
|
**POST `/api/v1/ml/feedback/run`** — Corps optionnel :
|
||||||
|
```json
|
||||||
|
{"date": "2026-04-29"}
|
||||||
|
```
|
||||||
|
ou
|
||||||
|
```json
|
||||||
|
{"backfill": "2026-04-20"}
|
||||||
|
```
|
||||||
|
|
||||||
|
**GET `/api/v1/ml/feedback/stats`** — Réponse :
|
||||||
|
```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"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Stratégies XGBoost** :
|
||||||
|
| Stratégie | Type pari | Condition | Mise |
|
||||||
|
|---|---|---|---|
|
||||||
|
| `xgboost_sg` | simple_gagnant | top1 ML, ml_score >= 70 | 1€ |
|
||||||
|
| `xgboost_value` | simple_gagnant | is_value_bet = 1 | 1€ |
|
||||||
|
| `xgboost_sp` | simple_place | top1 ML, ml_score >= 50 | 1€ |
|
||||||
|
| `xgboost_2sur4` | deux_sur_quatre | top4 ML, 6 combos | 6€ |
|
||||||
|
|
||||||
|
### Backtest
|
||||||
|
|
||||||
|
| Méthode | Path | Plan | Description |
|
||||||
|
|---|---|---|---|
|
||||||
|
| GET | `/api/v1/backtest` | pro | Résultats historiques des paris |
|
||||||
|
|
||||||
|
Query params : `start`, `end` (YYYY-MM-DD), `limit`, `offset`
|
||||||
|
|
||||||
|
### Export
|
||||||
|
|
||||||
|
| Méthode | Path | Plan | Description |
|
||||||
|
|---|---|---|---|
|
||||||
|
| GET | `/api/v1/export/csv` | pro | Export CSV |
|
||||||
|
|
||||||
|
Query params : `type=[predictions|bets]`, `date`, `start`, `end`
|
||||||
|
|
||||||
|
### Historique
|
||||||
|
|
||||||
|
| Méthode | Path | Plan | Description |
|
||||||
|
|---|---|---|---|
|
||||||
|
| GET | `/api/v1/history` | free+ | Historique prédictions ML |
|
||||||
|
|
||||||
|
Limites : Free = 7j, Premium = 90j, Pro = illimité
|
||||||
|
|
||||||
|
### Organisations
|
||||||
|
|
||||||
|
| Méthode | Path | Plan | Description |
|
||||||
|
|---|---|---|---|
|
||||||
|
| GET | `/api/v1/org/` | pro | Détails de l'organisation |
|
||||||
|
| POST | `/api/v1/org/` | pro | Créer une organisation |
|
||||||
|
| POST | `/api/v1/org/invite` | pro | Inviter un membre (max 5) |
|
||||||
|
| DELETE | `/api/v1/org/members/{id}` | pro | Retirer un membre |
|
||||||
|
|
||||||
|
### Utilisateur & Tokens
|
||||||
|
|
||||||
|
| Méthode | Path | Plan | Description |
|
||||||
|
|---|---|---|---|
|
||||||
|
| GET | `/api/v1/user/profile` | free+ | Profil utilisateur |
|
||||||
|
| PUT | `/api/v1/user/alerts` | premium+ | Config alertes Telegram |
|
||||||
|
| GET | `/api/v1/user/api-token` | pro | Token API personnel |
|
||||||
|
| POST | `/api/v1/user/api-token` | pro | Générer/régénérer token API |
|
||||||
|
| GET | `/api/v1/user/webhook` | pro | Config webhook |
|
||||||
|
| PUT | `/api/v1/user/webhook` | pro | Modifier webhook |
|
||||||
|
|
||||||
|
### Billing (Stripe)
|
||||||
|
|
||||||
|
| Méthode | Path | Auth | Description |
|
||||||
|
|---|---|---|---|
|
||||||
|
| POST | `/api/v1/billing/checkout` | Oui | Créer session Stripe Checkout |
|
||||||
|
| POST | `/api/v1/billing/portal` | Oui | Portail Stripe (gestion abonnement) |
|
||||||
|
| GET | `/api/v1/billing/status` | Oui | Statut abonnement actuel |
|
||||||
|
| POST | `/api/v1/billing/webhook` | Non | Webhook Stripe (events) |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Format de réponse uniforme
|
||||||
|
|
||||||
|
**Erreurs** :
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"status": "error",
|
||||||
|
"message": "Description de l'erreur",
|
||||||
|
"code": 400
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Listes paginées** :
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"pagination": {
|
||||||
|
"total": 150,
|
||||||
|
"limit": 20,
|
||||||
|
"offset": 0,
|
||||||
|
"has_more": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Architecture ML — Résumé
|
||||||
|
|
||||||
|
```
|
||||||
|
ml_predictions_cache (XGBoost v1)
|
||||||
|
→ ml_feedback_saas.py
|
||||||
|
→ table paris (source_reco = xgboost_*)
|
||||||
|
→ /api/v1/roi/by-model (ROI calculé)
|
||||||
|
→ /api/v1/ml/feedback/stats (stats)
|
||||||
|
→ dashboard_saas.html (Section Performance & ROI)
|
||||||
|
```
|
||||||
|
|
||||||
|
Voir documentation complète : `POD/Intelligence/ML_Predictions_SaaS.md`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Démarrage
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd /home/h3r7/turf_saas
|
||||||
|
source venv/bin/activate
|
||||||
|
python app_v1.py
|
||||||
|
# ou via gunicorn
|
||||||
|
gunicorn -w 2 -b 0.0.0.0:8792 app_v1:app
|
||||||
|
```
|
||||||
|
|
||||||
|
## Tests
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd /home/h3r7/turf_saas
|
||||||
|
source venv/bin/activate
|
||||||
|
python -m pytest tests/ -v
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
*Turf SaaS — H3R7Tech — Mise à jour 2026-04-30 (HRT-96)*
|
||||||
|
|||||||
339
POD/Intelligence/ML_Predictions_SaaS.md
Normal file
339
POD/Intelligence/ML_Predictions_SaaS.md
Normal file
@@ -0,0 +1,339 @@
|
|||||||
|
# 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 |
|
||||||
Reference in New Issue
Block a user