diff --git a/DOCUMENTATION.md b/DOCUMENTATION.md index bcecd72..56733c9 100644 --- a/DOCUMENTATION.md +++ b/DOCUMENTATION.md @@ -155,3 +155,284 @@ python app.py --- *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)* diff --git a/POD/Intelligence/ML_Predictions_SaaS.md b/POD/Intelligence/ML_Predictions_SaaS.md new file mode 100644 index 0000000..91ffd78 --- /dev/null +++ b/POD/Intelligence/ML_Predictions_SaaS.md @@ -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 |