Nexo-S commited on
Commit
04fa252
·
verified ·
1 Parent(s): b4f485e

Upload app.py

Browse files
Files changed (1) hide show
  1. app.py +86 -72
app.py CHANGED
@@ -79,7 +79,7 @@ except:
79
 
80
  # --- DB & SYNC ---
81
  REPO_ID = "Nexo-S/AlphaV15-Quant-Engine"
82
- DB_NAME = "alphatrade_v10.db" # 🎮 NOUVEAU MONDE - NIVEAU 0
83
  HF_TOKEN = os.environ.get("HF_TOKEN")
84
 
85
  def init_db():
@@ -90,7 +90,6 @@ def init_db():
90
  cursor = conn.execute("PRAGMA table_info(signals)")
91
  columns = [col[1] for col in cursor.fetchall()]
92
  conn.close()
93
- # 🔍 On ajoute 'confirmed' dans la vérification de structure
94
  if 'date' not in columns or 'peak_price' not in columns or 'confirmed' not in columns:
95
  reset_needed = True
96
  except Exception as e:
@@ -98,21 +97,19 @@ def init_db():
98
  reset_needed = True
99
 
100
  if reset_needed:
101
- print("⚠️ Structure obsolète. Reset de la base de données pour la Poignée de Main V14...")
102
  if os.path.exists(DB_NAME): os.remove(DB_NAME)
103
 
104
  try:
105
  with sqlite3.connect(DB_NAME) as conn:
106
- # --- 🛡️ TABLE DES SIGNAUX (MISE À JOUR) ---
107
  conn.execute('''CREATE TABLE IF NOT EXISTS signals (
108
  id INTEGER PRIMARY KEY AUTOINCREMENT,
109
  date TEXT, symbol TEXT, timeframe TEXT, direction TEXT,
110
  prob REAL, price REAL, tp REAL, sl REAL, status TEXT,
111
  regime INTEGER, prob_time REAL, prob_ml REAL, prob_lstm REAL, prob_sent REAL,
112
  peak_price REAL,
113
- confirmed INTEGER DEFAULT 0)''') # <--- AJOUTE CETTE COLONNE ICI
114
 
115
- # --- 🚀 AUTO-MIGRATION V12 (Reste inchangé) ---
116
  cursor = conn.cursor()
117
  cursor.execute("PRAGMA table_info(agent_logic)")
118
  al_cols = [col[1] for col in cursor.fetchall()]
@@ -135,11 +132,14 @@ def init_db():
135
 
136
  cursor.execute("SELECT COUNT(*) FROM agent_logic")
137
  if cursor.fetchone()[0] == 0:
138
- defaults = [('ALL', '5m', 1.5, 1.0, 0, 0, 0.60, 0.003), ('ALL', '15m', 2.0, 1.2, 0, 0, 0.55, 0.004),
139
- ('ALL', '1h', 2.5, 1.5, 0, 0, 0.52, 0.005), ('ALL', '4h', 3.0, 2.0, 0, 0, 0.50, 0.008),
140
- ('ALL', '1d', 4.0, 2.5, 0, 0, 0.50, 0.01)]
 
 
 
141
  conn.executemany("INSERT INTO agent_logic VALUES (?, ?, ?, ?, ?, ?, ?, ?)", defaults)
142
- print("✅ Base de données V14 (Synchronisée avec Pterodactyl) opérationnelle.")
143
  except Exception as e:
144
  print(f"❌ Erreur critique création DB: {e}")
145
 
@@ -191,6 +191,12 @@ try:
191
  except Exception as e: print(f"⚠️ Erreur IA : {e}")
192
 
193
  # --- 📊 FEATURES ENGINE ---
 
 
 
 
 
 
194
  def prepare_features_sync(symbol, timeframe='1h', limit_bars=600):
195
  try:
196
  now = datetime.now().timestamp()
@@ -208,21 +214,29 @@ def prepare_features_sync(symbol, timeframe='1h', limit_bars=600):
208
 
209
  if len(df) < 250: return pd.DataFrame()
210
 
 
211
  df["RSI"] = get_rsi(df["close"])
 
212
  df["EMA50"] = get_ema(df["close"], 50)
213
  df["EMA200"] = get_ema(df["close"], 200)
 
214
  df["ATR"] = get_atr(df)
215
  df["ATR_pct"] = (df["ATR"] / df["close"]) * 100
216
  df["EMA200_slope"] = (df["EMA200"] / df["EMA200"].shift(10)) - 1
217
  df["Drawdown"] = (df["close"] / df["close"].rolling(14).max()) - 1
 
 
218
  df["High_24h"], df["Low_24h"] = df["high"].rolling(24).max(), df["low"].rolling(24).min()
219
  df["Dist_High_24h"] = (df["High_24h"] - df["close"]) / df["close"]
