thibautmodrin commited on
Commit
ac0f7d0
·
1 Parent(s): ec323ea
Dockerfile CHANGED
@@ -23,15 +23,23 @@ RUN pip install --no-cache-dir \
23
  xgboost>=1.4.2 \
24
  joblib>=1.0.1 \
25
  pydantic>=1.8.2 \
26
- python-multipart>=0.0.5
 
 
 
 
 
27
 
28
  # Copie des fichiers avec les bonnes permissions
29
  COPY --chown=user requirements.txt $HOME/app/
30
  RUN pip install --no-cache-dir -r requirements.txt
31
 
32
- # Copie de l'application et des modèles
 
33
  COPY --chown=user ./app.py $HOME/app/
34
- COPY --chown=user ./models $HOME/app/models/
 
 
35
 
36
  # Variable d'environnement pour le port
37
  ENV PORT=7860
 
23
  xgboost>=1.4.2 \
24
  joblib>=1.0.1 \
25
  pydantic>=1.8.2 \
26
+ python-multipart>=0.0.5 \
27
+ pandas>=1.3.0 \
28
+ ta>=0.7.0
29
+
30
+ # Création des répertoires nécessaires
31
+ RUN mkdir -p $HOME/app/models
32
 
33
  # Copie des fichiers avec les bonnes permissions
34
  COPY --chown=user requirements.txt $HOME/app/
35
  RUN pip install --no-cache-dir -r requirements.txt
36
 
37
+ # Copie des fichiers Python
38
+ COPY --chown=user ./train_strategy_models.py $HOME/app/
39
  COPY --chown=user ./app.py $HOME/app/
40
+
41
+ # Création du répertoire models s'il n'existe pas déjà
42
+ RUN mkdir -p $HOME/app/models
43
 
44
  # Variable d'environnement pour le port
45
  ENV PORT=7860
app.py CHANGED
@@ -1,10 +1,21 @@
1
- from fastapi import FastAPI, HTTPException
2
  from fastapi.middleware.cors import CORSMiddleware
3
  from pydantic import BaseModel
4
- import joblib
5
  import numpy as np
 
6
  import os
7
  from pathlib import Path
 
 
 
 
 
 
 
 
 
 
8
 
9
  # Créer l'application FastAPI
10
  app = FastAPI(title="Strategy Selector API")
