Files
ahad-quant/AHAD_QUANT_Colab_Training.ipynb
2026-06-25 14:00:20 +03:00

1242 lines
59 KiB
Plaintext
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{
"nbformat": 4,
"nbformat_minor": 0,
"metadata": {
"colab": {
"provenance": [],
"gpuType": "T4",
"toc_visible": true
},
"kernelspec": {
"display_name": "Python 3",
"name": "python3"
},
"language_info": {
"name": "python"
},
"accelerator": "GPU"
},
"cells": [
{
"cell_type": "markdown",
"metadata": {
"id": "BsjhisC9CtK8"
},
"source": [
"# 🧠 AHAD QUANT V4 — Entraînement Complet sur Google Colab\n",
"\n",
"> **GPU recommandé** : Runtime → Modifier le type de Runtime → T4 GPU (gratuit)\n",
"\n",
"## 📋 Étapes\n",
"| # | Étape | Durée estimée |\n",
"|---|-------|---------------|\n",
"| 1 | Upload du projet + installation | ~5 min |\n",
"| 2 | Téléchargement données Forex | ~5 min |\n",
"| 3 | Entraînement ML (LGB + XGB + RF + TFT + TGRU) | ~45 min |\n",
"| 4 | Entraînement RL (PPO) | ~60 min |\n",
"| 5 | Export modèle unifié + téléchargement | ~2 min |\n",
"\n",
"---\n",
"⚠️ **Garde cet onglet actif** — Colab déconnecte après 90 min d'inactivité.\n",
"En cas de déconnexion : relancer depuis la **cellule 4.2** avec `--resume`."
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "wyUAORUuCtK_"
},
"source": [
"## 📦 ÉTAPE 1 — Upload & Installation"
]
},
{
"cell_type": "code",
"execution_count": 13,
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/",
"height": 160
},
"id": "rTjwFHDUCtLA",
"outputId": "532d5b73-5d63-4fdb-dd91-1b68c7903853"
},
"outputs": [
{
"output_type": "stream",
"name": "stdout",
"text": [
"📁 Sélectionne le fichier ahad_quant_v32_FINAL_updated_v4.zip ...\n"
]
},
{
"output_type": "display_data",
"data": {
"text/plain": [
"<IPython.core.display.HTML object>"
],
"text/html": [
"\n",
" <input type=\"file\" id=\"files-f3b4b8ed-6ba0-4237-8bf7-ee586407edae\" name=\"files[]\" multiple disabled\n",
" style=\"border:none\" />\n",
" <output id=\"result-f3b4b8ed-6ba0-4237-8bf7-ee586407edae\">\n",
" Upload widget is only available when the cell has been executed in the\n",
" current browser session. Please rerun this cell to enable.\n",
" </output>\n",
" <script>// Copyright 2017 Google LLC\n",
"//\n",
"// Licensed under the Apache License, Version 2.0 (the \"License\");\n",
"// you may not use this file except in compliance with the License.\n",
"// You may obtain a copy of the License at\n",
"//\n",
"// http://www.apache.org/licenses/LICENSE-2.0\n",
"//\n",
"// Unless required by applicable law or agreed to in writing, software\n",
"// distributed under the License is distributed on an \"AS IS\" BASIS,\n",
"// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n",
"// See the License for the specific language governing permissions and\n",
"// limitations under the License.\n",
"\n",
"/**\n",
" * @fileoverview Helpers for google.colab Python module.\n",
" */\n",
"(function(scope) {\n",
"function span(text, styleAttributes = {}) {\n",
" const element = document.createElement('span');\n",
" element.textContent = text;\n",
" for (const key of Object.keys(styleAttributes)) {\n",
" element.style[key] = styleAttributes[key];\n",
" }\n",
" return element;\n",
"}\n",
"\n",
"// Max number of bytes which will be uploaded at a time.\n",
"const MAX_PAYLOAD_SIZE = 100 * 1024;\n",
"\n",
"function _uploadFiles(inputId, outputId) {\n",
" const steps = uploadFilesStep(inputId, outputId);\n",
" const outputElement = document.getElementById(outputId);\n",
" // Cache steps on the outputElement to make it available for the next call\n",
" // to uploadFilesContinue from Python.\n",
" outputElement.steps = steps;\n",
"\n",
" return _uploadFilesContinue(outputId);\n",
"}\n",
"\n",
"// This is roughly an async generator (not supported in the browser yet),\n",
"// where there are multiple asynchronous steps and the Python side is going\n",
"// to poll for completion of each step.\n",
"// This uses a Promise to block the python side on completion of each step,\n",
"// then passes the result of the previous step as the input to the next step.\n",
"function _uploadFilesContinue(outputId) {\n",
" const outputElement = document.getElementById(outputId);\n",
" const steps = outputElement.steps;\n",
"\n",
" const next = steps.next(outputElement.lastPromiseValue);\n",
" return Promise.resolve(next.value.promise).then((value) => {\n",
" // Cache the last promise value to make it available to the next\n",
" // step of the generator.\n",
" outputElement.lastPromiseValue = value;\n",
" return next.value.response;\n",
" });\n",
"}\n",
"\n",
"/**\n",
" * Generator function which is called between each async step of the upload\n",
" * process.\n",
" * @param {string} inputId Element ID of the input file picker element.\n",
" * @param {string} outputId Element ID of the output display.\n",
" * @return {!Iterable<!Object>} Iterable of next steps.\n",
" */\n",
"function* uploadFilesStep(inputId, outputId) {\n",
" const inputElement = document.getElementById(inputId);\n",
" inputElement.disabled = false;\n",
"\n",
" const outputElement = document.getElementById(outputId);\n",
" outputElement.innerHTML = '';\n",
"\n",
" const pickedPromise = new Promise((resolve) => {\n",
" inputElement.addEventListener('change', (e) => {\n",
" resolve(e.target.files);\n",
" });\n",
" });\n",
"\n",
" const cancel = document.createElement('button');\n",
" inputElement.parentElement.appendChild(cancel);\n",
" cancel.textContent = 'Cancel upload';\n",
" const cancelPromise = new Promise((resolve) => {\n",
" cancel.onclick = () => {\n",
" resolve(null);\n",
" };\n",
" });\n",
"\n",
" // Wait for the user to pick the files.\n",
" const files = yield {\n",
" promise: Promise.race([pickedPromise, cancelPromise]),\n",
" response: {\n",
" action: 'starting',\n",
" }\n",
" };\n",
"\n",
" cancel.remove();\n",
"\n",
" // Disable the input element since further picks are not allowed.\n",
" inputElement.disabled = true;\n",
"\n",
" if (!files) {\n",
" return {\n",
" response: {\n",
" action: 'complete',\n",
" }\n",
" };\n",
" }\n",
"\n",
" for (const file of files) {\n",
" const li = document.createElement('li');\n",
" li.append(span(file.name, {fontWeight: 'bold'}));\n",
" li.append(span(\n",
" `(${file.type || 'n/a'}) - ${file.size} bytes, ` +\n",
" `last modified: ${\n",
" file.lastModifiedDate ? file.lastModifiedDate.toLocaleDateString() :\n",
" 'n/a'} - `));\n",
" const percent = span('0% done');\n",
" li.appendChild(percent);\n",
"\n",
" outputElement.appendChild(li);\n",
"\n",
" const fileDataPromise = new Promise((resolve) => {\n",
" const reader = new FileReader();\n",
" reader.onload = (e) => {\n",
" resolve(e.target.result);\n",
" };\n",
" reader.readAsArrayBuffer(file);\n",
" });\n",
" // Wait for the data to be ready.\n",
" let fileData = yield {\n",
" promise: fileDataPromise,\n",
" response: {\n",
" action: 'continue',\n",
" }\n",
" };\n",
"\n",
" // Use a chunked sending to avoid message size limits. See b/62115660.\n",
" let position = 0;\n",
" do {\n",
" const length = Math.min(fileData.byteLength - position, MAX_PAYLOAD_SIZE);\n",
" const chunk = new Uint8Array(fileData, position, length);\n",
" position += length;\n",
"\n",
" const base64 = btoa(String.fromCharCode.apply(null, chunk));\n",
" yield {\n",
" response: {\n",
" action: 'append',\n",
" file: file.name,\n",
" data: base64,\n",
" },\n",
" };\n",
"\n",
" let percentDone = fileData.byteLength === 0 ?\n",
" 100 :\n",
" Math.round((position / fileData.byteLength) * 100);\n",
" percent.textContent = `${percentDone}% done`;\n",
"\n",
" } while (position < fileData.byteLength);\n",
" }\n",
"\n",
" // All done.\n",
" yield {\n",
" response: {\n",
" action: 'complete',\n",
" }\n",
" };\n",
"}\n",
"\n",
"scope.google = scope.google || {};\n",
"scope.google.colab = scope.google.colab || {};\n",
"scope.google.colab._files = {\n",
" _uploadFiles,\n",
" _uploadFilesContinue,\n",
"};\n",
"})(self);\n",
"</script> "
]
},
"metadata": {}
},
{
"output_type": "stream",
"name": "stdout",
"text": [
"Saving ahad_quant_v32_CLEAN.zip to ahad_quant_v32_CLEAN.zip\n",
"\n",
"✅ Reçu : ahad_quant_v32_CLEAN.zip (0.2 MB)\n",
"📂 Projet extrait dans : /content/ahad_quant/ahad_quant_v32_fixed\n",
"✅ Répertoire de travail configuré\n"
]
}
],
"source": [
"# ── 1.1 Upload du ZIP AHAD QUANT ──────────────────────────────────────────────\n",
"from google.colab import files\n",
"import os, zipfile, shutil\n",
"\n",
"print('📁 Sélectionne le fichier ahad_quant_v32_FINAL_updated_v4.zip ...')\n",
"uploaded = files.upload()\n",
"\n",
"zip_name = list(uploaded.keys())[0]\n",
"print(f'\\n✅ Reçu : {zip_name} ({os.path.getsize(zip_name)/1024/1024:.1f} MB)')\n",
"\n",
"# Extraire dans /content/ahad_quant\n",
"os.makedirs('/content/ahad_quant', exist_ok=True)\n",
"with zipfile.ZipFile(zip_name, 'r') as z:\n",
" z.extractall('/content/ahad_quant')\n",
"\n",
"# Trouver le dossier extrait\n",
"subdirs = [d for d in os.listdir('/content/ahad_quant') if os.path.isdir(f'/content/ahad_quant/{d}')]\n",
"PROJECT_DIR = f'/content/ahad_quant/{subdirs[0]}'\n",
"print(f'📂 Projet extrait dans : {PROJECT_DIR}')\n",
"os.chdir(PROJECT_DIR)\n",
"print('✅ Répertoire de travail configuré')"
]
},
{
"cell_type": "code",
"execution_count": 14,
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "HAPFJvJkCtLC",
"outputId": "2356f3dd-ff8b-4d15-c261-6d2e2437889d"
},
"outputs": [
{
"output_type": "stream",
"name": "stdout",
"text": [
"📦 Installation des packages...\n",
"\n",
"✅ Packages installés\n"
]
}
],
"source": [
"# ── 1.2 Installation des dépendances ─────────────────────────────────────────\n",
"print('📦 Installation des packages...')\n",
"\n",
"# Packages standards depuis PyPI\n",
"!pip install -q lightgbm xgboost scikit-learn optuna yfinance stable-baselines3[extra] gymnasium shimmy python-dotenv\n",
"\n",
"# Torch GPU (CUDA 11.8) depuis l'index PyTorch dédié\n",
"!pip install -q torch torchvision --index-url https://download.pytorch.org/whl/cu118\n",
"\n",
"print('\\n✅ Packages installés')"
]
},
{
"cell_type": "code",
"execution_count": 16,
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "ScKxJIYmCtLD",
"outputId": "3896a169-43f0-4d7d-8566-2dd1f3c4afc0"
},
"outputs": [
{
"output_type": "stream",
"name": "stdout",
"text": [
"✅ GPU détecté : Tesla T4 (14.6 GB)\n",
"\n",
"📋 Config active :\n",
" DATA_SOURCE = yfinance\n",
" PAPER_MODE = true\n",
" MAX_SEQ_PER_PAIR = 4000\n",
" AHAD_QUANT_BATCH_SIZE = 512\n",
" AHAD_QUANT_MAX_EPOCHS = 20\n"
]
}
],
"source": [
"# ── 1.3 Configuration GPU + variables d'environnement ────────────────────────\n",
"import torch, os\n",
"\n",
"GPU_OK = torch.cuda.is_available()\n",
"if GPU_OK:\n",
" gpu_name = torch.cuda.get_device_name(0)\n",
" gpu_mem = torch.cuda.get_device_properties(0).total_memory / 1024**3\n",
" print(f'✅ GPU détecté : {gpu_name} ({gpu_mem:.1f} GB)')\n",
"else:\n",
" print('⚠️ Pas de GPU — entraînement plus lent (CPU). Active le GPU dans Runtime → Modifier le type de Runtime.')\n",
"\n",
"# Variables d'env pour AHAD QUANT\n",
"os.environ.update({\n",
" 'DATA_SOURCE' : 'yfinance', # source données (gratuit)\n",
" 'EXCHANGE' : 'paper', # pas de broker réel\n",
" 'PAPER_MODE' : 'true',\n",
" 'PAPER_INITIAL_BALANCE' : '10000',\n",
" # Mémoire : RAM SYSTÈME Colab (~12 Gi), indépendante de la VRAM du GPU\n",
" 'MAX_SEQ_PER_PAIR' : '4000',\n",
" # Batch size TFT/TGRU — GPU peut prendre 512, CPU limité\n",
" 'AHAD_QUANT_BATCH_SIZE' : '512' if GPU_OK else '128',\n",
" 'AHAD_QUANT_MAX_EPOCHS' : '20' if GPU_OK else '10',\n",
"})\n",
"\n",
"print('\\n📋 Config active :')\n",
"for k in ['DATA_SOURCE','PAPER_MODE','MAX_SEQ_PER_PAIR','AHAD_QUANT_BATCH_SIZE','AHAD_QUANT_MAX_EPOCHS']:\n",
" print(f' {k} = {os.environ[k]}')"
]
},
{
"cell_type": "code",
"execution_count": 17,
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "52yD66NnCtLE",
"outputId": "b0b873ed-ca6b-4642-b2e1-2566a2ec43ac"
},
"outputs": [
{
"output_type": "stream",
"name": "stdout",
"text": [
"Drive already mounted at /content/drive; to attempt to forcibly remount, call drive.mount(\"/content/drive\", force_remount=True).\n",
"✅ Drive monté — sauvegarde dans : /content/drive/MyDrive/AhadQuant_Models\n"
]
}
],
"source": [
"# ── 1.4 (Optionnel) Montage Google Drive pour sauvegarde auto ────────────────\n",
"USE_DRIVE = True # ← Mettre False si tu ne veux pas utiliser Drive\n",
"\n",
"DRIVE_SAVE_DIR = None\n",
"if USE_DRIVE:\n",
" from google.colab import drive\n",
" drive.mount('/content/drive')\n",
" DRIVE_SAVE_DIR = '/content/drive/MyDrive/AhadQuant_Models'\n",
" os.makedirs(DRIVE_SAVE_DIR, exist_ok=True)\n",
" print(f'✅ Drive monté — sauvegarde dans : {DRIVE_SAVE_DIR}')\n",
"else:\n",
" print('️ Drive désactivé — les modèles seront téléchargés en fin de notebook')"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "KnX1i32NCtLE"
},
"source": [
"## 📊 ÉTAPE 2 — Téléchargement des données Forex"
]
},
{
"cell_type": "code",
"execution_count": 18,
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "Gvan90BJCtLE",
"outputId": "745ad356-e0cb-43c6-fa02-dc8c14abc111"
},
"outputs": [
{
"output_type": "stream",
"name": "stdout",
"text": [
"📡 Téléchargement des données historiques Forex (Yahoo Finance)...\n",
" 25 paires × 730 jours × 1h ≈ 17 000 bougies/paire\n",
"\n",
"\n",
"✅ Données téléchargées : 25 fichiers dans data/\n"
]
}
],
"source": [
"# ── 2.1 Download 730 jours de données 1h pour les 25 paires ─────────────────\n",
"import subprocess, sys\n",
"\n",
"print('📡 Téléchargement des données historiques Forex (Yahoo Finance)...')\n",
"print(' 25 paires × 730 jours × 1h ≈ 17 000 bougies/paire\\n')\n",
"\n",
"result = subprocess.run(\n",
" [sys.executable, 'download_data.py'],\n",
" capture_output=False,\n",
" text=True\n",
")\n",
"\n",
"if result.returncode == 0:\n",
" data_files = [f for f in os.listdir('data') if f.endswith('.json')] if os.path.exists('data') else []\n",
" print(f'\\n✅ Données téléchargées : {len(data_files)} fichiers dans data/')\n",
"else:\n",
" print(f'\\n❌ Erreur download (code {result.returncode})')"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "fs-zpnwoCtLF"
},
"source": [
"## 🤖 ÉTAPE 3 — Entraînement ML (Ensemble + Deep Learning)"
]
},
{
"cell_type": "code",
"execution_count": 19,
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "otoBfmYuCtLF",
"outputId": "a0cfa69b-d997-4f1b-df85-50f107e94998"
},
"outputs": [
{
"output_type": "stream",
"name": "stdout",
"text": [
"🧠 Lancement entraînement ML...\n",
" LightGBM + XGBoost + RF + TFT + TransformerGRU + Meta-learner\n",
"\n",
"[DL] PyTorch 2.11.0+cu128 detected — TFT + TGRU enabled\n",
"=================================================================\n",
" AHAD QUANT — Ensemble Training Pipeline (V5)\n",
" LightGBM + XGBoost + RF + TFT + TransformerGRU + Meta-learner\n",
"=================================================================\n",
"\n",
"[0/7] Données disponibles : 25 fichiers dans data/\n",
"\n",
"[1/7] Loading data and building tabular features...\n",
" ✅ EURUSD — 17,222 samples (719 jours)\n",
" ✅ GBPUSD — 17,223 samples (719 jours)\n",
" ✅ USDJPY — 17,109 samples (714 jours)\n",
" ✅ USDCHF — 17,163 samples (716 jours)\n",
" ✅ AUDUSD — 17,321 samples (723 jours)\n",
" ✅ NZDUSD — 17,314 samples (723 jours)\n",
" ✅ USDCAD — 17,326 samples (723 jours)\n",
" ✅ EURGBP — 17,248 samples (720 jours)\n",
" ✅ EURJPY — 17,234 samples (719 jours)\n",
" ✅ EURCAD — 17,249 samples (720 jours)\n",
" ✅ EURCHF — 17,233 samples (719 jours)\n",
" ✅ EURAUD — 17,247 samples (720 jours)\n",
" ✅ EURNZD — 17,240 samples (720 jours)\n",
" ✅ GBPJPY — 17,233 samples (719 jours)\n",
" ✅ GBPCAD — 17,313 samples (723 jours)\n",
" ✅ GBPCHF — 17,230 samples (719 jours)\n",
" ✅ GBPAUD — 17,309 samples (723 jours)\n",
" ✅ AUDCAD — 17,318 samples (723 jours)\n",
" ✅ AUDNZD — 17,315 samples (723 jours)\n",
" ✅ AUDJPY — 17,234 samples (719 jours)\n",
" ✅ AUDCHF — 17,283 samples (722 jours)\n",
" ✅ CADJPY — 17,237 samples (720 jours)\n",
" ✅ CHFJPY — 17,221 samples (719 jours)\n",
" ✅ NZDJPY — 17,230 samples (719 jours)\n",
" ✅ NZDCAD — 17,309 samples (723 jours)\n",
"\n",
" Total: 431,361 samples | 62 features | 50.40% long labels\n",
"\n",
"[1b/7] Building sequence dataset (SEQ_LEN=168)...\n",
" → sampled 4,000 / 17,051 sequences (RAM cap)\n",
" [EURUSD ] 4,000 sequences\n",
" → sampled 4,000 / 17,052 sequences (RAM cap)\n",
" [GBPUSD ] 4,000 sequences\n",
" → sampled 4,000 / 16,938 sequences (RAM cap)\n",
" [USDJPY ] 4,000 sequences\n",
" → sampled 4,000 / 16,992 sequences (RAM cap)\n",
" [USDCHF ] 4,000 sequences\n",
" → sampled 4,000 / 17,150 sequences (RAM cap)\n",
" [AUDUSD ] 4,000 sequences\n",
" → sampled 4,000 / 17,143 sequences (RAM cap)\n",
" [NZDUSD ] 4,000 sequences\n",
" → sampled 4,000 / 17,155 sequences (RAM cap)\n",
" [USDCAD ] 4,000 sequences\n",
" → sampled 4,000 / 17,077 sequences (RAM cap)\n",
" [EURGBP ] 4,000 sequences\n",
" → sampled 4,000 / 17,063 sequences (RAM cap)\n",
" [EURJPY ] 4,000 sequences\n",
" → sampled 4,000 / 17,078 sequences (RAM cap)\n",
" [EURCAD ] 4,000 sequences\n",
" → sampled 4,000 / 17,062 sequences (RAM cap)\n",
" [EURCHF ] 4,000 sequences\n",
" → sampled 4,000 / 17,076 sequences (RAM cap)\n",
" [EURAUD ] 4,000 sequences\n",
" → sampled 4,000 / 17,069 sequences (RAM cap)\n",
" [EURNZD ] 4,000 sequences\n",
" → sampled 4,000 / 17,062 sequences (RAM cap)\n",
" [GBPJPY ] 4,000 sequences\n",
" → sampled 4,000 / 17,142 sequences (RAM cap)\n",
" [GBPCAD ] 4,000 sequences\n",
" → sampled 4,000 / 17,059 sequences (RAM cap)\n",
" [GBPCHF ] 4,000 sequences\n",
" → sampled 4,000 / 17,138 sequences (RAM cap)\n",
" [GBPAUD ] 4,000 sequences\n",
" → sampled 4,000 / 17,147 sequences (RAM cap)\n",
" [AUDCAD ] 4,000 sequences\n",
" → sampled 4,000 / 17,144 sequences (RAM cap)\n",
" [AUDNZD ] 4,000 sequences\n",
" → sampled 4,000 / 17,063 sequences (RAM cap)\n",
" [AUDJPY ] 4,000 sequences\n",
" → sampled 4,000 / 17,112 sequences (RAM cap)\n",
" [AUDCHF ] 4,000 sequences\n",
" → sampled 4,000 / 17,066 sequences (RAM cap)\n",
" [CADJPY ] 4,000 sequences\n",
" → sampled 4,000 / 17,050 sequences (RAM cap)\n",
" [CHFJPY ] 4,000 sequences\n",
" → sampled 4,000 / 17,059 sequences (RAM cap)\n",
" [NZDJPY ] 4,000 sequences\n",
" → sampled 4,000 / 17,138 sequences (RAM cap)\n",
" [NZDCAD ] 4,000 sequences\n",
"\n",
" Total : 100,000 sequences | shape X_seq=(100000, 168, 62) | 50.00% long labels\n",
" Sequence samples : 100,000 | shape: (100000, 168, 62)\n",
" Seq splits: train=70,000 | val=15,000 | test=15,000\n",
"\n",
"[2/7] Walk-forward cross-validation (tabular)...\n",
"\n",
" Walk-forward CV (4 windows):\n",
"Training until validation scores don't improve for 30 rounds\n",
"Did not meet early stopping. Best iteration is:\n",
"[500]\tvalid_0's binary_logloss: 0.537108\n",
" Window 1: train=215,680 test=53,920 acc=0.7102\n",
"Training until validation scores don't improve for 30 rounds\n",
"Did not meet early stopping. Best iteration is:\n",
"[500]\tvalid_0's binary_logloss: 0.536396\n",
" Window 2: train=269,600 test=53,920 acc=0.7090\n",
"Training until validation scores don't improve for 30 rounds\n",
"Did not meet early stopping. Best iteration is:\n",
"[500]\tvalid_0's binary_logloss: 0.529891\n",
" Window 3: train=323,520 test=53,920 acc=0.7125\n",
"Training until validation scores don't improve for 30 rounds\n",
"Did not meet early stopping. Best iteration is:\n",
"[500]\tvalid_0's binary_logloss: 0.530677\n",
" Window 4: train=377,440 test=53,920 acc=0.7138\n",
" CV accuracy: 0.7114 ± 0.0019\n",
"\n",
" Tabular splits: train=301,952 | val=64,704 | test=64,705\n",
"\n",
"[3/7] Hyperparameter search (30 Optuna trials)...\n",
"Training until validation scores don't improve for 30 rounds\n",
"Early stopping, best iteration is:\n",
"[306]\tvalid_0's binary_logloss: 0.531696\n",
"Training until validation scores don't improve for 30 rounds\n",
"Early stopping, best iteration is:\n",
"[256]\tvalid_0's binary_logloss: 0.536046\n",
"Training until validation scores don't improve for 30 rounds\n",
"Did not meet early stopping. Best iteration is:\n",
"[1000]\tvalid_0's binary_logloss: 0.531071\n",
"Training until validation scores don't improve for 30 rounds\n",
"Early stopping, best iteration is:\n",
"[176]\tvalid_0's binary_logloss: 0.534405\n",
"Training until validation scores don't improve for 30 rounds\n",
"Early stopping, best iteration is:\n",
"[320]\tvalid_0's binary_logloss: 0.532699\n",
"Training until validation scores don't improve for 30 rounds\n",
"Did not meet early stopping. Best iteration is:\n",
"[1000]\tvalid_0's binary_logloss: 0.529014\n",
"Training until validation scores don't improve for 30 rounds\n",
"Did not meet early stopping. Best iteration is:\n",
"[1000]\tvalid_0's binary_logloss: 0.533946\n",
"Training until validation scores don't improve for 30 rounds\n",
"Did not meet early stopping. Best iteration is:\n",
"[997]\tvalid_0's binary_logloss: 0.531701\n",
"Training until validation scores don't improve for 30 rounds\n",
"Early stopping, best iteration is:\n",
"[540]\tvalid_0's binary_logloss: 0.535239\n",
"Training until validation scores don't improve for 30 rounds\n",
"Early stopping, best iteration is:\n",
"[519]\tvalid_0's binary_logloss: 0.530513\n",
"Training until validation scores don't improve for 30 rounds\n",
"Did not meet early stopping. Best iteration is:\n",
"[996]\tvalid_0's binary_logloss: 0.530435\n",
"Training until validation scores don't improve for 30 rounds\n",
"Early stopping, best iteration is:\n",
"[725]\tvalid_0's binary_logloss: 0.529296\n",
"Training until validation scores don't improve for 30 rounds\n",
"Did not meet early stopping. Best iteration is:\n",
"[998]\tvalid_0's binary_logloss: 0.528426\n",
"Training until validation scores don't improve for 30 rounds\n",
"Did not meet early stopping. Best iteration is:\n",
"[987]\tvalid_0's binary_logloss: 0.529308\n",
"Training until validation scores don't improve for 30 rounds\n",
"Did not meet early stopping. Best iteration is:\n",
"[998]\tvalid_0's binary_logloss: 0.528884\n",
"Training until validation scores don't improve for 30 rounds\n",
"Did not meet early stopping. Best iteration is:\n",
"[1000]\tvalid_0's binary_logloss: 0.529866\n",
"Training until validation scores don't improve for 30 rounds\n",
"Early stopping, best iteration is:\n",
"[815]\tvalid_0's binary_logloss: 0.528147\n",
"Training until validation scores don't improve for 30 rounds\n",
"Early stopping, best iteration is:\n",
"[469]\tvalid_0's binary_logloss: 0.530574\n",
"Training until validation scores don't improve for 30 rounds\n",
"Did not meet early stopping. Best iteration is:\n",
"[1000]\tvalid_0's binary_logloss: 0.532568\n",
"Training until validation scores don't improve for 30 rounds\n",
"Early stopping, best iteration is:\n",
"[897]\tvalid_0's binary_logloss: 0.528453\n",
"Training until validation scores don't improve for 30 rounds\n",
"Did not meet early stopping. Best iteration is:\n",
"[1000]\tvalid_0's binary_logloss: 0.528905\n",
"Training until validation scores don't improve for 30 rounds\n",
"Early stopping, best iteration is:\n",
"[695]\tvalid_0's binary_logloss: 0.529934\n",
"Training until validation scores don't improve for 30 rounds\n",
"Early stopping, best iteration is:\n",
"[833]\tvalid_0's binary_logloss: 0.529049\n",
"Training until validation scores don't improve for 30 rounds\n",
"Did not meet early stopping. Best iteration is:\n",
"[1000]\tvalid_0's binary_logloss: 0.52851\n",
"Training until validation scores don't improve for 30 rounds\n",
"Early stopping, best iteration is:\n",
"[761]\tvalid_0's binary_logloss: 0.531103\n",
"Training until validation scores don't improve for 30 rounds\n",
"Did not meet early stopping. Best iteration is:\n",
"[1000]\tvalid_0's binary_logloss: 0.529707\n",
"Training until validation scores don't improve for 30 rounds\n",
"Did not meet early stopping. Best iteration is:\n",
"[1000]\tvalid_0's binary_logloss: 0.531528\n",
"Training until validation scores don't improve for 30 rounds\n",
"Did not meet early stopping. Best iteration is:\n",
"[1000]\tvalid_0's binary_logloss: 0.529486\n",
"Training until validation scores don't improve for 30 rounds\n",
"Did not meet early stopping. Best iteration is:\n",
"[1000]\tvalid_0's binary_logloss: 0.532756\n",
"Training until validation scores don't improve for 30 rounds\n",
"Early stopping, best iteration is:\n",
"[310]\tvalid_0's binary_logloss: 0.531737\n",
" Best LightGBM accuracy (Optuna): 0.7147\n",
"\n",
"[4/7] Training tabular base models (LightGBM + XGBoost + RF)...\n",
" Training LightGBM...\n",
"Training until validation scores don't improve for 50 rounds\n",
"[200]\tvalid_0's binary_logloss: 0.537594\n",
"[400]\tvalid_0's binary_logloss: 0.532418\n",
"[600]\tvalid_0's binary_logloss: 0.530308\n",
"[800]\tvalid_0's binary_logloss: 0.529119\n",
"[1000]\tvalid_0's binary_logloss: 0.528434\n",
"Early stopping, best iteration is:\n",
"[1018]\tvalid_0's binary_logloss: 0.528387\n",
" Training XGBoost...\n",
" Training RandomForest...\n",
"\n",
"[4b/7] Training Deep Learning models (TFT + TransformerGRU)...\n",
" Training Temporal Fusion Transformer...\n",
" [TFT] device=cuda | train=70,000 val=15,000 samples\n",
" [TFT] parameters: 669,305\n",
"/content/ahad_quant/ahad_quant_v32_fixed/tft_model.py:280: FutureWarning: `torch.cuda.amp.GradScaler(args...)` is deprecated. Please use `torch.amp.GradScaler('cuda', args...)` instead.\n",
" scaler_amp = torch.cuda.amp.GradScaler(enabled=torch.cuda.is_available())\n",
"/content/ahad_quant/ahad_quant_v32_fixed/tft_model.py:297: FutureWarning: `torch.cuda.amp.autocast(args...)` is deprecated. Please use `torch.amp.autocast('cuda', args...)` instead.\n",
" with torch.cuda.amp.autocast(enabled=torch.cuda.is_available()):\n",
" [TFT] epoch 1/30 train=0.6950 val=0.6942 val_acc=0.5015 (82s)\n",
" [TFT] epoch 2/30 train=0.6936 val=0.6953 val_acc=0.5015 (163s)\n",
" [TFT] epoch 3/30 train=0.6935 val=0.6931 val_acc=0.5053 (243s)\n",
" [TFT] epoch 4/30 train=0.6934 val=0.6935 val_acc=0.5007 (324s)\n",
" [TFT] epoch 5/30 train=0.6931 val=0.6934 val_acc=0.5003 (405s)\n",
" [TFT] epoch 6/30 train=0.6934 val=0.6940 val_acc=0.5033 (486s)\n",
" [TFT] epoch 7/30 train=0.6932 val=0.6936 val_acc=0.5002 (566s)\n",
" [TFT] epoch 8/30 train=0.6929 val=0.6938 val_acc=0.5059 (647s)\n",
" [TFT] epoch 9/30 train=0.6926 val=0.6932 val_acc=0.5073 (728s)\n",
" [TFT] early stopping at epoch 9\n",
" [TFT] training complete — best val_loss=0.6931\n",
"\n",
" Training TransformerGRU...\n",
" [TGRU] device=cuda | train=70,000 val=15,000 samples\n",
" [TGRU] parameters: 588,033\n",
"/content/ahad_quant/ahad_quant_v32_fixed/transformer_gru_model.py:275: FutureWarning: `torch.cuda.amp.GradScaler(args...)` is deprecated. Please use `torch.amp.GradScaler('cuda', args...)` instead.\n",
" scaler_amp = torch.cuda.amp.GradScaler(enabled=torch.cuda.is_available())\n",
"/content/ahad_quant/ahad_quant_v32_fixed/transformer_gru_model.py:292: FutureWarning: `torch.cuda.amp.autocast(args...)` is deprecated. Please use `torch.amp.autocast('cuda', args...)` instead.\n",
" with torch.cuda.amp.autocast(enabled=torch.cuda.is_available()):\n",
" [TGRU] epoch 1/30 train=0.6419 val=0.5863 val_acc=0.6751 (32s)\n",
" [TGRU] epoch 2/30 train=0.5910 val=0.5618 val_acc=0.6897 (64s)\n",
" [TGRU] epoch 3/30 train=0.5722 val=0.5608 val_acc=0.6885 (96s)\n",
" [TGRU] epoch 4/30 train=0.5620 val=0.5450 val_acc=0.7002 (128s)\n",
" [TGRU] epoch 5/30 train=0.5493 val=0.5325 val_acc=0.7067 (160s)\n",
" [TGRU] epoch 6/30 train=0.5413 val=0.5278 val_acc=0.7097 (192s)\n",
" [TGRU] epoch 7/30 train=0.5334 val=0.5214 val_acc=0.7157 (224s)\n",
" [TGRU] epoch 8/30 train=0.5262 val=0.5349 val_acc=0.7007 (255s)\n",
" [TGRU] epoch 9/30 train=0.5193 val=0.5043 val_acc=0.7247 (287s)\n",
" [TGRU] epoch 10/30 train=0.5138 val=0.4947 val_acc=0.7262 (319s)\n",
" [TGRU] epoch 11/30 train=0.5041 val=0.4924 val_acc=0.7249 (351s)\n",
" [TGRU] epoch 12/30 train=0.4999 val=0.4847 val_acc=0.7306 (383s)\n",
" [TGRU] epoch 13/30 train=0.4940 val=0.4821 val_acc=0.7277 (415s)\n",
" [TGRU] epoch 14/30 train=0.4866 val=0.4807 val_acc=0.7303 (447s)\n",
" [TGRU] epoch 15/30 train=0.4838 val=0.4750 val_acc=0.7301 (479s)\n",
" [TGRU] epoch 16/30 train=0.4791 val=0.4762 val_acc=0.7289 (511s)\n",
" [TGRU] epoch 17/30 train=0.4743 val=0.4713 val_acc=0.7335 (543s)\n",
" [TGRU] epoch 18/30 train=0.4716 val=0.4662 val_acc=0.7378 (575s)\n",
" [TGRU] epoch 19/30 train=0.4677 val=0.4707 val_acc=0.7357 (606s)\n",
" [TGRU] epoch 20/30 train=0.4647 val=0.4677 val_acc=0.7347 (639s)\n",
" [TGRU] epoch 21/30 train=0.4622 val=0.4695 val_acc=0.7347 (670s)\n",
" [TGRU] epoch 22/30 train=0.4592 val=0.4632 val_acc=0.7383 (702s)\n",
" [TGRU] epoch 23/30 train=0.4566 val=0.4646 val_acc=0.7373 (734s)\n",
" [TGRU] epoch 24/30 train=0.4544 val=0.4650 val_acc=0.7395 (766s)\n",
" [TGRU] epoch 25/30 train=0.4532 val=0.4667 val_acc=0.7361 (798s)\n",
" [TGRU] epoch 26/30 train=0.4521 val=0.4632 val_acc=0.7394 (830s)\n",
" [TGRU] epoch 27/30 train=0.4504 val=0.4662 val_acc=0.7381 (862s)\n",
" [TGRU] epoch 28/30 train=0.4497 val=0.4646 val_acc=0.7389 (894s)\n",
" [TGRU] epoch 29/30 train=0.4478 val=0.4644 val_acc=0.7387 (926s)\n",
" [TGRU] epoch 30/30 train=0.4488 val=0.4646 val_acc=0.7397 (958s)\n",
" [TGRU] training complete — best val_loss=0.4632\n",
"\n",
"❌ Échec entraînement ML (code -9) — 88.7 min\n",
"\n",
"--- Dernières lignes du log ---\n",
" [TFT] training complete — best val_loss=0.6931\n",
"\n",
" Training TransformerGRU...\n",
" [TGRU] device=cuda | train=70,000 val=15,000 samples\n",
" [TGRU] parameters: 588,033\n",
"/content/ahad_quant/ahad_quant_v32_fixed/transformer_gru_model.py:275: FutureWarning: `torch.cuda.amp.GradScaler(args...)` is deprecated. Please use `torch.amp.GradScaler('cuda', args...)` instead.\n",
" scaler_amp = torch.cuda.amp.GradScaler(enabled=torch.cuda.is_available())\n",
"/content/ahad_quant/ahad_quant_v32_fixed/transformer_gru_model.py:292: FutureWarning: `torch.cuda.amp.autocast(args...)` is deprecated. Please use `torch.amp.autocast('cuda', args...)` instead.\n",
" with torch.cuda.amp.autocast(enabled=torch.cuda.is_available()):\n",
" [TGRU] epoch 1/30 train=0.6419 val=0.5863 val_acc=0.6751 (32s)\n",
" [TGRU] epoch 2/30 train=0.5910 val=0.5618 val_acc=0.6897 (64s)\n",
" [TGRU] epoch 3/30 train=0.5722 val=0.5608 val_acc=0.6885 (96s)\n",
" [TGRU] epoch 4/30 train=0.5620 val=0.5450 val_acc=0.7002 (128s)\n",
" [TGRU] epoch 5/30 train=0.5493 val=0.5325 val_acc=0.7067 (160s)\n",
" [TGRU] epoch 6/30 train=0.5413 val=0.5278 val_acc=0.7097 (192s)\n",
" [TGRU] epoch 7/30 train=0.5334 val=0.5214 val_acc=0.7157 (224s)\n",
" [TGRU] epoch 8/30 train=0.5262 val=0.5349 val_acc=0.7007 (255s)\n",
" [TGRU] epoch 9/30 train=0.5193 val=0.5043 val_acc=0.7247 (287s)\n",
" [TGRU] epoch 10/30 train=0.5138 val=0.4947 val_acc=0.7262 (319s)\n",
" [TGRU] epoch 11/30 train=0.5041 val=0.4924 val_acc=0.7249 (351s)\n",
" [TGRU] epoch 12/30 train=0.4999 val=0.4847 val_acc=0.7306 (383s)\n",
" [TGRU] epoch 13/30 train=0.4940 val=0.4821 val_acc=0.7277 (415s)\n",
" [TGRU] epoch 14/30 train=0.4866 val=0.4807 val_acc=0.7303 (447s)\n",
" [TGRU] epoch 15/30 train=0.4838 val=0.4750 val_acc=0.7301 (479s)\n",
" [TGRU] epoch 16/30 train=0.4791 val=0.4762 val_acc=0.7289 (511s)\n",
" [TGRU] epoch 17/30 train=0.4743 val=0.4713 val_acc=0.7335 (543s)\n",
" [TGRU] epoch 18/30 train=0.4716 val=0.4662 val_acc=0.7378 (575s)\n",
" [TGRU] epoch 19/30 train=0.4677 val=0.4707 val_acc=0.7357 (606s)\n",
" [TGRU] epoch 20/30 train=0.4647 val=0.4677 val_acc=0.7347 (639s)\n",
" [TGRU] epoch 21/30 train=0.4622 val=0.4695 val_acc=0.7347 (670s)\n",
" [TGRU] epoch 22/30 train=0.4592 val=0.4632 val_acc=0.7383 (702s)\n",
" [TGRU] epoch 23/30 train=0.4566 val=0.4646 val_acc=0.7373 (734s)\n",
" [TGRU] epoch 24/30 train=0.4544 val=0.4650 val_acc=0.7395 (766s)\n",
" [TGRU] epoch 25/30 train=0.4532 val=0.4667 val_acc=0.7361 (798s)\n",
" [TGRU] epoch 26/30 train=0.4521 val=0.4632 val_acc=0.7394 (830s)\n",
" [TGRU] epoch 27/30 train=0.4504 val=0.4662 val_acc=0.7381 (862s)\n",
" [TGRU] epoch 28/30 train=0.4497 val=0.4646 val_acc=0.7389 (894s)\n",
" [TGRU] epoch 29/30 train=0.4478 val=0.4644 val_acc=0.7387 (926s)\n",
" [TGRU] epoch 30/30 train=0.4488 val=0.4646 val_acc=0.7397 (958s)\n",
" [TGRU] training complete — best val_loss=0.4632\n",
"\n"
]
}
],
"source": [
"# ── 3.1 Entraînement du pipeline ML complet (version diagnostic) ────────────\n",
"import subprocess, sys, time, os\n",
"\n",
"print('🧠 Lancement entraînement ML...')\n",
"print(' LightGBM + XGBoost + RF + TFT + TransformerGRU + Meta-learner\\n')\n",
"\n",
"t0 = time.time()\n",
"with open('train_log.txt', 'w') as logfile:\n",
" proc = subprocess.Popen(\n",
" [sys.executable, '-u', 'train.py'],\n",
" stdout=subprocess.PIPE,\n",
" stderr=subprocess.STDOUT,\n",
" text=True,\n",
" bufsize=1,\n",
" )\n",
" for line in proc.stdout:\n",
" print(line, end='')\n",
" logfile.write(line)\n",
" logfile.flush()\n",
" proc.wait()\n",
"\n",
"elapsed = (time.time() - t0) / 60\n",
"if proc.returncode == 0:\n",
" print(f'\\n✅ Entraînement ML terminé en {elapsed:.1f} min')\n",
"else:\n",
" print(f'\\n❌ Échec entraînement ML (code {proc.returncode}) — {elapsed:.1f} min')\n",
" print('\\n--- Dernières lignes du log ---')\n",
" with open('train_log.txt') as f:\n",
" lines = f.readlines()\n",
" print(''.join(lines[-40:]))"
]
},
{
"cell_type": "code",
"source": [
"import os, shutil\n",
"\n",
"if os.path.exists('model_ensemble.pkl'):\n",
" print(f\"model_ensemble.pkl : {os.path.getsize('model_ensemble.pkl')/1024/1024:.1f} MB\")\n",
" if DRIVE_SAVE_DIR:\n",
" shutil.copy('model_ensemble.pkl', f'{DRIVE_SAVE_DIR}/model_ensemble.pkl')\n",
" print(f\"💾 Sauvegardé sur Drive : {DRIVE_SAVE_DIR}/model_ensemble.pkl\")\n",
"else:\n",
" print(\"⚠️ model_ensemble.pkl non trouvé\")"
],
"metadata": {
"id": "5dy7w3W8IvvS"
},
"execution_count": null,
"outputs": []
},
{
"cell_type": "markdown",
"source": [
"# Nouvelle section"
],
"metadata": {
"id": "_tNgu2SucvZ-"
}
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "SByjDXk4CtLF"
},
"outputs": [],
"source": [
"# ── 3.2 Résultats ML (backtest rapide) ───────────────────────────────────────\n",
"import json\n",
"\n",
"# Lire les résultats de l'entraînement\n",
"results_files = ['last_retrain.json', 'backtest_results.json']\n",
"for rf in results_files:\n",
" if os.path.exists(rf):\n",
" with open(rf) as f:\n",
" data = json.load(f)\n",
" print(f'\\n📊 {rf}:')\n",
" for k, v in list(data.items())[:15]:\n",
" print(f' {k}: {v}')"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "pAM1LE6hCtLG"
},
"source": [
"## 🎮 ÉTAPE 4 — Entraînement RL (PPO Agent)\n",
"\n",
"> 💡 **En cas de déconnexion Colab** : remonte Drive, retourne dans le dossier projet, puis relance **la cellule 4.2** avec `--resume` — l'entraînement reprend depuis le dernier checkpoint automatiquement."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "s3hvvDF8CtLG"
},
"outputs": [],
"source": [
"# ── 4.1 Vérification pré-RL ──────────────────────────────────────────────────\n",
"import os, json\n",
"\n",
"print('🔍 Vérification des prérequis RL...')\n",
"checks = {\n",
" 'model_ensemble.pkl' : 'Modèle ML',\n",
" 'data/' : 'Données Forex',\n",
"}\n",
"ok = True\n",
"for path, label in checks.items():\n",
" exists = os.path.exists(path)\n",
" print(f' {\"✅\" if exists else \"❌\"} {label} ({path})')\n",
" if not exists:\n",
" ok = False\n",
"\n",
"if ok:\n",
" print('\\n✅ Tous les prérequis sont présents — tu peux lancer l\\'entraînement RL')\n",
"else:\n",
" print('\\n❌ Lance d\\'abord les étapes 2 et 3 !')\n",
"\n",
"# Config RL\n",
"RL_STEPS = 1_000_000 # ← Modifier ici si besoin (500k = rapide, 2M = meilleure qualité)\n",
"print(f'\\n⚙️ Steps RL configurés : {RL_STEPS:,}')\n",
"print(f' Durée estimée : ~{RL_STEPS/60000:.0f} min (GPU T4)')"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "huE9Oa6BCtLG"
},
"outputs": [],
"source": [
"# ── 4.2 Entraînement RL PPO ──────────────────────────────────────────────────\n",
"# ⚠️ En cas de reprise après déconnexion : changer --steps en 0 et garder --resume\n",
"import subprocess, sys, time, os, shutil\n",
"\n",
"RESUME = os.path.exists('rl_progress.json') and os.path.getsize('rl_progress.json') > 5\n",
"\n",
"if RESUME:\n",
" with open('rl_progress.json') as f:\n",
" prog = json.load(f)\n",
" done = prog.get('steps_done', 0)\n",
" total = prog.get('total_steps', RL_STEPS)\n",
" print(f'♻️ Reprise détectée : {done:,} / {total:,} steps déjà effectués')\n",
" resume_flag = ['--resume']\n",
"else:\n",
" print('🆕 Nouvel entraînement RL')\n",
" resume_flag = []\n",
"\n",
"print(f'\\n🎮 Lancement PPO ({RL_STEPS:,} steps)...')\n",
"print(' Checkpoints sauvegardés toutes les 50k steps dans rl_checkpoints/')\n",
"print(' Durée estimée : 45-90 min (GPU T4)\\n')\n",
"\n",
"t0 = time.time()\n",
"result = subprocess.run(\n",
" [sys.executable, 'rl_train.py', '--steps', str(RL_STEPS)] + resume_flag,\n",
" capture_output=False,\n",
" text=True\n",
")\n",
"elapsed = (time.time() - t0) / 60\n",
"\n",
"if result.returncode == 0:\n",
" print(f'\\n✅ Entraînement RL terminé en {elapsed:.1f} min')\n",
" for f in ['rl_agent.zip', 'rl_scaler.pkl']:\n",
" if os.path.exists(f):\n",
" print(f' {f} : {os.path.getsize(f)/1024:.0f} KB')\n",
"\n",
" # Sauvegarde Drive\n",
" if DRIVE_SAVE_DIR:\n",
" for f in ['rl_agent.zip', 'rl_scaler.pkl']:\n",
" if os.path.exists(f):\n",
" shutil.copy(f, f'{DRIVE_SAVE_DIR}/{f}')\n",
" # Sauvegarder aussi les checkpoints\n",
" if os.path.exists('rl_checkpoints'):\n",
" shutil.copytree('rl_checkpoints', f'{DRIVE_SAVE_DIR}/rl_checkpoints', dirs_exist_ok=True)\n",
" print(f' 💾 Sauvegardé sur Drive : {DRIVE_SAVE_DIR}/')\n",
"else:\n",
" print(f'\\n❌ Échec RL (code {result.returncode}) — {elapsed:.1f} min')\n",
" print(' ➡️ Relance cette cellule avec --resume pour reprendre')"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "HDnmu9rRCtLG"
},
"source": [
"## 📦 ÉTAPE 5 — Export modèle unifié & Téléchargement"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "yfCaEDZ9CtLH"
},
"outputs": [],
"source": [
"# ── 5.1 Export ahad_quant_unified.zip ─────────────────────────────────────────\n",
"import subprocess, sys, os, shutil\n",
"\n",
"print('📦 Export du modèle unifié...')\n",
"result = subprocess.run(\n",
" [sys.executable, 'export_unified.py'],\n",
" capture_output=False,\n",
" text=True\n",
")\n",
"\n",
"if result.returncode == 0 and os.path.exists('ahad_quant_unified.zip'):\n",
" size_mb = os.path.getsize('ahad_quant_unified.zip') / 1024 / 1024\n",
" print(f'✅ ahad_quant_unified.zip créé ({size_mb:.1f} MB)')\n",
"\n",
" if DRIVE_SAVE_DIR:\n",
" shutil.copy('ahad_quant_unified.zip', f'{DRIVE_SAVE_DIR}/ahad_quant_unified.zip')\n",
" print(f'💾 Sauvegardé sur Drive : {DRIVE_SAVE_DIR}/ahad_quant_unified.zip')\n",
"else:\n",
" print('❌ Export échoué')"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "nDiLmnW4CtLH"
},
"outputs": [],
"source": [
"# ── 5.2 Créer un bundle complet avec tous les modèles ────────────────────────\n",
"import zipfile, os, datetime, shutil\n",
"\n",
"timestamp = datetime.datetime.now().strftime('%Y%m%d_%H%M')\n",
"bundle_name = f'ahad_quant_trained_{timestamp}.zip'\n",
"\n",
"FILES_TO_BUNDLE = [\n",
" 'ahad_quant_unified.zip', # modèle unifié complet\n",
" 'model_ensemble.pkl', # modèle ML ensemble\n",
" 'rl_agent.zip', # agent RL PPO\n",
" 'rl_scaler.pkl', # scaler RL\n",
" 'backtest_results.json', # résultats backtest\n",
" 'last_retrain.json', # méta-infos entraînement\n",
"]\n",
"\n",
"print(f'📦 Création du bundle {bundle_name}...')\n",
"with zipfile.ZipFile(bundle_name, 'w', zipfile.ZIP_DEFLATED) as zf:\n",
" for f in FILES_TO_BUNDLE:\n",
" if os.path.exists(f):\n",
" zf.write(f)\n",
" print(f' + {f} ({os.path.getsize(f)/1024:.0f} KB)')\n",
" else:\n",
" print(f' ⚠️ {f} — non trouvé, ignoré')\n",
"\n",
" # Ajouter les checkpoints RL\n",
" if os.path.exists('rl_checkpoints'):\n",
" for root, dirs, files in os.walk('rl_checkpoints'):\n",
" for file in files:\n",
" fp = os.path.join(root, file)\n",
" zf.write(fp)\n",
" print(f' + rl_checkpoints/ ({len(os.listdir(\"rl_checkpoints\"))} fichiers)')\n",
"\n",
"size_mb = os.path.getsize(bundle_name) / 1024 / 1024\n",
"print(f'\\n✅ Bundle créé : {bundle_name} ({size_mb:.1f} MB)')\n",
"\n",
"if DRIVE_SAVE_DIR:\n",
" shutil.copy(bundle_name, f'{DRIVE_SAVE_DIR}/{bundle_name}')\n",
" print(f'💾 Sauvegardé sur Drive : {DRIVE_SAVE_DIR}/{bundle_name}')"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "OIgijId7CtLH"
},
"outputs": [],
"source": [
"# ── 5.3 Téléchargement direct depuis Colab ───────────────────────────────────\n",
"from google.colab import files\n",
"import os\n",
"\n",
"# Choisir ce qu'on veut télécharger\n",
"TO_DOWNLOAD = [\n",
" bundle_name, # bundle complet (recommandé)\n",
" # 'ahad_quant_unified.zip', # ou juste le modèle unifié\n",
"]\n",
"\n",
"for f in TO_DOWNLOAD:\n",
" if os.path.exists(f):\n",
" size_mb = os.path.getsize(f) / 1024 / 1024\n",
" print(f'⬇️ Téléchargement de {f} ({size_mb:.1f} MB)...')\n",
" files.download(f)\n",
" else:\n",
" print(f'⚠️ {f} non trouvé')\n",
"\n",
"print('\\n✅ Terminé ! Place les fichiers .pkl et .zip dans ton dossier ahad_quant_v32_fixed/ sur ta machine.')"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "aVRssBSDCtLH"
},
"source": [
"## 🔁 BONUS — Relancer uniquement le RL après déconnexion\n",
"\n",
"Si Colab s'est déconnecté **pendant le RL** (étape 4), exécute ces cellules dans l'ordre :"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "RsKDPLODCtLI"
},
"outputs": [],
"source": [
"# ── REPRISE RAPIDE (après déconnexion pendant le RL) ─────────────────────────\n",
"# 1. Monte Drive\n",
"from google.colab import drive\n",
"drive.mount('/content/drive')\n",
"\n",
"# 2. Réinstalle les packages\n",
"!pip install -q lightgbm xgboost scikit-learn optuna yfinance stable-baselines3[extra] gymnasium shimmy python-dotenv torch --index-url https://download.pytorch.org/whl/cu118\n",
"\n",
"# 3. Remonte le projet depuis Drive\n",
"import os, shutil, json\n",
"DRIVE_SAVE_DIR = '/content/drive/MyDrive/AhadQuant_Models'\n",
"PROJECT_DIR = '/content/ahad_quant_resume'\n",
"os.makedirs(PROJECT_DIR, exist_ok=True)\n",
"\n",
"# Copie le ZIP du projet depuis Drive (si tu l'avais mis là)\n",
"# OU remonte le ZIP original depuis upload\n",
"# from google.colab import files\n",
"# uploaded = files.upload() # ← décommente si besoin\n",
"\n",
"# 4. Copie les modèles existants depuis Drive\n",
"for f in ['model_ensemble.pkl', 'rl_agent.zip', 'rl_scaler.pkl', 'rl_progress.json']:\n",
" src = f'{DRIVE_SAVE_DIR}/{f}'\n",
" if os.path.exists(src):\n",
" shutil.copy(src, f'{PROJECT_DIR}/{f}')\n",
" print(f'✅ Restauré : {f}')\n",
"\n",
"if os.path.exists(f'{DRIVE_SAVE_DIR}/rl_checkpoints'):\n",
" shutil.copytree(f'{DRIVE_SAVE_DIR}/rl_checkpoints', f'{PROJECT_DIR}/rl_checkpoints', dirs_exist_ok=True)\n",
" print('✅ Restauré : rl_checkpoints/')\n",
"\n",
"# 5. Affiche la progression\n",
"prog_file = f'{PROJECT_DIR}/rl_progress.json'\n",
"if os.path.exists(prog_file):\n",
" with open(prog_file) as f:\n",
" prog = json.load(f)\n",
" steps_done = prog.get('steps_done', 0)\n",
" total = prog.get('total_steps', 1_000_000)\n",
" pct = steps_done / total * 100\n",
" print(f'\\n📊 Progression RL : {steps_done:,} / {total:,} steps ({pct:.1f}%)')\n",
" print('➡️ Maintenant exécute la cellule 4.2 — la reprise sera détectée automatiquement')"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "VcruETjFCtLI"
},
"source": [
"## 📋 RÉCAPITULATIF — Fichiers à récupérer\n",
"\n",
"Après l'entraînement, copie ces fichiers dans ton dossier `ahad_quant_v32_fixed/` sur ta machine Windows :\n",
"\n",
"| Fichier | Description | Obligatoire |\n",
"|---------|-------------|-------------|\n",
"| `ahad_quant_unified.zip` | Modèle complet (ML + RL) | ✅ |\n",
"| `model_ensemble.pkl` | Ensemble ML seul | ✅ |\n",
"| `rl_agent.zip` | Agent PPO seul | ✅ |\n",
"| `rl_scaler.pkl` | Scaler features RL | ✅ |\n",
"| `rl_checkpoints/` | Checkpoints intermédiaires | Optionnel |\n",
"\n",
"Ensuite, depuis le Web UI : **lance le bot** — il chargera `ahad_quant_unified.zip` automatiquement."
]
},
{
"cell_type": "code",
"source": [
"!free -h"
],
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "HWq8mt_cc0pa",
"outputId": "e35c3e7a-7b80-4abf-84b0-7e5ce2d226c6"
},
"execution_count": 8,
"outputs": [
{
"output_type": "stream",
"name": "stdout",
"text": [
" total used free shared buff/cache available\n",
"Mem: 12Gi 1.1Gi 10Gi 2.0Mi 785Mi 11Gi\n",
"Swap: 0B 0B 0B\n"
]
}
]
}
]
}