220
  df["Dist_Low_24h"] = (df["close"] - df["Low_24h"]) / df["close"]
221
  df["EMA_dist"] = (df["close"] - df["EMA50"]) / df["EMA50"]
222
  df["EMA_slope"] = (df["EMA50"] / df["EMA50"].shift(5)) - 1
 
223
  df["ATR_ratio"] = df["ATR"] / df["close"]
224
  df["VOL_ratio"] = df["vol"] / df["vol"].rolling(24).mean()
 
225
 
 
226
  diff = df["High_24h"] - df["Low_24h"]
227
  df["Fib_618"] = df["Low_24h"] + (diff * 0.618)
228
  df["Dist_Fib_618"] = (df["close"] - df["Fib_618"]) / df["close"]
@@ -245,6 +259,8 @@ def prepare_features_sync(symbol, timeframe='1h', limit_bars=600):
245
  p_low, p_high = df["low"].rolling(24).min().shift(1), df["high"].rolling(24).max().shift(1)
246
  df["Sweep_Low"] = ((df["low"] < p_low) & (df["close"] > p_low)).astype(int)
247
  df["Sweep_High"] = ((df["high"] > p_high) & (df["close"] < p_high)).astype(int)
 
 
248
  df['return_1h'], df['return_3h'], df['return_12h'] = df['close'].pct_change(1), df['close'].pct_change(3), df['close'].pct_change(12)
249
  df['RSI_lag1'], df['RSI_lag2'] = df["RSI"].shift(1), df["RSI"].shift(2)
250
  df['VOL_RATIO'] = df['vol'] / df['vol'].rolling(20).mean()
@@ -258,14 +274,16 @@ async def predict_signal(symbol, timeframe="1h"):
258
  memory_guard()
259
  symbol = str(symbol).strip().upper()
260
 
261
- # 1. Préparation des données
262
  df = prepare_features_sync(symbol, timeframe)
263
  if df.empty: return {"status": "error", "message": f"Data insuffisante pour {timeframe}"}
264
 
265
  last_row = df.iloc[[-1]]
266
  prix, atr = float(last_row['close'].iloc[0]), float(last_row['ATR'].iloc[0])
 
 
 
267
 
268
- # 2. Analyse des 3 cerveaux
269
  regime_scaled = regime_scaler.transform(last_row[["ATR_pct", "EMA200_slope", "Drawdown", "RSI_Macro"]])
270
  regime_pred = int(regime_model.predict(regime_scaled)[0])
271
  ml_cols = ["RSI", "Dist_High_24h", "Dist_Low_24h", "EMA_dist", "EMA_slope", "ATR_ratio", "VOL_ratio"]
@@ -277,14 +295,17 @@ async def predict_signal(symbol, timeframe="1h"):
277
  lstm_prob = float(lstm_brain.predict(np.expand_dims(lstm_data.values, axis=0), verbose=0)[0][0])
278
  p_sent = await get_crypto_sentiment(symbol)
279
 
280
- # 3. Pondération adaptative
281
- if timeframe in ["5m", "15m"]: wm, wt, wl, ws = 0.45, 0.35, 0.15, 0.05
282
- elif timeframe in ["4h", "1d"]: wl, wm, wt, ws = 0.60, 0.15, 0.15, 0.10
283
- else: wt, wm, wl, ws = 0.25, 0.25, 0.25, 0.25
 
 
 
284
 
285
  final_p = (time_prob * wt) + (ml_prob * wm) + (lstm_prob * wl) + (p_sent * ws)
286
 
287
- # 4. Bonus Smart Money (SMC)
288
  sweep_low, sweep_high = int(last_row["Sweep_Low"].iloc[0]), int(last_row["Sweep_High"].iloc[0])
289
  smc_status = "AUCUN"
290
  if sweep_low == 1:
@@ -293,18 +314,22 @@ async def predict_signal(symbol, timeframe="1h"):
293
  elif sweep_high == 1:
294
  final_p = max(0.05, final_p - 0.20)
295
  smc_status = "SHORT SWEEP 🐋"
296
-
297
- # 5. Calcul TP/SL (V12 ISOLATION PAR SYMBOLE)
 
 
 
 
 
 
 
298
  with sqlite3.connect(DB_NAME) as conn:
299
- # On cherche les stats pour CE symbole précis
300
  res = conn.execute("SELECT tp_mult, sl_mult, min_prob, min_tp_dist FROM agent_logic WHERE symbol = ? AND timeframe = ?", (symbol, timeframe)).fetchone()
301
  if res:
302
  tp_m, sl_m, agent_min_prob, agent_min_tp_dist = res
303
  else:
