# --- 🏴‍☠️ MONKEY PATCH & IMPORTS -- import huggingface_hub try: from huggingface_hub import HfFolder except ImportError: class MockHfFolder: @staticmethod def get_token(): return None @staticmethod def save_token(token): pass @staticmethod def delete_token(): pass huggingface_hub.HfFolder = MockHfFolder import sys, os, sqlite3, shutil, pandas as pd, asyncio, joblib, json, numpy as np, gc, warnings, psutil import ccxt.async_support as ccxt_async from datetime import datetime, timezone from huggingface_hub import HfApi, hf_hub_download import gradio as gr import ccxt import time import random import aiohttp from types import ModuleType import yfinance as yf import warnings warnings.filterwarnings("ignore", category=UserWarning, module="sklearn") # --- 🦖 IMPORT LIGHTGBM --- try: import lightgbm as lgb LGBM_AVAILABLE = True except ImportError: LGBM_AVAILABLE = False print("⚠️ LightGBM non installé.") try: import MetaTrader5 as mt5 MT5_AVAILABLE = True except ImportError: MT5_AVAILABLE = False print("🌐 [CLOUD MODE] MetaTrader5 non détecté.") # --- 🥷 NINJA HACK : MOCK PANDAS_TA --- if "pandas_ta" not in sys.modules: mock_ta = ModuleType("pandas_ta") sys.modules["pandas_ta"] = mock_ta if not hasattr(pd.DataFrame, "ta"): class FakeTA: def __getattr__(self, name): return lambda *args, **kwargs: None pd.DataFrame.ta = property(lambda self: FakeTA()) # --- 🛑 ANTI-CRASH & CPU OPTIMIZATION --- os.environ["CUDA_VISIBLE_DEVICES"] = "-1" import tensorflow as tf from tensorflow.keras import backend as K tf.config.threading.set_intra_op_parallelism_threads(2) tf.config.threading.set_inter_op_parallelism_threads(2) current_dir = os.path.dirname(os.path.abspath(__file__)) if current_dir not in sys.path: sys.path.append(current_dir) # --- SINGLETON EXCHANGE --- class ExchangeManager: _instance = None @classmethod def get_instance(cls): if cls._instance is None: cls._instance = ccxt_async.kucoin({"enableRateLimit": True, "timeout": 30000}) return cls._instance exchange_sync = ccxt.kucoin({"enableRateLimit": True, "timeout": 30000}) market_cache, last_fetch_time = {}, {} # 🛡️ FIX : Cache dynamique selon la timeframe pour éviter l'amnésie def get_cache_ttl(tf): if tf == '1m': return 20 # 20 secondes de cache pour le 1m if tf == '5m': return 60 # 1 minute pour le 5m if tf == '15m': return 180 # 3 minutes pour le 15m return 300 # 5 minutes pour le 1h et 4h DREAM_MODE_ACTIVE = True try: from sentiment_engine import get_crypto_sentiment except: async def get_crypto_sentiment(symbol): return 0.5 try: from ensemble import combine_scores except: def combine_scores(symbol, timeframe, t, m, l, sent, r): # Formule PRO : 30% Time, 30% ML, 30% LightGBM, 10% Sentiment score = (t * 0.30) + (m * 0.30) + (l * 0.30) + (sent * 0.10) return score, 0.30, 0.30, 0.30, 0.10 # --- DB, SYNC & HUGGING FACE BACKUP --- DB_NAME = "alphatrade_v31_dino.db" HF_TOKEN = os.environ.get("HF_TOKEN") HF_REPO_ID = "Nexo-S/AlphaTrade-DB" def restore_db_from_hf(): if not HF_TOKEN: return # 1. Tentative de restauration de l'ADN (DB) try: print("🔄 [SYSTEM] Restauration ADN depuis HF...") file_path = hf_hub_download(repo_id=HF_REPO_ID, filename=DB_NAME, repo_type="dataset", token=HF_TOKEN) shutil.copy(file_path, DB_NAME) print("✅ [RESTORE] ADN récupéré !") except Exception as e: print(f"⚠️ Aucun backup ADN trouvé (1er lancement) : {e}") # 2. Tentative de restauration du modèle DINO # 2. Tentative de restauration des modèles DINO (Multiples) global dino_brains dino_brains = {} for crypto in ["BTC", "ETH", "SOL"]: filename = f"dino_lgbm_{crypto}.txt" try: print(f"🔄 [SYSTEM] Restauration du DINO {crypto} depuis HF...") file_path = hf_hub_download(repo_id=HF_REPO_ID, filename=filename, repo_type="dataset", local_dir=".", token=HF_TOKEN) # 🛡️ LE VACCIN EST ICI : On vérifie que le fichier pèse plus de 100 octets (qu'il n'est pas vide) if os.path.exists(file_path) and os.path.getsize(file_path) > 100: dino_brains[crypto] = lgb.Booster(model_file=file_path) print(f"✅ [RESTORE] Modèle Dino {crypto} récupéré et chargé !") else: print(f"⚠️ [ALERTE] Fichier {filename} vide ou corrompu. Mode aveugle activé pour {crypto}.") except Exception as e: print(f"⚠️ Aucun modèle trouvé pour {crypto}. Pense à Forcer l'Entraînement de {crypto}.") def backup_db_to_hf(): if not HF_TOKEN: return try: api = HfApi() api.upload_file(path_or_fileobj=DB_NAME, path_in_repo=DB_NAME, repo_id=HF_REPO_ID, repo_type="dataset", token=HF_TOKEN) print("☁️ [BACKUP] ADN sauvegardé sur Hugging Face !") except Exception as e: print(f"❌ Erreur Backup HF : {e}") def init_db(): try: with sqlite3.connect(DB_NAME) as conn: # 1. Table des Signaux (Historique) conn.execute('''CREATE TABLE IF NOT EXISTS signals ( id INTEGER PRIMARY KEY AUTOINCREMENT, date TEXT, symbol TEXT, timeframe TEXT, direction TEXT, prob REAL, price REAL, tp REAL, sl REAL, status TEXT, regime INTEGER, prob_time REAL, prob_ml REAL, prob_lstm REAL, prob_sent REAL, peak_price REAL, confirmed INTEGER DEFAULT 0)''') cursor = conn.cursor() # 2. Table de l'ADN (Logique des Agents) # On vérifie si la table existe déjà pour éviter les conflits de structure cursor.execute("SELECT name FROM sqlite_master WHERE type='table' AND name='agent_logic'") table_exists = cursor.fetchone() if not table_exists: conn.execute('''CREATE TABLE agent_logic ( symbol TEXT, timeframe TEXT, regime INTEGER, -- ⬅️ Ajouté ici tp_mult REAL, sl_mult REAL, score REAL, last_pnl REAL, min_prob REAL, min_tp_dist REAL, generation INTEGER DEFAULT 1, best_tp REAL, best_sl REAL, PRIMARY KEY (symbol, timeframe, regime))''') # ⬅️ CLÉ PRIMAIRE TRIPLE # 3. Insertion des réglages par défaut # On initialise les réglages sur le régime 3 (RANGE/CHAOS) par défaut defaults = [ # Symbol, TF, Regime, TP, SL, Score, PNL, Prob, Dist, Gen, B_TP, B_SL ('ALL', '15m', 3, 1.5, 1.0, 0, 0, 0.60, 0.003, 1, 0, 0), ('ALL', '1h', 3, 2.0, 1.5, 0, 0, 0.55, 0.005, 1, 0, 0), ('ALL', '4h', 3, 3.0, 2.0, 0, 0, 0.50, 0.008, 1, 0, 0) ] # On utilise 12 points d'interrogation pour les 12 colonnes conn.executemany("INSERT INTO agent_logic VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", defaults) conn.commit() print("✅ [DB] Table agent_logic initialisée avec Scénarios.") else: # Optionnel : Vérifier si la colonne regime existe déjà (pour les mises à jour sans tout supprimer) cursor.execute("PRAGMA table_info(agent_logic)") columns = [col[1] for col in cursor.fetchall()] if 'regime' not in columns: print("⚠️ [DB] Ancienne structure détectée. Suppression pour mise à jour...") conn.execute("DROP TABLE agent_logic") init_db() # On relance pour recréer proprement except Exception as e: print(f"❌ Erreur DB: {e}") import asyncio # --- 💾 SAUVEGARDE BLINDÉE (ANTI-EMBOUTEILLAGE) --- async def save_to_db(data_tuple): for tentative in range(5): # Le Cerveau va insister 5 fois si la porte est fermée try: with sqlite3.connect(DB_NAME, timeout=20) as conn: conn.execute('''INSERT INTO signals (date, symbol, timeframe, direction, prob, price, tp, sl, status, regime, prob_time, prob_ml, prob_lstm, prob_sent, peak_price) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)''', data_tuple) conn.commit() return # Succès, on quitte la fonction except sqlite3.OperationalError as e: if "locked" in str(e): await asyncio.sleep(1) # La base est lue par le Radar, on attend 1 seconde else: print(f"❌ [ERREUR DB] Sauvegarde échouée : {e}") break # 🚀 INITIALISATION restore_db_from_hf() init_db() # --- 🛡️ STATE MANAGER --- active_signals_state = {} last_signals_sent = {} # ⬅️ LA RUSTINE CRITIQUE EST LÀ def clean_old_db_signals(symbol, timeframe): try: with sqlite3.connect(DB_NAME, timeout=10) as conn: # 🛡️ LA SÉCURITÉ : On ne touche qu'aux signaux NON-CONFIRMÉS (Les fantômes) # Et on retire le "confirmed = 0" du SET (il ne faut pas dé-confirmer un trade) conn.execute("UPDATE signals SET status = 'REMPLACÉ ♻️' WHERE symbol = ? AND timeframe = ? AND status = 'EN_COURS' AND confirmed = 0", (symbol, timeframe)) conn.commit() except Exception as e: print(f"⚠️ Erreur nettoyage DB : {e}") def memory_guard(): if psutil.virtual_memory().percent > 80: K.clear_session() gc.collect() # --- 🛠️ MOTEUR MATHS --- def get_ema(series, period): return series.ewm(span=period, adjust=False).mean() def get_rsi(series, period=14): delta = series.diff() gain = (delta.where(delta > 0, 0)).rolling(window=period).mean() loss = (-delta.where(delta < 0, 0)).rolling(window=period).mean() return 100 - (100 / (1 + (gain / (loss + 1e-9)))) def get_atr(df, period=14): h_l = df['high'] - df['low'] h_c = (df['high'] - df['close'].shift()).abs() l_c = (df['low'] - df['close'].shift()).abs() return pd.concat([h_l, h_c, l_c], axis=1).max(axis=1).rolling(period).mean() def get_vwap(df): v = df['vol'] tp = (df['high'] + df['low'] + df['close']) / 3 return (tp * v).cumsum() / (v.cumsum() + 1e-9) # --- 🧠 CHARGEMENT IA --- try: ml_model = joblib.load("ml_model_v9.pkl") time_model = joblib.load("time_model.pkl") regime_model = joblib.load("regime_model.pkl") regime_scaler = joblib.load("regime_scaler.pkl") except Exception as e: print(f"⚠️ Erreur IA Classique : {e}") try: dino_brain = lgb.Booster(model_file='dino_lgbm_model.txt') if LGBM_AVAILABLE else None except: dino_brain = None # --- 🌐 HTTP SESSION MANAGER --- global_aio_session = None async def get_aio_session(): global global_aio_session if global_aio_session is None or global_aio_session.closed: # On force un connecteur limité pour éviter les fuites de mémoire (Max 100 connexions) connector = aiohttp.TCPConnector(limit=100) global_aio_session = aiohttp.ClientSession(connector=connector) return global_aio_session async def fetch_kucoin_futures_data(symbol): try: ku_sym = symbol.replace("/", "").replace("BTC", "XBT") + "M" session = await get_aio_session() # On utilise la session globale ouverte oi_url = f"https://api-futures.kucoin.com/api/v1/open-interest?symbol={ku_sym}" trade_url = f"https://api-futures.kucoin.com/api/v1/trade/history?symbol={ku_sym}" async with session.get(oi_url) as r1, session.get(trade_url) as r2: oi_json = await r1.json() trades = (await r2.json()).get("data", []) cvd = sum([float(t.get("size", 0)) if t.get("side") == "buy" else -float(t.get("size", 0)) for t in trades]) return {"oi": float(oi_json.get("data", {}).get("value", 0)), "cvd": cvd} except Exception as e: print(f"⚠️ Erreur Flux KuCoin ({symbol}) : {e}") return {"oi": 0, "cvd": 0} def prepare_features_sync(symbol, timeframe='1h', limit_bars=600): try: now = datetime.now().timestamp() cache_key = f"{symbol}_{timeframe}" # 🛡️ FIX : On utilise le TTL dynamique ici ttl = get_cache_ttl(timeframe) if cache_key in market_cache and now - last_fetch_time.get(cache_key, 0) < ttl: df = market_cache[cache_key].copy() else: fetch_symbol = symbol if "/USDT" in symbol else symbol.replace("/USD", "/USDT") if "/" not in fetch_symbol: fetch_symbol += "/USDT" df = pd.DataFrame() for attempt in range(3): try: bars = exchange_sync.fetch_ohlcv(fetch_symbol, timeframe, limit=limit_bars) df = pd.DataFrame(bars, columns=['ts', 'open', 'high', 'low', 'close', 'vol']) if not df.empty: break except: time.sleep(2) if df.empty: return pd.DataFrame() market_cache[cache_key], last_fetch_time[cache_key] = df.copy(), now if len(df) < 50: return pd.DataFrame() df["RSI"], df["RSI_9"] = get_rsi(df["close"]), get_rsi(df["close"], 9) df["EMA50"], df["EMA200"] = get_ema(df["close"], 50), get_ema(df["close"], 200) df["VWAP"] = get_vwap(df) df["ATR"] = get_atr(df) df["ATR_pct"] = (df["ATR"] / df["close"]) * 100 df["EMA200_slope"] = (df["EMA200"] / df["EMA200"].shift(10)) - 1 df["Drawdown"] = (df["close"] / df["close"].rolling(14).max()) - 1 df["High_24h"], df["Low_24h"] = df["high"].rolling(24).max(), df["low"].rolling(24).min() df["Dist_High_24h"] = (df["High_24h"] - df["close"]) / df["close"] df["Dist_Low_24h"] = (df["close"] - df["Low_24h"]) / df["close"] df["EMA_dist"] = (df["close"] - df["EMA50"]) / df["EMA50"] df["EMA_slope"] = (df["EMA50"] / df["EMA50"].shift(5)) - 1 df["Price_vs_VWAP"] = (df["close"] - df["VWAP"]) / df["VWAP"] df["ATR_ratio"] = df["ATR"] / df["close"] df["VOL_ratio"] = df["vol"] / (df["vol"].rolling(24).mean() + 1e-9) df["Vol_Spike"] = df["vol"] / (df["vol"].rolling(5).mean() + 1e-9) diff = df["High_24h"] - df["Low_24h"] df["Fib_618"] = df["Low_24h"] + (diff * 0.618) df["Dist_Fib_618"] = (df["close"] - df["Fib_618"]) / df["close"] df["Market_Trend"] = df["EMA200_slope"] p_low, p_high = df["low"].rolling(24).min().shift(1), df["high"].rolling(24).max().shift(1) df["Sweep_Low"] = ((df["low"] < p_low) & (df["close"] > p_low)).astype(int) df["Sweep_High"] = ((df["high"] > p_high) & (df["close"] < p_high)).astype(int) df['return_1h'], df['return_3h'], df['return_12h'] = df['close'].pct_change(1), df['close'].pct_change(3), df['close'].pct_change(12) df['RSI_lag1'], df['RSI_lag2'] = df["RSI"].shift(1), df["RSI"].shift(2) df['VOL_RATIO'] = df['vol'] / (df['vol'].rolling(20).mean() + 1e-9) df['vol_lag1'], df['RSI_Macro'] = df['vol'].shift(1), df["RSI"] return df.dropna().copy() except Exception as e: print(f"❌ Error Stats: {e}"); return pd.DataFrame() import numpy as np def detect_chart_scenario(df, df_15m=None, df_1h=None, df_4h=None): try: # 🛠️ Fix colonnes KuCoin if 'vol' in df.columns and 'volume' not in df.columns: df = df.rename(columns={'vol': 'volume'}) df = df.copy().dropna() if len(df) < 100: return 3 last = df.tail(100) c, h, l, v = last['close'].values, last['high'].values, last['low'].values, last['volume'].values ema200, ema50 = df['EMA200'], df['EMA50'] # --- 1. VWAP INSTITUTIONNEL (Sécurisé) --- typical_price = (df['high'] + df['low'] + df['close']) / 3 vwap = (typical_price * df['volume']).cumsum() / (df['volume'].cumsum() + 1e-9) price_above_vwap = df['close'].iloc[-1] > vwap.iloc[-1] price_below_vwap = df['close'].iloc[-1] < vwap.iloc[-1] # ======================== # 🔥 VWAP BANDS (Institutional zones) # ======================== vwap_std = df['close'].rolling(20).std() vwap_upper = vwap + vwap_std vwap_lower = vwap - vwap_std price_extended_high = df['close'].iloc[-1] > vwap_upper.iloc[-1] price_extended_low = df['close'].iloc[-1] < vwap_lower.iloc[-1] # --- 2. MULTI TIMEFRAME MACRO (15m, 1h, 4h) --- mtf_bull = mtf_bear = 0 def analyze_tf(tf_df): if tf_df is None or len(tf_df) < 50 or 'EMA200' not in tf_df.columns: return 0 slope = (tf_df['EMA200'].iloc[-1] / tf_df['EMA200'].iloc[-20]) - 1 return 1 if slope > 0.0001 else (-1 if slope < -0.0001 else 0) for tf_data in [df_15m, df_1h, df_4h]: res = analyze_tf(tf_data) if res == 1: mtf_bull += 1 elif res == -1: mtf_bear += 1 # ======================== # 🔥 ORDER BLOCK STRENGTH # ======================== last_candles = df.tail(20) bullish_ob = False bearish_ob = False ob_strength = 0 for i in range(len(last_candles) - 3): c1 = last_candles.iloc[i] c2 = last_candles.iloc[i + 1] body_size = abs(c2['close'] - c2['open']) candle_range = c2['high'] - c2['low'] + 1e-9 strength = body_size / candle_range if c1['close'] < c1['open'] and c2['close'] > c2['open']: bullish_ob = True ob_strength = max(ob_strength, strength) if c1['close'] > c1['open'] and c2['close'] < c2['open']: bearish_ob = True ob_strength = max(ob_strength, strength) # --- 4. LIQUIDITY SWEEP --- recent_high, prev_high = np.max(h[-10:]), np.max(h[-30:-10]) recent_low, prev_low = np.min(l[-10:]), np.min(l[-30:-10]) liquidity_sweep_high = recent_high > prev_high and c[-1] < recent_high liquidity_sweep_low = recent_low < prev_low and c[-1] > recent_low liquidity_high_zone = np.mean(h[-50:]) liquidity_low_zone = np.mean(l[-50:]) near_liquidity_high = abs(c[-1] - liquidity_high_zone) / c[-1] < 0.002 near_liquidity_low = abs(c[-1] - liquidity_low_zone) / c[-1] < 0.002 # --- 5. STRUCTURE ET RSI --- range_start, range_end = np.mean(h[:20] - l[:20]), np.mean(h[-20:] - l[-20:]) squeeze = range_end < range_start * 0.65 slope_ema200 = (ema200.iloc[-1] / ema200.iloc[-50]) - 1 slope_ema50 = (ema50.iloc[-1] / ema50.iloc[-20]) - 1 trend_strength = abs(slope_ema200) + abs(slope_ema50) trend_up, trend_down = (slope_ema200 > 0 and slope_ema50 > 0), (slope_ema200 < 0 and slope_ema50 < 0) bos_up = np.max(h[-20:]) > np.max(h[-40:-20]) bos_down = np.min(l[-20:]) < np.min(l[-40:-20]) # ======================== # 🔥 FAKE BREAKOUT FILTER (Déplacé ici pour corriger le bug !) # ======================== fake_breakout_up = bos_up and c[-1] < recent_high fake_breakout_down = bos_down and c[-1] > recent_low bullish_div = bearish_div = False if 'RSI' in df.columns: rsi = df['RSI'].values bearish_div = np.max(h[-15:]) >= np.max(h[-30:-15]) and np.max(rsi[-15:]) < np.max(rsi[-30:-15]) bullish_div = np.min(l[-15:]) <= np.min(l[-30:-15]) and np.min(rsi[-15:]) > np.min(rsi[-30:-15]) vol_recent, vol_past = np.mean(v[-15:]), np.mean(v[-40:-15]) vol_inc = vol_recent > vol_past * 1.2 # 🔥 CONTINUATION PRO MAX if squeeze and trend_strength > 0.004 and price_above_vwap and mtf_trend_up and not price_extended_high: return 4 # 🔴 REVERSAL SMART MONEY if (liquidity_sweep_high or bearish_ob or bearish_div or price_extended_high or near_liquidity_high) and trend_up: return 5 if (liquidity_sweep_low or bullish_ob or bullish_div or price_extended_low or near_liquidity_low) and trend_down: return 5 # 🟢 TREND INSTITUTIONNEL if trend_strength > 0.006 and vol_inc and ob_strength > 0.5: if price_above_vwap and mtf_trend_up and not fake_breakout_up: return 0 if price_below_vwap and mtf_trend_down and not fake_breakout_down: return 1 # 🟡 BREAKOUT PROPRE if vol_inc and not fake_breakout_up and bos_up and price_above_vwap: return 0 if vol_inc and not fake_breakout_down and bos_down and price_below_vwap: return 1 return 3 # RANGE / CHAOS par défaut except Exception as e: print(f"⚠️ Erreur Detect Scenario: {e}") return 3 async def predict_signal(symbol, timeframe="1h"): try: memory_guard() symbol = str(symbol).strip().upper() df = prepare_features_sync(symbol, timeframe) if df.empty: return {"status": "error", "message": "Data insuffisante"} last_row = df.iloc[[-1]] prix, atr = float(last_row['close'].iloc[0]), float(last_row['ATR'].iloc[0]) vwap = float(last_row['VWAP'].iloc[0]) vol_spike = float(last_row['Vol_Spike'].iloc[0]) rsi_9 = float(last_row['RSI_9'].iloc[0]) # 🌊 1. COLLECTE DES DONNÉES (Flux, Sentiment, OI) futures_data = await fetch_kucoin_futures_data(symbol) oi, cvd = futures_data["oi"], futures_data["cvd"] p_sent = await get_crypto_sentiment(symbol) # 🏗️ 1.5 CHARGEMENT MULTI-TIMEFRAME MACRO df_15m = prepare_features_sync(symbol, "15m", limit_bars=150) df_1h = prepare_features_sync(symbol, "1h", limit_bars=150) df_4h = prepare_features_sync(symbol, "4h", limit_bars=150) # 🧠 2. DÉTECTION DU SCÉNARIO (L'œil + L'IA) regime_scaled = regime_scaler.transform(last_row[["ATR_pct", "EMA200_slope", "Drawdown", "RSI_Macro"]]) regime_ml = int(regime_model.predict(regime_scaled)[0]) # On passe les dataframes MTF à la fonction pattern_id = detect_chart_scenario(df, df_15m, df_1h, df_4h) final_scenario = pattern_id if pattern_id in [4, 5] else regime_ml # 🧠 3. CALCUL DES PROBABILITÉS IA ml_cols = ["RSI", "Dist_High_24h", "Dist_Low_24h", "EMA_dist", "EMA_slope", "ATR_ratio", "VOL_ratio"] ml_prob = float(ml_model.predict_proba(last_row[ml_cols])[0][1]) time_cols = ['return_1h', 'return_3h', 'return_12h', 'RSI_lag1', 'RSI_lag2', 'vol_lag1', 'VOL_RATIO'] time_prob = float(time_model.predict_proba(last_row[time_cols])[0][1]) # 🧠 SÉLECTION DU CERVEAU DINO (Schizophrénie) sym_clean = symbol.replace("/", "").replace("USDT", "").replace("USD", "").upper() if 'dino_brains' in globals() and sym_clean in dino_brains: dino_prob = float(dino_brains[sym_clean].predict(last_row[ml_cols].values)[0]) elif 'dino_brain' in globals() and dino_brain: # Sécurité : Fallback sur l'ancien modèle si besoin dino_prob = float(dino_brain.predict(last_row[ml_cols].values)[0]) else: dino_prob = 0.5 # Si aucun cerveau n'est entraîné, on reste neutre # ⚡ 4. ENSEMBLE V30 final_p, wt, wm, wl, ws = combine_scores(symbol, timeframe, time_prob, ml_prob, dino_prob, p_sent, final_scenario) # 🐋 5. BOOSTS (Smart Money & Scalping) smc_status = "AUCUN" if int(last_row["Sweep_Low"].iloc[0]) == 1 and cvd > 0: final_p = min(0.95, final_p + 0.20); smc_status = "LONG SWEEP + CVD 🐋" elif int(last_row["Sweep_High"].iloc[0]) == 1 and cvd < 0: final_p = max(0.05, final_p - 0.20); smc_status = "SHORT SWEEP + CVD 🐋" if timeframe in ["1m", "5m"]: if final_p > 0.5 and vol_spike > 1.5 and rsi_9 < 70: final_p = min(0.95, final_p + 0.10) elif final_p < 0.5 and vol_spike > 1.5 and rsi_9 > 30: final_p = max(0.05, final_p - 0.10) # 🛡️ 6. RÉCUPÉRATION ADN (Multi-Scénarios) + BOUCLIER V32 with sqlite3.connect(DB_NAME) as conn: 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() if not res: res_def = conn.execute("SELECT tp_mult, sl_mult, min_prob, min_tp_dist FROM agent_logic WHERE symbol = 'ALL' AND timeframe = ?", (timeframe,)).fetchone() 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) 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)) conn.commit() else: tp_m, sl_m, agent_min_prob, agent_min_tp_dist = res tp_m, sl_m = safe_agent_values(tp_m, sl_m) # 🔥 LE PATCH V32 (ANTI-CORRUPTION) EST LÀ : if sl_m > 5.0 or tp_m > 5.0: print(f"🚨 [V32] CORRUPTION DETECTÉE SUR {symbol} (SLx{round(sl_m,1)}) → NEUTRALISATION") tp_m, sl_m = 1.5, 1.0 # On force le reset local else: # On bride les valeurs par sécurité extrême tp_m = float(np.clip(tp_m, 0.5, 3.0)) sl_m = float(np.clip(sl_m, 0.5, 2.0)) # 📐 7. CALCUL DES NIVEAUX (TP/SL) tp = prix + (atr * tp_m) if final_p > 0.5 else prix - (atr * tp_m) sl = prix - (atr * sl_m) if final_p > 0.5 else prix + (atr * sl_m) strength = abs(final_p - 0.5) * 2 conf_val = max(0, min(1, 1 - np.std([time_prob, ml_prob, dino_prob, p_sent]))) composite_score = max(0, min(100, (strength * 45) + (conf_val * 40) + (15 if final_scenario in [0, 1] else 5))) # 🛑 8. SYSTÈME DE VÉTO dist_tp_pct = abs(tp - prix) / prix ema200_val = float(last_row["EMA200"].iloc[0]) mkt_trend = float(last_row["Market_Trend"].iloc[0]) print(f"🧠 [IA] {symbol} [{timeframe}] | Scénario: {final_scenario} | Proba: {round(final_p, 4)} | SMC: {smc_status}") veto, veto_reason = False, "" 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)}" elif dist_tp_pct < agent_min_tp_dist: veto, veto_reason = True, "Gain potentiel trop faible" elif final_p < 0.5 and (prix > ema200_val or prix > vwap): veto, veto_reason = True, "Short Interdit (Prix > Bull Lines)" elif final_p > 0.5 and (prix < ema200_val or prix < vwap): veto, veto_reason = True, "Long Interdit (Prix < Bull Lines)" elif "BTC" not in symbol and "ETH" not in symbol and "SOL" not in symbol: if final_p < 0.5 and mkt_trend > 0.002: veto, veto_reason = True, "Trend Global Haussier" elif final_p > 0.5 and mkt_trend < -0.002: veto, veto_reason = True, "Trend Global Baissier" if veto: return {"symbol": symbol, "timeframe": timeframe, "status": "veto", "message": veto_reason, "scenario": final_scenario} # 🛑 8.5 ANTI-SPAM (On bloque ici si le signal a déjà été envoyé) if not should_send_signal(symbol, timeframe, final_p): return {"symbol": symbol, "timeframe": timeframe, "status": "veto", "message": "Anti-Spam Actif (Déjà envoyé)", "scenario": final_scenario} # 💾 9. ENREGISTREMENT DB 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) await save_to_db(db_task) print(f"💾 [DB] Signal {symbol} [{timeframe}] sauvegardé. En attente du Bras...") return { "symbol": symbol, "timeframe": timeframe, "status": "success", "final_score": round(final_p, 4), "score": int(composite_score), "smart_money": smc_status, "price": prix, "tp": round(tp, 6), "sl": round(sl, 6), "scenario": final_scenario, "confluence": round(conf_val * 100, 1) } except Exception as e: return {"status": "error", "message": str(e)} # ============================= def mutate_agent(symbol, timeframe, regime, success=True): import sqlite3, random, numpy as np, time for tentative in range(5): # 🔄 Tente 5 fois si la base est verrouillée try: with sqlite3.connect(DB_NAME, timeout=20) as conn: conn.row_factory = sqlite3.Row row = conn.execute( "SELECT * FROM agent_logic WHERE symbol = ? AND timeframe = ? AND regime = ?", (symbol, timeframe, regime) ).fetchone() if not row: res_def = conn.execute( "SELECT * FROM agent_logic WHERE symbol = 'ALL' AND timeframe = ?", (timeframe,) ).fetchone() 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) else: tp, sl, prob = row['tp_mult'], row['sl_mult'], row['min_prob'] # 🧠 MUTATION DOUCE if success: new_tp = tp * random.uniform(1.01, 1.05) new_sl = sl * random.uniform(0.98, 1.00) new_prob = max(0.55, prob - 0.003) else: new_tp = tp * random.uniform(0.95, 0.99) new_sl = sl * random.uniform(1.01, 1.05) new_prob = min(0.85, prob + 0.005) # 🛡️ HARD CLAMP (ANTI-EXPLOSION) new_tp = float(np.clip(new_tp, 0.5, 3.0)) new_sl = float(np.clip(new_sl, 0.5, 2.0)) conn.execute(''' INSERT OR REPLACE INTO agent_logic (symbol, timeframe, regime, tp_mult, sl_mult, min_prob, min_tp_dist, generation) VALUES (?, ?, ?, ?, ?, ?, ?, COALESCE((SELECT generation FROM agent_logic WHERE symbol=? AND timeframe=? AND regime=?)+1, 1)) ''', ( symbol, timeframe, regime, new_tp, new_sl, new_prob, 0.001, symbol, timeframe, regime )) conn.commit() return # ✅ Succès, on sort de la fonction except sqlite3.OperationalError as e: if "locked" in str(e): time.sleep(1) # La porte est bloquée, on attend 1 seconde else: print(f"❌ [ERREUR DB] Mutation échouée : {e}") break except Exception as e: print(f"🧬 Erreur Mutation V32: {e}") break # ============================= # 🛡️ 3. PROTECTION LECTURE DB # ============================= def safe_agent_values(tp_m, sl_m): import numpy as np # 🔥 Anti corruption if sl_m > 10 or tp_m > 10: print("🚨 CORRUPTION DETECTÉE → RESET LOCAL") return 1.5, 1.0 tp_m = float(np.clip(tp_m, 0.5, 3.0)) sl_m = float(np.clip(sl_m, 0.5, 2.0)) return tp_m, sl_m # --- 🌙 DREAM LOOP V31 (CORRIGÉE) --- async def dream_simulation_loop(): while True: if DREAM_MODE_ACTIVE: print("🌙 [DREAM MODE] Le bot rêve et s'entraîne à la salle... 🥊") for symbol in AUTO_SYMBOLS: for tf in AUTO_TIMEFRAMES: try: df = prepare_features_sync(symbol, timeframe=tf, limit_bars=200) if df.empty or len(df) < 50: continue # 1. On voyage dans le temps (une bougie au hasard) idx = random.randint(10, len(df) - 30) row_actuelle = df.iloc[idx] prix = float(row_actuelle['close']) atr = float(row_actuelle['ATR']) # 2. On récupère le régime de marché (Scénario) # 2. On calcule le VRAI scénario de cette époque ! # On coupe le graphique pile au moment du passé qu'on a choisi df_past = df.iloc[:idx+1] # Le bot analyse la structure du marché de l'époque current_scenario = detect_chart_scenario(df_past, df_past, df_past, df_past, df_past) # 3. On extrait l'ADN actuel (les multiplicateurs) with sqlite3.connect(DB_NAME) as conn: res = conn.execute("SELECT tp_mult, sl_mult FROM agent_logic WHERE symbol = ? AND timeframe = ? AND regime = ?", (symbol, tf, current_scenario)).fetchone() tp_m, sl_m = res if res else (1.5, 1.0) # 4. Simulation de combat (Long ou Short) depuis CETTE époque is_long = random.choice([True, False]) tp = prix + (atr * tp_m) if is_long else prix - (atr * tp_m) sl = prix - (atr * sl_m) if is_long else prix + (atr * sl_m) # 5. On regarde les 24 bougies suivantes pour voir qui gagne future_data = df.iloc[idx+1 : idx+25] win = False for _, row in future_data.iterrows(): if is_long: if row['high'] >= tp: win = True; break if row['low'] <= sl: win = False; break else: if row['low'] <= tp: win = True; break if row['high'] >= sl: win = False; break # 6. 🧬 ÉVOLUTION : On récompense ou on punit l'ADN ! mutate_agent(symbol, tf, current_scenario, success=win) await asyncio.sleep(5) except Exception as e: pass # On reste silencieux pendant le sommeil await asyncio.sleep(60) def should_send_signal(symbol, timeframe, proba): global last_signals_sent key = (symbol, timeframe) now = time.time() # 🛡️ FIX : La prison est adaptée à la Timeframe tf_blocks = {"1m": 60, "5m": 300, "15m": 900, "1h": 3600, "4h": 14400} block_time = tf_blocks.get(timeframe, 3600) # Par défaut 1h si inconnu if key in last_signals_sent: last_proba, last_time = last_signals_sent[key] # Tolérance de 5% d'écart max, et blocage selon le tf_block if abs(last_proba - proba) < 0.05 and (now - last_time) < block_time: return False last_signals_sent[key] = (proba, now) return True # --- ⚖️ JUDGE API V31 (VERSION NETTOYÉE POUR SL PHYSIQUE) --- def run_judge_api(live_prices_json="{}"): try: import json try: raw_prices = json.loads(live_prices_json) if isinstance(live_prices_json, str) else {} live_prices = {k.lower(): v for k, v in raw_prices.items()} except: live_prices = {} with sqlite3.connect(DB_NAME, timeout=10) as conn: conn.row_factory = sqlite3.Row cursor = conn.cursor() cursor.execute("SELECT * FROM signals WHERE status = 'EN_COURS'") trades = cursor.fetchall() closed_trades = [] watching_info = [] # 🎯 Contiendra uniquement des dicts pour le Bras current_time = datetime.now(timezone.utc) max_age_minutes = {"15m": 30, "1h": 60, "4h": 240} # Le 4h expire après 4 heures for t in trades: try: sym_db = t['symbol'] epic = sym_db.replace("/", "").replace("USDT", "USD").lower() if not epic.endswith("m"): epic += "m" is_live = epic in live_prices if is_live and t['confirmed'] == 0: cursor.execute("UPDATE signals SET confirmed = 1 WHERE id = ?", (t['id'],)) print(f"✅ [AUTO-CONFIRM] ID: {t['id']}") if not is_live: date_str = t['date'].replace('Z', '+00:00') signal_time = datetime.fromisoformat(date_str) age_minutes = (current_time - signal_time).total_seconds() / 60.0 max_age = max_age_minutes.get(t['timeframe'], 60) if t['confirmed'] == 1: cursor.execute("UPDATE signals SET status = 'FERMÉ PAR MT5 🛑' WHERE id = ?", (t['id'],)) closed_trades.append({"symbol": t['symbol'], "id": t['id'], "direction": t['direction']}) elif age_minutes > max_age: cursor.execute("UPDATE signals SET status = 'EXPIRÉ ⏰' WHERE id = ?", (t['id'],)) continue # --- CALCUL DU TRAILING --- current_price = float(live_prices[epic]) sl_dyn, peak = t['sl'], t['peak_price'] new_peak = max(peak, current_price) if t['direction'] == 'HAUSSIER' else min(peak, current_price) dist_totale = abs(t['tp'] - t['price']) if t['direction'] == 'HAUSSIER': mouvement = current_price - t['price'] else: mouvement = t['price'] - current_price progression = mouvement / dist_totale if (dist_totale > 0 and mouvement > 0) else 0 nouveau_sl = sl_dyn dist_sl_initial = abs(t['price'] - sl_dyn) # LOGIQUE AGRESSIVE if t['direction'] == 'HAUSSIER': if progression >= 0.60: nouveau_sl = max(sl_dyn, t['price'] + (dist_totale * 0.50)) elif progression >= 0.40: nouveau_sl = max(sl_dyn, t['price'] + (dist_totale * 0.20)) elif progression >= 0.20: nouveau_sl = max(sl_dyn, t['price'] + (dist_totale * 0.02)) elif progression >= 0.10: nouveau_sl = max(sl_dyn, t['price'] - (dist_sl_initial * 0.50)) else: if progression >= 0.60: nouveau_sl = min(sl_dyn, t['price'] - (dist_totale * 0.50)) elif progression >= 0.40: nouveau_sl = min(sl_dyn, t['price'] - (dist_totale * 0.20)) elif progression >= 0.20: nouveau_sl = min(sl_dyn, t['price'] - (dist_totale * 0.02)) elif progression >= 0.10: nouveau_sl = min(sl_dyn, t['price'] + (dist_sl_initial * 0.50)) cursor.execute("UPDATE signals SET peak_price = ?, sl = ? WHERE id = ?", (new_peak, nouveau_sl, t['id'])) # --- DÉCISION DE CLÔTURE --- outcome, reward = None, 0 if t['direction'] == 'HAUSSIER': if current_price >= t['tp']: outcome, reward = "GAGNÉ ✅", 3 elif current_price <= nouveau_sl: outcome, reward = ("GAGNÉ (PARTIEL) 💸", 1) if nouveau_sl > t['price'] else ("PERDU ❌", -5) else: if current_price <= t['tp']: outcome, reward = "GAGNÉ ✅", 3 elif current_price >= nouveau_sl: outcome, reward = ("GAGNÉ (PARTIEL) 💸", 1) if nouveau_sl < t['price'] else ("PERDU ❌", -5) if outcome: mutate_agent(t['symbol'], t['timeframe'], t['regime'], success=(reward>0)) cursor.execute("UPDATE signals SET status=? WHERE id=?", (outcome, t['id'])) closed_trades.append({"symbol": t['symbol'], "id": t['id'], "direction": t['direction']}) else: # 🎯 ON N'AJOUTE DANS WATCHING QUE SI LE TRADE EST ENCORE OUVERT watching_info.append({ "id": t['id'], "symbol": t['symbol'], "sl": round(nouveau_sl, 6), "prog": round(progression, 4) }) except Exception as e: print(f"⚠️ Erreur Trade ID {t['id']}: {e}") conn.commit() # Retour propre : data contient les fermetures, watching contient les mises à jour SL return { "status": "updates" if closed_trades else "waiting", "data": closed_trades, "watching": watching_info } except Exception as e: return {"status": "error", "message": str(e)} # --- 📡 LECTURE RADAR OPTIMISÉE --- def get_active_signals(): try: with sqlite3.connect(DB_NAME, timeout=20) as conn: conn.row_factory = sqlite3.Row cursor = conn.cursor() # 🎯 On ne prend QUE les signaux NON CONFIRMÉS pour ne pas surcharger le Bras cursor.execute("SELECT * FROM signals WHERE status = 'EN_COURS' AND confirmed = 0") signaux = [dict(row) for row in cursor.fetchall()] print(f"📡 [API RADAR] {len(signaux)} nouveaux signaux trouvés pour le Bras.") return signaux except Exception as e: print(f"❌ [ERREUR CRITIQUE CLOUD] get_active_signals a planté : {e}") return [] # --- 📊 GET BOT SKILLS (UI Dashboard) --- def get_bot_skills(): try: scenario_map = {0: "BULL RUN", 1: "BEAR RUN", 2: "PULLBACK", 3: "RANGE/CHAOS", 4: "SQUEEZE", 5: "REVERSAL"} with sqlite3.connect(DB_NAME) as conn: return [[r[0], r[1], scenario_map.get(r[2], "INCONNU"), f"x{round(r[3], 2)}", f"x{round(r[4], 2)}", f"{round(r[5]*100)}%", f"🧬 Gen {r[6]}"] for r in conn.cursor().execute("SELECT symbol, timeframe, regime, tp_mult, sl_mult, min_prob, generation FROM agent_logic ORDER BY symbol, timeframe, regime").fetchall()] except Exception as e: return [[f"Erreur: {str(e)}", "-", "-", "-", "-", "-", "-"]] # --- 🧠 TRAINING ENGINE --- def trigger_training(symbol="BTC/USD"): try: memory_guard() # 🎯 1. Nettoyage du symbole pour le nom de fichier (ex: BTC/USDT -> BTC) sym_clean = symbol.replace("/", "").replace("USDT", "").replace("USD", "").upper() if not sym_clean: sym_clean = "DEFAULT" # On crée un nom de fichier unique par crypto model_filename = f"dino_lgbm_{sym_clean}.txt" bars = exchange_sync.fetch_ohlcv(symbol, timeframe='1h', limit=1500) df = pd.DataFrame(bars, columns=['ts', 'open', 'high', 'low', 'close', 'vol']) if len(df) < 500: return f"❌ Historique insuffisant pour {symbol}." df_final = prepare_features_sync(symbol, '1h', limit_bars=1000) if df_final.empty or len(df_final) < 100: return f"❌ Données vides pour {symbol}." if LGBM_AVAILABLE: ml_cols = ["RSI", "Dist_High_24h", "Dist_Low_24h", "EMA_dist", "EMA_slope", "ATR_ratio", "VOL_ratio"] df_final['Target'] = (df_final['close'].shift(-1) > df_final['close']).astype(int) df_train = df_final.dropna(subset=ml_cols + ['Target']) X, y = df_train[ml_cols], df_train['Target'] params = {'objective': 'binary', 'metric': 'binary_logloss', 'boosting_type': 'gbdt', 'learning_rate': 0.05, 'num_leaves': 31, 'verbose': -1} model = lgb.train(params, lgb.Dataset(X, label=y), 100) # 💾 2. Sauvegarde avec le nom unique model.save_model(model_filename) try: api = HfApi() api.upload_file( path_or_fileobj=model_filename, path_in_repo=model_filename, repo_id=HF_REPO_ID, repo_type="dataset", token=HF_TOKEN ) print(f"☁️ [BACKUP] {model_filename} sauvegardé sur le Cloud !") except Exception as e: print(f"⚠️ Erreur backup modèle {sym_clean} : {e}") # 🧠 3. On met à jour le dictionnaire global des cerveaux DINO global dino_brains if 'dino_brains' not in globals(): dino_brains = {} dino_brains[sym_clean] = lgb.Booster(model_file=model_filename) print(f"🧠 [IA] Cerveau {sym_clean} chargé en mémoire vive.") # Ces deux là restent uniques car ils sont sûrement génériques dans ton code global ml_model, time_model try: ml_model, time_model = joblib.load("ml_model_v9.pkl"), joblib.load("time_model.pkl") except: pass # Si les pkl n'existent pas, on ne crashe pas tout gc.collect() return f"✅ IA ré-entraînée et sauvegardée pour {symbol} ({model_filename})." except Exception as e: return f"❌ Erreur Training {symbol} : {e}" # --- 🚀 MOTEURS AUTO-PILOTE --- AUTO_SYMBOLS = ["BTC/USD", "ETH/USD", "SOL/USD"] AUTO_TIMEFRAMES = ["15m", "1h", "4h"] def set_bot_mode(mode): global DREAM_MODE_ACTIVE target_mode = mode[0] if isinstance(mode, list) else str(mode) if "LIVE" in target_mode.upper(): DREAM_MODE_ACTIVE, msg = False, "🛰️ Mode LIVE activé" else: DREAM_MODE_ACTIVE, msg = True, "🌙 Mode DREAM activé" return {"status": "success", "mode": "DREAM" if DREAM_MODE_ACTIVE else "LIVE", "message": msg} async def universal_scanner_loop(): print("👁️ [SCANNER] Le Cerveau Global est en ligne H24...") while True: for symbol in AUTO_SYMBOLS: for tf in AUTO_TIMEFRAMES: try: # 🧹 1. ON NETTOIE AVANT DE PRÉDIRE (On vire les vieux fantômes) clean_old_db_signals(symbol, tf) # 🧠 2. ON PRÉDIT ET ON SAUVEGARDE LE NOUVEAU SIGNAL pred = await predict_signal(symbol, timeframe=tf) if pred and pred.get("status") == "success": print(f"✅ [SIGNAL VALIDÉ] {symbol} [{tf}] - Probabilité: {pred['final_score']}") # Ligne clean_old supprimée d'ici pour ne pas tuer le trade ! active_signals_state[(symbol, tf)] = pred if not DREAM_MODE_ACTIVE: print(f"🎯 [LIVE] Action requise pour {symbol} [{tf}] : Passage d'ordre.") await asyncio.sleep(5) except Exception as e: print(f"⚠️ Erreur Scanner {symbol} [{tf}]: {e}") await asyncio.sleep(60) # --- ⚖️ TOOLS --- def keep_alive_ping(): return {"status": "awake", "time": datetime.now(timezone.utc).isoformat()} def confirm_trade_api(trade_id, real_price): try: with sqlite3.connect(DB_NAME) as conn: conn.row_factory, cursor = sqlite3.Row, conn.cursor() cursor.execute("SELECT price, tp, sl FROM signals WHERE id = ?", (int(trade_id),)) t = cursor.fetchone() if not t: return {"status": "error"} ecart = float(real_price) - t['price'] cursor.execute("UPDATE signals SET confirmed = 1, price = ?, tp = ?, sl = ?, peak_price = ? WHERE id = ?", (float(real_price), t['tp'] + ecart, t['sl'] + ecart, float(real_price), int(trade_id))) conn.commit() print(f"🤝 [BRIDGE] Le Bras a exécuté l'ID {trade_id} à {real_price}$. Synchronisation OK.") return {"status": "success"} except: return {"status": "error"} def cancel_trade_api(trade_id): try: with sqlite3.connect(DB_NAME) as conn: conn.execute("UPDATE signals SET status = 'ANNULÉ 🗑️', confirmed = 0 WHERE id = ?", (int(trade_id),)); conn.commit() return {"status": "success"} except: return {"status": "error"} def reset_trade_history(): """ Supprime UNIQUEMENT les trades (la table signals) Garde intacte la mémoire génétique (la table agents) """ try: with sqlite3.connect(DB_NAME, timeout=10) as conn: cursor = conn.cursor() # 1. On supprime tout l'historique des trades (brouillons, validés, expirés...) cursor.execute("DELETE FROM signals") # 2. (Optionnel mais propre) On remet le compteur d'ID à 1 cursor.execute("DELETE FROM sqlite_sequence WHERE name='signals'") conn.commit() return "✅ RESET RÉUSSI : Tous les trades ont été effacés. L'ADN est conservé !" except Exception as e: return f"❌ ERREUR RESET : {e}" def training_wrapper(symbol, *args): return trigger_training(str(symbol).strip().upper() if isinstance(symbol, str) else "BTC/USD") import atexit @atexit.register def close_session(): global global_aio_session if global_aio_session and not global_aio_session.closed: asyncio.run(global_aio_session.close()) # --- 🎨 INTERFACE GRADIO V30 --- with gr.Blocks(theme=gr.themes.Monochrome()) as iface: gr.Markdown("# 🦖 Alpha V30 Dinosaur Engine (Master Edition)") with gr.Tab("Admin"): gr.Button("Forcer Entraînement", variant="stop").click(fn=training_wrapper, inputs=gr.Textbox(label="Symbole à recalibrer", value="BTC/USDT"), outputs=gr.Textbox(), api_name="trigger_training") gr.Markdown("---") gr.Markdown("### ⚠️ Zone de Danger") reset_btn = gr.Button("🗑️ Purger tous les trades (Garder l'ADN)", variant="primary") reset_out = gr.Textbox(label="Résultat du nettoyage") reset_btn.click(fn=reset_trade_history, inputs=[], outputs=reset_out, api_name="reset_trades") with gr.Tab("🎯 Prédictions"): gr.Button("Predict", variant="primary").click(fn=predict_signal, inputs=[gr.Textbox(label="Symbole", value="BTC/USD"), gr.Dropdown(choices=["15m", "1h", "4h"], value="1h")], outputs=gr.JSON()) with gr.Tab("⚖️ Système"): gr.Button("Judge").click(fn=run_judge_api, inputs=gr.Textbox(value="{}", visible=False), outputs=gr.JSON(), api_name="run_judge_api") gr.Button("Get Active", visible=False).click(fn=get_active_signals, outputs=gr.JSON(), api_name="get_active_signals") gr.Button("Ping", visible=False).click(fn=keep_alive_ping, outputs=gr.JSON(), api_name="keep_alive_ping") gr.Button("Set Mode", visible=False).click(fn=set_bot_mode, inputs=gr.Textbox(visible=False), outputs=gr.JSON(), api_name="set_bot_mode") with gr.Tab("📊 Stats (ADN)"): skills_table = gr.Dataframe(headers=["Marché", "Timeframe", "Scénario", "Cible (TP)", "Risque (SL)", "Peur Min.", "Génération"], value=get_bot_skills(), interactive=False) with gr.Row(): gr.Button("🔄 Actualiser").click(get_bot_skills, outputs=skills_table) gr.Button(visible=False).click(fn=confirm_trade_api, inputs=[gr.Textbox(visible=False), gr.Textbox(visible=False)], outputs=gr.JSON(), api_name="confirm_trade_api") gr.Button(visible=False).click(fn=cancel_trade_api, inputs=gr.Textbox(visible=False), outputs=gr.JSON(), api_name="cancel_trade_api") iface.load(get_bot_skills, outputs=skills_table) # --- ☁️ SAUVEGARDE CLOUD SILENCIEUSE --- async def hf_backup_loop(): while True: await asyncio.sleep(1800) # Attendre 30 minutes (1800 secondes) print("☁️ [SYSTEM] Lancement du backup planifié...") backup_db_to_hf() import threading _auto_pilot_started = False def run_auto_pilot(): global _auto_pilot_started if _auto_pilot_started: return _auto_pilot_started = True print("⏳ [SYSTEM] Attente 15s avant propulsion Master V31...") time.sleep(15) try: loop = asyncio.new_event_loop() asyncio.set_event_loop(loop) loop.create_task(universal_scanner_loop()) loop.create_task(dream_simulation_loop()) loop.create_task(hf_backup_loop()) # ⬅️ LA SAUVEGARDE EST BIEN ICI loop.run_forever() except Exception as e: print(f"⚠️ Erreur Auto-Pilote : {e}") if __name__ == "__main__": try: threading.Thread(target=run_auto_pilot, daemon=True).start() except Exception as e: print(f"⚠️ Erreur Thread : {e}") # 🛑 LE LAUNCH DOIT TOUJOURS ÊTRE LA TOUTE DERNIÈRE LIGNE DU FICHIER ! iface.launch(server_name="0.0.0.0", server_port=7860, show_api=True)