@@ -21,34 +32,11 @@ app.add_middleware(
21
  # Obtenir le chemin absolu du dossier models
22
  BASE_DIR = Path(__file__).resolve().parent
23
  MODELS_DIR = BASE_DIR / "models"
 
24
 
25
  print(f"Dossier de base : {BASE_DIR}")
26
  print(f"Dossier des modèles : {MODELS_DIR}")
27
 
28
- # Charger les modèles
29
- try:
30
- # Vérifier si le dossier models existe
31
- if not MODELS_DIR.exists():
32
- raise FileNotFoundError(f"Le dossier models n'existe pas : {MODELS_DIR}")
33
-
34
- # Vérifier si les fichiers existent
35
- model_files = ["model_profit.joblib", "model_drawdown.joblib", "model_params.joblib"]
36
- for file in model_files:
37
- if not (MODELS_DIR / file).exists():
38
- raise FileNotFoundError(f"Fichier manquant : {file}")
39
-
40
- # Charger les modèles
41
- model_profit = joblib.load(MODELS_DIR / "model_profit.joblib")
42
- model_drawdown = joblib.load(MODELS_DIR / "model_drawdown.joblib")
43
- model_params = joblib.load(MODELS_DIR / "model_params.joblib")
44
- print("Modèles chargés avec succès")
45
- print(f"Dossier des modèles : {MODELS_DIR}")
46
- print(f"Contenu du dossier : {list(MODELS_DIR.glob('*'))}")
47
-
48
- except Exception as e:
49
- print(f"Erreur lors du chargement des modèles : {str(e)}")
50
- raise
51
-
52
  class MarketData(BaseModel):
53
  RSI: float
54
  ADX: float
@@ -59,6 +47,21 @@ class MarketData(BaseModel):
59
  Chikou_MACD_Pente_Signal: int
60
  ADX_Stoch_Volatility_MA_Signal: int
61
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
62
  @app.get("/")
63
  async def read_root():
64
  return {
@@ -73,16 +76,87 @@ async def read_root():
73
 
74
  @app.get("/health")
75
  async def health_check():
76
- return {"status": "healthy"}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
77
 
78
  @app.post("/predict")
79
  async def predict(data: MarketData):
 
 
 
80
  try:
81
- # Log de la requête
82
- print(f"Requête reçue : {data.dict()}")
 
 
 
 
 
 
83
 
84
- features = []
85
- features.extend([
 
86
  data.Ichimoku_ADX_Volatility_Signal,
87
  data.BB_Stoch_ATR_Signal,
88
  data.Chikou_MACD_Pente_Signal,
@@ -91,43 +165,35 @@ async def predict(data: MarketData):
91
  data.ADX,
92
  data.Volatility_20,
93
  data.MACD
94
- ])
95
-
96
- features_array = np.array(features).reshape(1, -1)
97
 
98
- # Log des features
99
- print(f"Features préparées : {features_array}")
100
-
101
- profit_strategy = int(model_profit.predict(features_array)[0])
102
- drawdown_strategy = int(model_drawdown.predict(features_array)[0])
103
-
104
- strategies = model_params["strategies"]
105
-
106
- response = {
107
- "best_profit_strategy": strategies[profit_strategy],
108
- "best_profit_signal": get_strategy_signal(data, profit_strategy),
109
- "best_drawdown_strategy": strategies[drawdown_strategy],
110
- "best_drawdown_signal": get_strategy_signal(data, drawdown_strategy)
 
 
 
 
 
 
 
111
  }
112
-
113
- print(f"Réponse : {response}")
114
- return response
115
-
116
  except Exception as e:
117
- print(f"Erreur lors de la prédiction : {str(e)}")
118
- raise HTTPException(status_code=500, detail=str(e))
119
-
120
- def get_strategy_signal(data: MarketData, strategy_index: int) -> int:
121
- """
122
- Récupère le signal de la stratégie sélectionnée
123
- """
124
- signals = [
125
- data.Ichimoku_ADX_Volatility_Signal,
126
- data.BB_Stoch_ATR_Signal,
127
- data.Chikou_MACD_Pente_Signal,
128
- data.ADX_Stoch_Volatility_MA_Signal
129
- ]
130
- return signals[strategy_index]
131
 
132
  if __name__ == "__main__":
133
  import uvicorn
 
1
+ from fastapi import FastAPI, HTTPException, UploadFile, File
2
  from fastapi.middleware.cors import CORSMiddleware
3
  from pydantic import BaseModel
4
+ import pandas as pd
5
  import numpy as np
6
+ import joblib
7
  import os
8
  from pathlib import Path
9
+ from io import StringIO
10
+ from typing import Dict, Any
11
+ import json
12
+
13
+ # Import des fonctions de train_strategy_models.py
14
+ from train_strategy_models import (
15
+ preprocess_data,
16
+ train_models,
17
+ save_models
18
+ )
19
 
20
  # Créer l'application FastAPI
21
  app = FastAPI(title="Strategy Selector API")
 
32
  # Obtenir le chemin absolu du dossier models
33
  BASE_DIR = Path(__file__).resolve().parent
34
  MODELS_DIR = BASE_DIR / "models"
35
+ MODELS_DIR.mkdir(exist_ok=True)
36
 
37
  print(f"Dossier de base : {BASE_DIR}")
38
  print(f"Dossier des modèles : {MODELS_DIR}")
39
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
40
  class MarketData(BaseModel):
41
  RSI: float
42
  ADX: float
 
47
  Chikou_MACD_Pente_Signal: int
48
  ADX_Stoch_Volatility_MA_Signal: int
49
 
50
+ class TrainingResponse(BaseModel):
51
+ status: str
52
+ message: str
53
+ details: Dict[str, Any]
54
+
55
+ def load_models():
56
+ """Charge les modèles existants"""
57
+ try:
58
+ model_profit = joblib.load(MODELS_DIR / "model_profit.joblib")
59
+ model_drawdown = joblib.load(MODELS_DIR / "model_drawdown.joblib")
60
+ model_params = joblib.load(MODELS_DIR / "model_params.joblib")
61
+ return model_profit, model_drawdown, model_params
62
+ except:
63
+ return None, None, None
64
+
65
  @app.get("/")
66
  async def read_root():
67
  return {
 
76
 
77
  @app.get("/health")
78
  async def health_check():
79
+ """
80
+ Endpoint pour vérifier l'état de l'API et des modèles
81
+ """
82
+ model_profit, model_drawdown, model_params = load_models()
83
+
84
+ return {
85
+ "status": "healthy",
86
+ "models_loaded": all([model_profit, model_drawdown, model_params]),
87
+ "models_path": str(MODELS_DIR),
88
+ "available_endpoints": [
89
+ "/train (POST) - Entraîner les modèles",
90
+ "/predict (POST) - Faire des prédictions",
91
+ "/health (GET) - Vérifier l'état"
92
+ ]
93
+ }
94
+
95
+ @app.post("/train")
96
+ async def train_from_csv(file: UploadFile = File(...)) -> TrainingResponse:
97
+ """
98
+ Endpoint pour entraîner les modèles à partir des données CSV
99
+ """
100
+ try:
101
+ # Lire le contenu du fichier
102
+ content = await file.read()
103
+ content_str = content.decode('utf-8')
104
+
105
+ # Convertir en DataFrame
106
+ df = pd.read_csv(StringIO(content_str), parse_dates=['Date'], index_col='Date')
107
+ print(f"Données reçues : {len(df)} lignes")
108
+
109
+ # Prétraiter les données
110
+ df_processed = preprocess_data(df)
111
+ print("Prétraitement terminé")
112
+
113
+ # Entraîner les modèles
114
+ model_profit, model_drawdown, features, strategies, split_info = train_models(df_processed)
115
+ print("Entraînement terminé")
116
+
117
+ # Sauvegarder les modèles
118
+ save_models(model_profit, model_drawdown, features, strategies, split_info)
119
+ print("Modèles sauvegardés")
120
+
121
+ return TrainingResponse(
122
+ status="success",
123
+ message="Modèles entraînés avec succès",
124
+ details={
125
+ "data_shape": df.shape,
126
+ "training_period": {
127
+ "start": str(df.index[0]),
128
+ "end": str(df.index[-1])
129
+ },
130
+ "split_info": split_info,
131
+ "features": features,
132
+ "strategies": strategies
133
+ }
134
+ )
135
+
136
+ except Exception as e:
137
+ raise HTTPException(
138
+ status_code=500,
139
+ detail=f"Erreur lors de l'entraînement : {str(e)}"
140
+ )
141
 
142
  @app.post("/predict")
143
  async def predict(data: MarketData):
144
+ """
145
+ Endpoint pour faire des prédictions avec les modèles entraînés
146
+ """
147
  try:
148
+ # Charger les modèles
149
+ model_profit, model_drawdown, model_params = load_models()
150
+
151
+ if not all([model_profit, model_drawdown, model_params]):
152
+ raise HTTPException(
153
+ status_code=400,
154
+ detail="Modèles non disponibles. Veuillez d'abord entraîner les modèles."
155
+ )
156
 
157
+ # Préparer les features dans le bon ordre
158
+ features = model_params['features']
159
+ X = np.array([[
160
  data.Ichimoku_ADX_Volatility_Signal,
161
  data.BB_Stoch_ATR_Signal,
162
  data.Chikou_MACD_Pente_Signal,
 
165
  data.ADX,
166
  data.Volatility_20,
167
  data.MACD
168
+ ]])
 
 
169
 
170
+ # Faire les prédictions
171
+ profit_strategy_idx = model_profit.predict(X)[0]
172
+ drawdown_strategy_idx = model_drawdown.predict(X)[0]
173
+
174
+ # Obtenir les noms des stratégies
175
+ strategies = model_params['strategies']
176
+
177
+ # Récupérer les signaux correspondants
178
+ signals = [
179
+ data.Ichimoku_ADX_Volatility_Signal,
180
+ data.BB_Stoch_ATR_Signal,
181
+ data.Chikou_MACD_Pente_Signal,
182
+ data.ADX_Stoch_Volatility_MA_Signal
183
+ ]
184
+
185
+ return {
186
+ "best_profit_strategy": strategies[profit_strategy_idx],
187
+ "best_profit_signal": signals[profit_strategy_idx],
188
+ "best_drawdown_strategy": strategies[drawdown_strategy_idx],
189
+ "best_drawdown_signal": signals[drawdown_strategy_idx]
190
  }
191
+
 
 
 
192
  except Exception as e:
193
+ raise HTTPException(
194
+ status_code=500,
195
+ detail=f"Erreur lors de la prédiction : {str(e)}"
196
+ )
 
 
 
 
 
 
 
 
 
 
197
 
198
  if __name__ == "__main__":
199
  import uvicorn
models/model_drawdown.joblib DELETED
@@ -1,3 +0,0 @@
1
- version https://git-lfs.github.com/spec/v1
2
- oid sha256:df58af35ba26e6d8c412ac4874e6d99f0c9a5476549d4e7d10a1ccddfdb74819
3
- size 496205
 
 
 
 
models/model_params.joblib DELETED
@@ -1,3 +0,0 @@
1
- version https://git-lfs.github.com/spec/v1
2
- oid sha256:d3a2388fefcb35864aaa64d1ee5d8e2228cf7c6ed0a307610d57dd8fb314bed0
3
- size 260
 
 
 
 
models/model_profit.joblib DELETED
@@ -1,3 +0,0 @@
1
- version https://git-lfs.github.com/spec/v1
2
- oid sha256:aa5950828eb9be49ec5d2f9fe77619b07b60ec674490e5ab914c9b661f79c24b
3
- size 556198
 
 
 
 
requirements.txt CHANGED
@@ -5,4 +5,6 @@ scikit-learn>=0.24.2
5
  xgboost>=1.4.2
6
  joblib>=1.0.1
7
  pydantic>=1.8.2
8
- python-multipart>=0.0.5
 
 
 
5
  xgboost>=1.4.2
6
  joblib>=1.0.1
7
  pydantic>=1.8.2
8
+ python-multipart>=0.0.5
9
+ pandas>=1.3.0
10
+ ta>=0.7.0
train_strategy_models.py ADDED
@@ -0,0 +1,384 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import pandas as pd
2
+ import numpy as np
3
+ from xgboost import XGBClassifier
4
+ from sklearn.metrics import accuracy_score, classification_report
5
+ import ta
6
+ import joblib
7
+ import os
8
+ from sklearn.model_selection import train_test_split, TimeSeriesSplit
9
+
10
+ def preprocess_data(df):
11
+ """Prétraitement des données avec calcul des indicateurs techniques"""
12
+
13
+ # Convertir les colonnes en float si nécessaire
14
+ price_columns = ['Close', 'High', 'Low', 'Open']
15
+ for col in price_columns:
16
+ df[col] = pd.to_numeric(df[col], errors='coerce')
17
+
18
+ # Calculer les indicateurs techniques
19
+ # RSI
20
+ df['RSI'] = ta.momentum.RSIIndicator(df['Close'], window=14).rsi()
21
+
22
+ # ADX
23
+ adx = ta.trend.ADXIndicator(df['High'], df['Low'], df['Close'], window=14)
24
+ df['ADX'] = adx.adx()
25
+
26
+ # Volatilité
27
+ df['Volatility_20'] = df['Close'].rolling(window=20).std()
28
+
29
+ # MACD
30
+ macd = ta.trend.MACD(df['Close'])
31
+ df['MACD'] = macd.macd()
32
+
33
+ # Ichimoku
34
+ ichimoku = ta.trend.IchimokuIndicator(df['High'], df['Low'])
35
+ df['Tenkan_sen'] = ichimoku.ichimoku_conversion_line()
36
+ df['Kijun_sen'] = ichimoku.ichimoku_base_line()
37
+ df['Senkou_Span_A'] = ichimoku.ichimoku_a()
38
+ df['Senkou_Span_B'] = ichimoku.ichimoku_b()
39
+ df['Chikou_Span'] = df['Close'].shift(-26)
40
+
41
+ # Calcul des signaux des stratégies
42
+ df['Ichimoku_ADX_Volatility_Signal'] = calculate_ichimoku_adx_volatility_signal(df)
43
+ df['BB_Stoch_ATR_Signal'] = calculate_bb_stoch_atr_signal(df)
44
+ df['Chikou_MACD_Pente_Signal'] = calculate_chikou_macd_signal(df)
45
+ df['ADX_Stoch_Volatility_MA_Signal'] = calculate_adx_stoch_volatility_signal(df)
46
+
47
+ # Ajouter les colonnes temporelles
48
+ df['hour'] = pd.to_datetime(df.index).hour
49
+ df['day'] = pd.to_datetime(df.index).day
50
+ df['month'] = pd.to_datetime(df.index).month
51
+
52
+ return df
53
+
54
+ def calculate_ichimoku_adx_volatility_signal(df):
55
+ """Calcul du signal Ichimoku + ADX + Volatilité"""
56
+ signal = np.zeros(len(df))
57
+
58
+ # Conditions pour le signal
59
+ bullish = (df['Close'] > df['Senkou_Span_A']) & \
60
+ (df['Close'] > df['Senkou_Span_B']) & \
61
+ (df['ADX'] > 25) & \
62
+ (df['Volatility_20'] < df['Volatility_20'].rolling(5).mean())
63
+
64
+ bearish = (df['Close'] < df['Senkou_Span_A']) & \
65
+ (df['Close'] < df['Senkou_Span_B']) & \
66
+ (df['ADX'] > 25) & \
67
+ (df['Volatility_20'] < df['Volatility_20'].rolling(5).mean())
68
+
69
+ signal[bullish] = 1
70
+ signal[bearish] = -1
71
+
72
+ return signal
73
+
74
+ def calculate_bb_stoch_atr_signal(df):
75
+ """Calcul du signal Bollinger + Stochastique + ATR"""
76
+ bb = ta.volatility.BollingerBands(df['Close'])
77
+ stoch = ta.momentum.StochasticOscillator(df['High'], df['Low'], df['Close'])
78
+ atr = ta.volatility.AverageTrueRange(df['High'], df['Low'], df['Close'])
79
+
80
+ signal = np.zeros(len(df))
81
+
82
+ bullish = (df['Close'] < bb.bollinger_lband()) & \
83
+ (stoch.stoch() < 20) & \
84
+ (atr.average_true_range() < atr.average_true_range().rolling(5).mean())
85
+
86
+ bearish = (df['Close'] > bb.bollinger_hband()) & \
87
+ (stoch.stoch() > 80) & \
88
+ (atr.average_true_range() < atr.average_true_range().rolling(5).mean())
89
+
90
+ signal[bullish] = 1
91
+ signal[bearish] = -1
92
+
93
+ return signal
94
+
95
+ def calculate_chikou_macd_signal(df):
96
+ """Calcul du signal Chikou Span + MACD"""
97
+ signal = np.zeros(len(df))
98
+
99
+ bullish = (df['Chikou_Span'] > df['Close'].shift(26)) & \
100
+ (df['MACD'] > 0)
101
+
102
+ bearish = (df['Chikou_Span'] < df['Close'].shift(26)) & \
103
+ (df['MACD'] < 0)
104
+
105
+ signal[bullish] = 1
106
+ signal[bearish] = -1
107
+
108
+ return signal
109
+
110
+ def calculate_adx_stoch_volatility_signal(df):
111
+ """Calcul du signal ADX + Stochastique + Volatilité"""
112
+ stoch = ta.momentum.StochasticOscillator(df['High'], df['Low'], df['Close'])
113
+
114
+ signal = np.zeros(len(df))
115
+
116
+ bullish = (df['ADX'] > 25) & \
117
+ (stoch.stoch() < 20) & \
118
+ (df['Volatility_20'] < df['Volatility_20'].rolling(5).mean())
119
+
120
+ bearish = (df['ADX'] > 25) & \
121
+ (stoch.stoch() > 80) & \
122
+ (df['Volatility_20'] < df['Volatility_20'].rolling(5).mean())
123
+
124
+ signal[bullish] = 1
125
+ signal[bearish] = -1
126
+
127
+ return signal
128
+
129
+ def calculate_strategy_performance(df, strategies, look_ahead=10):
130
+ """
131
+ Calcule les performances (profit et drawdown) pour chaque stratégie
132
+ """
133
+ max_profits = np.zeros((len(df), len(strategies)))
134
+ max_drawdowns = np.zeros((len(df), len(strategies)))
135
+
136
+ for i in range(len(df) - look_ahead):
137
+ close_start = df['Close'].iloc[i]
138
+ future_closes = df['Close'].iloc[i:i + look_ahead + 1].values
139
+ future_highs = df['High'].iloc[i:i + look_ahead + 1].values
140
+ future_lows = df['Low'].iloc[i:i + look_ahead + 1].values
141
+
142
+ for j, strategy in enumerate(strategies):
143
+ signal = df[strategy].iloc[i]
144
+
145
+ if signal == 1: # Achat
146
+ max_profit = (max(future_highs) - close_start) / close_start * 100
147
+ max_drawdown = (close_start - min(future_lows)) / close_start * 100
148
+ max_profits[i, j] = max_profit
149
+ max_drawdowns[i, j] = max_drawdown if max_drawdown > 0 else 0
150
+
151
+ elif signal == -1: # Vente
152
+ max_profit = (close_start - min(future_lows)) / close_start * 100
153
+ max_drawdown = (max(future_highs) - close_start) / close_start * 100
154
+ max_profits[i, j] = max_profit
155
+ max_drawdowns[i, j] = max_drawdown if max_drawdown > 0 else 0
156
+
157
+ else: # Neutre
158
+ max_profits[i, j] = 0
159
+ max_drawdowns[i, j] = 0
160
+
161
+ # Identifier la meilleure stratégie pour le profit
162
+ best_strategy_max_profit = np.argmax(max_profits, axis=1)
163
+
164
+ # Identifier la stratégie qui minimise le drawdown
165
+ best_strategy_max_drawdown = np.full(len(df), -1, dtype=int)
166
+ for i in range(len(df)):
167
+ active_strategies = np.where(max_drawdowns[i, :] > 0)[0]
168
+ if len(active_strategies) > 0:
169
+ min_drawdown_idx = active_strategies[np.argmin(max_drawdowns[i, active_strategies])]
170
+ best_strategy_max_drawdown[i] = min_drawdown_idx
171
+
172
+ return best_strategy_max_profit, best_strategy_max_drawdown
173
+
174
+ def train_models(df):
175
+ """Entraînement des modèles de sélection de stratégie avec split temporel"""
176
+
177
+ strategies = [
178
+ 'Ichimoku_ADX_Volatility_Signal',
179
+ 'BB_Stoch_ATR_Signal',
180
+ 'Chikou_MACD_Pente_Signal',
181
+ 'ADX_Stoch_Volatility_MA_Signal'
182
+ ]
183
+
184
+ continuous_features = ['RSI', 'ADX', 'Volatility_20', 'MACD']
185
+ features = strategies + continuous_features
186
+
187
+ # Calcul des meilleures stratégies basé sur les performances
188
+ print("Calcul des performances des stratégies...")
189
+ best_strategy_max_profit, best_strategy_max_drawdown = calculate_strategy_performance(df, strategies)
190
+
191
+ # Préparation des données
192
+ X = df[features].values
193
+ y_profit = best_strategy_max_profit
194
+ y_drawdown = best_strategy_max_drawdown
195
+
196
+ # Supprimer les lignes où y_drawdown est -1
197
+ valid_drawdown_mask = y_drawdown != -1
198
+ X_drawdown = X[valid_drawdown_mask]
199
+ y_drawdown = y_drawdown[valid_drawdown_mask]
200
+
201
+ # Division temporelle des données (80% train, 20% test)
202
+ train_size_profit = int(len(X) * 0.8)
203
+ train_size_drawdown = int(len(X_drawdown) * 0.8)
204
+
205
+ # Split pour le modèle de profit
206
+ X_train_profit = X[:train_size_profit]
207
+ X_test_profit = X[train_size_profit:]
208
+ y_train_profit = y_profit[:train_size_profit]
209
+ y_test_profit = y_profit[train_size_profit:]
210
+
211
+ # Split pour le modèle de drawdown
212
+ X_train_drawdown = X_drawdown[:train_size_drawdown]
213
+ X_test_drawdown = X_drawdown[train_size_drawdown:]
214
+ y_train_drawdown = y_drawdown[:train_size_drawdown]
215
+ y_test_drawdown = y_drawdown[train_size_drawdown:]
216
+
217
+ # Afficher la distribution temporelle
218
+ print("\nPériode d'entraînement profit:")
219
+ print(f"Du : {df.index[0]}")
220
+ print(f"Au : {df.index[train_size_profit-1]}")
221
+ print("\nPériode de test profit:")
222
+ print(f"Du : {df.index[train_size_profit]}")
223
+ print(f"Au : {df.index[-1]}")
224
+
225
+ # Validation avec TimeSeriesSplit
226
+ tscv = TimeSeriesSplit(n_splits=5)
227
+
228
+ # Entraînement du modèle de profit
229
+ print("\nEntraînement du modèle de profit maximal...")
230
+ model_profit = XGBClassifier(
231
+ n_estimators=100,
232
+ learning_rate=0.1,
233
+ max_depth=5,
234
+ random_state=42,
235
+ eval_metric='mlogloss'
236
+ )
237
+
238
+ # Validation croisée temporelle pour le modèle de profit
239
+ print("\nValidation croisée temporelle pour le modèle de profit:")
240
+ for fold, (train_index, val_index) in enumerate(tscv.split(X_train_profit)):
241
+ X_fold_train, X_fold_val = X_train_profit[train_index], X_train_profit[val_index]
242
+ y_fold_train, y_fold_val = y_train_profit[train_index], y_train_profit[val_index]
243
+
244
+ model_profit.fit(X_fold_train, y_fold_train)
245
+ fold_score = model_profit.score(X_fold_val, y_fold_val)
246
+ print(f"Fold {fold + 1} - Score: {fold_score:.4f}")
247
+
248
+ # Entraînement final sur l'ensemble complet d'entraînement
249
+ model_profit.fit(
250
+ X_train_profit,
251
+ y_train_profit,
252
+ eval_set=[(X_train_profit, y_train_profit), (X_test_profit, y_test_profit)],
253
+ verbose=True
254
+ )
255
+
256
+ # Même processus pour le modèle de drawdown
257
+ print("\nEntraînement du modèle de drawdown minimal...")
258
+ model_drawdown = XGBClassifier(
259
+ n_estimators=100,
260
+ learning_rate=0.1,
261
+ max_depth=5,
262
+ random_state=42,
263
+ eval_metric='mlogloss'
264
+ )
265
+
266
+ print("\nValidation croisée temporelle pour le modèle de drawdown:")
267
+ for fold, (train_index, val_index) in enumerate(tscv.split(X_train_drawdown)):
268
+ X_fold_train, X_fold_val = X_train_drawdown[train_index], X_train_drawdown[val_index]
269
+ y_fold_train, y_fold_val = y_train_drawdown[train_index], y_train_drawdown[val_index]
270
+
271
+ model_drawdown.fit(X_fold_train, y_fold_train)
272
+ fold_score = model_drawdown.score(X_fold_val, y_fold_val)
273
+ print(f"Fold {fold + 1} - Score: {fold_score:.4f}")
274
+
275
+ # Entraînement final
276
+ model_drawdown.fit(
277
+ X_train_drawdown,
278
+ y_train_drawdown,
279
+ eval_set=[(X_train_drawdown, y_train_drawdown), (X_test_drawdown, y_test_drawdown)],
280
+ verbose=True
281
+ )
282
+
283
+ # Évaluation des modèles
284
+ y_pred_profit = model_profit.predict(X_test_profit)
285
+ y_pred_drawdown = model_drawdown.predict(X_test_drawdown)
286
+
287
+ print("\nPerformance du modèle de profit maximal sur les données de test:")
288
+ print(classification_report(
289
+ y_test_profit,
290
+ y_pred_profit,
291
+ target_names=strategies
292
+ ))
293
+
294
+ print("\nPerformance du modèle de drawdown minimal sur les données de test:")
295
+ print(classification_report(
296
+ y_test_drawdown,
297
+ y_pred_drawdown,
298
+ target_names=strategies
299
+ ))
300
+
301
+ # Sauvegarder les périodes d'entraînement et de test
302
+ split_info = {
303
+ 'profit': {
304
+ 'train_start': df.index[0],
305
+ 'train_end': df.index[train_size_profit-1],
306
+ 'test_start': df.index[train_size_profit],
307
+ 'test_end': df.index[-1]
308
+ },
309
+ 'drawdown': {
310
+ 'train_start': df.index[valid_drawdown_mask][0],
311
+ 'train_end': df.index[valid_drawdown_mask][train_size_drawdown-1],
312
+ 'test_start': df.index[valid_drawdown_mask][train_size_drawdown],
313
+ 'test_end': df.index[valid_drawdown_mask][-1]
314
+ }
315
+ }
316
+
317
+ return model_profit, model_drawdown, features, strategies, split_info
318
+
319
+ def save_models(model_profit, model_drawdown, features, strategies, split_info):
320
+ """Sauvegarde des modèles entraînés et leurs paramètres"""
321
+ if not os.path.exists('models'):
322
+ os.makedirs('models')
323
+
324
+ joblib.dump(model_profit, 'models/model_profit.joblib')
325
+ joblib.dump(model_drawdown, 'models/model_drawdown.joblib')
326
+
327
+ model_params = {
328
+ 'features': features,
329
+ 'strategies': strategies,
330
+ 'split_info': split_info # Sauvegarder les périodes d'entraînement et de test
331
+ }
332
+ joblib.dump(model_params, 'models/model_params.joblib')
333
+
334
+ print("Modèles et paramètres sauvegardés dans le dossier 'models/'")
335
+
336
+ def predict_best_strategy(new_data):
337
+ """
338
+ Prédit la meilleure stratégie pour de nouvelles données
339
+ """
340
+ # Charger les modèles et paramètres
341
+ model_profit = joblib.load('models/model_profit.joblib')
342
+ model_drawdown = joblib.load('models/model_drawdown.joblib')
343
+ params = joblib.load('models/model_params.joblib')
344
+
345
+ # Prétraiter les nouvelles données
346
+ processed_data = preprocess_data(new_data)
347
+
348
+ # Préparer les features
349
+ X = processed_data[params['features']].values
350
+
351
+ # Faire les prédictions
352
+ profit_strategy_idx = model_profit.predict(X)
353
+ drawdown_strategy_idx = model_drawdown.predict(X)
354
+
355
+ # Obtenir les probabilités de prédiction
356
+ profit_proba = model_profit.predict_proba(X)
357
+ drawdown_proba = model_drawdown.predict_proba(X)
358
+
359
+ # Convertir les indices en noms de stratégies
360
+ profit_strategy = params['strategies'][profit_strategy_idx[-1]]
361
+ drawdown_strategy = params['strategies'][drawdown_strategy_idx[-1]]
362
+
363
+ return {
364
+ 'best_profit_strategy': profit_strategy,
365
+ 'profit_confidence': float(np.max(profit_proba[-1])),
366
+ 'best_drawdown_strategy': drawdown_strategy,
367
+ 'drawdown_confidence': float(np.max(drawdown_proba[-1]))
368
+ }
369
+
370
+ def main():
371
+ print("Chargement des données...")
372
+ df=pd.read_csv('EURUSD_4H.csv',sep=';',index_col=0,parse_dates=True)
373
+
374
+ print("Prétraitement des données...")
375
+ df = preprocess_data(df)
376
+
377
+ print("Entraînement des modèles...")
378
+ model_profit, model_drawdown, features, strategies, split_info = train_models(df)
379
+
380
+ print("Sauvegarde des modèles...")
381
+ save_models(model_profit, model_drawdown, features, strategies, split_info)
382
+
383
+ if __name__ == "__main__":
384
+ main()