304
- # Si le symbole n'existe pas encore, on prend les stats de base ('ALL')
305
  res_fallback = conn.execute("SELECT tp_mult, sl_mult, min_prob, min_tp_dist FROM agent_logic WHERE symbol = 'ALL' AND timeframe = ?", (timeframe,)).fetchone()
306
- tp_m, sl_m, agent_min_prob, agent_min_tp_dist = res_fallback if res_fallback else (3.0, 1.5, 0.55, 0.004)
307
- # On initialise immédiatement le nouveau cerveau dans la DB
308
  conn.execute("INSERT OR IGNORE INTO agent_logic (symbol, timeframe, tp_mult, sl_mult, score, last_pnl, min_prob, min_tp_dist) VALUES (?, ?, ?, ?, ?, ?, ?, ?)",
309
  (symbol, timeframe, tp_m, sl_m, 0, 0, agent_min_prob, agent_min_tp_dist))
310
  conn.commit()
@@ -312,7 +337,7 @@ async def predict_signal(symbol, timeframe="1h"):
312
  tp = prix + (atr * tp_m) if final_p > 0.5 else prix - (atr * tp_m)
313
  sl = prix - (atr * sl_m) if final_p > 0.5 else prix + (atr * sl_m)
314
 
315
- # 6. Scores
316
  strength = abs(final_p - 0.5) * 2
317
  conf_val = max(0, min(1, 1 - np.std([time_prob, ml_prob, lstm_prob, p_sent])))
318
  score_base = (strength * 45) + (conf_val * 40)
@@ -320,15 +345,14 @@ async def predict_signal(symbol, timeframe="1h"):
320
  composite_score = max(0, min(100, score_base + regime_bonus))
321
 
322
  # ==========================================
323
- # 🛑 LE VETO DYNAMIQUE (BOUCLIER EMA200)
324
  # ==========================================
325
  veto = False
326
  veto_reason = ""
327
  dist_tp_pct = abs(tp - prix) / prix
328
  dist_fib = float(last_row["Dist_Fib_618"].iloc[0])
329
  mkt_trend = float(last_row["Market_Trend"].iloc[0])
330
- ema200_slope_val = float(last_row["EMA200_slope"].iloc[0])
331
- ema200_val = float(last_row["EMA200"].iloc[0]) # 🚀 NOUVEAU: On extrait la ligne EMA 200
332
 
333
  if final_p < agent_min_prob and final_p > (1 - agent_min_prob):
334
  veto, veto_reason = True, f"Confiance ({round(final_p, 2)}) < {round(agent_min_prob, 2)}"
@@ -339,14 +363,13 @@ async def predict_signal(symbol, timeframe="1h"):
339
 
340
  elif final_p > 0.5 and dist_fib < -0.03: veto, veto_reason = True, "Support Fib 61.8 trop loin"
341
  elif final_p < 0.5 and dist_fib > 0.03: veto, veto_reason = True, "Résistance Fib 61.8 trop loin"
342
- elif timeframe in ["5m", "15m"] and smc_status == "AUCUN": veto, veto_reason = True, "Aucun Sweep SMC"
343
  elif dist_tp_pct < agent_min_tp_dist: veto, veto_reason = True, "Gain potentiel trop faible"
344
 
345
- # 🛡️ LE GARDE-FOU ABSOLU (Interdiction stricte de contrer le TGV)
346
- elif final_p < 0.5 and prix > ema200_val:
347
- veto, veto_reason = True, "Garde-Fou: Prix > EMA200 (Short Suicide 🚫)"
348
- elif final_p > 0.5 and prix < ema200_val:
349
- veto, veto_reason = True, "Garde-Fou: Prix < EMA200 (Long Suicide 🚫)"
350
 
351
  if veto:
352
  print(f"🛑 [VETO] {symbol}: {veto_reason}")
@@ -405,8 +428,8 @@ def trigger_training(symbol="BTC/USD"):
405
  return f"❌ Erreur Training : {str(e)}"
406
 
407
  # --- 🚀 MOTEUR AUTO-PILOTE ---
408
- AUTO_SYMBOLS = ["BTC/USD", "ETH/USD", "SOL/USD", "BNB/USD"]
409
- AUTO_TIMEFRAMES = ["5m", "15m", "1h", "4h", "1d"]
410
 
411
  async def auto_predict_loop():
412
  while True:
@@ -522,7 +545,7 @@ def run_judge_api():
522
  ticker = exchange_sync.fetch_ticker(fetch_symbol)
523
  current_price = ticker['last']
524
 
525
- # --- 🚀 LOGIQUE TRAILING STOP (RESPIRATION V16) ---
526
  sl_dynamique = t['sl']
527
  peak = t['peak_price']
528
 
@@ -534,26 +557,25 @@ def run_judge_api():
534
 
535
  nouveau_sl = sl_dynamique
536
 
537
- # --- 🛡️ LA STRATÉGIE DE L'ÉTAU (DESSERRÉ) ---
538
- # On laisse le marché respirer pour éviter de se faire tuer par le Spread
539
  if t['direction'] == 'HAUSSIER':
540
- # PALIER 3 : 80% du but -> On verrouille 60%
541
- if progression >= 0.80:
542
  nouveau_sl = max(sl_dynamique, t['price'] + (chemin_total * 0.60))
543
- # PALIER 2 : 60% du but -> On verrouille 30%
544
- elif progression >= 0.60:
545
- nouveau_sl = max(sl_dynamique, t['price'] + (chemin_total * 0.30))
546
- # PALIER 1 : 40% du but -> BREAK-EVEN (On a assez de marge pour couvrir les frais)
547
- elif progression >= 0.40:
548
- nouveau_sl = max(sl_dynamique, t['price'])
549
 
550
  else: # BAISSIER (SELL)
551
- if progression >= 0.80:
552
  nouveau_sl = min(sl_dynamique, t['price'] - (chemin_total * 0.60))
553
- elif progression >= 0.60:
554
- nouveau_sl = min(sl_dynamique, t['price'] - (chemin_total * 0.30))
555
- elif progression >= 0.40:
556
- nouveau_sl = min(sl_dynamique, t['price'])
557
 
558
  sl_dynamique = nouveau_sl
559
  cursor.execute("UPDATE signals SET peak_price = ?, sl = ? WHERE id = ?", (new_peak, sl_dynamique, t['id']))
@@ -579,55 +601,47 @@ def run_judge_api():
579
 
580
  if outcome:
581
  # =========================================================
582
- # --- 🪞 LA VÉRITÉ BRUTALE (CALCUL PNL RÉEL V16) ---
583
  # =========================================================
584
- # On simule l'exposition réelle avec levier pour le calcul des centimes
585
- taille_position_virtuelle = 500.0
586
 
587
- # On simule la taxe invisible de Capital.com (le Spread)
588
- spread_commission = 0.50
 
589
 
590
- # 1. Calcul du gain brut basé sur le VRAI mouvement de prix
591
  if t['direction'] == 'HAUSSIER':
592
  pnl_brut = ((current_price - t['price']) / t['price']) * taille_position_virtuelle
593
  else:
594
  pnl_brut = ((t['price'] - current_price) / t['price']) * taille_position_virtuelle
595
 
596
- # 2. Le vrai PNL net (Ce qui atterrit sur ton compte)
597
- pnl_dollars = pnl_brut - spread_commission
598
 
599
- # 3. Correction honnête du statut Discord
600
  if outcome == "SL TOUCHÉ 🛡️" and pnl_dollars < 0:
601
- outcome = "TUÉ PAR LE SPREAD 🩸"
602
  elif outcome == "GAGNÉ (PARTIEL) 💸" and pnl_dollars <= 0:
603
- outcome = "FAUX GAIN (SPREAD) 📉"
604
 
605
- # =========================================================
606
- # --- 🎮 V12 : ÉVOLUTION RPG (ISOLATION + ANTI YO-YO) ---
607
- # =========================================================
608
  cursor.execute("SELECT tp_mult, sl_mult, score, min_prob FROM agent_logic WHERE symbol = ? AND timeframe = ?", (t['symbol'], t['timeframe']))
609
  row = cursor.fetchone()
610
 
611
  if row:
612
  tp_m, sl_m, score_ia, min_p = row
613
  else:
614
- # Fallback si jamais la DB a raté la création
615
- tp_m, sl_m, score_ia, min_p = (3.0, 1.5, 0, 0.55)
616
  cursor.execute("INSERT INTO agent_logic (symbol, timeframe, tp_mult, sl_mult, score, min_prob) VALUES (?, ?, ?, ?, ?, ?)",
617
  (t['symbol'], t['timeframe'], tp_m, sl_m, score_ia, min_p))
618
 
619
- # LISSAGE ANTI YO-YO (Amortissement à 1.5%)
620
  if reward > 0:
621
- tp_m = min(5.0, tp_m * 1.015)
622
  min_p = max(0.50, min_p - 0.005)
623
  elif reward < 0:
624
- sl_m = max(1.0, sl_m * 0.985)
625
- tp_m = max(1.5, tp_m * 0.985)
626
  min_p = min(0.65, min_p + 0.01)
627
 
628
  cursor.execute("UPDATE agent_logic SET tp_mult=?, sl_mult=?, score=score+?, min_prob=? WHERE symbol=? AND timeframe=?",
629
  (tp_m, sl_m, reward, min_p, t['symbol'], t['timeframe']))
630
- # =========================================================
631
 
632
  cursor.execute("UPDATE signals SET status=? WHERE id=?", (outcome, t['id']))
