Nexo-S commited on
Commit
63fc3a4
·
verified ·
1 Parent(s): 30b64bc

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +279 -67
app.py CHANGED
@@ -109,6 +109,7 @@ def backup_db_to_hf():
109
  def init_db():
110
  try:
111
  with sqlite3.connect(DB_NAME) as conn:
 
112
  conn.execute('''CREATE TABLE IF NOT EXISTS signals (
113
  id INTEGER PRIMARY KEY AUTOINCREMENT,
114
  date TEXT, symbol TEXT, timeframe TEXT, direction TEXT,
@@ -117,25 +118,53 @@ def init_db():
117
  peak_price REAL, confirmed INTEGER DEFAULT 0)''')
118
 
119
  cursor = conn.cursor()
120
- cursor.execute("PRAGMA table_info(agent_logic)")
121
- if not [col[1] for col in cursor.fetchall()]:
122
- conn.execute('''CREATE TABLE IF NOT EXISTS agent_logic (
123
- symbol TEXT, timeframe TEXT, tp_mult REAL, sl_mult REAL,
124
- score REAL, last_pnl REAL, min_prob REAL, min_tp_dist REAL,
125
- generation INTEGER DEFAULT 1, best_tp REAL, best_sl REAL,
126
- PRIMARY KEY (symbol, timeframe))''')
127
-
128
- cursor.execute("SELECT COUNT(*) FROM agent_logic")
129
- if cursor.fetchone()[0] == 0:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
130
  defaults = [
131
- ('ALL', '1m', 1.0, 0.8, 0, 0, 0.65, 0.001, 1, 0, 0),
132
- ('ALL', '5m', 1.2, 0.9, 0, 0, 0.62, 0.002, 1, 0, 0),
133
- ('ALL', '15m', 1.5, 1.0, 0, 0, 0.60, 0.003, 1, 0, 0),
134
- ('ALL', '1h', 2.0, 1.5, 0, 0, 0.55, 0.005, 1, 0, 0),
135
- ('ALL', '4h', 3.0, 2.0, 0, 0, 0.50, 0.008, 1, 0, 0)
 
136
  ]
137
- conn.executemany("INSERT INTO agent_logic VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", defaults)
138
- except Exception as e: print(f"❌ Erreur DB: {e}")
 
 
 
 
 
 
 
 
 
 
 
 
 
139
 
140
  async def save_to_db(data):
141
  try:
@@ -266,6 +295,8 @@ def prepare_features_sync(symbol, timeframe='1h', limit_bars=600):
266
  return df.dropna().copy()
267
  except Exception as e: print(f"❌ Error Stats: {e}"); return pd.DataFrame()
268
 
 
 
269
  async def predict_signal(symbol, timeframe="1h"):
270
  try:
271
  memory_guard()
@@ -279,14 +310,26 @@ async def predict_signal(symbol, timeframe="1h"):
279
  vol_spike = float(last_row['Vol_Spike'].iloc[0])
280
  rsi_9 = float(last_row['RSI_9'].iloc[0])
281
 
 
282
  futures_data = await fetch_kucoin_futures_data(symbol)
283
  oi, cvd = futures_data["oi"], futures_data["cvd"]
 
284
 
 
 
285
  regime_scaled = regime_scaler.transform(last_row[["ATR_pct", "EMA200_slope", "Drawdown", "RSI_Macro"]])
286
- regime_pred = int(regime_model.predict(regime_scaled)[0])
287
 
 
 
 
 
 
 
 
288
  ml_cols = ["RSI", "Dist_High_24h", "Dist_Low_24h", "EMA_dist", "EMA_slope", "ATR_ratio", "VOL_ratio"]
289
  ml_prob = float(ml_model.predict_proba(last_row[ml_cols])[0][1])
 
290
  time_cols = ['return_1h', 'return_3h', 'return_12h', 'RSI_lag1', 'RSI_lag2', 'vol_lag1', 'VOL_RATIO']
291
  time_prob = float(time_model.predict_proba(last_row[time_cols])[0][1])
292
 
@@ -294,85 +337,229 @@ async def predict_signal(symbol, timeframe="1h"):
294
  if dino_brain:
295
  try: dino_prob = float(dino_brain.predict(last_row[ml_cols].values)[0])
296
  except: dino_prob = 0.5
297
-
298
- p_sent = await get_crypto_sentiment(symbol)
299
 
300
- # ⚡ ENSEMBLE V30
301
- final_p, wt, wm, wl, ws = combine_scores(symbol, timeframe, time_prob, ml_prob, dino_prob, p_sent, regime_pred)
302
 
 
303
  smc_status = "AUCUN"
 
304
  if int(last_row["Sweep_Low"].iloc[0]) == 1 and cvd > 0:
305
- final_p = min(0.95, final_p + 0.20)
306
- smc_status = "LONG SWEEP + CVD 🐋"
307
  elif int(last_row["Sweep_High"].iloc[0]) == 1 and cvd < 0:
308
- final_p = max(0.05, final_p - 0.20)
309
- smc_status = "SHORT SWEEP + CVD 🐋"
310
 
 
311
  if timeframe in ["1m", "5m"]:
312
  if final_p > 0.5 and vol_spike > 1.5 and rsi_9 < 70: final_p = min(0.95, final_p + 0.10)
313
  elif final_p < 0.5 and vol_spike > 1.5 and rsi_9 > 30: final_p = max(0.05, final_p - 0.10)
314
 
 
315
  with sqlite3.connect(DB_NAME) as conn:
316
- res = conn.execute("SELECT tp_mult, sl_mult, min_prob, min_tp_dist FROM agent_logic WHERE symbol = ? AND timeframe = ?", (symbol, timeframe)).fetchone()
317
- if res: tp_m, sl_m, agent_min_prob, agent_min_tp_dist = res
318
- else:
319
- res_fallback = conn.execute("SELECT tp_mult, sl_mult, min_prob, min_tp_dist FROM agent_logic WHERE symbol = 'ALL' AND timeframe = ?", (timeframe,)).fetchone()
320
- 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)
321
- conn.execute("INSERT OR IGNORE INTO agent_logic (symbol, timeframe, tp_mult, sl_mult, score, last_pnl, min_prob, min_tp_dist, generation, best_tp, best_sl) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", (symbol, timeframe, tp_m, sl_m, 0, 0, agent_min_prob, agent_min_tp_dist, 1, 0, 0))
 
322
  conn.commit()
 
323
 
 
324
  tp = prix + (atr * tp_m) if final_p > 0.5 else prix - (atr * tp_m)
325
  sl = prix - (atr * sl_m) if final_p > 0.5 else prix + (atr * sl_m)
326
 
 
327
  strength = abs(final_p - 0.5) * 2
328
  conf_val = max(0, min(1, 1 - np.std([time_prob, ml_prob, dino_prob, p_sent])))
329
- composite_score = max(0, min(100, (strength * 45) + (conf_val * 40) + (15 if regime_pred in [0, 1] else 5)))
330
 
331
- veto, veto_reason = False, ""
332
  dist_tp_pct = abs(tp - prix) / prix
333
- dist_fib = float(last_row["Dist_Fib_618"].iloc[0])
334
- mkt_trend = float(last_row["Market_Trend"].iloc[0])
335
  ema200_val = float(last_row["EMA200"].iloc[0])
336
-
 
 
 
337
  if final_p < agent_min_prob and final_p > (1 - agent_min_prob): veto, veto_reason = True, f"Confiance ({round(final_p, 2)}) < {round(agent_min_prob, 2)}"
338
- elif "BTC" not in symbol and "ETH" not in symbol:
339
- if final_p < 0.5 and mkt_trend > 0.002: veto, veto_reason = True, "Marché Global Haussier"
340
- elif final_p > 0.5 and mkt_trend < -0.002: veto, veto_reason = True, "Marché Global Baissier"
341
- elif final_p > 0.5 and dist_fib < -0.03: veto, veto_reason = True, "Support Fib 61.8 trop loin"
342
- elif final_p < 0.5 and dist_fib > 0.03: veto, veto_reason = True, "Résistance Fib 61.8 trop loin"
343
  elif dist_tp_pct < agent_min_tp_dist: veto, veto_reason = True, "Gain potentiel trop faible"
344
- elif final_p < 0.5 and (prix > ema200_val or prix > vwap): veto, veto_reason = True, "Short Interdit 🚫"
345
- elif final_p > 0.5 and (prix < ema200_val or prix < vwap): veto, veto_reason = True, "Long Interdit 🚫"
 
 
 
346
 
347
  if veto:
348
- return {"symbol": symbol, "timeframe": timeframe, "status": "veto", "message": veto_reason, "price": prix, "final_score": round(final_p, 4)}
 
349
 
350
- db_task = (datetime.now(timezone.utc).isoformat(), symbol, timeframe, 'HAUSSIER' if final_p > 0.5 else 'BAISSIER', final_p, prix, tp, sl, 'EN_COURS', regime_pred, time_prob, ml_prob, dino_prob, p_sent, prix)
 
351
  await save_to_db(db_task)
352
 
353
  return {
354
  "symbol": symbol, "timeframe": timeframe, "status": "success", "final_score": round(final_p, 4), "score": int(composite_score),
355
- "smart_money": smc_status, "price": prix, "tp": round(tp, 6), "sl": round(sl, 6), "regime": regime_pred, "confluence": round(conf_val * 100, 1)
356
  }
357
  except Exception as e: return {"status": "error", "message": str(e)}
358
 
359
  # --- 🧬 FONCTION D'ÉVOLUTION ---
360
- def mutate_agent(symbol, timeframe, success=True):
 
361
  try:
362
  with sqlite3.connect(DB_NAME) as conn:
363
  conn.row_factory = sqlite3.Row
364
- row = conn.execute("SELECT * FROM agent_logic WHERE symbol = ? AND timeframe = ?", (symbol, timeframe)).fetchone()
365
- if not row: return
 
366
 
