thibautmodrin commited on
Commit
118bc22
·
1 Parent(s): cc9632a
Files changed (1) hide show
  1. app.py +70 -60
app.py CHANGED
@@ -7,14 +7,20 @@ 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
@@ -34,25 +40,42 @@ 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
43
  Volatility_20: float
44
  MACD: float
 
45
  Ichimoku_ADX_Volatility_Signal: int
46
  BB_Stoch_ATR_Signal: int
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 are_models_available():
56
  """Vérifie si tous les modèles nécessaires sont disponibles"""
57
  required_files = ["model_profit.joblib", "model_drawdown.joblib", "model_params.joblib"]
58
  return all((MODELS_DIR / file).exists() for file in required_files)
@@ -61,14 +84,16 @@ def load_models():
61
  """Charge les modèles existants"""
62
  try:
63
  if not are_models_available():
 
64
  return None, None, None
65
 
66
  model_profit = joblib.load(MODELS_DIR / "model_profit.joblib")
67
  model_drawdown = joblib.load(MODELS_DIR / "model_drawdown.joblib")
68
  model_params = joblib.load(MODELS_DIR / "model_params.joblib")
 
69
  return model_profit, model_drawdown, model_params
70
  except Exception as e:
71
- print(f"Erreur lors du chargement des modèles : {str(e)}")
72
  return None, None, None
73
 
74
  @app.get("/")
@@ -77,17 +102,17 @@ async def read_root():
77
  "message": "Strategy Selector API",
78
  "version": "1.0",
79
  "status": "running",
 
80
  "endpoints": {
81
  "predict": "/predict (POST)",
 
82
  "health": "/health (GET)"
83
  }
84
  }
85
 
86
  @app.get("/health")
87
  async def health_check():
88
- """
89
- Endpoint pour vérifier l'état de l'API et des modèles
90
- """
91
  models_available = are_models_available()