633
 
 
79
 
80
  # --- DB & SYNC ---
81
  REPO_ID = "Nexo-S/AlphaV15-Quant-Engine"
82
+ DB_NAME = "alphatrade_v22_scalp.db" # 🎮 NOUVEAU MONDE - NIVEAU 0
83
  HF_TOKEN = os.environ.get("HF_TOKEN")
84
 
85
  def init_db():
 
90
  cursor = conn.execute("PRAGMA table_info(signals)")
91
  columns = [col[1] for col in cursor.fetchall()]
92
  conn.close()
 
93
  if 'date' not in columns or 'peak_price' not in columns or 'confirmed' not in columns:
94
  reset_needed = True
95
  except Exception as e:
 
97
  reset_needed = True
98
 
99
  if reset_needed:
100
+ print("⚠️ Structure obsolète. Reset de la base de données...")
101
  if os.path.exists(DB_NAME): os.remove(DB_NAME)
102
 
103
  try:
104
  with sqlite3.connect(DB_NAME) as conn:
 
105
  conn.execute('''CREATE TABLE IF NOT EXISTS signals (
106
  id INTEGER PRIMARY KEY AUTOINCREMENT,
107
  date TEXT, symbol TEXT, timeframe TEXT, direction TEXT,
108
  prob REAL, price REAL, tp REAL, sl REAL, status TEXT,
109
  regime INTEGER, prob_time REAL, prob_ml REAL, prob_lstm REAL, prob_sent REAL,
110
  peak_price REAL,
111
+ confirmed INTEGER DEFAULT 0)''')
112
 
 
113
  cursor = conn.cursor()
114
  cursor.execute("PRAGMA table_info(agent_logic)")
115
  al_cols = [col[1] for col in cursor.fetchall()]
 
132
 
133
  cursor.execute("SELECT COUNT(*) FROM agent_logic")
134
  if cursor.fetchone()[0] == 0:
135
+ # NOUVEAU: Defaults ultra-agressifs pour le Scalping
136
+ defaults = [('ALL', '1m', 1.0, 0.8, 0, 0, 0.65, 0.001),
137
+ ('ALL', '5m', 1.2, 0.9, 0, 0, 0.62, 0.002),
138
+ ('ALL', '15m', 1.5, 1.0, 0, 0, 0.60, 0.003),
139
+ ('ALL', '1h', 2.0, 1.5, 0, 0, 0.55, 0.005),
140
+ ('ALL', '4h', 3.0, 2.0, 0, 0, 0.50, 0.008)]
141
  conn.executemany("INSERT INTO agent_logic VALUES (?, ?, ?, ?, ?, ?, ?, ?)", defaults)
142
+ print("✅ Base de données Scalping (MT5 Exness) opérationnelle.")
143
  except Exception as e:
144
  print(f"❌ Erreur critique création DB: {e}")
145
 
 
191
  except Exception as e: print(f"⚠️ Erreur IA : {e}")
192
 
193
  # --- 📊 FEATURES ENGINE ---
194
+ def get_vwap(df):
195
+ """Calcul du Volume Weighted Average Price (Outil #1 du Scalpeur)"""
196
+ v = df['vol']
197
+ tp = (df['high'] + df['low'] + df['close']) / 3
198
+ return (tp * v).cumsum() / v.cumsum()
199
+
200
  def prepare_features_sync(symbol, timeframe='1h', limit_bars=600):
201
  try:
202
  now = datetime.now().timestamp()
 
214
 
215
  if len(df) < 250: return pd.DataFrame()
216
 
217
+ # --- FONDATIONS TECHNIQUES ---
218
  df["RSI"] = get_rsi(df["close"])
219
+ df["RSI_9"] = get_rsi(df["close"], 9) # ⚡ NOUVEAU: RSI Nerveux pour Scalping
220
  df["EMA50"] = get_ema(df["close"], 50)
221
  df["EMA200"] = get_ema(df["close"], 200)
222
+ df["VWAP"] = get_vwap(df) # ⚡ NOUVEAU: Le juge de paix
223
  df["ATR"] = get_atr(df)
224
  df["ATR_pct"] = (df["ATR"] / df["close"]) * 100
225
  df["EMA200_slope"] = (df["EMA200"] / df["EMA200"].shift(10)) - 1
226
  df["Drawdown"] = (df["close"] / df["close"].rolling(14).max()) - 1
227
+
228
+ # --- DISTANCES & SLOPES ---
229
  df["High_24h"], df["Low_24h"] = df["high"].rolling(24).max(), df["low"].rolling(24).min()
230
  df["Dist_High_24h"] = (df["High_24h"] - df["close"]) / df["close"]
231
  df["Dist_Low_24h"] = (df["close"] - df["Low_24h"]) / df["close"]