367
- tp, sl, prob = row['tp_mult'], row['sl_mult'], row['min_prob']
368
- if success: new_tp, new_sl, new_prob = tp * random.uniform(1.01, 1.05), sl * random.uniform(0.98, 1.00), max(0.60, prob - 0.01)
369
- else: new_tp, new_sl, new_prob = tp * random.uniform(0.90, 0.98), sl * random.uniform(1.02, 1.10), min(0.85, prob + 0.02)
370
-
371
- conn.execute('''UPDATE agent_logic SET tp_mult = ?, sl_mult = ?, min_prob = ?, generation = generation + 1 WHERE symbol = ? AND timeframe = ?''', (new_tp, new_sl, new_prob, symbol, timeframe))
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
372
 
373
- # ☁️ SAUVEGARDE HF APRÈS MUTATION
374
  backup_db_to_hf()
375
- except Exception as e: print(f"🧬 Erreur Mutation : {e}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
376
 
377
  # --- 🧠 TRAINING ENGINE ---
378
  def trigger_training(symbol="BTC/USD"):
@@ -440,21 +627,31 @@ async def dream_simulation_loop():
440
  df = prepare_features_sync(symbol, timeframe=tf, limit_bars=200)
441
  if df.empty or len(df) < 50: continue
442
 
 
443
  idx = random.randint(10, len(df) - 30)
 
444
  pred = await predict_signal(symbol, timeframe=tf)
 
445
  if pred.get("status") == "success":
 
 
 
446
  future_data = df.iloc[idx+1 : idx+25]
447
  win = False
448
  for _, row in future_data.iterrows():
449
- if pred["final_score"] > 0.5:
450
  if row['high'] >= pred['tp']: win = True; break
451
  if row['low'] <= pred['sl']: win = False; break
452
- else:
453
  if row['low'] <= pred['tp']: win = True; break
454
  if row['high'] >= pred['sl']: win = False; break
455
- mutate_agent(symbol, tf, success=win)
 
 
 
456
  await asyncio.sleep(3)
457
- except Exception: pass
 
458
  await asyncio.sleep(60)
459
 
460
  # --- ⚖️ TOOLS ---
@@ -482,29 +679,35 @@ def run_judge_api():
482
  try:
483
  with sqlite3.connect(DB_NAME, timeout=10) as conn:
484
  conn.row_factory, cursor = sqlite3.Row, conn.cursor()
 
485
  cursor.execute("SELECT * FROM signals WHERE status = 'EN_COURS' AND confirmed = 1")
486
  trades = cursor.fetchall()
 
487
  if not trades: return {"status": "waiting"}
488
  closed_trades = []
 
489
  for t in trades:
490
  try:
491
  sym_db, current_price = t['symbol'], None
 
492
  if MT5_AVAILABLE:
493
  epic = sym_db.replace("/", "").replace("USDT", "USD")
494
  if not epic.endswith("m"): epic += "m"
495
  tick = mt5.symbol_info_tick(epic)
496
  if tick: current_price = tick.last
 
497
  if current_price is None:
498
  f_sym = sym_db if "/USDT" in sym_db else sym_db.replace("/USD", "/USDT")
499
  if "/" not in f_sym: f_sym += "/USDT"
500
  current_price = exchange_sync.fetch_ticker(f_sym)['last']
501
 
502
- diff = (t['price'] - current_price) if t['direction'] == 'BAISSIER' else (current_price - t['price'])
503
  sl_dyn, peak = t['sl'], t['peak_price']
504
  new_peak = max(peak, current_price) if t['direction'] == 'HAUSSIER' else min(peak, current_price)
505
  progression = abs(current_price - t['price']) / abs(t['tp'] - t['price']) if abs(t['tp'] - t['price']) > 0 else 0
506
 
507
  nouveau_sl = sl_dyn
 
508
  if t['direction'] == 'HAUSSIER':
509
  if progression >= 0.75: nouveau_sl = max(sl_dyn, t['price'] + (abs(t['tp'] - t['price']) * 0.60))
510
  elif progression >= 0.50: nouveau_sl = max(sl_dyn, t['price'] + (abs(t['tp'] - t['price']) * 0.25))
@@ -513,6 +716,8 @@ def run_judge_api():
513
  elif progression >= 0.50: nouveau_sl = min(sl_dyn, t['price'] - (abs(t['tp'] - t['price']) * 0.25))
514
 
515
  cursor.execute("UPDATE signals SET peak_price = ?, sl = ? WHERE id = ?", (new_peak, nouveau_sl, t['id']))
 
 
516
  outcome, reward = None, 0
517
  if t['direction'] == 'HAUSSIER':
518
  if current_price >= t['tp']: outcome, reward = "GAGNÉ ✅", 3
@@ -521,15 +726,22 @@ def run_judge_api():
521
  if current_price <= t['tp']: outcome, reward = "GAGNÉ ✅", 3
522
  elif current_price >= nouveau_sl: outcome, reward = ("GAGNÉ (PARTIEL) 💸", 1) if nouveau_sl < t['price'] else ("PERDU ❌", -5)
523
 
 
524
  if outcome:
525
- mutate_agent(t['symbol'], t['timeframe'], success=(reward>0))
 
 
 
526
  cursor.execute("UPDATE signals SET status=? WHERE id=?", (outcome, t['id']))
527
- closed_trades.append({"symbol": t['symbol'], "id": t['id']})
528
- except: pass
 
 
 
529
  conn.commit()
530
  return {"status": "updates", "data": closed_trades} if closed_trades else {"status": "waiting"}
531
- except Exception as e: return {"status": "error", "message": str(e)}
532
-
533
  def training_wrapper(symbol, *args): return trigger_training(str(symbol).strip().upper() if isinstance(symbol, str) else "BTC/USD")
534
  def get_bot_skills():
535
  try:
 
109
  def init_db():
110
  try:
111
  with sqlite3.connect(DB_NAME) as conn:
112
+ # 1. Table des Signaux (Historique)
113
  conn.execute('''CREATE TABLE IF NOT EXISTS signals (
114
  id INTEGER PRIMARY KEY AUTOINCREMENT,
115
  date TEXT, symbol TEXT, timeframe TEXT, direction TEXT,
 
118
  peak_price REAL, confirmed INTEGER DEFAULT 0)''')
119
 
120
  cursor = conn.cursor()
121
+
122
+ # 2. Table de l'ADN (Logique des Agents)
123
+ # On vérifie si la table existe déjà pour éviter les conflits de structure
124
+ cursor.execute("SELECT name FROM sqlite_master WHERE type='table' AND name='agent_logic'")
125
+ table_exists = cursor.fetchone()
126
+
127
+ if not table_exists:
128
+ conn.execute('''CREATE TABLE agent_logic (
129
+ symbol TEXT,
130
+ timeframe TEXT,
131
+ regime INTEGER, -- ⬅️ Ajouté ici
132
+ tp_mult REAL,
133
+ sl_mult REAL,
134
+ score REAL,
135
+ last_pnl REAL,
136
+ min_prob REAL,
137
+ min_tp_dist REAL,
138
+ generation INTEGER DEFAULT 1,
139
+ best_tp REAL,
140
+ best_sl REAL,
141
+ PRIMARY KEY (symbol, timeframe, regime))''') # ⬅️ CLÉ PRIMAIRE TRIPLE
142
+
143
+ # 3. Insertion des réglages par défaut
144
+ # On initialise les réglages sur le régime 3 (RANGE/CHAOS) par défaut
145
  defaults = [
146
+ # Symbol, TF, Regime, TP, SL, Score, PNL, Prob, Dist, Gen, B_TP, B_SL
147
+ ('ALL', '1m', 3, 1.0, 0.8, 0, 0, 0.65, 0.001, 1, 0, 0),
148
+ ('ALL', '5m', 3, 1.2, 0.9, 0, 0, 0.62, 0.002, 1, 0, 0),
149
+ ('ALL', '15m', 3, 1.5, 1.0, 0, 0, 0.60, 0.003, 1, 0, 0),
150
+ ('ALL', '1h', 3, 2.0, 1.5, 0, 0, 0.55, 0.005, 1, 0, 0),
151
+ ('ALL', '4h', 3, 3.0, 2.0, 0, 0, 0.50, 0.008, 1, 0, 0)
152
  ]
153
+ # On utilise 12 points d'interrogation pour les 12 colonnes
154
+ conn.executemany("INSERT INTO agent_logic VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", defaults)
155
+ conn.commit()
156
+ print("✅ [DB] Table agent_logic initialisée avec Scénarios.")
157
+ else:
158
+ # Optionnel : Vérifier si la colonne regime existe déjà (pour les mises à jour sans tout supprimer)
159
+ cursor.execute("PRAGMA table_info(agent_logic)")
160
+ columns = [col[1] for col in cursor.fetchall()]
161
+ if 'regime' not in columns:
162
+ print("⚠️ [DB] Ancienne structure détectée. Suppression pour mise à jour...")
163
+ conn.execute("DROP TABLE agent_logic")
164
+ init_db() # On relance pour recréer proprement
165
+
166
+ except Exception as e:
167
+ print(f"❌ Erreur DB: {e}")
168
 
169
  async def save_to_db(data):
170
  try:
 
295
  return df.dropna().copy()
296
  except Exception as e: print(f"❌ Error Stats: {e}"); return pd.DataFrame()
297
 
298
+ import numpy as np
299
+
300
  async def predict_signal(symbol, timeframe="1h"):
301
  try:
302
  memory_guard()
 
310
  vol_spike = float(last_row['Vol_Spike'].iloc[0])
311
  rsi_9 = float(last_row['RSI_9'].iloc[0])
312
 
313
+ # 🌊 1. COLLECTE DES DONNÉES (Flux, Sentiment, OI)
314
  futures_data = await fetch_kucoin_futures_data(symbol)
315
  oi, cvd = futures_data["oi"], futures_data["cvd"]
316
+ p_sent = await get_crypto_sentiment(symbol)
317
 
318
+ # 🧠 2. DÉTECTION DU SCÉNARIO (L'œil + L'IA)
319
+ # On calcule le régime via le modèle ML
320
  regime_scaled = regime_scaler.transform(last_row[["ATR_pct", "EMA200_slope", "Drawdown", "RSI_Macro"]])
321
+ regime_ml = int(regime_model.predict(regime_scaled)[0])
322
 
323
+ # On calcule le pattern via ton nouveau détecteur (Continuation/Reversal)
324
+ pattern_id = detect_chart_scenario(df)
325
+
326
+ # Priorité : Si un pattern fort (Squeeze/Reversal) est vu, il devient le Maître
327
+ final_scenario = pattern_id if pattern_id in [4, 5] else regime_ml
328
+
329
+ # 🧠 3. CALCUL DES PROBABILITÉS IA
330
  ml_cols = ["RSI", "Dist_High_24h", "Dist_Low_24h", "EMA_dist", "EMA_slope", "ATR_ratio", "VOL_ratio"]
331
  ml_prob = float(ml_model.predict_proba(last_row[ml_cols])[0][1])
332
+
333
  time_cols = ['return_1h', 'return_3h', 'return_12h', 'RSI_lag1', 'RSI_lag2', 'vol_lag1', 'VOL_RATIO']
334
  time_prob = float(time_model.predict_proba(last_row[time_cols])[0][1])
335
 
 
337
  if dino_brain:
338
  try: dino_prob = float(dino_brain.predict(last_row[ml_cols].values)[0])
339
  except: dino_prob = 0.5
 
 
340
 
341
+ # ⚡ 4. ENSEMBLE V30 (Poids dynamiques basés sur le scénario final)
342
+ final_p, wt, wm, wl, ws = combine_scores(symbol, timeframe, time_prob, ml_prob, dino_prob, p_sent, final_scenario)
343
 
344
+ # 🐋 5. BOOSTS (Smart Money & Scalping)
345
  smc_status = "AUCUN"
346
+ # Boost Smart Money (CVD + Sweep)
347
  if int(last_row["Sweep_Low"].iloc[0]) == 1 and cvd > 0:
348
+ final_p = min(0.95, final_p + 0.20); smc_status = "LONG SWEEP + CVD 🐋"
 
349
  elif int(last_row["Sweep_High"].iloc[0]) == 1 and cvd < 0:
350
+ final_p = max(0.05, final_p - 0.20); smc_status = "SHORT SWEEP + CVD 🐋"
 
351
 
352
+ # Boost Scalping (Vol Spike)
353
  if timeframe in ["1m", "5m"]:
354
  if final_p > 0.5 and vol_spike > 1.5 and rsi_9 < 70: final_p = min(0.95, final_p + 0.10)
355
  elif final_p < 0.5 and vol_spike > 1.5 and rsi_9 > 30: final_p = max(0.05, final_p - 0.10)
356
 
357
+ # 🛡️ 6. RÉCUPÉRATION ADN (Multi-Scénarios)
358
  with sqlite3.connect(DB_NAME) as conn:
359
+ res = conn.execute("SELECT tp_mult, sl_mult, min_prob, min_tp_dist FROM agent_logic WHERE symbol = ? AND timeframe = ? AND regime = ?", (symbol, timeframe, final_scenario)).fetchone()
360
+ if not res:
361
+ # Fallback sur l'ADN 'ALL' par défaut
362
+ res_def = conn.execute("SELECT tp_mult, sl_mult, min_prob, min_tp_dist FROM agent_logic WHERE symbol = 'ALL' AND timeframe = ?", (timeframe,)).fetchone()
363
+ tp_m, sl_m, agent_min_prob, agent_min_tp_dist = res_def if res_def else (1.5, 1.0, 0.55, 0.002)
364
+ # On initialise l'ADN pour ce nouveau scénario
365
+ conn.execute("INSERT OR IGNORE INTO agent_logic (symbol, timeframe, regime, tp_mult, sl_mult, min_prob, min_tp_dist, generation) VALUES (?, ?, ?, ?, ?, ?, ?, 1)", (symbol, timeframe, final_scenario, tp_m, sl_m, agent_min_prob, agent_min_tp_dist))
366
  conn.commit()
367
+ else: tp_m, sl_m, agent_min_prob, agent_min_tp_dist = res
368
 
369
+ # 📐 7. CALCUL DES NIVEAUX (TP/SL)
370
  tp = prix + (atr * tp_m) if final_p > 0.5 else prix - (atr * tp_m)
371
  sl = prix - (atr * sl_m) if final_p > 0.5 else prix + (atr * sl_m)
372
 
373
+ # 📊 Stats de confiance pour le dashboard
374
  strength = abs(final_p - 0.5) * 2
375
  conf_val = max(0, min(1, 1 - np.std([time_prob, ml_prob, dino_prob, p_sent])))
376
+ composite_score = max(0, min(100, (strength * 45) + (conf_val * 40) + (15 if final_scenario in [0, 1] else 5)))
377
 
378
+ # 🛑 8. SYSTÈME DE VÉTO (Sécurité Maximale)
379
  dist_tp_pct = abs(tp - prix) / prix
 
 
380
  ema200_val = float(last_row["EMA200"].iloc[0])
381
+ mkt_trend = float(last_row["Market_Trend"].iloc[0])
382
+ dist_fib = float(last_row["Dist_Fib_618"].iloc[0])
383
+
384
+ veto, veto_reason = False, ""
385
  if final_p < agent_min_prob and final_p > (1 - agent_min_prob): veto, veto_reason = True, f"Confiance ({round(final_p, 2)}) < {round(agent_min_prob, 2)}"
 
 
 
 
 
386
  elif dist_tp_pct < agent_min_tp_dist: veto, veto_reason = True, "Gain potentiel trop faible"
387
+ elif final_p < 0.5 and (prix > ema200_val or prix > vwap): veto, veto_reason = True, "Short Interdit (Prix > Bull Lines)"
388
+ elif final_p > 0.5 and (prix < ema200_val or prix < vwap): veto, veto_reason = True, "Long Interdit (Prix < Bull Lines)"
389
+ elif "BTC" not in symbol and "ETH" not in symbol:
390
+ if final_p < 0.5 and mkt_trend > 0.002: veto, veto_reason = True, "Trend Global Haussier"
391
+ elif final_p > 0.5 and mkt_trend < -0.002: veto, veto_reason = True, "Trend Global Baissier"
392
 
393
  if veto:
394
+ print(f"🛑 [VETO] {symbol} (Scénario {final_scenario}): {veto_reason}")
395
+ return {"symbol": symbol, "timeframe": timeframe, "status": "veto", "message": veto_reason, "scenario": final_scenario}
396
 
397
+ # 💾 9. ENREGISTREMENT DB ET RÉPONSE
398
+ db_task = (datetime.now(timezone.utc).isoformat(), symbol, timeframe, 'HAUSSIER' if final_p > 0.5 else 'BAISSIER', final_p, prix, tp, sl, 'EN_COURS', final_scenario, time_prob, ml_prob, dino_prob, p_sent, prix)
399
  await save_to_db(db_task)
400
 
401
  return {
402
  "symbol": symbol, "timeframe": timeframe, "status": "success", "final_score": round(final_p, 4), "score": int(composite_score),
403
+ "smart_money": smc_status, "price": prix, "tp": round(tp, 6), "sl": round(sl, 6), "scenario": final_scenario, "confluence": round(conf_val * 100, 1)
404
  }
405
  except Exception as e: return {"status": "error", "message": str(e)}
406
 
407
  # --- 🧬 FONCTION D'ÉVOLUTION ---
408
+ def mutate_agent(symbol, timeframe, regime, success=True):
409
+ """Mutation chirurgicale : Le bot apprend spécifiquement pour CE scénario"""
410
  try:
411
  with sqlite3.connect(DB_NAME) as conn:
412
  conn.row_factory = sqlite3.Row
413
+ # On cherche l'ADN spécifique (Symbole + TF + Regime)
414
+ row = conn.execute("SELECT * FROM agent_logic WHERE symbol = ? AND timeframe = ? AND regime = ?",
415
+ (symbol, timeframe, regime)).fetchone()
416
 
417
+ # Si le bot n'a jamais vu ce scénario, on crée une ligne à partir des défauts
418
+ if not row:
419
+ res_def = conn.execute("SELECT * FROM agent_logic WHERE symbol = 'ALL' AND timeframe = ?", (timeframe,)).fetchone()
420
+ tp, sl, prob = (res_def['tp_mult'], res_def['sl_mult'], res_def['min_prob']) if res_def else (1.5, 1.0, 0.60)
421
+ else:
422
+ tp, sl, prob = row['tp_mult'], row['sl_mult'], row['min_prob']
423
+
424
+ # LOGIQUE DE MUTATION : Plus agressive si succès, plus prudente si échec
425
+ if success:
426
+ # On augmente le TP (On laisse courir les gains) et on réduit un peu le SL
427
+ new_tp, new_sl, new_prob = tp * random.uniform(1.02, 1.08), sl * random.uniform(0.97, 1.00), max(0.55, prob - 0.005)
428
+ else:
429
+ # On réduit le TP (Objectif plus proche) et on écarte le SL (Donner de l'air)
430
+ new_tp, new_sl, new_prob = tp * random.uniform(0.92, 0.98), sl * random.uniform(1.03, 1.12), min(0.85, prob + 0.01)
431
+
432
+ # Mise à jour ou Insertion
433
+ conn.execute('''INSERT OR REPLACE INTO agent_logic
434
+ (symbol, timeframe, regime, tp_mult, sl_mult, min_prob, min_tp_dist, generation)
435
+ VALUES (?, ?, ?, ?, ?, ?, ?, COALESCE((SELECT generation FROM agent_logic WHERE symbol=? AND timeframe=? AND regime=?)+1, 1))''',
436
+ (symbol, timeframe, regime, new_tp, new_sl, new_prob, 0.001, symbol, timeframe, regime))
437
+ conn.commit()
438
 
 
439
  backup_db_to_hf()
440
+ except Exception as e: print(f"🧬 Erreur Mutation Scénario : {e}")
441
+
442
+
443
+ def detect_chart_scenario(df):
444
+ try:
445
+ df = df.copy().dropna()
446
+
447
+ # ========================
448
+ # DATA
449
+ # ========================
450
+ last = df.tail(100)
451
+
452
+ c = last['close'].values
453
+ h = last['high'].values
454
+ l = last['low'].values
455
+ v = last['volume'].values
456
+
457
+ ema200 = df['EMA200']
458
+ ema50 = df['EMA50']
459
+
460
+ # ========================
461
+ # 1. VOLATILITY / SQUEEZE
462
+ # ========================
463
+ range_start = np.mean(h[:20] - l[:20])
464
+ range_end = np.mean(h[-20:] - l[-20:])
465
+
466
+ squeeze = range_end < range_start * 0.65
467
+
468
+ # ========================
469
+ # 2. TREND (EMA + STRUCTURE)
470
+ # ========================
471
+ slope_ema200 = (ema200.iloc[-1] / ema200.iloc[-50]) - 1
472
+ slope_ema50 = (ema50.iloc[-1] / ema50.iloc[-20]) - 1
473
+
474
+ trend_strength = abs(slope_ema200) + abs(slope_ema50)
475
+
476
+ trend_up = slope_ema200 > 0 and slope_ema50 > 0
477
+ trend_down = slope_ema200 < 0 and slope_ema50 < 0
478
+
479
+ # ========================
480
+ # 3. BREAK OF STRUCTURE (BOS)
481
+ # ========================
482
+ recent_high = np.max(h[-20:])
483
+ prev_high = np.max(h[-40:-20])
484
+
485
+ recent_low = np.min(l[-20:])
486
+ prev_low = np.min(l[-40:-20])
487
+
488
+ bos_up = recent_high > prev_high
489
+ bos_down = recent_low < prev_low
490
+
491
+ # ========================
492
+ # 4. DOUBLE TOP / BOTTOM
493
+ # ========================
494
+ top_1, top_2 = np.max(h[-30:-15]), np.max(h[-15:])
495
+ low_1, low_2 = np.min(l[-30:-15]), np.min(l[-15:])
496
+
497
+ double_top = abs(top_1 - top_2) / top_1 < 0.004
498
+ double_bottom = abs(low_1 - low_2) / low_1 < 0.004
499
+
500
+ # ========================
501
+ # 5. RSI (DIVERGENCE)
502
+ # ========================
503
+ if 'RSI' in df.columns:
504
+ rsi = df['RSI'].values
505
+
506
+ rsi_high_1 = np.max(rsi[-30:-15])
507
+ rsi_high_2 = np.max(rsi[-15:])
508
+
509
+ rsi_low_1 = np.min(rsi[-30:-15])
510
+ rsi_low_2 = np.min(rsi[-15:])
511
+
512
+ bearish_div = top_2 >= top_1 and rsi_high_2 < rsi_high_1
513
+ bullish_div = low_2 <= low_1 and rsi_low_2 > rsi_low_1
514
+ else:
515
+ bearish_div = bullish_div = False
516
+
517
+ # ========================
518
+ # 6. VOLUME ANALYSIS
519
+ # ========================
520
+ vol_recent = np.mean(v[-15:])
521
+ vol_past = np.mean(v[-40:-15])
522
+
523
+ volume_increasing = vol_recent > vol_past * 1.2
524
+ volume_dropping = vol_recent < vol_past * 0.8
525
+
526
+ # ========================
527
+ # 7. MOMENTUM (PRICE SPEED)
528
+ # ========================
529
+ momentum = (c[-1] / c[-10]) - 1
530
+
531
+ # ========================
532
+ # DECISION LOGIC
533
+ # ========================
534
+
535
+ # 🔥 CONTINUATION (SQUEEZE + TREND + VOLUME BUILDUP)
536
+ if squeeze and trend_strength > 0.004 and volume_dropping:
537
+ return 4
538
+
539
+ # 🔴 REVERSAL (STRUCTURE + DIVERGENCE)
540
+ if (double_top or bearish_div) and trend_up:
541
+ return 5
542
+
543
+ if (double_bottom or bullish_div) and trend_down:
544
+ return 5
545
+
546
+ # 🟢 STRONG TREND
547
+ if trend_strength > 0.006 and volume_increasing:
548
+ return 0 if trend_up else 1
549
+
550
+ # 🟡 BREAKOUT (BOS + VOLUME)
551
+ if bos_up and volume_increasing:
552
+ return 0
553
+
554
+ if bos_down and volume_increasing:
555
+ return 1
556
+
557
+ # ⚪ RANGE / CHAOS
558
+ return 3
559
+
560
+ except Exception as e:
561
+ print("Error:", e)
562
+ return 3
563
 
564
  # --- 🧠 TRAINING ENGINE ---
565
  def trigger_training(symbol="BTC/USD"):
 
627
  df = prepare_features_sync(symbol, timeframe=tf, limit_bars=200)
628
  if df.empty or len(df) < 50: continue
629
 
630
+ # Simulation sur un point aléatoire du passé
631
  idx = random.randint(10, len(df) - 30)
632
+ # On récupère la prédiction (qui contient maintenant le 'scenario')
633
  pred = await predict_signal(symbol, timeframe=tf)
634
+
635
  if pred.get("status") == "success":
636
+ # On récupère le scénario détecté pour la mutation
637
+ current_scenario = pred.get("scenario", 3)
638
+
639
  future_data = df.iloc[idx+1 : idx+25]
640
  win = False
641
  for _, row in future_data.iterrows():
642
+ if pred["final_score"] > 0.5: # LONG
643
  if row['high'] >= pred['tp']: win = True; break
644
  if row['low'] <= pred['sl']: win = False; break
645
+ else: # SHORT
646
  if row['low'] <= pred['tp']: win = True; break
647
  if row['high'] >= pred['sl']: win = False; break
648
+
649
+ # ✅ CORRECTION ICI : On passe le scénario à la mutation
650
+ mutate_agent(symbol, tf, current_scenario, success=win)
651
+
652
  await asyncio.sleep(3)
653
+ except Exception as e:
654
+ print(f"⚠️ Erreur Dream Loop: {e}")
655
  await asyncio.sleep(60)
656
 
657
  # --- ⚖️ TOOLS ---
 
679
  try:
680
  with sqlite3.connect(DB_NAME, timeout=10) as conn:
681
  conn.row_factory, cursor = sqlite3.Row, conn.cursor()
682
+ # On récupère tout, y compris la colonne 'regime' enregistrée à l'ouverture
683
  cursor.execute("SELECT * FROM signals WHERE status = 'EN_COURS' AND confirmed = 1")
684
  trades = cursor.fetchall()
685
+
686
  if not trades: return {"status": "waiting"}
687
  closed_trades = []
688
+
689
  for t in trades:
690
  try:
691
  sym_db, current_price = t['symbol'], None
692
+ # --- 1. RÉCUPÉRATION PRIX TEMPS RÉEL (MT5 ou KuCoin) ---
693
  if MT5_AVAILABLE:
694
  epic = sym_db.replace("/", "").replace("USDT", "USD")
695
  if not epic.endswith("m"): epic += "m"
696
  tick = mt5.symbol_info_tick(epic)
697
  if tick: current_price = tick.last
698
+
699
  if current_price is None:
700
  f_sym = sym_db if "/USDT" in sym_db else sym_db.replace("/USD", "/USDT")
701
  if "/" not in f_sym: f_sym += "/USDT"
702
  current_price = exchange_sync.fetch_ticker(f_sym)['last']
703
 
704
+ # --- 2. CALCUL TRAILING STOP DYNAMIQUE ---
705
  sl_dyn, peak = t['sl'], t['peak_price']
706
  new_peak = max(peak, current_price) if t['direction'] == 'HAUSSIER' else min(peak, current_price)
707
  progression = abs(current_price - t['price']) / abs(t['tp'] - t['price']) if abs(t['tp'] - t['price']) > 0 else 0
708
 
709
  nouveau_sl = sl_dyn
710
+ # On sécurise les profits selon l'avancement vers le TP
711
  if t['direction'] == 'HAUSSIER':
712
  if progression >= 0.75: nouveau_sl = max(sl_dyn, t['price'] + (abs(t['tp'] - t['price']) * 0.60))
713
  elif progression >= 0.50: nouveau_sl = max(sl_dyn, t['price'] + (abs(t['tp'] - t['price']) * 0.25))
 
716
  elif progression >= 0.50: nouveau_sl = min(sl_dyn, t['price'] - (abs(t['tp'] - t['price']) * 0.25))
717
 
718
  cursor.execute("UPDATE signals SET peak_price = ?, sl = ? WHERE id = ?", (new_peak, nouveau_sl, t['id']))
719
+
720
+ # --- 3. VÉRIFICATION SORTIE (TP ou SL) ---
721
  outcome, reward = None, 0
722
  if t['direction'] == 'HAUSSIER':
723
  if current_price >= t['tp']: outcome, reward = "GAGNÉ ✅", 3
 
726
  if current_price <= t['tp']: outcome, reward = "GAGNÉ ✅", 3
727
  elif current_price >= nouveau_sl: outcome, reward = ("GAGNÉ (PARTIEL) 💸", 1) if nouveau_sl < t['price'] else ("PERDU ❌", -5)
728
 
729
+ # --- 4. MUTATION CHIRURGICALE (Le moment clé) ---
730
  if outcome:
731
+ # ON PASSE LE RÉGIME SPÉCIFIQUE DU TRADE !
732
+ # Ça permet de muter l'ADN 'Triangle' si le trade était un 'Triangle'
733
+ mutate_agent(t['symbol'], t['timeframe'], t['regime'], success=(reward > 0))
734
+
735
  cursor.execute("UPDATE signals SET status=? WHERE id=?", (outcome, t['id']))
736
+ closed_trades.append({"symbol": t['symbol'], "id": t['id'], "outcome": outcome})
737
+
738
+ except Exception as inner_e:
739
+ print(f"⚠️ Erreur Juge sur {t['symbol']}: {inner_e}")
740
+
741
  conn.commit()
742
  return {"status": "updates", "data": closed_trades} if closed_trades else {"status": "waiting"}
743
+ except Exception as e:
744
+ return {"status": "error", "message": str(e)}
745
  def training_wrapper(symbol, *args): return trigger_training(str(symbol).strip().upper() if isinstance(symbol, str) else "BTC/USD")
746
  def get_bot_skills():
747
  try: