From d4f770f0b44fae215f59f0b347bd43d2cd6c3306 Mon Sep 17 00:00:00 2001 From: chpeu <129604005+chpeu@users.noreply.github.com> Date: Tue, 11 Nov 2025 10:44:25 +0100 Subject: [PATCH] 3 --- core/position_manager.py | 3 + .../src/lib/components/VariablesPanel.svelte | 14 +- frontend/src/routes/+page.svelte | 16 ++- main.py | 123 +++++++++++++++++- 4 files changed, 147 insertions(+), 9 deletions(-) diff --git a/core/position_manager.py b/core/position_manager.py index 5a671816..3c3f7fa0 100644 --- a/core/position_manager.py +++ b/core/position_manager.py @@ -376,6 +376,9 @@ def open_position( from config import TRADING_CONFIG self.tpsl_config.win_streak = self.config.win_streak self.tpsl_config.loss_streak = self.config.loss_streak + # đŸ”„ FIX: Mettre Ă  jour paramĂštres FIXE depuis TRADING_CONFIG (au lieu de self.config qui n'est pas mis Ă  jour dynamiquement) + self.tpsl_config.fixed_tp_pct = TRADING_CONFIG.get('tp_percent', 0.6) + self.tpsl_config.fixed_sl_pct = TRADING_CONFIG.get('sl_percent', 0.25) # ✅ Mettre Ă  jour paramĂštres ATR depuis TRADING_CONFIG self.tpsl_config.atr_mult_tp = TRADING_CONFIG.get('atr_mult_tp', 1.5) self.tpsl_config.atr_mult_sl = TRADING_CONFIG.get('atr_mult_sl', 1.0) diff --git a/frontend/src/lib/components/VariablesPanel.svelte b/frontend/src/lib/components/VariablesPanel.svelte index 2d898131..9520973a 100644 --- a/frontend/src/lib/components/VariablesPanel.svelte +++ b/frontend/src/lib/components/VariablesPanel.svelte @@ -327,7 +327,12 @@ } else if (typeof stateData.config[key] === 'object' && stateData.config[key] !== null) { newConfig[key] = { ...stateData.config[key] }; } else { - newConfig[key] = stateData.config[key]; + // đŸ”„ FIX: Pour les boolĂ©ens (checkboxes), s'assurer qu'ils sont bien convertis + if (typeof stateData.config[key] === 'boolean') { + newConfig[key] = stateData.config[key]; + } else { + newConfig[key] = stateData.config[key]; + } } } }); @@ -554,7 +559,12 @@ } else if (typeof data.updated[key] === 'object' && data.updated[key] !== null) { newConfig[key] = { ...data.updated[key] }; } else { - newConfig[key] = data.updated[key]; + // đŸ”„ FIX: Pour les boolĂ©ens (checkboxes), s'assurer qu'ils sont bien convertis + if (typeof data.updated[key] === 'boolean') { + newConfig[key] = data.updated[key]; + } else { + newConfig[key] = data.updated[key]; + } } } }); diff --git a/frontend/src/routes/+page.svelte b/frontend/src/routes/+page.svelte index 07d685ef..4deb1d1d 100644 --- a/frontend/src/routes/+page.svelte +++ b/frontend/src/routes/+page.svelte @@ -201,15 +201,21 @@ stopScanning(); }); - ws.on('connect', async () => { - console.log('✅ WebSocket connectĂ©'); - backendConnected = true; - backendError = ''; - // đŸ”„ FIX: Reset trades au dĂ©marrage du bot (nouvelles sessions) + // đŸ”„ FIX: Écouter l'Ă©vĂ©nement reset_session depuis le backend (au dĂ©marrage, AVANT le scan) + ws.on('reset_session', async (data: any) => { + console.log('🔄 Reset session reçu depuis backend:', data); const { clearHistory } = await import('$lib/stores/trades'); clearHistory(); const { resetSessionStats } = await import('$lib/stores/stats'); resetSessionStats(); + // Note: Les graphiques seront automatiquement rĂ©initialisĂ©s via les stores rĂ©initialisĂ©s + }); + + ws.on('connect', async () => { + console.log('✅ WebSocket connectĂ©'); + backendConnected = true; + backendError = ''; + // đŸ”„ FIX: Ne plus rĂ©initialiser ici - le backend enverra reset_session au dĂ©marrage // đŸ”„ FIX: Charger l'Ă©tat initial quand le WebSocket se connecte try { await loadInitialState(); diff --git a/main.py b/main.py index d21fd6f9..b36615e6 100644 --- a/main.py +++ b/main.py @@ -69,6 +69,7 @@ # Initialisation FastAPI app = FastAPI(title="Trade Cursor v7.0") + # đŸ”„ FIX: Exception handler global pour Ă©viter 503 sur /api/state from fastapi.exceptions import RequestValidationError from starlette.exceptions import HTTPException as StarletteHTTPException @@ -185,6 +186,27 @@ async def dispatch(self, request: StarletteRequest, call_next): set_websocket_manager_routes(ws_manager) logger.info("✅ ws_manager injectĂ© dans API routes") +# đŸ”„ FIX: ÉvĂ©nement de dĂ©marrage FastAPI pour rĂ©initialiser le frontend AVANT le scan +@app.on_event("startup") +async def startup_event(): + """ÉvĂ©nement de dĂ©marrage - rĂ©initialiser le frontend AVANT le scan""" + try: + # Initialiser les instances si pas dĂ©jĂ  fait + init_instances() + + # Attendre un peu pour que les connexions WebSocket soient prĂȘtes + await asyncio.sleep(1.0) + + # đŸ”„ FIX: Émettre Ă©vĂ©nement de rĂ©initialisation pour synchroniser le frontend IMMÉDIATEMENT + if ws_manager: + await ws_manager.emit('reset_session', { + 'timestamp': time.time(), + 'reason': 'backend_startup' + }) + logger.info("✅ ÉvĂ©nement reset_session Ă©mis au dĂ©marrage (AVANT le scan)") + except Exception as e: + logger.warning(f"⚠ Erreur Ă©vĂ©nement startup: {e}") + # đŸ”„ PHASE 4: Fichier de persistance pour trade history # đŸ”„ FIX: Fichier historique par instance pour Ă©viter conflits multi-instances # Utiliser le port comme identifiant d'instance (dĂ©faut: 5000) @@ -995,7 +1017,7 @@ def init_instances(): # La DB est dĂ©jĂ  initialisĂ©e dans __init__ (via _init_database()) logger.info(f"✅ Analytics DB prĂȘte: {ANALYTICS_DB_PATH}") - # đŸ”„ FIX: RĂ©initialiser les stats au dĂ©marrage du bot + # đŸ”„ FIX: RĂ©initialiser les stats au dĂ©marrage du bot (AVANT le scan) if analytics_db: try: # Vider tous les trades de la base de donnĂ©es pour remettre les stats Ă  zĂ©ro @@ -2073,6 +2095,22 @@ async def websocket_endpoint(websocket: WebSocket): except Exception as e: logger.error(f"❌ Erreur envoi logs: {e}") + # đŸ”„ FIX: Émettre reset_session Ă  chaque nouvelle connexion pour rĂ©initialiser le frontend + # (en plus de l'Ă©vĂ©nement startup, pour les clients qui se connectent aprĂšs le dĂ©marrage) + # Toujours Ă©mettre pour s'assurer que le frontend est rĂ©initialisĂ© mĂȘme si connectĂ© aprĂšs le dĂ©marrage + try: + await ws_manager.send_personal_message({ + 'type': 'event', + 'event': 'reset_session', + 'data': { + 'timestamp': time.time(), + 'reason': 'new_connection' + } + }, websocket) + logger.debug("✅ ÉvĂ©nement reset_session envoyĂ© Ă  la nouvelle connexion") + except Exception as e: + logger.debug(f"⚠ Erreur envoi reset_session: {e}") + # Boucle bidirectionnelle : recevoir et traiter messages try: while True: @@ -2244,12 +2282,28 @@ async def websocket_endpoint(websocket: WebSocket): 'success': True, 'session_id': session_id or f"live_{int(time.time())}", 'config': { + # Patterns Techniques + 'use_breakout': TRADING_CONFIG.get('use_breakout', True), + 'use_snr': TRADING_CONFIG.get('use_snr', True), + 'use_wick': TRADING_CONFIG.get('use_wick', True), + 'use_divergence': TRADING_CONFIG.get('use_divergence', True), + # Patterns de Bougies + 'use_engulfing': TRADING_CONFIG.get('use_engulfing', True), + 'use_hammer': TRADING_CONFIG.get('use_hammer', True), + 'use_shooting_star': TRADING_CONFIG.get('use_shooting_star', True), + 'use_doji': TRADING_CONFIG.get('use_doji', True), + 'use_marubozu': TRADING_CONFIG.get('use_marubozu', True), + 'use_morning_star': TRADING_CONFIG.get('use_morning_star', True), + 'use_evening_star': TRADING_CONFIG.get('use_evening_star', True), + # Validation Setups + 'use_confluence': TRADING_CONFIG.get('use_confluence', False), 'volume_multiplier': TRADING_CONFIG.get('volume_multiplier', 0.95), 'min_score_required': TRADING_CONFIG.get('min_score_required', 7.5), - 'use_confluence': TRADING_CONFIG.get('use_confluence', False), + # TP/SL Configuration 'tp_sl_mode': TRADING_CONFIG.get('tp_sl_mode', 'FIXE'), 'tp_percent': TRADING_CONFIG.get('tp_percent', 0.25), 'sl_percent': TRADING_CONFIG.get('sl_percent', 0.25), + # Seuils & Filtres 'snr_threshold': TRADING_CONFIG.get('snr_threshold', 0.25), 'breakout_threshold': TRADING_CONFIG.get('breakout_threshold', 0.35), 'wick_ratio_max': TRADING_CONFIG.get('wick_ratio_max', 2.8), @@ -2260,8 +2314,34 @@ async def websocket_endpoint(websocket: WebSocket): 'optimal_atr_min_5m': TRADING_CONFIG.get('optimal_atr_min_5m', 0.22), 'optimal_atr_max_5m': TRADING_CONFIG.get('optimal_atr_max_5m', 1.4), 'trend_timeframe': TRADING_CONFIG.get('trend_timeframe', '15m'), + # Money Management 'account_size': TRADING_CONFIG.get('account_size', 1000.0), 'risk_per_trade': TRADING_CONFIG.get('risk_per_trade', 2.0), + # Mode ATR + 'atr_mult_tp': TRADING_CONFIG.get('atr_mult_tp', 1.5), + 'atr_mult_sl': TRADING_CONFIG.get('atr_mult_sl', 1.0), + 'atr_min': TRADING_CONFIG.get('atr_min', 0.15), + 'atr_max': TRADING_CONFIG.get('atr_max', 1.5), + # Mode ESCALIER + 'partial_tp_percent': TRADING_CONFIG.get('partial_tp_percent', 50), + 'escalier_level1_pnl': TRADING_CONFIG.get('escalier_level1_pnl', 0.20), + 'escalier_level1_size': TRADING_CONFIG.get('escalier_level1_size', 25), + 'escalier_level2_pnl': TRADING_CONFIG.get('escalier_level2_pnl', 0.35), + 'escalier_level2_size': TRADING_CONFIG.get('escalier_level2_size', 25), + 'escalier_level3_pnl': TRADING_CONFIG.get('escalier_level3_pnl', 0.50), + 'escalier_level3_size': TRADING_CONFIG.get('escalier_level3_size', 25), + 'escalier_level4_pnl': TRADING_CONFIG.get('escalier_level4_pnl', 0.80), + 'escalier_level4_size': TRADING_CONFIG.get('escalier_level4_size', 25), + # Trailing Stop + 'trailing_enabled': TRADING_CONFIG.get('trailing_enabled', True), + 'trailing_trigger_pnl': TRADING_CONFIG.get('trailing_trigger_pnl', 0.25), + 'trailing_atr_multiplier': TRADING_CONFIG.get('trailing_atr_multiplier', 0.4), + 'trailing_min_distance': TRADING_CONFIG.get('trailing_min_distance', 0.08), + 'trailing_max_distance': TRADING_CONFIG.get('trailing_max_distance', 0.25), + # Scanner + 'top_pairs_limit': TRADING_CONFIG.get('top_pairs_limit', 20), + 'balance_score_min': TRADING_CONFIG.get('balance_score_min', 0.0), + # Autres 'telegram_enabled': TELEGRAM_ENABLED # đŸ”„ MIGRATION COMPLÈTE: Exposer statut Telegram }, 'scanner': { @@ -2670,6 +2750,45 @@ async def handle_client_command(command: str, params: dict): if updated: logger.info(f"✅ Config mise Ă  jour via WebSocket: {updated}") await add_log('INFO', 'Config mise Ă  jour', str(updated)) + + # đŸ”„ FIX: Mettre Ă  jour immĂ©diatement toutes les instances qui utilisent la config + # Mettre Ă  jour position_config si nĂ©cessaire (sans rĂ©initialiser complĂštement) + if position_config: + from config import TRADING_CONFIG + # Mettre Ă  jour les valeurs TP/SL si elles ont changĂ© + if 'tp_sl_mode' in updated: + tp_sl_mode = TRADING_CONFIG.get('tp_sl_mode', 'FIXE') + position_config.use_atr = (tp_sl_mode == 'ATR' or tp_sl_mode == 'TP_MULTI') + if 'tp_percent' in updated: + position_config.fixed_tp_pct = TRADING_CONFIG.get('tp_percent', 0.6) + if 'sl_percent' in updated: + position_config.fixed_sl_pct = TRADING_CONFIG.get('sl_percent', 0.25) + if 'atr_mult_tp' in updated: + position_config.atr_mult_tp = TRADING_CONFIG.get('atr_mult_tp', 1.5) + if 'atr_mult_sl' in updated: + position_config.atr_mult_sl = TRADING_CONFIG.get('atr_mult_sl', 1.0) + if 'atr_min' in updated: + position_config.atr_min = TRADING_CONFIG.get('atr_min', 0.15) + if 'atr_max' in updated: + position_config.atr_max = TRADING_CONFIG.get('atr_max', 1.5) + + # đŸ”„ FIX: Mettre Ă  jour aussi position_manager.tpsl_config si une position est active + if position_manager and position_manager.active_position: + from config import TRADING_CONFIG + # Mettre Ă  jour les valeurs TP/SL dans tpsl_config pour les prochaines positions + if 'tp_percent' in updated: + position_manager.tpsl_config.fixed_tp_pct = TRADING_CONFIG.get('tp_percent', 0.6) + if 'sl_percent' in updated: + position_manager.tpsl_config.fixed_sl_pct = TRADING_CONFIG.get('sl_percent', 0.25) + if 'atr_mult_tp' in updated: + position_manager.tpsl_config.atr_mult_tp = TRADING_CONFIG.get('atr_mult_tp', 1.5) + if 'atr_mult_sl' in updated: + position_manager.tpsl_config.atr_mult_sl = TRADING_CONFIG.get('atr_mult_sl', 1.0) + if 'atr_min' in updated: + position_manager.tpsl_config.atr_min = TRADING_CONFIG.get('atr_min', 0.15) + if 'atr_max' in updated: + position_manager.tpsl_config.atr_max = TRADING_CONFIG.get('atr_max', 1.5) + # đŸ”„ BIDIRECTIONNEL: Émettre Ă©vĂ©nement de mise Ă  jour de config pour synchroniser le frontend await ws_manager.emit('config_updated', { 'updated': updated,