232
  df["EMA_dist"] = (df["close"] - df["EMA50"]) / df["EMA50"]
233
  df["EMA_slope"] = (df["EMA50"] / df["EMA50"].shift(5)) - 1
234
+ df["Price_vs_VWAP"] = (df["close"] - df["VWAP"]) / df["VWAP"] # ⚡ Distance au VWAP
235
  df["ATR_ratio"] = df["ATR"] / df["close"]
236
  df["VOL_ratio"] = df["vol"] / df["vol"].rolling(24).mean()
237
+ df["Vol_Spike"] = df["vol"] / df["vol"].rolling(5).mean() # ⚡ NOUVEAU: Explosion de volume
238
 
239
+ # --- SMC & FIBO ---
240
  diff = df["High_24h"] - df["Low_24h"]
241
  df["Fib_618"] = df["Low_24h"] + (diff * 0.618)
242
  df["Dist_Fib_618"] = (df["close"] - df["Fib_618"]) / df["close"]
 
259
  p_low, p_high = df["low"].rolling(24).min().shift(1), df["high"].rolling(24).max().shift(1)
260
  df["Sweep_Low"] = ((df["low"] < p_low) & (df["close"] > p_low)).astype(int)
261
  df["Sweep_High"] = ((df["high"] > p_high) & (df["close"] < p_high)).astype(int)
262
+
263
+ # --- FEATURES POUR LES MODELES ---
264
  df['return_1h'], df['return_3h'], df['return_12h'] = df['close'].pct_change(1), df['close'].pct_change(3), df['close'].pct_change(12)
265
  df['RSI_lag1'], df['RSI_lag2'] = df["RSI"].shift(1), df["RSI"].shift(2)
266
  df['VOL_RATIO'] = df['vol'] / df['vol'].rolling(20).mean()
 
274
  memory_guard()
275
  symbol = str(symbol).strip().upper()
276
 
 
277
  df = prepare_features_sync(symbol, timeframe)
278
  if df.empty: return {"status": "error", "message": f"Data insuffisante pour {timeframe}"}
279
 
280
  last_row = df.iloc[[-1]]
281
  prix, atr = float(last_row['close'].iloc[0]), float(last_row['ATR'].iloc[0])
282
+ vwap = float(last_row['VWAP'].iloc[0]) # ⚡ Extraction VWAP
283
+ vol_spike = float(last_row['Vol_Spike'].iloc[0]) # ⚡ Extraction Spike
284
+ rsi_9 = float(last_row['RSI_9'].iloc[0]) # ⚡ Extraction RSI Rapide
285
 
286
+ # --- 🧠 CERVEAUX IA ---
287
  regime_scaled = regime_scaler.transform(last_row[["ATR_pct", "EMA200_slope", "Drawdown", "RSI_Macro"]])
288
  regime_pred = int(regime_model.predict(regime_scaled)[0])
289
  ml_cols = ["RSI", "Dist_High_24h", "Dist_Low_24h", "EMA_dist", "EMA_slope", "ATR_ratio", "VOL_ratio"]
 
295
  lstm_prob = float(lstm_brain.predict(np.expand_dims(lstm_data.values, axis=0), verbose=0)[0][0])
296
  p_sent = await get_crypto_sentiment(symbol)
297
 
298
+ # --- ⚖️ PONDÉRATION ADAPTATIVE (FOCUS MOMENTUM SCALPING) ---
299
+ if timeframe in ["1m", "5m", "15m"]:
300
+ wt, wm, wl, ws = 0.50, 0.25, 0.15, 0.10 # On fait confiance au TimeModel pour la vitesse
301
+ elif timeframe in ["1h", "4h", "1d"]:
302
+ wl, wm, wt, ws = 0.60, 0.15, 0.15, 0.10
303
+ else:
304
+ wt, wm, wl, ws = 0.25, 0.25, 0.25, 0.25
305
 
306
  final_p = (time_prob * wt) + (ml_prob * wm) + (lstm_prob * wl) + (p_sent * ws)
307
 
308
+ # --- 🐋 BONUS SMART MONEY & SCALPING NITRO ---
309
  sweep_low, sweep_high = int(last_row["Sweep_Low"].iloc[0]), int(last_row["Sweep_High"].iloc[0])
310
  smc_status = "AUCUN"
311
  if sweep_low == 1:
 
314
  elif sweep_high == 1:
315
  final_p = max(0.05, final_p - 0.20)
316
  smc_status = "SHORT SWEEP 🐋"