92
  return {
93
  "status": "healthy",
@@ -102,10 +127,10 @@ async def health_check():
102
 
103
  @app.post("/train")
104
  async def train_from_csv(file: UploadFile = File(...)) -> TrainingResponse:
105
- """
106
- Endpoint pour entraîner les modèles à partir des données CSV
107
- """
108
  try:
 
 
109
  # Lire et valider le contenu du fichier
110
  content = await file.read()
111
  content_str = content.decode('utf-8')
@@ -115,26 +140,27 @@ async def train_from_csv(file: UploadFile = File(...)) -> TrainingResponse:
115
 
116
  # Vérifier les colonnes requises
117
  required_columns = ['Date', 'Open', 'High', 'Low', 'Close']
118
- if not all(col in df.columns for col in required_columns):
119
- raise ValueError(f"Colonnes manquantes. Requis: {required_columns}")
 
120
 
121
  # Configurer l'index temporel
122
  df['Date'] = pd.to_datetime(df['Date'])
123
  df.set_index('Date', inplace=True)
124
 
125
- print(f"Données reçues : {len(df)} lignes")
126
 
127
  # Prétraiter les données
128
  df_processed = preprocess_data(df)
129
- print("Prétraitement terminé")
130
 
131
  # Entraîner les modèles
132
  model_profit, model_drawdown, features, strategies, split_info = train_models(df_processed)
133
- print("Entraînement terminé")
134
 
135
  # Sauvegarder les modèles
136
  save_models(model_profit, model_drawdown, features, strategies, split_info)
137
- print("Modèles sauvegardés")
138
 
139
  return TrainingResponse(
140
  status="success",
@@ -152,6 +178,7 @@ async def train_from_csv(file: UploadFile = File(...)) -> TrainingResponse:
152
  )
153
 
154
  except Exception as e:
 
155
  raise HTTPException(
156
  status_code=500,
157
  detail=f"Erreur lors de l'entraînement : {str(e)}"
@@ -159,64 +186,47 @@ async def train_from_csv(file: UploadFile = File(...)) -> TrainingResponse:
159
 
160
  @app.post("/predict")
161
  async def predict(data: MarketData):
162
- """
163
- Endpoint pour faire des prédictions avec les modèles entraînés
164
- """
165
  try:
 
 
166
  # Vérifier si les modèles sont disponibles
167
  if not are_models_available():
 
168
  return {
169
  "status": "error",
170
- "message": "Modèles non disponibles",
171
  "best_profit_strategy": "Ichimoku_ADX_Volatility_Signal",
172
  "best_profit_signal": 0,
173
  "best_drawdown_strategy": "BB_Stoch_ATR_Signal",
174
  "best_drawdown_signal": 0
175
  }
176
 
177
- # Charger les modèles
178
- model_profit, model_drawdown, model_params = load_models()
179
-
180
- # Préparer les features dans le bon ordre
181
- features = [
182
- data.Ichimoku_ADX_Volatility_Signal,
183
- data.BB_Stoch_ATR_Signal,
184
- data.Chikou_MACD_Pente_Signal,
185
- data.ADX_Stoch_Volatility_MA_Signal,
186
- data.RSI,
187
- data.ADX,
188
- data.Volatility_20,
189
- data.MACD
190
- ]
191
-
192
- X = np.array(features).reshape(1, -1)
193
-
194
- # Faire les prédictions
195
- profit_strategy_idx = int(model_profit.predict(X)[0])
196
- drawdown_strategy_idx = int(model_drawdown.predict(X)[0])
197
-
198
- # Obtenir les noms des stratégies
199
- strategies = model_params["strategies"]
200
-
201
- # Récupérer les signaux correspondants
202
- signals = [
203
- data.Ichimoku_ADX_Volatility_Signal,
204
- data.BB_Stoch_ATR_Signal,
205
- data.Chikou_MACD_Pente_Signal,
206
- data.ADX_Stoch_Volatility_MA_Signal
207
- ]
208
-
209
  return {
210
  "status": "success",
211
- "best_profit_strategy": strategies[profit_strategy_idx],
212
- "best_profit_signal": signals[profit_strategy_idx],
213
- "best_drawdown_strategy": strategies[drawdown_strategy_idx],
214
- "best_drawdown_signal": signals[drawdown_strategy_idx]
215
  }
216
 
217
  except Exception as e:
218
- print(f"Erreur lors de la prédiction : {str(e)}")
219
- # Retourner une réponse par défaut en cas d'erreur
220
  return {
221
  "status": "error",
222
  "message": str(e),
 
7
  import os
8
  from pathlib import Path
9
  from io import StringIO
10
+ from typing import Dict, Any, Optional
11
  import json
12
+ import logging
13
+
14
+ # Configuration des logs
15
+ logging.basicConfig(level=logging.INFO)
16
+ logger = logging.getLogger(__name__)
17
 
18
  # Import des fonctions de train_strategy_models.py
19
  from train_strategy_models import (
20
  preprocess_data,
21
  train_models,
22
+ save_models,
23
+ predict_best_strategy
24
  )
25
 
26
  # Créer l'application FastAPI
 
40
  MODELS_DIR = BASE_DIR / "models"
41
  MODELS_DIR.mkdir(exist_ok=True)
42
 
43
+ logger.info(f"Dossier de base : {BASE_DIR}")
44
+ logger.info(f"Dossier des modèles : {MODELS_DIR}")
45
 
46
+ # Mise à jour de la classe MarketData pour correspondre aux features
47
  class MarketData(BaseModel):
48
+ # Features continus
49
  RSI: float
50
  ADX: float
51
  Volatility_20: float
52
  MACD: float
53
+ # Signaux des stratégies
54
  Ichimoku_ADX_Volatility_Signal: int
55
  BB_Stoch_ATR_Signal: int
56
  Chikou_MACD_Pente_Signal: int
57
  ADX_Stoch_Volatility_MA_Signal: int
58
 
59
+ class Config:
60
+ schema_extra = {
61
+ "example": {
62
+ "RSI": 50.0,
63
+ "ADX": 25.0,
64
+ "Volatility_20": 0.001,
65
+ "MACD": 0.0,
66
+ "Ichimoku_ADX_Volatility_Signal": 0,
67
+ "BB_Stoch_ATR_Signal": 0,
68
+ "Chikou_MACD_Pente_Signal": 0,
69
+ "ADX_Stoch_Volatility_MA_Signal": 0
70
+ }
71
+ }
72
+
73
  class TrainingResponse(BaseModel):
74
  status: str
75
  message: str
76
  details: Dict[str, Any]
77
 
78
+ def are_models_available() -> bool:
79
  """Vérifie si tous les modèles nécessaires sont disponibles"""
80
  required_files = ["model_profit.joblib", "model_drawdown.joblib", "model_params.joblib"]
81
  return all((MODELS_DIR / file).exists() for file in required_files)
 
84
  """Charge les modèles existants"""
85
  try:
86
  if not are_models_available():
87
+ logger.warning("Modèles non disponibles")
88
  return None, None, None
89
 
90
  model_profit = joblib.load(MODELS_DIR / "model_profit.joblib")
91
  model_drawdown = joblib.load(MODELS_DIR / "model_drawdown.joblib")
92
  model_params = joblib.load(MODELS_DIR / "model_params.joblib")
93
+ logger.info("Modèles chargés avec succès")
94
  return model_profit, model_drawdown, model_params
95
  except Exception as e:
96
+ logger.error(f"Erreur lors du chargement des modèles : {str(e)}")
97
  return None, None, None
98
 
99
  @app.get("/")
 
102
  "message": "Strategy Selector API",
103
  "version": "1.0",
104
  "status": "running",
105
+ "models_available": are_models_available(),
106
  "endpoints": {
107
  "predict": "/predict (POST)",
108
+ "train": "/train (POST)",
109
  "health": "/health (GET)"
110
  }
111
  }
112
 
113
  @app.get("/health")
114
  async def health_check():
115
+ """Endpoint pour vérifier l'état de l'API et des modèles"""
 
 
116
  models_available = are_models_available()
117
  return {
118
  "status": "healthy",
 
127
 
128
  @app.post("/train")
129
  async def train_from_csv(file: UploadFile = File(...)) -> TrainingResponse:
130
+ """Endpoint pour entraîner les modèles à partir des données CSV"""
 
 
131
  try:
132
+ logger.info(f"Réception du fichier : {file.filename}")
133
+
134
  # Lire et valider le contenu du fichier
135
  content = await file.read()
136
  content_str = content.decode('utf-8')
 
140
 
141
  # Vérifier les colonnes requises
142
  required_columns = ['Date', 'Open', 'High', 'Low', 'Close']
143
+ missing_columns = [col for col in required_columns if col not in df.columns]
144
+ if missing_columns:
145
+ raise ValueError(f"Colonnes manquantes : {missing_columns}")
146
 
147
  # Configurer l'index temporel
148
  df['Date'] = pd.to_datetime(df['Date'])
149
  df.set_index('Date', inplace=True)
150
 
151
+ logger.info(f"Données reçues : {len(df)} lignes")
152
 
153
  # Prétraiter les données
154
  df_processed = preprocess_data(df)
155
+ logger.info("Prétraitement terminé")
156
 
157
  # Entraîner les modèles
158
  model_profit, model_drawdown, features, strategies, split_info = train_models(df_processed)
159
+ logger.info("Entraînement terminé")
160
 
161
  # Sauvegarder les modèles
162
  save_models(model_profit, model_drawdown, features, strategies, split_info)
163
+ logger.info("Modèles sauvegardés")
164
 
165
  return TrainingResponse(
166
  status="success",
 
178
  )
179
 
180
  except Exception as e:
181
+ logger.error(f"Erreur lors de l'entraînement : {str(e)}")
182
  raise HTTPException(
183
  status_code=500,
184
  detail=f"Erreur lors de l'entraînement : {str(e)}"
 
186
 
187
  @app.post("/predict")
188
  async def predict(data: MarketData):
189
+ """Endpoint pour faire des prédictions avec les modèles entraînés"""
 
 
190
  try:
191
+ logger.info("Réception d'une requête de prédiction")
192
+
193
  # Vérifier si les modèles sont disponibles
194
  if not are_models_available():
195
+ logger.warning("Modèles non disponibles")
196
  return {
197
  "status": "error",
198
+ "message": "Modèles non disponibles - utilisation des valeurs par défaut",
199
  "best_profit_strategy": "Ichimoku_ADX_Volatility_Signal",
200
  "best_profit_signal": 0,
201
  "best_drawdown_strategy": "BB_Stoch_ATR_Signal",
202
  "best_drawdown_signal": 0
203
  }
204
 
205
+ # Créer un DataFrame avec une seule ligne
206
+ df = pd.DataFrame([{
207
+ 'RSI': data.RSI,
208
+ 'ADX': data.ADX,
209
+ 'Volatility_20': data.Volatility_20,
210
+ 'MACD': data.MACD,
211
+ 'Ichimoku_ADX_Volatility_Signal': data.Ichimoku_ADX_Volatility_Signal,
212
+ 'BB_Stoch_ATR_Signal': data.BB_Stoch_ATR_Signal,
213
+ 'Chikou_MACD_Pente_Signal': data.Chikou_MACD_Pente_Signal,
214
+ 'ADX_Stoch_Volatility_MA_Signal': data.ADX_Stoch_Volatility_MA_Signal
215
+ }])
216
+
217
+ # Utiliser la fonction de prédiction
218
+ result = predict_best_strategy(df)
219
+ if result is None:
220
+ raise Exception("Erreur lors de la prédiction")
221
+
222
+ logger.info(f"Prédiction réussie : {result}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
223
  return {
224
  "status": "success",
225
+ **result
 
 
 
226
  }
227
 
228
  except Exception as e:
229
+ logger.error(f"Erreur lors de la prédiction : {str(e)}")
 
230
  return {
231
  "status": "error",
232
  "message": str(e),