317
+
318
+ # NITRO SCALPING : Si on a un pic de volume dans le sens de la tendance
319
+ if timeframe in ["1m", "5m"]:
320
+ if final_p > 0.5 and vol_spike > 1.5 and rsi_9 < 70:
321
+ final_p = min(0.95, final_p + 0.10) # Coup de boost à l'achat
322
+ elif final_p < 0.5 and vol_spike > 1.5 and rsi_9 > 30:
323
+ final_p = max(0.05, final_p - 0.10) # Coup de boost à la vente
324
+
325
+ # --- 🛡️ CALCUL TP/SL (BASE DE DONNÉES ISOLÉE) ---
326
  with sqlite3.connect(DB_NAME) as conn:
 
327
  res = conn.execute("SELECT tp_mult, sl_mult, min_prob, min_tp_dist FROM agent_logic WHERE symbol = ? AND timeframe = ?", (symbol, timeframe)).fetchone()
328
  if res:
329
  tp_m, sl_m, agent_min_prob, agent_min_tp_dist = res
330
  else:
 
331
  res_fallback = conn.execute("SELECT tp_mult, sl_mult, min_prob, min_tp_dist FROM agent_logic WHERE symbol = 'ALL' AND timeframe = ?", (timeframe,)).fetchone()
332
+ tp_m, sl_m, agent_min_prob, agent_min_tp_dist = res_fallback if res_fallback else (1.5, 1.0, 0.55, 0.002)
 
333
  conn.execute("INSERT OR IGNORE INTO agent_logic (symbol, timeframe, tp_mult, sl_mult, score, last_pnl, min_prob, min_tp_dist) VALUES (?, ?, ?, ?, ?, ?, ?, ?)",
334
  (symbol, timeframe, tp_m, sl_m, 0, 0, agent_min_prob, agent_min_tp_dist))
335
  conn.commit()
 
337
  tp = prix + (atr * tp_m) if final_p > 0.5 else prix - (atr * tp_m)
338
  sl = prix - (atr * sl_m) if final_p > 0.5 else prix + (atr * sl_m)
339
 
340
+ # Scores et Regimes
341
  strength = abs(final_p - 0.5) * 2
342
  conf_val = max(0, min(1, 1 - np.std([time_prob, ml_prob, lstm_prob, p_sent])))
343
  score_base = (strength * 45) + (conf_val * 40)
 
345
  composite_score = max(0, min(100, score_base + regime_bonus))
346
 
347
  # ==========================================
348
+ # 🛑 LE VETO DYNAMIQUE (BOUCLIER EMA200 + VWAP)
349
  # ==========================================
350
  veto = False
351
  veto_reason = ""
352
  dist_tp_pct = abs(tp - prix) / prix
353
  dist_fib = float(last_row["Dist_Fib_618"].iloc[0])
354
  mkt_trend = float(last_row["Market_Trend"].iloc[0])
355
+ ema200_val = float(last_row["EMA200"].iloc[0])
 
356
 
357
  if final_p < agent_min_prob and final_p > (1 - agent_min_prob):
358
  veto, veto_reason = True, f"Confiance ({round(final_p, 2)}) < {round(agent_min_prob, 2)}"
 
363
 
364
  elif final_p > 0.5 and dist_fib < -0.03: veto, veto_reason = True, "Support Fib 61.8 trop loin"
365
  elif final_p < 0.5 and dist_fib > 0.03: veto, veto_reason = True, "Résistance Fib 61.8 trop loin"
 
366
  elif dist_tp_pct < agent_min_tp_dist: veto, veto_reason = True, "Gain potentiel trop faible"
367
 
368
+ # 🛡️ LE GARDE-FOU ABSOLU (Interdiction stricte de contrer le TGV institutionnel)
369
+ elif final_p < 0.5 and (prix > ema200_val or prix > vwap):
370
+ veto, veto_reason = True, "Garde-Fou: Prix > EMA200/VWAP (Short Interdit 🚫)"
371
+ elif final_p > 0.5 and (prix < ema200_val or prix < vwap):
372
+ veto, veto_reason = True, "Garde-Fou: Prix < EMA200/VWAP (Long Interdit 🚫)"
373
 
374
  if veto:
375
  print(f"🛑 [VETO] {symbol}: {veto_reason}")
 
428
  return f"❌ Erreur Training : {str(e)}"
429
 
430
  # --- 🚀 MOTEUR AUTO-PILOTE ---
431
+ AUTO_SYMBOLS = ["BTC/USD"]
432
+ AUTO_TIMEFRAMES = ["1m", "5m", "15m", "1h", "4h"]
433
 
434
  async def auto_predict_loop():
435
  while True:
 
545
  ticker = exchange_sync.fetch_ticker(fetch_symbol)
546
  current_price = ticker['last']
547
 
548
+ # --- 🚀 LOGIQUE TRAILING STOP (SCALPING AGRESSIF) ---
549
  sl_dynamique = t['sl']
550
  peak = t['peak_price']
551
 
 
557
 
558
  nouveau_sl = sl_dynamique
559
 
560
+ # --- 🛡️ LA STRATÉGIE DE L'ÉTAU (SERRÉ POUR SCALPING) ---
 
561
  if t['direction'] == 'HAUSSIER':
562
+ # PALIER 3 : 75% du but -> On verrouille 60%
563
+ if progression >= 0.75:
564
  nouveau_sl = max(sl_dynamique, t['price'] + (chemin_total * 0.60))
565
+ # PALIER 2 : 50% du but -> On verrouille 25% (On est déjà en profit assuré)
566
+ elif progression >= 0.50:
567
+ nouveau_sl = max(sl_dynamique, t['price'] + (chemin_total * 0.25))
568
+ # PALIER 1 : 25% du but -> BREAK-EVEN (Serrage très rapide)
569
+ elif progression >= 0.25:
570
+ nouveau_sl = max(sl_dynamique, t['price'] + (chemin_total * 0.05)) # BE + Micro profit pour couvrir commission
571
 
572
  else: # BAISSIER (SELL)
573
+ if progression >= 0.75:
574
  nouveau_sl = min(sl_dynamique, t['price'] - (chemin_total * 0.60))
575
+ elif progression >= 0.50:
576
+ nouveau_sl = min(sl_dynamique, t['price'] - (chemin_total * 0.25))
577
+ elif progression >= 0.25:
578
+ nouveau_sl = min(sl_dynamique, t['price'] - (chemin_total * 0.05))
579
 
580
  sl_dynamique = nouveau_sl
581
  cursor.execute("UPDATE signals SET peak_price = ?, sl = ? WHERE id = ?", (new_peak, sl_dynamique, t['id']))
 
601
 
602
  if outcome:
603
  # =========================================================
604
+ # --- 🪞 LA VÉRITÉ BRUTALE (EXNESS MT5 EDITION) ---
605
  # =========================================================
606
+ taille_position_virtuelle = 1000.0 # Standard pour estimer
 
607
 
608
+ # NOUVEAU: Exness a des spreads très serrés (surtout Raw/Zero), on ajuste l'estimation
609
+ # Sur BTC, le spread tourne autour de 0.5 à 2.0 pips. On compte aussi la commission.
610
+ spread_commission_estimee = 0.25
611
 
 
612
  if t['direction'] == 'HAUSSIER':
613
  pnl_brut = ((current_price - t['price']) / t['price']) * taille_position_virtuelle
614
  else:
615
  pnl_brut = ((t['price'] - current_price) / t['price']) * taille_position_virtuelle
616
 
617
+ pnl_dollars = pnl_brut - spread_commission_estimee
 
618
 
 
619
  if outcome == "SL TOUCHÉ 🛡️" and pnl_dollars < 0:
620
+ outcome = "TUÉ PAR LE SPREAD/COMM 🩸"
621
  elif outcome == "GAGNÉ (PARTIEL) 💸" and pnl_dollars <= 0:
622
+ outcome = "FAUX GAIN (FRAIS EXNESS) 📉"
623
 
624
+ # --- 🎮 ÉVOLUTION RPG ---
 
 
625
  cursor.execute("SELECT tp_mult, sl_mult, score, min_prob FROM agent_logic WHERE symbol = ? AND timeframe = ?", (t['symbol'], t['timeframe']))
626
  row = cursor.fetchone()
627
 
628
  if row:
629
  tp_m, sl_m, score_ia, min_p = row
630
  else:
631
+ tp_m, sl_m, score_ia, min_p = (1.5, 1.0, 0, 0.55)
 
632
  cursor.execute("INSERT INTO agent_logic (symbol, timeframe, tp_mult, sl_mult, score, min_prob) VALUES (?, ?, ?, ?, ?, ?)",
633
  (t['symbol'], t['timeframe'], tp_m, sl_m, score_ia, min_p))
634
 
 
635
  if reward > 0:
636
+ tp_m = min(4.0, tp_m * 1.01) # Moins d'inflation de TP pour garder le mode Scalp
637
  min_p = max(0.50, min_p - 0.005)
638
  elif reward < 0:
639
+ sl_m = max(0.8, sl_m * 0.985) # On serre le SL si on perd
640
+ tp_m = max(1.1, tp_m * 0.985) # On abaisse le TP si on perd trop (Scalping de survie)
641
  min_p = min(0.65, min_p + 0.01)
642
 
643
  cursor.execute("UPDATE agent_logic SET tp_mult=?, sl_mult=?, score=score+?, min_prob=? WHERE symbol=? AND timeframe=?",
644
  (tp_m, sl_m, reward, min_p, t['symbol'], t['timeframe']))
 
645
 
646
  cursor.execute("UPDATE signals SET status=? WHERE id=?", (outcome, t['id']))
647