#!/usr/bin/env python3
"""
SERVEUR UNIFIÉ - Toutes les fonctionnalités crypto trading
"""
from fastapi import FastAPI, HTTPException, Depends, status
from fastapi.responses import HTMLResponse
from fastapi.staticfiles import StaticFiles
from fastapi.templating import Jinja2Templates
from fastapi import Request
from fastapi.security import HTTPBasic, HTTPBasicCredentials
from contextlib import asynccontextmanager
import uvicorn
import asyncio
import threading
import logging
import time
import os
import secrets
from datetime import datetime
from typing import Annotated

logger = logging.getLogger(__name__)

def get_file_modification_time(filepath: str) -> str:
    """Récupère la date de dernière modification d'un fichier"""
    try:
        import os
        from datetime import datetime
        
        if os.path.exists(filepath):
            mtime = os.path.getmtime(filepath)
            return datetime.fromtimestamp(mtime).strftime("%Y-%m-%d %H:%M:%S")
        else:
            return "Fichier non trouvé"
    except Exception as e:
        return f"Erreur: {e}"

# Configuration du logger pour écrire dans le même fichier que le trading engine
def setup_unified_logging():
    """Configure le logger du serveur pour utiliser le même fichier que le trading engine"""
    from logging.handlers import RotatingFileHandler
    import os
    
    # Créer le dossier logs s'il n'existe pas
    logs_dir = "logs"
    if not os.path.exists(logs_dir):
        os.makedirs(logs_dir)
    
    # Ne configurer que si pas déjà fait
    if not logger.handlers:
        logger.setLevel(logging.DEBUG)
        
        # Handler avec rotation (même configuration que trading engine)
        handler = RotatingFileHandler(
            os.path.join(logs_dir, "trading_engine.log"),
            maxBytes=10*1024*1024,  # 10MB
            backupCount=5
        )
        
        # Format des logs (même que trading engine)
        formatter = logging.Formatter(
            '%(asctime)s - %(levelname)s - [SERVER] - %(message)s'
        )
        handler.setFormatter(formatter)
        
        logger.addHandler(handler)
        logger.info("=== UNIFIED TRADING SERVER STARTED ===")
        
        # Log des dates de modification des fichiers principaux
        logger.info("FILE MODIFICATION DATES:")
        logger.info(f"  unified_trading_server.py: {get_file_modification_time('unified_trading_server.py')}")
        logger.info(f"  real_time_trading_engine.py: {get_file_modification_time('real_time_trading_engine.py')}")
        logger.info(f"  hyperliquid_client.py: {get_file_modification_time('hyperliquid_client.py')}")
        logger.info(f"  trading_config.py: {get_file_modification_time('trading_config.py')}")

# Configurer le logging unifié
setup_unified_logging()

# Configuration du serveur et authentification
APP_PORT = int(os.getenv("APP_PORT", "8888"))
APP_HOST = os.getenv("APP_HOST", "0.0.0.0")
AUTH_USERNAME = os.getenv("AUTH_USERNAME", "admin")
AUTH_PASSWORD = os.getenv("AUTH_PASSWORD", "trading2024")

# Authentification basique
security = HTTPBasic()

def verify_credentials(credentials: Annotated[HTTPBasicCredentials, Depends(security)]):
    """Vérifie les identifiants pour l'authentification basique"""
    correct_username = secrets.compare_digest(credentials.username, AUTH_USERNAME)
    correct_password = secrets.compare_digest(credentials.password, AUTH_PASSWORD)
    
    if not (correct_username and correct_password):
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="Identifiants incorrects",
            headers={"WWW-Authenticate": "Basic"},
        )
    return credentials.username

from crypto_trader.oracles.technical_indicators_oracle import TechnicalIndicatorsOracle
from crypto_trader.oracles.sentiment_oracle import SentimentOracle
from crypto_trader.oracles.tradfi_oracle import TradFiOracle
from crypto_trader.services.position_persistence import position_persistence
from crypto_trader.models.indicators import IndicatorSignal
from crypto_trader.services.price_service import PriceService
from crypto_trader.services.executed_signals_service import ExecutedSignalsService
from crypto_trader.config import settings

# Instances globales des oracles et services - TIMEFRAMES LONGS
tech_oracle = TechnicalIndicatorsOracle(update_interval=1800)  # 30 minutes (au lieu de 5min)
sentiment_oracle = SentimentOracle(update_interval=3600)  # 1 heure (au lieu de 15min)
tradfi_oracle = TradFiOracle(update_interval=14400)  # 4 heures (au lieu de 1h)
price_service = PriceService()
executed_signals_service = ExecutedSignalsService(price_service)

# Cache des leverages Hyperliquid (mis à jour périodiquement)
hyperliquid_leverages_cache = {}
last_leverage_update = None

def update_hyperliquid_leverages():
    """Met à jour le cache des leverages depuis l'API Hyperliquid"""
    global hyperliquid_leverages_cache, last_leverage_update
    try:
        from datetime import datetime, timedelta
        
        # Éviter les appels trop fréquents (cache 1h)
        if (last_leverage_update and 
            datetime.now() - last_leverage_update < timedelta(hours=1)):
            return
        
        leverages = hyperliquid_client.get_available_leverages()
        if leverages:
            hyperliquid_leverages_cache = leverages
            last_leverage_update = datetime.now()
            logger.debug(f"Leverages Hyperliquid mis à jour: {len(leverages)} tokens")
    except Exception as e:
        logger.error(f"Erreur mise à jour leverages Hyperliquid: {e}")

def normalize_symbol_for_price_oracle(symbol: str) -> str:
    """Convertir un symbole vers format Hyperliquid (-USD)"""
    if symbol.endswith('-USD') or symbol.endswith('PERP'):
        return symbol  # Déjà au format Hyperliquid
    if symbol.endswith('USDT'):
        return symbol.replace('USDT', '-USD')
    return f"{symbol}-USD"

def analyze_multi_timeframe_alignment(symbol: str, expected_signal: str) -> float:
    """
    Analyse l'alignement des signaux sur plusieurs timeframes
    Retourne un score entre 0 et 1 (1 = parfaitement aligné)
    """
    try:
        timeframes = ['15m', '1h', '4h']  # Du plus court au plus long
        weights = {'15m': 0.2, '1h': 0.3, '4h': 0.5}  # Plus de poids sur les timeframes longs
        
        aligned_score = 0.0
        total_weight = 0.0
        
        for tf in timeframes:
            cache_key = f"{symbol}_{tf}"
            if cache_key in tech_oracle.indicators_cache:
                indicators = tech_oracle.indicators_cache[cache_key]
                if indicators and hasattr(indicators, 'overall_signal'):
                    signal = indicators.overall_signal
                    weight = weights[tf]
                    
                    # Vérifier l'alignement
                    if expected_signal == 'BUY' and signal in ['BUY', 'STRONG_BUY']:
                        aligned_score += weight
                    elif expected_signal == 'SELL' and signal in ['SELL', 'STRONG_SELL']:
                        aligned_score += weight
                    
                    total_weight += weight
        
        if total_weight > 0:
            return aligned_score / total_weight
        return 0.0
        
    except Exception as e:
        logger.debug(f"Erreur analyse multi-timeframe pour {symbol}: {e}")
        return 0.5  # Retourne neutre en cas d'erreur

def calculate_signal_quality_score_OLD(signal: dict) -> float:
    """OBSOLÈTE - Calcule un score de qualité pour un signal (0-1)

    Cette fonction est remplacée par le système pragmatic_signals qui génère
    directement des scores de qualité valides dans les signaux.
    Prend en compte: confidence, momentum, volume, convergence d'indicateurs
    """
    score = 0.0
    
    # Base confidence (40%) - avec boost minimum pour éviter les scores trop bas
    confidence = signal.get('confidence', 0.5)
    # Boost minimum: si confidence > 0, garantir au moins 20% de base
    if confidence > 0:
        confidence = max(0.2, confidence)
    score += confidence * 0.40
    
    # Momentum des indicateurs (30%)
    if 'indicators' in signal:
        indicators = signal['indicators']
        momentum_score = 0.0
        
        # RSI momentum
        if 'rsi' in indicators:
            rsi = indicators['rsi']
            if signal['signal_type'] == 'BUY' and rsi < 40:
                momentum_score += 0.5
            elif signal['signal_type'] == 'SELL' and rsi > 60:
                momentum_score += 0.5
        
        # MACD momentum  
        if 'macd_bullish' in indicators:
            if (signal['signal_type'] == 'BUY' and indicators['macd_bullish']) or \
               (signal['signal_type'] == 'SELL' and not indicators['macd_bullish']):
                momentum_score += 0.5
        
        score += min(momentum_score, 1.0) * 0.30
    
    # Volume confirmation (20%)
    if 'volume_ratio' in signal and signal['volume_ratio'] > 1.2:
        score += 0.20
    
    # Timeframe alignment (10%)
    if 'timeframe_alignment' in signal:
        score += signal['timeframe_alignment'] * 0.10
    
    return min(1.0, score)

def is_market_liquid_enough(symbol: str, volume_24h: float = None) -> bool:
    """Vérifie si un marché a suffisamment de liquidité pour être tradé"""
    # Toujours inclure les symboles haute liquidité (format Hyperliquid)
    if symbol in settings.high_liquidity_symbols:
        return True
    
    # Convertir vers format Hyperliquid pour vérification si nécessaire
    symbol_hyperliquid = normalize_symbol_for_price_oracle(symbol)
    if symbol_hyperliquid in settings.high_liquidity_symbols:
        return True
    
    # Si on a le volume 24h, vérifier le seuil minimum
    if volume_24h is not None:
        return volume_24h >= settings.min_volume_24h_usdt
    
    # Par défaut, récupérer le volume depuis l'oracle Hyperliquid
    try:
        price_data = price_service.oracle.get_latest_price(symbol_hyperliquid)
        if price_data and hasattr(price_data, 'volume_24h'):
            return price_data.volume_24h >= settings.min_volume_24h_usdt
    except Exception as e:
        logger.error(f"Erreur vérification liquidité {symbol}: {e}")
    
    # Si pas de données, rejeter par sécurité (sauf si dans la liste haute liquidité)
    return False

def get_optimal_leverage(symbol: str, confidence: float) -> int:
    """Détermine le leverage optimal selon l'API Hyperliquid"""
    # Mettre à jour le cache si nécessaire
    update_hyperliquid_leverages()
    
    # Récupérer le leverage max depuis Hyperliquid
    max_leverage = hyperliquid_leverages_cache.get(symbol, 3)  # Défaut conservateur
    
    # Approche large et agressive - utilise quasi le max leverage disponible
    if symbol in ['BTC', 'ETH']:  # Moins volatils - leverage max ou quasi
        confidence_multiplier = 1.0 if confidence > 0.05 else 0.95  # 95-100% du max
    elif symbol in ['SOL', 'AVAX', 'LINK', 'UNI', 'ADA', 'DOT']:  # Majeurs établis - très agressif
        confidence_multiplier = 0.95 if confidence > 0.06 else 0.85  # 85-95% du max
    else:  # Tous les autres - encore très agressif pour apprendre
        confidence_multiplier = 0.85 if confidence > 0.08 else 0.75   # 75-85% du max
    
    optimal_leverage = int(max_leverage * confidence_multiplier)
    return max(1, min(optimal_leverage, max_leverage))  # Entre 1 et max Hyperliquid

def get_existing_positions():
    """Récupère les positions existantes LIVE depuis Hyperliquid avec PnL exact"""
    existing_positions = {}
    try:
        if live_engine.trading_mode in ['LIVE_MANUAL', 'LIVE_AUTO'] and live_engine.hyperliquid_client:
            # Utiliser get_positions() qui donne les données exactes et à jour
            hl_positions = live_engine.hyperliquid_client.get_positions()
            if hl_positions:
                for pos in hl_positions:
                    symbol = pos['symbol'].replace('-PERP', '')
                    size = float(pos.get('size', 0))
                    
                    # Ne garder que les positions ouvertes (size != 0)
                    if size != 0:
                        # Récupérer le PnL exact depuis les données raw
                        raw_data = pos.get('raw_position_data', {})
                        pnl_usd = float(raw_data.get('unrealizedPnl', 0))
                        
                        # Calculer le PnL% basé sur la marge utilisée
                        entry_price = float(pos.get('entry_price', 1))
                        margin_used = abs(size * entry_price)
                        pnl_percent = (pnl_usd / margin_used * 100) if margin_used > 0 else 0
                        
                        existing_positions[symbol] = {
                            'side': pos['side'].upper(),
                            'pnl_percent': pnl_percent,
                            'pnl': pnl_usd,
                            'size': size,
                            'entry_price': entry_price,
                            'current_price': float(pos.get('mark_price', entry_price))
                        }
                        
            positions_debug = [(k, f"{v['pnl_percent']:.1f}%") for k, v in existing_positions.items()]
            logger.debug(f"Positions LIVE récupérées: {positions_debug}")
        else:
            # Mode SIMULATION : utiliser les positions locales
            for pos_id, pos in live_engine.positions.items():
                symbol = pos.symbol
                existing_positions[symbol] = {
                    'side': pos.side,
                    'pnl_percent': pos.pnl_percent,
                    'pnl': pos.pnl,
                    'size': pos.size,
                    'entry_price': pos.entry_price,
                    'current_price': pos.current_price
                }
            positions_debug = [(k, f"{v['pnl_percent']:.1f}%") for k, v in existing_positions.items()]
            logger.debug(f"Positions SIMULATION récupérées: {positions_debug}")
            
    except Exception as e:
        logger.warning(f"Erreur récupération positions existantes: {e}")
    return existing_positions

def should_filter_opposite_signal(symbol, signal_type, confidence, existing_positions):
    """Filtre intelligent pour éviter les retournements coûteux et les doublons"""
    if symbol not in existing_positions:
        return False  # Pas de position existante, autoriser
    
    existing_pos = existing_positions[symbol]
    existing_side = existing_pos['side']
    
    # Vérifier si c'est un signal opposé
    is_opposite = (
        (existing_side == 'LONG' and signal_type == 'SELL') or
        (existing_side == 'SHORT' and signal_type == 'BUY')
    )
    
    # Vérifier si c'est un signal identique (doublon)
    is_duplicate = (
        (existing_side == 'LONG' and signal_type == 'BUY') or
        (existing_side == 'SHORT' and signal_type == 'SELL')
    )
    
    if is_duplicate:
        logger.debug(f"FILTRE: {symbol} signal {signal_type} dupliqué - position {existing_side} déjà ouverte")
        return True  # Filtrer les doublons
    
    if not is_opposite:
        return False  # Ni opposé ni dupliqué, autoriser
    
    # Règles pour filtrer les signaux opposés coûteux:
    
    # 1. Seuil de confidence élevé requis (15% au lieu de 5%)
    if confidence < 0.15:
        logger.debug(f"FILTRE: {symbol} signal {signal_type} confidence {confidence:.2%} < 15% requis pour retournement")
        return True
    
    # 2. Zone de neutralité PnL (-2% à +2%)
    pnl_percent = existing_pos.get('pnl_percent', 0)
    
    if -2 <= pnl_percent <= 2:
        logger.info(f"FILTRE: {symbol} en zone neutre PnL {pnl_percent:.1f}%, éviter retournement")
        return True
    
    # 3. Si perte significative (>5%), autoriser retournement pour limiter dégâts
    if pnl_percent < -5:
        logger.info(f"ACCEPTE: {symbol} position perdante {pnl_percent:.1f}%, autoriser retournement")
        return False
    
    # 4. Si gain significatif (>8%), requiert confidence très élevée (20%)
    if pnl_percent > 8 and confidence < 0.20:
        logger.info(f"FILTRE: {symbol} position gagnante {pnl_percent:.1f}%, besoin confidence {confidence:.2%} > 20%")
        return True
    
    return False  # Autorise par défaut si règles spéciales non déclenchées

def generate_signals_for_api_OLD():
    """OBSOLÈTE - Génère des signaux de trading basés sur les oracles avec filtrage intelligent

    Cette fonction est remplacée par generate_pragmatic_signals() qui utilise
    le système de signaux pragmatiques avec des scores valides (0.6-0.95)
    """
    signals = []
    
    # Récupérer les positions existantes pour filtrage intelligent
    existing_positions = get_existing_positions()
    if existing_positions:
        logger.info(f"Filtrage intelligent: {len(existing_positions)} positions existantes")
    
    # Récupérer les données des oracles
    try:
        # Debug: vérifier le cache des indicateurs techniques (seulement si déjà initialisé)
        if len(tech_oracle.indicators_cache) > 0:
            logger.debug(f"Cache indicateurs techniques: {len(tech_oracle.indicators_cache)} entrées")
        
        # Indicateurs techniques avec prix du cache oracle
        for cache_key, indicators in tech_oracle.indicators_cache.items():
            if indicators and hasattr(indicators, 'overall_signal'):
                # Extraire le symbole de la clé cache (ex: "BTCUSDT_1h" -> "BTCUSDT")
                symbol = cache_key.split('_')[0]  # Garde BTCUSDT complet
                
                # Utiliser le prix déjà dans l'oracle
                current_price = getattr(indicators, 'current_price', 0) or getattr(indicators, 'close_price', 0)
                
                # Traiter le signal global des indicateurs techniques
                signal_type = indicators.overall_signal
                confidence = indicators.signal_strength
                
                # Ne traiter que les signaux d'achat/vente (pas HOLD) et vérifier la liquidité
                if signal_type in ['BUY', 'SELL', 'STRONG_BUY', 'STRONG_SELL'] and current_price > 0:
                    # Vérifier la liquidité du marché
                    if not is_market_liquid_enough(symbol):
                        continue  # Ignorer les marchés peu liquides
                    
                    # Analyser la convergence multi-timeframe
                    timeframe_alignment = analyze_multi_timeframe_alignment(symbol, signal_type)
                    if timeframe_alignment < 0.5:  # Au moins 50% des timeframes doivent être alignés
                        logger.debug(f"Signal {symbol} rejeté: alignement timeframe insuffisant ({timeframe_alignment:.1%})")
                        continue
                    
                    # Convertir STRONG_BUY/STRONG_SELL en BUY/SELL et ajuster la confidence
                    if signal_type == 'STRONG_BUY':
                        signal_type = 'BUY'
                        confidence = min(1.0, confidence * 1.5 * timeframe_alignment)  # Boost avec alignement
                    elif signal_type == 'STRONG_SELL':
                        signal_type = 'SELL'
                        confidence = min(1.0, confidence * 1.5 * timeframe_alignment)
                    
                    # Calculer symbol_clean pour le filtrage
                    symbol_clean = symbol.replace('USDT', '')
                    
                    # Filtrage intelligent pour éviter retournements coûteux
                    if should_filter_opposite_signal(symbol_clean, signal_type, confidence, existing_positions):
                        continue  # Signal filtré, passer au suivant
                    
                    if signal_type == 'BUY':
                        # Target raisonnable pour technique: 3-15% gain selon confidence
                        target_multiplier = 1.03 + (confidence * 0.12)  # 3% à 15% gain max
                        target_price = current_price * target_multiplier
                        stop_loss_pct = 0.15  # Perte max 15% de la mise
                    elif signal_type == 'SELL':
                        # Target raisonnable pour short technique: 3-15% baisse selon confidence
                        target_multiplier = 0.97 - (confidence * 0.12)  # 3% à 15% baisse max
                        target_price = current_price * target_multiplier
                        stop_loss_pct = 0.15  # Perte max 15% de la mise
                    
                    # Calculer le leverage optimal
                    optimal_leverage = get_optimal_leverage(symbol_clean, confidence)
                    
                    # Générer un reasoning basé sur les indicateurs
                    reasoning = f"Technical analysis: {signal_type} signal with {confidence:.1%} confidence"
                    
                    # Collecter les indicateurs pour le scoring
                    signal_indicators = {}
                    if hasattr(indicators, 'rsi'):
                        signal_indicators['rsi'] = indicators.rsi
                        reasoning += f", RSI: {indicators.rsi:.1f}"
                    if hasattr(indicators, 'macd') and hasattr(indicators, 'macd_signal'):
                        macd_bullish = indicators.macd > indicators.macd_signal
                        signal_indicators['macd_bullish'] = macd_bullish
                        reasoning += f", MACD: {'bullish' if macd_bullish else 'bearish'}"
                    if hasattr(indicators, 'volume') and hasattr(indicators, 'volume_ma'):
                        volume_ratio = indicators.volume / indicators.volume_ma if indicators.volume_ma > 0 else 1.0
                        signal_indicators['volume_ratio'] = volume_ratio
                        if volume_ratio > 1.2:
                            reasoning += f", Volume: {volume_ratio:.1f}x avg"
                    
                    # Créer le signal avec tous les indicateurs
                    signal_dict = {
                        'symbol': symbol_clean,
                        'signal_type': signal_type,
                        'side': 'LONG' if signal_type == 'BUY' else 'SHORT',  # Conversion pour SimpleTradingEngine
                        'confidence': confidence,
                        'current_price': current_price,
                        'target_price': round(target_price, 6) if target_price else 0,
                        'stop_loss_pct': stop_loss_pct,  # Pourcentage de perte max sur la mise
                        'leverage': optimal_leverage,  # Leverage optimal pour ce marché
                        'reasoning': reasoning,
                        'timestamp': datetime.now().isoformat(),
                        'timeframe': cache_key.split('_')[1] if '_' in cache_key else '30m',  # Timeframe plus long
                        'source': 'technical_indicators',
                        'indicators': signal_indicators,
                        'timeframe_alignment': timeframe_alignment,
                        'volume_ratio': signal_indicators.get('volume_ratio', 1.0)
                    }
                    
                    # Calculer le score de qualité
                    quality_score = calculate_signal_quality_score(signal_dict)
                    signal_dict['quality_score'] = quality_score
                    
                    # Ne garder que les signaux de qualité acceptable
                    if quality_score >= 0.45:  # Seuil de qualité ajusté pour plus d'opportunités
                        signals.append(signal_dict)
                        logger.info(f"Signal accepté: {symbol_clean} {signal_type} - Score: {quality_score:.2f}")
                    else:
                        logger.debug(f"Signal rejeté: {symbol_clean} {signal_type} - Score insuffisant: {quality_score:.2f}")
        
        # Sentiment de marché avec prix du cache
        for symbol, sentiment_data in sentiment_oracle.sentiment_cache.items():
            if sentiment_data and hasattr(sentiment_data, 'sentiment_score'):
                # Utiliser le prix du cache oracle ou du service si disponible
                current_price = getattr(sentiment_data, 'current_price', 0)
                if not current_price:
                    # Essayer de récupérer depuis le cache du price_service
                    price_cache = getattr(price_service.oracle, 'price_cache', {})
                    if symbol in price_cache:
                        current_price = price_cache[symbol].price
                
                # Logique de signal rééquilibrée basée sur le sentiment
                sentiment_score = sentiment_data.sentiment_score
                confidence = abs(sentiment_score)
                
                # Seuils ajustés pour capturer 15-25% de signaux en marché haussier
                if sentiment_score > 0.08:  # Sentiment fort positif (8%)
                    signal_type = 'BUY'
                elif sentiment_score < -0.10:  # Sentiment fort négatif (-10%)
                    signal_type = 'SELL'
                elif sentiment_score > 0.04:  # Sentiment modérément positif (4%)
                    signal_type = 'BUY'
                    confidence *= 0.8  # Réduire la confidence pour les signaux modérés
                elif sentiment_score < -0.06:  # Sentiment modérément négatif (-6%)
                    signal_type = 'SELL'
                    confidence *= 0.8  # Réduire la confidence pour les signaux modérés
                else:
                    # Ne pas générer de signaux neutres - ignorer le signal
                    continue
                    
                # Vérifier la liquidité
                if not is_market_liquid_enough(symbol):
                    continue  # Ignorer les marchés peu liquides
                    
                # Filtrage intelligent
                if should_filter_opposite_signal(symbol, signal_type, confidence, existing_positions):
                    continue  # Signal filtré
                
                # Calculer target_price et stop_loss basés sur le signal et le prix
                if signal_type == 'BUY' and current_price > 0:
                    # Target raisonnable: 2-10% gain selon confidence
                    target_multiplier = 1.02 + (confidence * 0.8)  # 2% à 12% gain max
                    target_price = current_price * target_multiplier
                    stop_loss_pct = 0.15  # Perte max 15% de la mise
                elif signal_type == 'SELL' and current_price > 0:
                    # Target raisonnable pour short: 2-10% baisse selon confidence
                    target_multiplier = 0.98 - (confidence * 0.8)  # 2% à 12% baisse max
                    target_price = current_price * target_multiplier
                    stop_loss_pct = 0.15  # Perte max 15% de la mise
                else:
                    target_price = current_price
                    stop_loss_pct = 0.0
                
                # Calculer le leverage optimal
                symbol_clean = symbol.replace('USDT', '')
                optimal_leverage = get_optimal_leverage(symbol_clean, confidence)
                
                signals.append({
                    'symbol': symbol_clean,
                    'signal_type': signal_type,
                    'side': 'LONG' if signal_type == 'BUY' else 'SHORT',  # Conversion pour SimpleTradingEngine
                    'confidence': round(confidence, 2),
                    'current_price': current_price,
                    'target_price': round(target_price, 6) if target_price else 0,
                    'stop_loss_pct': stop_loss_pct,  # Pourcentage de perte max sur la mise
                    'leverage': optimal_leverage,  # Leverage optimal pour ce marché
                    'reasoning': f"Sentiment score: {sentiment_data.sentiment_score:.2f}",
                    'timestamp': datetime.now().isoformat(),
                    'timeframe': '1h',  # Timeframe plus long pour sentiment
                    'source': 'sentiment'
                })
        
        # Signaux croisés: sentiment + technique pour renforcer la confidence
        tech_signals = {s['symbol']: s for s in signals if s['source'] == 'technical_indicators'}
        sentiment_signals = {s['symbol']: s for s in signals if s['source'] == 'sentiment'}
        
        for symbol in set(tech_signals.keys()) & set(sentiment_signals.keys()):
            tech_sig = tech_signals[symbol]
            sent_sig = sentiment_signals[symbol]
            
            # Si les deux sources sont d'accord sur la direction
            if tech_sig['signal_type'] == sent_sig['signal_type']:
                # Créer un signal combiné avec confidence renforcée
                combined_confidence = min(1.0, (tech_sig['confidence'] + sent_sig['confidence']) * 0.7)  # Moyenne pondérée
                
                # Utiliser le meilleur prix et target
                current_price = max(tech_sig['current_price'], sent_sig['current_price'])
                
                if tech_sig['signal_type'] == 'BUY':
                    target_price = current_price * (1.05 + combined_confidence * 0.15)  # 5% à 20%
                else:  # SELL
                    target_price = current_price * (0.95 - combined_confidence * 0.15)  # -5% à -20%
                
                symbol_clean = symbol.replace('USDT', '') if 'USDT' in symbol else symbol
                optimal_leverage = get_optimal_leverage(symbol_clean, combined_confidence)
                
                signals.append({
                    'symbol': symbol_clean,
                    'signal_type': tech_sig['signal_type'],
                    'side': 'LONG' if tech_sig['signal_type'] == 'BUY' else 'SHORT',  # Conversion pour SimpleTradingEngine
                    'confidence': combined_confidence,
                    'current_price': current_price,
                    'target_price': round(target_price, 6),
                    'stop_loss_pct': 0.15,
                    'leverage': optimal_leverage,
                    'reasoning': f"COMBINED: Tech + Sentiment agree on {tech_sig['signal_type']} ({combined_confidence:.1%})",
                    'timestamp': datetime.now().isoformat(),
                    'timeframe': '1h-4h',  # Timeframe combiné plus long
                    'source': 'combined_tech_sentiment'
                })
                
    except Exception as e:
        logger.error(f"Erreur génération signaux oracles: {e}")
        return []
    
    return signals

# Imports pour le trading live
from simple_trading_system.simple_trading_engine import SimpleTradingEngine
from simple_trading_system.profit_taker import ProfitTaker
from simple_trading_system.pragmatic_signals import PragmaticSignalGenerator
from hyperliquid_client import hyperliquid_client
import trading_config

# Middleware pour l'authentification globale (optionnel)
from starlette.middleware.base import BaseHTTPMiddleware
from starlette.responses import Response
import base64

class BasicAuthMiddleware(BaseHTTPMiddleware):
    async def dispatch(self, request, call_next):
        # Exclure certains endpoints publics si nécessaire
        public_paths = ["/docs", "/openapi.json", "/favicon.ico"]
        
        if request.url.path in public_paths:
            return await call_next(request)
        
        # Vérifier l'authentification
        authorization = request.headers.get("Authorization")
        if authorization:
            try:
                scheme, credentials = authorization.split()
                if scheme.lower() == "basic":
                    decoded = base64.b64decode(credentials).decode("utf-8")
                    username, password = decoded.split(":")
                    
                    if username == AUTH_USERNAME and password == AUTH_PASSWORD:
                        return await call_next(request)
            except:
                pass
        
        # Demander l'authentification
        return Response(
            content="Non autorisé",
            status_code=401,
            headers={"WWW-Authenticate": "Basic"}
        )

@asynccontextmanager
async def lifespan(app: FastAPI):
    # Startup
    global profit_taker_task, oracle_tasks
    print("DEMARRAGE DU SERVEUR UNIFIE CRYPTO TRADING")

    # Connecter les oracles au moteur de trading
    live_engine.price_oracle = price_service.oracle
    live_engine.technical_oracle = tech_oracle

    # Le SimpleTradingEngine n'a pas de persistance - il démarre clean

    # Démarrer le moteur de trading avec monitoring des erreurs
    def wrapped_trading_loop():
        try:
            live_engine.trading_loop()
        except Exception as e:
            print(f"[CRITICAL] Trading loop crashed: {e}")
            import traceback
            traceback.print_exc()
            print("[CRITICAL] Trading loop will attempt to restart...")

    # Démarrer le moteur de trading (synchrone) dans un thread
    print("Démarrage du SimpleTradingEngine...")
    live_engine.start()  # Le SimpleTradingEngine a une méthode start() qui démarre un thread
    print("SimpleTradingEngine démarré")

    # Démarrer le ProfitTaker en mode LIVE seulement
    if profit_taker:
        print("Démarrage du ProfitTaker...")
        profit_taker.start()
        print("ProfitTaker démarré avec stop-loss adaptatif (-4% lev 8x+, -5% lev 5-7x, -6% lev 3-4x, -8% lev <3x) + trailing stop 3%")
    
    # Démarrer les oracles et le service de prix
    try:
        oracle_tasks.append(asyncio.create_task(price_service.start()))
        oracle_tasks.append(asyncio.create_task(tech_oracle.start()))
        oracle_tasks.append(asyncio.create_task(sentiment_oracle.start()))
        oracle_tasks.append(asyncio.create_task(tradfi_oracle.start()))
        
        # Démarrer la tâche de mise à jour des signaux actifs
        oracle_tasks.append(asyncio.create_task(update_active_signals_loop()))
        
        print("Oracles, services et mise à jour des signaux démarrés")
    except Exception as e:
        logger.error(f"Erreur démarrage services: {e}")
    
    yield
    
    # Shutdown
    print("ARRET DU SERVEUR UNIFIE CRYPTO TRADING")

    # Arrêter le ProfitTaker
    if profit_taker:
        print("Arrêt du ProfitTaker...")
        profit_taker.stop()
        print("ProfitTaker arrêté")

    # Le SimpleTradingEngine n'a pas de persistance

    # Arrêter le moteur de trading
    print("Arrêt du SimpleTradingEngine...")
    live_engine.stop()
    print("SimpleTradingEngine arrêté")
    
    # Arrêter les services
    await price_service.stop()
    await tech_oracle.stop()
    await sentiment_oracle.stop()
    await tradfi_oracle.stop()
    for task in oracle_tasks:
        if task and not task.done():
            task.cancel()
    oracle_tasks.clear()
    print("Services arrêtés")

app = FastAPI(title="Unified Crypto Trading Platform", version="1.0.0", lifespan=lifespan)

# Ajouter le middleware d'authentification
app.add_middleware(BasicAuthMiddleware)

# Mount static files and templates
app.mount("/static", StaticFiles(directory="crypto_trader/web/static"), name="static")
templates = Jinja2Templates(directory="crypto_trader/web/templates")

# Valider et afficher la configuration
print("=" * 60)
print("CHARGEMENT DE LA CONFIGURATION TRADING")
print(f"Mode configuré: {trading_config.TRADING_MODE}")
print("=" * 60)
trading_config.validate_config()

# Instance globale du générateur de signaux pragmatiques avec accès au client Hyperliquid
pragmatic_signal_generator = PragmaticSignalGenerator(hyperliquid_client=hyperliquid_client, logger=logger)

# Instance globale du moteur de trading configurée selon trading_config
if trading_config.TRADING_MODE == "SIMULATION":
    live_engine = SimpleTradingEngine(trading_mode='SIMULATION', initial_balance=trading_config.SIMULATION_INITIAL_BALANCE)
    # En mode simulation, pas de ProfitTaker (pas de vraies positions)
    profit_taker = None
else:
    # En mode LIVE, l'initial_balance n'est pas utilisée (utilise le capital Hyperliquid réel)
    live_engine = SimpleTradingEngine(trading_mode='LIVE_AUTO', initial_balance=0.0, hyperliquid_client=hyperliquid_client)
    # Instancier le ProfitTaker avec le même client Hyperliquid
    profit_taker = ProfitTaker(hyperliquid_client=hyperliquid_client)
    # Partager le cooldown manager entre SimpleTradingEngine et ProfitTaker
    profit_taker.cooldown_manager = live_engine.cooldown_manager

profit_taker_task = None
oracle_tasks = []

# Fonction pour générer des signaux pragmatiques au lieu des anciens signaux techniques
def generate_pragmatic_signals():
    """Génère des signaux avec filtre intelligent multi-critères"""
    print("🚀🚀🚀 generate_pragmatic_signals() DÉBUT", flush=True)
    try:
        # Générer les signaux bruts
        print("🔧 Avant appel pragmatic_signal_generator.generate_signals()", flush=True)
        raw_signals = pragmatic_signal_generator.generate_signals()
        print(f"🔧 Après appel - reçu {len(raw_signals) if raw_signals else 0} signaux", flush=True)
        logger.info(f"📊 Générés {len(raw_signals)} signaux bruts")

        if not raw_signals:
            return []

        # DEBUG: Logger les scores des signaux bruts
        scores = [s.get('score', 0) for s in raw_signals]
        if scores:
            logger.info(f"🔍 Scores bruts: min={min(scores):.3f}, max={max(scores):.3f}, avg={sum(scores)/len(scores):.3f}")
            # Top 5 scores
            top_signals = sorted(raw_signals, key=lambda x: x.get('score', 0), reverse=True)[:5]
            for sig in top_signals:
                logger.info(f"   TOP: {sig['symbol']} {sig['side']} score={sig['score']:.3f} conf={sig.get('confidence', 0):.0f}%")

        # Préparer contexte marché
        btc_macro = pragmatic_signal_generator.get_btc_macro_trend()
        market_context = {
            'btc_trend': btc_macro.get('trend', 'NEUTRAL'),
            'btc_strength': btc_macro.get('strength', 0),
            'active_positions': len(live_engine.get_current_positions_dict())
        }

        # Appliquer filtre intelligent
        from simple_trading_system.signal_quality_filter import IntelligentSignalFilter
        signal_filter = IntelligentSignalFilter(logger)
        filtered = signal_filter.filter_signals(raw_signals, market_context)

        logger.info(f"✅ Filtre: {len(raw_signals)} → {len(filtered)} signaux validés")
        return filtered

    except Exception as e:
        logger.error(f"❌ ERREUR génération signaux: {e}")
        import traceback
        logger.error(f"Traceback:\n{traceback.format_exc()}")
        return []

# Injecter le fournisseur de signaux pragmatiques dans le moteur de trading
live_engine.set_signals_provider(generate_pragmatic_signals)

# Simple engine n'a pas de service de persistance

# Le mode est déjà configuré dans le constructeur

# Configuration gérée automatiquement par le simple engine

async def update_active_signals_loop():
    """Boucle de mise à jour des prix des signaux actifs"""
    while True:
        try:
            updated_count = executed_signals_service.update_signal_prices()
            if updated_count > 0:
                logger.debug(f"Prix mis à jour pour {updated_count} signaux actifs")
            await asyncio.sleep(120)  # Mise à jour toutes les 2 minutes pour éviter erreurs 429
        except Exception as e:
            logger.error(f"Erreur mise à jour signaux actifs: {e}")
            await asyncio.sleep(60)  # Attendre 1 minute en cas d'erreur

# ============================================================================
# PAGES WEB
# ============================================================================

@app.get("/")
async def dashboard(request: Request):
    """Dashboard principal unifié"""
    return templates.TemplateResponse("dashboard.html", {"request": request})

@app.get("/oracles")
async def oracles_page(request: Request):
    return templates.TemplateResponse("dashboard.html", {"request": request})

@app.get("/trading") 
async def trading_page(request: Request):
    return templates.TemplateResponse("dashboard.html", {"request": request})

@app.get("/portfolio")
async def portfolio_page(request: Request):
    return templates.TemplateResponse("dashboard.html", {"request": request})

# ============================================================================
# API ENDPOINTS - SIGNAUX DE TRADING
# ============================================================================

@app.get("/api/trading/signals")
async def get_enhanced_signals():
    """API endpoint pour signaux de trading améliorés"""
    try:
        enhanced_signals = generate_pragmatic_signals()
        enhanced_signals.sort(key=lambda x: x.get('quality_score', 0), reverse=True)
        
        signal_types = {}
        tradable_count = 0
        
        for signal in enhanced_signals:
            stype = signal.get('signal_type', 'HOLD')
            signal_types[stype] = signal_types.get(stype, 0) + 1
            if stype in ['BUY', 'SELL']:
                tradable_count += 1
        
        return {
            "status": "success",
            "timestamp": datetime.now().isoformat(),
            "signals_count": len(enhanced_signals),
            "tradable_count": tradable_count,
            "signal_distribution": signal_types,
            "signals": enhanced_signals
        }
        
    except Exception as e:
        raise HTTPException(status_code=500, detail=f"Erreur génération signaux: {str(e)}")

@app.post("/api/trading/execute-signal")
async def execute_signal(signal_data: dict):
    """Exécuter un signal et créer une position trackée"""
    try:
        position_size = signal_data.get('position_size', 1000.0)  # Défaut 1000 USD
        signal_id = executed_signals_service.execute_signal(signal_data, position_size)
        
        return {
            "status": "success",
            "signal_id": signal_id,
            "message": f"Signal {signal_data['symbol']} {signal_data['signal_type']} exécuté",
            "timestamp": datetime.now().isoformat()
        }
        
    except Exception as e:
        raise HTTPException(status_code=500, detail=f"Erreur exécution signal: {str(e)}")

@app.get("/api/trading/active-signals")
async def get_active_signals():
    """Récupérer tous les signaux actifs avec PnL"""
    try:
        # Mettre à jour les prix avant de retourner
        updated_count = executed_signals_service.update_signal_prices()
        active_signals = executed_signals_service.get_active_signals()
        
        return {
            "status": "success",
            "timestamp": datetime.now().isoformat(),
            "updated_count": updated_count,
            "active_count": len(active_signals),
            "signals": [signal.model_dump() for signal in active_signals]
        }
        
    except Exception as e:
        raise HTTPException(status_code=500, detail=f"Erreur récupération signaux actifs: {str(e)}")

@app.post("/api/trading/close-signal/{signal_id}")
async def close_signal(signal_id: str, reason: str = "Manual close"):
    """Fermer un signal (position)"""
    try:
        success = executed_signals_service.close_signal(signal_id, reason)
        
        if success:
            return {
                "status": "success",
                "message": f"Signal {signal_id} fermé",
                "timestamp": datetime.now().isoformat()
            }
        else:
            raise HTTPException(status_code=404, detail="Signal non trouvé ou déjà fermé")
            
    except Exception as e:
        raise HTTPException(status_code=500, detail=f"Erreur fermeture signal: {str(e)}")

@app.get("/api/debug/price_cache")
async def debug_price_cache():
    """Debug du cache de prix"""
    try:
        cache_data = {}
        cache = price_service.oracle.price_cache if price_service.oracle else {}
        
        for symbol, price_data in cache.items():
            cache_data[symbol] = {
                "price": price_data.price,
                "volume_24h": price_data.volume_24h,
                "timestamp": price_data.timestamp
            }
                
        return {
            "status": "success", 
            "cache_size": len(cache),
            "cache_data": cache_data
        }
        
    except Exception as e:
        return {"status": "error", "error": str(e)}

@app.get("/api/debug/liquidity")
async def debug_liquidity():
    """Debug des volumes 24h et liquidité des marchés"""
    try:
        liquidity_data = {}
        
        for symbol in settings.watched_symbols:
            # Convertir vers format Hyperliquid pour l'oracle de prix
            price_symbol = normalize_symbol_for_price_oracle(symbol)
            price_data = price_service.oracle.get_latest_price(price_symbol)
            if price_data:
                is_liquid = is_market_liquid_enough(symbol, price_data.volume_24h)
                is_high_liquid = symbol in settings.high_liquidity_symbols
                
                liquidity_data[symbol] = {
                    'volume_24h': price_data.volume_24h,
                    'volume_24h_formatted': f"${price_data.volume_24h:,.0f}",
                    'is_liquid': is_liquid,
                    'is_high_liquidity': is_high_liquid,
                    'min_threshold': settings.min_volume_24h_usdt,
                    'meets_volume': price_data.volume_24h >= settings.min_volume_24h_usdt
                }
        
        # Trier par volume décroissant
        sorted_data = dict(sorted(liquidity_data.items(), key=lambda x: x[1]['volume_24h'], reverse=True))
        
        return {
            'status': 'success',
            'liquidity_data': sorted_data,
            'settings': {
                'min_volume_24h_usdt': settings.min_volume_24h_usdt,
                'high_liquidity_count': len(settings.high_liquidity_symbols)
            }
        }
        
    except Exception as e:
        raise HTTPException(status_code=500, detail=f"Erreur debug liquidité: {str(e)}")

@app.get("/api/debug/hyperliquid_meta")
async def debug_hyperliquid_meta():
    """Debug: afficher les méta-informations Hyperliquid pour les contraintes d'ordres"""
    if not live_engine.hyperliquid_client:
        return {"error": "Client Hyperliquid non disponible"}
    
    try:
        meta_info = live_engine.hyperliquid_client.get_meta_info()
        if not meta_info:
            return {"error": "Aucune méta-information récupérée"}
            
        # Chercher spécifiquement les infos DOGE
        doge_info = None
        if "universe" in meta_info:
            for token_info in meta_info["universe"]:
                if token_info.get("name") == "DOGE":
                    doge_info = token_info
                    break
        
        return {
            "doge_info": doge_info,
            "total_tokens": len(meta_info.get("universe", [])),
            "sample_tokens": meta_info.get("universe", [])[:5] if meta_info.get("universe") else []
        }
    except Exception as e:
        return {"error": f"Erreur récupération méta: {str(e)}"}

@app.get("/api/dashboard/summary")
async def get_dashboard_summary():
    """Résumé dashboard principal"""
    try:
        # Calculs directs depuis l'instance live_engine
        signals = generate_signals_for_api()
        
        signal_counts = {"BUY": 0, "SELL": 0, "HOLD": 0}
        for signal in signals:
            stype = signal.get('signal_type', 'HOLD')
            if stype in signal_counts:
                signal_counts[stype] += 1
        
        # Initialiser les variables par défaut - seront utilisées si Hyperliquid échoue
        current_balance = live_engine.current_balance
        total_active_pnl = 0
        for pos in live_engine.positions.values():
            live_engine.update_position_pnl(pos)
            total_active_pnl += pos.pnl
        total_closed_pnl = sum(pos.pnl for pos in live_engine.closed_positions)
        total_pnl = total_active_pnl + total_closed_pnl
        active_positions = len(live_engine.positions)
        total_trades = len(live_engine.closed_positions)
        win_rate = (sum(1 for pos in live_engine.closed_positions if pos.pnl > 0) / len(live_engine.closed_positions) * 100) if live_engine.closed_positions else 0
        data_source = "simulation"
        
        # SURCHARGER avec les données LIVE - compter directement les positions actives depuis l'API
        if live_engine.trading_mode in ['LIVE_MANUAL', 'LIVE_AUTO']:
            try:
                # Récupérer directement les positions depuis l'endpoint /api/trading/positions
                import aiohttp
                async with aiohttp.ClientSession() as session:
                    async with session.get("http://localhost:8888/api/trading/positions") as resp:
                        if resp.status == 200:
                            positions_data = await resp.json()
                            if positions_data.get("status") == "success":
                                active_positions_list = positions_data.get("active_positions", [])
                                active_positions = len(active_positions_list)
                                total_active_pnl = sum(pos.get("pnl", 0) for pos in active_positions_list)
                            else:
                                active_positions = 0 
                                total_active_pnl = 0
                        else:
                            active_positions = 0
                            total_active_pnl = 0
                
                # Historique depuis l'API trading history
                try:
                    from crypto_trader.services.position_persistence import PositionPersistenceService
                    persistence = PositionPersistenceService()
                    closed_positions = persistence.load_closed_positions()
                    total_trades = len(closed_positions)
                    
                    if closed_positions:
                        profitable_trades = sum(1 for pos in closed_positions if pos.pnl > 0)
                        win_rate = (profitable_trades / len(closed_positions) * 100)
                        total_closed_pnl = sum(pos.pnl for pos in closed_positions)
                        total_pnl = total_active_pnl + total_closed_pnl
                except:
                    total_trades = 0
                    total_pnl = total_active_pnl
                
                data_source = "positions_api"
                                    
            except Exception as e:
                logger.error(f"Erreur récupération données live Hyperliquid: {e}")
                data_source = "simulation_fallback"
        
        return {
            "status": "success",
            "summary": {
                "total_signals": len(signals),
                "buy_signals": signal_counts['BUY'],
                "sell_signals": signal_counts['SELL'],
                "hold_signals": signal_counts['HOLD'],
                "engine_status": "active" if live_engine.running else "stopped",
                "current_balance": current_balance,
                "total_pnl": total_pnl,
                "active_positions": active_positions,
                "total_trades": total_trades,
                "win_rate": win_rate,
                "data_source": data_source
            },
            "timestamp": datetime.now().isoformat()
        }
    except Exception as e:
        return {
            "status": "error", 
            "error": str(e),
            "timestamp": datetime.now().isoformat()
        }

@app.get("/api/system/status")  
async def get_system_status():
    """Status du système avec métriques live trading"""
    try:
        signals = generate_signals_for_api()
        
        # Compter les signaux par type
        signal_counts = {"BUY": 0, "SELL": 0, "HOLD": 0}
        for signal in signals:
            stype = signal.get('signal_type', 'HOLD')
            if stype in signal_counts:
                signal_counts[stype] += 1
        
        # Données du moteur live
        engine_status = live_engine.get_status_dict()
        
        return {
            "status": "operational",
            "timestamp": datetime.now().isoformat(),
            "oracles": {
                "binance_spot": {
                    "status": "active",
                    "data_count": len([s for s in signals if s.get('current_price')]),
                    "last_update": datetime.now().isoformat()
                },
                "hyperliquid_futures": {
                    "status": "active", 
                    "data_count": signal_counts['BUY'] + signal_counts['SELL'],
                    "last_update": datetime.now().isoformat()
                }
            },
            "trading_engine": {
                "status": "active" if live_engine.running else "stopped",
                "signals_count": len(signals),
                "buy_signals": signal_counts['BUY'],
                "sell_signals": signal_counts['SELL'],
                "hold_signals": signal_counts['HOLD'],
                "live_trading": engine_status
            }
        }
        
    except Exception as e:
        return {
            "status": "error",
            "error": str(e),
            "timestamp": datetime.now().isoformat()
        }

@app.get("/api/trading/simulation")
async def get_trading_simulation():
    """Simulation de trading basée sur le moteur live"""
    try:
        engine_status = live_engine.get_status_dict()
        metrics = engine_status.get('metrics', {})
        
        return {
            "status": "success",
            "simulation": {
                "total_potential_profit": metrics.get('total_pnl', 0),
                "total_potential_risk": abs(metrics.get('avg_loss', 0)) * metrics.get('total_trades', 1),
                "net_potential": metrics.get('total_pnl', 0),
                "return_percentage": ((metrics.get('current_balance', 10000) - 10000) / 10000) * 100,
                "risk_reward_ratio": metrics.get('profit_factor', 1),
                "live_balance": metrics.get('current_balance', 10000),
                "active_positions": metrics.get('active_positions', 0),
                "total_trades": metrics.get('total_trades', 0),
                "win_rate": metrics.get('win_rate', 0)
            },
            "timestamp": datetime.now().isoformat()
        }
        
    except Exception as e:
        raise HTTPException(status_code=500, detail=f"Erreur simulation: {str(e)}")

# ============================================================================
# API ENDPOINTS - TRADING LIVE
# ============================================================================

@app.get("/api/trading/live")
async def get_live_trading_data():
    """Données de trading en temps réel"""
    try:
        engine_status = live_engine.get_status_dict()
        metrics = engine_status.get('metrics', {})
        
        # En mode LIVE, corriger les métriques avec les vraies positions actives
        if live_engine.trading_mode in ['LIVE_MANUAL', 'LIVE_AUTO']:
            try:
                # Récupérer le nombre de positions actives depuis l'API positions
                import aiohttp
                async with aiohttp.ClientSession() as session:
                    async with session.get("http://localhost:8888/api/trading/positions") as resp:
                        if resp.status == 200:
                            positions_data = await resp.json()
                            if positions_data.get("status") == "success":
                                active_positions_count = len(positions_data.get("active_positions", []))
                                metrics['active_positions'] = active_positions_count
                
                # Récupérer la balance Hyperliquid si possible
                if live_engine.hyperliquid_client:
                    account_state = live_engine.hyperliquid_client.get_account_state()
                    if account_state:
                        margin_summary = account_state.get('marginSummary', {})
                        if margin_summary:
                            metrics['current_balance'] = float(margin_summary.get('accountValue', 0))
            except Exception as e:
                logger.error(f"Erreur récupération données live pour /trading/live: {e}")
        
        return {
            "status": "success",
            "engine_status": engine_status.get('engine_status'),
            "current_mode": live_engine.trading_mode,
            "metrics": metrics,
            "active_positions": engine_status.get('active_positions', []),
            "recent_trades": engine_status.get('recent_trades', []),
            "timestamp": engine_status.get('timestamp')
        }
        
    except Exception as e:
        raise HTTPException(status_code=500, detail=f"Erreur données live: {str(e)}")

@app.get("/api/system/engine_info")
async def get_engine_info():
    """Informations sur le moteur de trading actuel"""
    return {
        "engine_type": "SimpleTradingEngine",
        "version": "1.0",
        "capabilities": {
            "position_opening": True,
            "automatic_closing": False,
            "trailing_stops": False,
            "take_profits": False,
            "manual_closing": "Via Hyperliquid interface"
        },
        "note": "Moteur simplifié axé sur l'ouverture de positions uniquement"
    }

@app.get("/api/trading/positions")
async def get_positions():
    """Positions ouvertes et fermées"""
    try:
        from dataclasses import asdict
        
        # Debug: vérifier le cache de prix
        price_cache_size = len(price_service.oracle.price_cache) if price_service.oracle else 0
        logger.info(f"Cache prix: {price_cache_size} entrées")
        
        # Récupérer les positions selon le mode
        active_positions_list = []
        
        if live_engine.trading_mode in ['LIVE_MANUAL', 'LIVE_AUTO'] and live_engine.hyperliquid_client:
            # Mode LIVE: utiliser la fonction centralisée pour les données exactes
            try:
                existing_positions = get_existing_positions()
                for symbol, pos_data in existing_positions.items():
                        symbol_clean = symbol
                        entry_price = pos_data['entry_price']
                        size = pos_data['size']
                        side = pos_data['side']
                        unrealized_pnl = pos_data['pnl']
                        
                         # Utiliser le prix actuel depuis les données centralisées
                        current_price = pos_data.get('current_price', entry_price)
                        if price_service and price_service.oracle:
                            # Debug: vérifier le cache de prix
                            cache_symbols = list(price_service.oracle.price_cache.keys())
                            #logger.info(f"Cache Hyperliquid: {len(cache_symbols)} symbols: {cache_symbols}")
                            
                            # Recherche directe par nom de symbole (Hyperliquid utilise les noms comme clés)
                            #logger.info(f"Recherche prix pour {symbol_clean} dans le cache Hyperliquid")
                            cached_price = price_service.oracle.get_latest_price(symbol_clean)
                            if cached_price:
                                current_price = cached_price.price
                                #logger.info(f"Prix trouvé pour {symbol_clean}: ${current_price}")
                            else:
                                logger.warning(f"Aucun prix trouvé pour {symbol_clean} dans le cache Hyperliquid")
                        
                        # Calculer le PnL nous-mêmes avec le prix actuel
                        calculated_pnl_usd = 0
                        pnl_percent_base = 0
                        if current_price != entry_price and entry_price > 0:
                            if side.upper() == 'LONG':
                                pnl_percent_base = ((current_price - entry_price) / entry_price) * 100
                                calculated_pnl_usd = size * (current_price - entry_price)
                            else:  # SHORT
                                pnl_percent_base = ((entry_price - current_price) / entry_price) * 100
                                calculated_pnl_usd = size * (entry_price - current_price)
                        
                        # Utiliser le PnL Hyperliquid s'il est non-nul, sinon notre calcul
                        final_pnl = unrealized_pnl if abs(unrealized_pnl) > 0.01 else calculated_pnl_usd
                        
                        #logger.info(f"Position {symbol_clean}: HL_PnL={unrealized_pnl}, Calculated_PnL={calculated_pnl_usd:.2f}, Final={final_pnl:.2f}")
                        
                        # Calculer valeur notionnelle
                        notional_value = size * entry_price
                        
                        # Récupérer le leverage réel depuis les données de position Hyperliquid
                        real_leverage = 5  # Par défaut conservateur
                        
                        # Debug: examiner le contenu de hl_pos
                        #logger.debug(f"DEBUG hl_pos pour {symbol_clean}: {hl_pos}")
                        
                        # Essayer différentes méthodes pour récupérer le leverage
                        leverage_found = False
                        
                        if hl_pos and 'leverage' in hl_pos:
                            leverage_val = hl_pos['leverage']
                            if isinstance(leverage_val, (int, float)):
                                real_leverage = int(leverage_val)
                                leverage_found = True
                                #logger.info(f"Leverage trouvé dans hl_pos: {real_leverage}x pour {symbol_clean}")
                            elif isinstance(leverage_val, str) and leverage_val.isdigit():
                                real_leverage = int(leverage_val)
                                leverage_found = True
                                #logger.info(f"Leverage trouvé dans hl_pos (string): {real_leverage}x pour {symbol_clean}")
                            elif isinstance(leverage_val, dict) and 'value' in leverage_val:
                                # Hyperliquid retourne le leverage comme {'type': 'cross', 'value': 10}
                                real_leverage = int(leverage_val['value'])
                                leverage_found = True
                                leverage_type = leverage_val.get('type', 'unknown')
                                #logger.info(f"Leverage trouvé dans hl_pos (dict): {real_leverage}x ({leverage_type}) pour {symbol_clean}")
                            else:
                                #logger.warning(f"Leverage dans hl_pos format inattendu: {leverage_val} (type: {type(leverage_val)})")
                                pass
                        
                        if not leverage_found:
                            # Fallback: essayer de récupérer le leverage depuis l'ordre original
                            try:
                                # Récupérer les fills récents pour ce symbole pour trouver le leverage utilisé
                                fills = live_engine.hyperliquid_client.get_fill_info(limit=50)
                                symbol_fills = [f for f in fills if f.get('coin') == symbol_clean]
                                
                                if symbol_fills:
                                    # Prendre le fill le plus récent pour ce symbole
                                    latest_fill = symbol_fills[0]
                                    logger.debug(f"Fill récent pour {symbol_clean}: {latest_fill}")
                                    
                                    # Hyperliquid ne stocke pas le leverage dans les fills
                                    # Utiliser le leverage maximum disponible pour ce token comme estimation
                                    available_leverages = live_engine.hyperliquid_client.get_available_leverages()
                                    if symbol_clean in available_leverages:
                                        max_leverage = available_leverages[symbol_clean]
                                        # Utiliser un leverage modéré (75% du max, minimum 3x)
                                        real_leverage = max(3, min(20, int(max_leverage * 0.75)))
                                        logger.info(f"Leverage estimé via max disponible: {real_leverage}x pour {symbol_clean} (max={max_leverage})")
                                    else:
                                        real_leverage = 5
                                        logger.info(f"Leverage par défaut: {real_leverage}x pour {symbol_clean}")
                                else:
                                    real_leverage = 5  # Fallback si pas de fills
                                    logger.info(f"Leverage fallback: {real_leverage}x pour {symbol_clean} (pas de fills)")
                            except Exception as e:
                                real_leverage = 5  # Fallback en cas d'erreur
                                logger.warning(f"Erreur récupération leverage pour {symbol_clean}: {e}")
                        
                        # Calculer la marge utilisée basée sur le leverage estimé
                        margin_used = notional_value / real_leverage
                        
                        # CORRECTION DU BUG: Calculer les niveaux take_profit/stop_loss manquants
                        risk_pct = 0.05  # 5% comme dans le trading engine
                        profit_multiplier = 2.0  # 2x le risque
                        
                        if side.upper() == 'LONG':
                            stop_loss = entry_price * (1 - risk_pct)  # -5%
                            take_profit = entry_price * (1 + risk_pct * profit_multiplier)  # +10%
                        else:  # SHORT
                            stop_loss = entry_price * (1 + risk_pct)  # +5%
                            take_profit = entry_price * (1 - risk_pct * profit_multiplier)  # -10%
                        
                        # CORRECTION DU BUG: Calculer le vrai PnL% avec leverage
                        pnl_percent = pnl_percent_base * real_leverage
                        
                        # Calculer la durée à partir des fills
                        duration = calculate_position_duration(symbol_clean, entry_price, size)
                        
                        # Calculer la taille de position réelle (capital investi, pas contrats avec levier)
                        notional_value = size * current_price  # Valeur notionnelle totale (avec levier)
                        real_position_size = notional_value / real_leverage  # Capital réellement investi
                        
                        logger.debug(f"POSITION SIZE CALC {symbol_clean}: contracts={size:.4f}, price=${current_price:.2f}, leverage={real_leverage}x")
                        logger.debug(f"  -> notional=${notional_value:.2f}, real_size=${real_position_size:.2f}")
                        
                        # Convertir position Hyperliquid au format dashboard
                        active_positions_list.append({
                            "id": f"HL_{symbol_clean}_{int(datetime.now().timestamp())}",
                            "symbol": symbol_clean,
                            "side": side.upper(),
                            "size": round(real_position_size, 2),  # Capital investi, pas contrats avec levier
                            "notional_size": round(notional_value, 2),  # Valeur notionnelle (avec levier)
                            "contract_size": round(size, 4),  # Nombre de contrats (pour debug)
                            "entry_price": entry_price,
                            "current_price": current_price,
                            "pnl": round(final_pnl, 2),
                            "pnl_percent": round(pnl_percent, 2),
                            "leverage": real_leverage,
                            "duration": duration,
                            "margin_used": round(margin_used, 2),
                            "source": "HYPERLIQUID",
                            "take_profit": round(take_profit, 5),  # AJOUTÉ !
                            "stop_loss": round(stop_loss, 5),      # AJOUTÉ !
                        })
                logger.info(f"Récupéré {len(active_positions_list)} positions Hyperliquid")
            except Exception as e:
                logger.error(f"Erreur récupération positions Hyperliquid: {e}")
        
        # En mode LIVE, on n'utilise QUE les positions Hyperliquid (pas de DB locale)
        # En mode SIMULATION, on utilise les positions du moteur local
        if live_engine.trading_mode not in ['LIVE_MANUAL', 'LIVE_AUTO']:
            # Mode SIMULATION: utiliser les positions du moteur
            # Mettre à jour les prix des positions actives avant de les retourner
            for pos in live_engine.positions.values():
                live_engine.update_position_pnl(pos)
            
            # Ajouter les positions du moteur de simulation
            for pos in sorted(live_engine.positions.values(), key=lambda p: p.timestamp, reverse=True):
                active_positions_list.append({
                    "id": pos.id,
                    "symbol": pos.symbol,
                    "side": pos.side,
                    "size": pos.size,
                    "entry_price": pos.entry_price,
                    "current_price": pos.current_price,
                    "pnl": pos.pnl,
                    "pnl_percent": pos.pnl_percent,
                    "leverage": pos.leverage,
                    "duration": pos.duration,
                    "margin_used": pos.margin_used,
                    "source": "ENGINE"
                })
        
        # Récupérer historique enrichi depuis la DB pour avoir entry_price, leverage etc.
        from crypto_trader.services.position_persistence import position_persistence
        db_history = position_persistence.get_position_history(limit=20)
        
        # Debug logging
        logger.info(f"DB history retrieved: {len(db_history)} positions")
        closed_count = sum(1 for pos in db_history if pos.status == 'CLOSED')
        logger.info(f"Closed positions count: {closed_count}")
        if db_history:
            first_pos = db_history[0]
            logger.info(f"First position sample: id={first_pos.id}, entry={first_pos.entry_price}, current={first_pos.current_price}")
            if first_pos.status == 'CLOSED':
                logger.info(f"First closed position: entry_price={first_pos.entry_price}, duration={first_pos.duration}")
        
        result = {
            "status": "success", 
            "active_positions": active_positions_list,
            "closed_positions": [],
            "timestamp": datetime.now().isoformat()
        }
        
        # Build closed positions with error handling
        closed_positions = []
        logger.info(f"Processing {len(db_history)} positions for closed status")
        for pos in db_history:
            logger.debug(f"Position {pos.id} has status: '{pos.status}'")
            if pos.status == 'CLOSED':
                try:
                    pos_dict = {
                        "id": pos.id,
                        "symbol": pos.symbol,
                        "side": pos.side,
                        "entry_price": float(pos.entry_price) if pos.entry_price is not None else 0.0,
                        "exit_price": float(pos.current_price) if pos.current_price is not None else 0.0,
                        "pnl": float(pos.pnl) if pos.pnl is not None else 0.0,
                        "pnl_percent": float(pos.pnl_percent) if pos.pnl_percent is not None else 0.0,
                        "close_reason": str(pos.close_reason or 'N/A'),
                        "duration": str(pos.duration or 'N/A'),
                        "leverage": int(pos.leverage) if pos.leverage is not None else 1,
                        "timestamp": str(pos.timestamp),
                        "close_timestamp": str(pos.close_timestamp or datetime.now().isoformat())
                    }
                    closed_positions.append(pos_dict)
                    logger.debug(f"Added closed position: {pos.id} with entry_price: {pos.entry_price}")
                except Exception as pos_error:
                    logger.error(f"Error processing closed position {pos.id}: {pos_error}")
                    logger.error(f"Position data: entry_price={getattr(pos, 'entry_price', 'MISSING')}, current_price={getattr(pos, 'current_price', 'MISSING')}")
        
        # Update the response
        result["closed_positions"] = closed_positions[:20]
        return result
        
    except Exception as e:
        raise HTTPException(status_code=500, detail=f"Erreur positions: {str(e)}")

@app.post("/api/trading/close_position/{position_id}")
async def close_position(position_id: str):
    """Fermer une position spécifique"""
    try:
        # Log toutes les demandes de fermeture manuelle
        logger.critical(f"API CLOSE POSITION REQUEST: {position_id} - Mode: {live_engine.trading_mode}")
        # En mode LIVE, fermer la position Hyperliquid directement
        if live_engine.trading_mode in ['LIVE_MANUAL', 'LIVE_AUTO']:
            # Extraire le symbol depuis l'ID Hyperliquid (format: HL_SYMBOL_timestamp)
            if position_id.startswith('HL_'):
                parts = position_id.split('_')
                if len(parts) >= 3:
                    symbol = parts[1]  # BTC, ADA, etc.
                    
                    # Utiliser la méthode factoriser du trading engine
                    logger.critical(f"MANUAL HYPERLIQUID CLOSE: Attempting to close {symbol}")
                    result = live_engine.close_hyperliquid_position_by_symbol(symbol)
                    
                    if result.get('success'):
                        logger.critical(f"MANUAL HYPERLIQUID CLOSE SUCCESS: {symbol} - {result.get('message', '')}")
                        return {
                            "status": "success", 
                            "message": result.get('message', f"Position {symbol} fermée"),
                            "position_id": position_id
                        }
                    else:
                        logger.critical(f"MANUAL HYPERLIQUID CLOSE FAILED: {symbol} - {result.get('error', 'Inconnue')}")
                        raise HTTPException(status_code=400, detail=f"Erreur fermeture Hyperliquid: {result.get('error', 'Inconnue')}")
                else:
                    raise HTTPException(status_code=400, detail="Format d'ID position invalide")
            else:
                raise HTTPException(status_code=404, detail="Position Hyperliquid non trouvée")
        else:
            # Mode simulation - utiliser le moteur
            position = live_engine.positions.get(position_id)
            if not position:
                logger.warning(f"MANUAL SIMULATION CLOSE FAILED: Position {position_id} not found")
                raise HTTPException(status_code=404, detail="Position non trouvée")
            
            # Fermer la position
            logger.critical(f"MANUAL SIMULATION CLOSE: {position.symbol} {position.side} - Position: {position_id}")
            live_engine.close_position(position, "FERMETURE_MANUELLE")
            
            return {
                "status": "success", 
                "message": f"Position {position.symbol} {position.side} fermée",
                "position_id": position_id
            }
        
    except HTTPException:
        raise
    except Exception as e:
        raise HTTPException(status_code=500, detail=f"Erreur fermeture position: {str(e)}")

@app.post("/api/trading/purge_all_positions")
async def purge_all_positions():
    """Purger TOUTES les positions (actives + historique) - DANGER"""
    try:
        from crypto_trader.services.position_persistence import position_persistence
        
        # Fermer toutes les positions actives d'abord
        closed_count = 0
        for position_id in list(live_engine.positions.keys()):
            position = live_engine.positions.get(position_id)
            if position:
                live_engine.close_position(position, "PURGED")
                closed_count += 1
        
        # Supprimer l'historique de la base de données
        db_count = position_persistence.purge_all_positions()
        
        # Vider le cache du moteur
        live_engine.closed_positions.clear()
        
        logger.warning(f"PURGE COMPLÈTE: {closed_count} positions actives fermées, {db_count} positions supprimées de la DB")
        
        return {
            "status": "success", 
            "message": f"Purge complète effectuée: {closed_count} actives + {db_count} historique",
            "closed_active": closed_count,
            "deleted_history": db_count
        }
        
    except Exception as e:
        logger.error(f"Erreur purge: {e}")
        raise HTTPException(status_code=500, detail=f"Erreur purge: {str(e)}")

@app.delete("/api/trading/position/{position_id}")
async def delete_position_from_history(position_id: str):
    """Supprimer une position de l'historique"""
    try:
        from crypto_trader.services.position_persistence import position_persistence
        
        # Supprimer de la base de données
        success = position_persistence.delete_position(position_id)
        
        if success:
            # Supprimer aussi du cache si présent
            live_engine.closed_positions = [
                pos for pos in live_engine.closed_positions if pos.id != position_id
            ]
            
            return {"status": "success", "message": f"Position {position_id} supprimée"}
        else:
            raise HTTPException(status_code=404, detail="Position non trouvée")
            
    except HTTPException:
        raise
    except Exception as e:
        raise HTTPException(status_code=500, detail=f"Erreur suppression: {str(e)}")

@app.post("/api/trading/refresh_positions")
async def refresh_positions():
    """Bouton manuel pour rafraîchir les positions"""
    try:
        # Forcer la mise à jour des prix pour toutes les positions
        updated_count = 0
        for pos in live_engine.positions.values():
            live_engine.update_position_pnl(pos)
            updated_count += 1
        
        # Mettre à jour aussi les signaux exécutés si le service est disponible
        if executed_signals_service:
            signals_updated = executed_signals_service.update_signal_prices()
        else:
            signals_updated = 0
            
        return {
            "status": "success",
            "message": f"Prix mis à jour: {updated_count} positions, {signals_updated} signaux",
            "timestamp": datetime.now().isoformat()
        }
        
    except Exception as e:
        raise HTTPException(status_code=500, detail=f"Erreur rafraîchissement: {str(e)}")

@app.post("/api/trading/force_close_all")
async def force_close_all_positions():
    """Forcer la fermeture de TOUTES les positions en utilisant l'endpoint qui fonctionne"""
    try:
        logger.critical("API FORCE CLOSE ALL POSITIONS REQUEST - Début de fermeture massive")
        # Récupérer directement les positions depuis Hyperliquid
        hl_positions = live_engine.hyperliquid_client.get_positions()
        
        if not hl_positions:
            return {"status": "success", "message": "Aucune position à fermer"}
        
        closed_positions = []
        failed_positions = []
        
        print(f"\n[FORCE CLOSE ALL] {len(hl_positions)} positions à fermer")
        
        # Construire l'ID de position au format attendu et fermer
        for position in hl_positions:
            symbol = position['symbol'].replace('-PERP', '')
            side = position['side']
            
            # Construire l'ID au format HL_SYMBOL_timestamp comme dans /api/trading/positions
            position_id = f"HL_{symbol}_{int(time.time() * 1000) % 10000000000}"
            
            try:
                # Appeler directement la fonction close_position existante
                result = await close_position(position_id)
                
                if result.get('status') == 'success':
                    closed_positions.append(f"{symbol} {side}")
                    print(f"[FORCE CLOSE ALL] ✅ {symbol} {side} fermée")
                else:
                    # Si l'échec est dû à un ID incorrect, essayer une fermeture directe
                    try:
                        # Fermeture directe via place_market_order
                        size = abs(position['size'])
                        is_buy = (side == 'SHORT')  # Ordre opposé pour fermer
                        
                        force_close_tag = f"FORCE_CLOSE_{side}_{symbol}"
                        order_result = live_engine.hyperliquid_client.place_market_order(
                            symbol=symbol,
                            is_buy=is_buy,
                            size=size,
                            reduce_only=True,
                            order_tag=force_close_tag
                        )
                        
                        if order_result and order_result.get('status', {}).get('filled'):
                            closed_positions.append(f"{symbol} {side}")
                            print(f"[FORCE CLOSE ALL] ✅ {symbol} {side} fermée (direct)")
                        else:
                            failed_positions.append(f"{symbol}: Ordre non rempli")
                            print(f"[FORCE CLOSE ALL] ❌ {symbol} échec direct")
                            
                    except Exception as e2:
                        failed_positions.append(f"{symbol}: {str(e2)}")
                        print(f"[FORCE CLOSE ALL] ❌ {symbol} erreur directe: {e2}")
                    
            except Exception as e:
                failed_positions.append(f"{symbol}: {str(e)}")
                print(f"[FORCE CLOSE ALL] ❌ {symbol} erreur: {e}")
        
        # Attendre un peu et vérifier
        import time as time_module
        time_module.sleep(1)
        
        # Vérifier les positions restantes
        remaining = live_engine.hyperliquid_client.get_positions()
        remaining_count = len(remaining) if remaining else 0
        
        # Résumé
        message = f"Fermées: {len(closed_positions)}/{len(hl_positions)} positions"
        if failed_positions:
            message += f" | Échecs: {len(failed_positions)}"
        if remaining_count > 0:
            message += f" | {remaining_count} restantes"
            
        return {
            "status": "success" if not failed_positions else "partial",
            "message": message,
            "closed": closed_positions,
            "failed": failed_positions,
            "remaining": remaining_count
        }
        
    except Exception as e:
        logger.error(f"Erreur force_close_all: {e}")
        return {"status": "error", "message": str(e)}

@app.post("/api/trading/control/{action}")
async def control_engine(action: str):
    """Contrôle du moteur de trading"""
    try:
        if action == "start":
            if not live_engine.running:
                live_engine.running = True
                global engine_task
                
                async def wrapped_trading_loop():
                    try:
                        await live_engine.trading_loop()
                    except Exception as e:
                        print(f"[CRITICAL] Trading loop crashed: {e}")
                        import traceback
                        traceback.print_exc()
                        print("[CRITICAL] Trading loop will attempt to restart...")
                
                engine_task = asyncio.create_task(wrapped_trading_loop())
                return {"status": "success", "message": "Moteur démarré"}
            else:
                return {"status": "info", "message": "Moteur déjà en cours"}
                
        elif action == "stop":
            live_engine.stop()
            return {"status": "success", "message": "Moteur arrêté"}
            
        elif action == "close_all":
            # Fermer toutes les positions Hyperliquid réelles
            closed_count = 0
            failed_count = 0
            
            try:
                # Le SimpleTradingEngine ne gère pas les fermetures - utiliser le ProfitTaker ou l'API directe
                if profit_taker and live_engine.hyperliquid_client:
                    # Utiliser l'API Hyperliquid directement pour fermer toutes les positions
                    positions = live_engine.hyperliquid_client.get_positions()
                    if positions:
                        for pos in positions:
                            try:
                                result = live_engine.hyperliquid_client.close_position(pos['symbol'])
                                if result.get('success'):
                                    closed_count += 1
                                else:
                                    failed_count += 1
                                    logger.error(f"Échec fermeture {pos['symbol']}: {result.get('error')}")
                            except Exception as e:
                                failed_count += 1
                                logger.error(f"Erreur fermeture {pos['symbol']}: {e}")

                    # Attendre un peu pour que les fermetures se propagent
                    import time
                    time.sleep(2)

                    # Vérifier combien de positions restent
                    remaining_positions = live_engine.hyperliquid_client.get_positions()
                    remaining_count = len(remaining_positions) if remaining_positions else 0

                    if remaining_count > 0:
                        message = f"{closed_count} positions fermées, {remaining_count} positions restantes"
                    else:
                        message = f"Toutes les positions fermées: {closed_count} réussies, {failed_count} échouées"
                else:
                    message = "Aucune position à fermer (mode simulation ou pas de client)"

            except Exception as e:
                logger.error(f"Erreur lors de la fermeture Hyperliquid: {e}")
                message = f"{closed_count} positions locales fermées, erreur Hyperliquid: {str(e)}"

            return {"status": "success", "message": message}
            
        else:
            raise HTTPException(status_code=400, detail=f"Action inconnue: {action}")
            
    except Exception as e:
        raise HTTPException(status_code=500, detail=f"Erreur contrôle: {str(e)}")

@app.post("/api/trading/set_mode/{mode}")
async def set_trading_mode(mode: str):
    """Changer le mode de trading"""
    try:
        valid_modes = ['SIMULATION', 'LIVE_MANUAL', 'LIVE_AUTO']
        if mode not in valid_modes:
            raise HTTPException(status_code=400, detail=f"Mode invalide: {mode}. Modes valides: {valid_modes}")
        
        # Changer le mode
        if mode in ['LIVE_MANUAL', 'LIVE_AUTO']:
            live_engine.set_trading_mode(mode, hyperliquid_client)
        else:
            live_engine.set_trading_mode(mode, None)
        
        return {
            "status": "success",
            "message": f"Mode de trading changé vers {mode}",
            "current_mode": live_engine.trading_mode
        }
        
    except Exception as e:
        raise HTTPException(status_code=500, detail=f"Erreur changement mode: {str(e)}")

# ============================================================================
# API ENDPOINTS - PENDING ORDERS (LIVE MANUAL MODE)
# ============================================================================

@app.get("/api/trading/pending_orders")
async def get_pending_orders():
    """Récupérer tous les ordres en attente de confirmation"""
    try:
        return {"pending_orders": list(live_engine.pending_orders.values())}
    except Exception as e:
        raise HTTPException(status_code=500, detail=f"Erreur récupération ordres: {str(e)}")

@app.post("/api/trading/confirm_order/{order_id}")
async def confirm_pending_order(order_id: str):
    """Confirmer et exécuter un ordre en attente"""
    try:
        if order_id not in live_engine.pending_orders:
            raise HTTPException(status_code=404, detail="Ordre non trouvé")
        
        order = live_engine.pending_orders[order_id]
        
        # Exécuter l'ordre sur Hyperliquid
        result = live_engine.execute_pending_order(order_id)
        
        if result.get('success'):
            return {
                "status": "success", 
                "message": f"Ordre {order_id} confirmé et exécuté",
                "position_id": result.get('position_id')
            }
        else:
            return {
                "status": "error",
                "message": f"Erreur exécution: {result.get('error', 'Erreur inconnue')}"
            }
            
    except Exception as e:
        raise HTTPException(status_code=500, detail=f"Erreur confirmation: {str(e)}")

@app.post("/api/trading/reject_order/{order_id}")
async def reject_pending_order(order_id: str):
    """Rejeter un ordre en attente"""
    try:
        if order_id not in live_engine.pending_orders:
            raise HTTPException(status_code=404, detail="Ordre non trouvé")
        
        order = live_engine.pending_orders.pop(order_id)
        
        return {
            "status": "success",
            "message": f"Ordre {order['symbol']} {order['side']} rejeté"
        }
        
    except Exception as e:
        raise HTTPException(status_code=500, detail=f"Erreur rejet: {str(e)}")

@app.post("/api/trading/execute_signal")
async def execute_signal_to_pending(request_data: dict):
    """
    Convertit un signal en ordre en attente dans le moteur de trading live
    """
    try:
        symbol = request_data.get('symbol')
        signal_type = request_data.get('signal_type')
        
        if not symbol or not signal_type or signal_type == 'HOLD':
            return {"success": False, "error": "Signal invalide ou HOLD"}
        
        # Récupérer le signal actuel pour ce symbole depuis les oracles
        current_signals = generate_signals_for_api()
        matching_signal = None
        
        for signal in current_signals:
            if signal['symbol'] == symbol and signal['signal_type'] == signal_type:
                matching_signal = signal
                break
        
        if not matching_signal:
            return {"success": False, "error": f"Signal {symbol} {signal_type} non trouvé dans les oracles actuels"}
        
        # Créer un ordre en attente via le moteur live
        result = live_engine.create_pending_order_from_signal(matching_signal)
        
        if result.get('success'):
            return {
                "success": True,
                "order_id": result.get('order_id'),
                "message": f"Ordre en attente créé pour {symbol} {signal_type}",
                "order": result.get('order')
            }
        else:
            return {
                "success": False,
                "error": result.get('error', 'Erreur inconnue')
            }
            
    except Exception as e:
        return {"success": False, "error": f"Erreur serveur: {str(e)}"}

# Cache global pour tracker les premières apparitions de positions
position_first_seen = {}

# Import position bias analyzer
from position_bias_analyzer import analyze_signal_with_positions, get_portfolio_context

def calculate_position_duration(symbol: str, entry_price: float, current_size: float) -> str:
    """
    Calcule la durée d'une position - version simplifiée avec cache
    """
    global position_first_seen
    
    try:
        # Créer une clé unique pour cette position
        position_key = f"{symbol}_{entry_price}_{current_size}"
        
        # Si c'est la première fois qu'on voit cette position, enregistrer le timestamp
        current_time = datetime.now()
        if position_key not in position_first_seen:
            position_first_seen[position_key] = current_time
            return "0m"  # Position juste détectée
        
        # Calculer la durée depuis la première détection
        start_time = position_first_seen[position_key]
        duration_seconds = (current_time - start_time).total_seconds()
        
        # Formater la durée
        if duration_seconds < 3600:  # Moins d'1h
            minutes = int(duration_seconds / 60)
            return f"{minutes}m"
        elif duration_seconds < 86400:  # Moins d'1j
            hours = int(duration_seconds / 3600)
            minutes = int((duration_seconds % 3600) / 60)
            return f"{hours}h{minutes}m"
        else:  # Plus d'1j
            days = int(duration_seconds / 86400)
            hours = int((duration_seconds % 86400) / 3600)
            return f"{days}j{hours}h"
            
    except Exception as e:
        logger.error(f"Erreur calcul durée position {symbol}: {e}")
        return "Erreur"

@app.get("/api/trading/portfolio_context")
async def get_portfolio_context_api():
    """Récupérer le contexte du portfolio pour l'analyse de biais"""
    try:
        hyperliquid_positions = []
        
        # Récupérer les positions Hyperliquid
        if live_engine and live_engine.hyperliquid_client:
            try:
                hl_positions = live_engine.hyperliquid_client.get_positions()
                if hl_positions:
                    for pos in hl_positions:
                        symbol = pos['symbol'].replace('-PERP', '')
                        raw_data = pos.get('raw_position_data', {})
                        unrealized_pnl = float(raw_data.get('unrealizedPnl', 0))
                        
                        entry_price = pos['entry_price']
                        size = abs(pos['size'])
                        leverage_info = raw_data.get('leverage', {})
                        actual_leverage = 1
                        if isinstance(leverage_info, dict):
                            actual_leverage = int(leverage_info.get('value', 1))
                        elif isinstance(leverage_info, (int, float)):
                            actual_leverage = int(leverage_info)
                        
                        notional_value = size * entry_price
                        margin_used = notional_value / actual_leverage if actual_leverage > 0 else notional_value
                        pnl_percent = (unrealized_pnl / margin_used * 100) if margin_used > 0 else 0
                        
                        hyperliquid_positions.append({
                            'symbol': symbol,
                            'side': pos['side'].upper(),
                            'pnl_percent': pnl_percent,
                            'size': size,
                            'entry_price': entry_price,
                            'leverage': actual_leverage
                        })
            except Exception as e:
                logger.warning(f"Erreur récupération positions pour contexte: {e}")
        
        # Analyser le contexte
        if hyperliquid_positions:
            portfolio_context = get_portfolio_context(hyperliquid_positions)
            
            # Exemples de signaux enrichis
            sample_signals = [
                {"symbol": "BTC", "signal_type": "BUY", "confidence": 0.6, "reasoning": "Technical bullish"},
                {"symbol": "ETH", "signal_type": "SELL", "confidence": 0.7, "reasoning": "Bearish momentum"}
            ]
            
            enhanced_samples = []
            for signal in sample_signals:
                enhanced = analyze_signal_with_positions(hyperliquid_positions, signal)
                enhanced_samples.append({
                    'original': signal,
                    'enhanced': enhanced,
                    'confidence_change': enhanced['confidence'] - signal['confidence']
                })
            
            return {
                "status": "success",
                "portfolio_context": portfolio_context,
                "positions": hyperliquid_positions,
                "sample_enhancements": enhanced_samples,
                "timestamp": datetime.now().isoformat()
            }
        else:
            return {
                "status": "success",
                "portfolio_context": {"status": "no_positions"},
                "positions": [],
                "message": "Aucune position ouverte pour analyse",
                "timestamp": datetime.now().isoformat()
            }
            
    except Exception as e:
        logger.error(f"Erreur contexte portfolio: {e}")
        return {"status": "error", "message": str(e)}

@app.get("/api/trading/settings")
async def get_trading_settings():
    """Récupérer la configuration de trading actuelle"""
    try:
        import trading_config
        
        return {
            "status": "success",
            "settings": {
                # Mode de trading
                "trading_mode": trading_config.TRADING_MODE,
                "auto_trading_enabled": trading_config.AUTO_TRADING_ENABLED,
                
                # Paramètres financiers
                "simulation_initial_balance": trading_config.SIMULATION_INITIAL_BALANCE,
                "position_size_percent": trading_config.MAX_SIMULTANEOUS_POSITIONS,
                "max_simultaneous_positions": trading_config.MAX_SIMULTANEOUS_POSITIONS,
                "min_confidence_threshold": trading_config.MIN_CONFIDENCE_THRESHOLD,
                
                # Gestion des risques
                "profit_target_multiplier": trading_config.PROFIT_TARGET_MULTIPLIER,
                "trailing_stop_distance": trading_config.TRAILING_STOP_DISTANCE,
                
                # Oracles et signaux
                "signal_refresh_interval": trading_config.SIGNAL_REFRESH_INTERVAL,
                "enable_binance_oracle": trading_config.ENABLE_BINANCE_ORACLE,
                "enable_hyperliquid_oracle": trading_config.ENABLE_HYPERLIQUID_ORACLE,
                "enable_sentiment_oracle": trading_config.ENABLE_SENTIMENT_ORACLE,
                
                # Sécurité
                "enable_safety_checks": trading_config.ENABLE_SAFETY_CHECKS,
                "max_daily_loss_percent": trading_config.MAX_DAILY_LOSS_PERCENT,
                "emergency_stop": trading_config.EMERGENCY_STOP,
                
                # Logging
                "log_level": trading_config.LOG_LEVEL,
                "log_to_file": trading_config.LOG_TO_FILE,
            },
            "live_engine_settings": {
                "current_balance": live_engine.current_balance if live_engine else 0,
                "active_positions": len(live_engine.positions) if live_engine else 0,
                "trading_mode_actual": live_engine.trading_mode if live_engine else "UNKNOWN"
            }
        }
    except Exception as e:
        logger.error(f"Erreur récupération paramètres: {e}")
        return {"status": "error", "message": str(e)}

@app.post("/api/trading/settings")
async def update_trading_settings(settings: dict):
    """Mettre à jour la configuration de trading"""
    try:
        import trading_config
        
        # Validation des paramètres critiques
        if "trading_mode" in settings:
            valid_modes = ["SIMULATION", "LIVE_MANUAL", "LIVE_AUTO"]
            if settings["trading_mode"] not in valid_modes:
                return {"status": "error", "message": f"Mode invalide. Modes valides: {valid_modes}"}
        
        # Mettre à jour les paramètres dans le moteur de trading
        if live_engine:
            if "position_size_percent" in settings:
                live_engine.position_size = settings["position_size_percent"] / 100.0
            
            if "max_simultaneous_positions" in settings:
                live_engine.max_positions = settings["max_simultaneous_positions"]
                
            if "min_confidence_threshold" in settings:
                live_engine.min_confidence = settings["min_confidence_threshold"] / 100.0
                
            if "trailing_stop_distance" in settings:
                live_engine.trailing_stop_distance = settings["trailing_stop_distance"] / 100.0
        
        # Note: Pour une implémentation complète, il faudrait aussi mettre à jour trading_config.py
        # Ici on met à jour seulement le moteur en cours
        
        return {
            "status": "success", 
            "message": "Paramètres mis à jour avec succès",
            "note": "Redémarrage nécessaire pour certains paramètres"
        }
        
    except Exception as e:
        logger.error(f"Erreur mise à jour paramètres: {e}")
        return {"status": "error", "message": str(e)}

@app.post("/api/trading/cleanup_orders")
async def cleanup_expired_orders():
    """Nettoyer manuellement les ordres expirés"""
    try:
        expired_count = live_engine.cleanup_expired_orders()
        
        return {
            "status": "success",
            "message": f"{expired_count} ordres expirés supprimés",
            "expired_count": expired_count
        }
        
    except Exception as e:
        raise HTTPException(status_code=500, detail=f"Erreur nettoyage: {str(e)}")

# ============================================================================
# API ENDPOINTS - TRADING HISTORY
# ============================================================================

@app.get("/api/trading/history")
async def get_trading_history(mode: str = "all", limit: int = 50):
    """
    Récupère l'historique de trading :
    - mode='simulation' : historique du moteur seulement
    - mode='hyperliquid' : historique Hyperliquid seulement  
    - mode='all' : historique combiné (défaut)
    """
    try:
        result = []
        
        if mode in ['all', 'hyperliquid'] and live_engine.trading_mode != 'SIMULATION':
            # Récupérer l'historique des positions Hyperliquid regroupées
            hyperliquid_positions = hyperliquid_client.get_position_history(limit=limit)
            
            # Formater les positions pour l'affichage
            for position in hyperliquid_positions:
                # Convertir les timestamps pour l'affichage
                if 'open_time' in position and isinstance(position['open_time'], (int, float)):
                    position['open_timestamp'] = datetime.fromtimestamp(position['open_time']).isoformat()
                
                if 'close_time' in position and isinstance(position['close_time'], (int, float)):
                    position['close_timestamp'] = datetime.fromtimestamp(position['close_time']).isoformat()
                    position['timestamp'] = position['close_time']  # Pour le tri
                else:
                    position['timestamp'] = position.get('open_time', time.time())
                
                # Corriger le PnL en déduisant les frais
                if position.get('pnl') is not None and position.get('fees') is not None:
                    # Le PnL d'Hyperliquid est brut, on doit déduire les frais
                    pnl_brut = position['pnl']
                    fees = position['fees']
                    position['pnl'] = pnl_brut - fees  # PnL net après frais

                # Calculer le PnL en pourcentage en utilisant la méthode centralisée
                if position.get('entry_price') and position.get('exit_price'):
                    pnl_data = hyperliquid_client.calculate_position_pnl(
                        entry_price=position['entry_price'],
                        current_price=position['exit_price'],
                        size=position.get('size', 0),
                        side=position['side']
                    )
                    position['pnl_percent'] = pnl_data['pnl_percent']
                else:
                    position['pnl_percent'] = 0
                
                # Déterminer le statut
                position['status'] = 'CLOSED' if position.get('exit_price') else 'OPEN'
            
            result.extend(hyperliquid_positions)
            
        if mode in ['all', 'simulation']:
            # Récupérer l'historique du moteur (positions fermées)
            engine_trades = []
            for pos_id, position in live_engine.positions.items():
                if hasattr(position, 'status') and position.status == 'CLOSED':
                    # Convertir position fermée en format trade
                    engine_trade = {
                        'id': f"eng_{pos_id}",
                        'symbol': position.symbol,
                        'side': position.side,
                        'operation': 'ENGINE',
                        'size': position.size,
                        'price': position.entry_price,
                        'timestamp': getattr(position, 'close_time', time.time()),
                        'fee': 0,  # Frais simulés
                        'pnl': getattr(position, 'realized_pnl', 0),
                        'source': 'ENGINE'
                    }
                    engine_trades.append(engine_trade)
            
            result.extend(engine_trades)
        
        # Trier par timestamp décroissant
        result.sort(key=lambda x: x['timestamp'], reverse=True)
        
        # Limiter les résultats
        result = result[:limit]
        
        return {
            "success": True,
            "count": len(result),
            "mode": mode,
            "trading_mode": live_engine.trading_mode,
            "trades": result
        }
        
    except Exception as e:
        return {"success": False, "error": str(e)}

# ============================================================================
# API ENDPOINTS - PORTFOLIO
# ============================================================================

@app.get("/api/portfolio")
async def get_portfolio_data():
    """Données du portfolio réel depuis Hyperliquid + simulation"""
    try:
        # Récupération des données réelles depuis Hyperliquid
        portfolio_data = hyperliquid_client.get_portfolio_summary()
        
        # Combinaison avec les données du moteur de trading live
        engine_status = live_engine.get_status_dict()
        engine_metrics = engine_status.get('metrics', {})
        
        return {
            "status": "success",
            "real_portfolio": {
                "account_value": portfolio_data.get("account_value", 0),
                "available_balance": portfolio_data.get("available_balance", 0),
                "margin_used": portfolio_data.get("margin_used", 0),
                "unrealized_pnl": portfolio_data.get("unrealized_pnl", 0),
                "active_positions_count": portfolio_data.get("active_positions", 0),
                "account_address": portfolio_data.get("account_address", ""),
                "positions": portfolio_data.get("positions", []),
                "balances": portfolio_data.get("balances", {})
            },
            "simulation_engine": {
                "current_balance": engine_metrics.get("current_balance", 10000),
                "total_pnl": engine_metrics.get("total_pnl", 0),
                "total_trades": engine_metrics.get("total_trades", 0),
                "win_rate": engine_metrics.get("win_rate", 0),
                "active_positions": engine_metrics.get("active_positions", 0),
                "active_positions_data": engine_status.get("active_positions", []),
                "recent_trades": engine_status.get("recent_trades", [])
            },
            "timestamp": datetime.now().isoformat()
        }
        
    except Exception as e:
        return {
            "status": "error",
            "error": str(e),
            "real_portfolio": {
                "account_value": 0,
                "available_balance": 0,
                "margin_used": 0,
                "unrealized_pnl": 0,
                "active_positions_count": 0,
                "positions": [],
                "balances": {},
                "error": "Failed to fetch Hyperliquid data"
            },
            "simulation_engine": {
                "current_balance": 0,
                "total_pnl": 0,
                "total_trades": 0,
                "win_rate": 0,
                "active_positions": 0
            },
            "timestamp": datetime.now().isoformat()
        }

@app.get("/api/debug/hyperliquid_positions")
async def debug_hyperliquid_positions():
    """Endpoint de debug pour voir les positions Hyperliquid raw"""
    try:
        if hyperliquid_client:
            positions = hyperliquid_client.get_positions()
            return {
                "positions": positions,
                "count": len(positions) if positions else 0,
                "non_zero": [p for p in (positions or []) if float(p.get('szi', 0)) != 0] if positions else []
            }
        else:
            return {"error": "Client Hyperliquid non disponible"}
    except Exception as e:
        return {"error": f"Erreur récupération positions: {str(e)}"}

@app.get("/api/debug/trading_logs")
async def get_trading_logs(lines: int = 100):
    """Endpoint pour consulter les derniers logs du trading engine"""
    try:
        log_file = "logs/trading_engine.log"
        if not os.path.exists(log_file):
            return {"error": "Fichier de log non trouvé"}
        
        # Lire les dernières lignes du fichier de log
        with open(log_file, 'r', encoding='utf-8', errors='ignore') as f:
            all_lines = f.readlines()
            recent_lines = all_lines[-lines:] if len(all_lines) > lines else all_lines
        
        return {
            "total_lines": len(all_lines),
            "showing": len(recent_lines),
            "logs": [line.strip() for line in recent_lines]
        }
    except Exception as e:
        return {"error": f"Erreur lecture logs: {str(e)}"}

@app.get("/api/debug/engine_state")
async def debug_engine_state():
    """Debug: État du moteur de trading"""
    return {
        "trading_mode": live_engine.trading_mode,
        "hyperliquid_client_attached": live_engine.hyperliquid_client is not None,
        "hyperliquid_client_type": type(live_engine.hyperliquid_client).__name__ if live_engine.hyperliquid_client else None,
        "current_balance_simulation": live_engine.current_balance,
        "positions_count": len(live_engine.positions),
        "closed_positions_count": len(live_engine.closed_positions)
    }

@app.get("/api/debug/hyperliquid_test")
async def debug_hyperliquid_test():
    """Debug: Tester directement l'API Hyperliquid du moteur"""
    try:
        if not live_engine.hyperliquid_client:
            return {"error": "Pas de client Hyperliquid attaché"}
        
        account_state = live_engine.hyperliquid_client.get_account_state()
        if account_state:
            margin_summary = account_state.get('marginSummary', {})
            balance = float(margin_summary.get('accountValue', 0)) if margin_summary else 0
            return {
                "success": True,
                "account_state_keys": list(account_state.keys()),
                "margin_summary": margin_summary,
                "balance_parsed": balance
            }
        else:
            return {"error": "account_state is None"}
    except Exception as e:
        import traceback
        return {
            "error": str(e),
            "traceback": traceback.format_exc()
        }

@app.put("/api/trading/config")
async def update_trading_config(config_data: dict):
    """Mettre à jour la configuration de trading"""
    try:
        # Valider et appliquer les paramètres au moteur de trading
        updated_params = {}
        
        if 'max_positions' in config_data:
            live_engine.max_positions = max(1, min(20, int(config_data['max_positions'])))
            updated_params['max_positions'] = live_engine.max_positions
            
        if 'position_size_pct' in config_data:
            live_engine.position_size_pct = max(0.05, min(0.25, float(config_data['position_size_pct'])))
            updated_params['position_size_pct'] = live_engine.position_size_pct
            
        if 'min_confidence' in config_data:
            live_engine.min_confidence = max(0.01, min(0.95, float(config_data['min_confidence'])))
            updated_params['min_confidence'] = live_engine.min_confidence
            
        if 'trailing_stop_distance' in config_data:
            live_engine.trailing_stop_distance = max(0.10, min(0.30, float(config_data['trailing_stop_distance'])))
            updated_params['trailing_stop_distance'] = live_engine.trailing_stop_distance
            
        if 'profit_target_multiplier' in config_data:
            live_engine.profit_target_multiplier = max(1.0, min(5.0, float(config_data['profit_target_multiplier'])))
            updated_params['profit_target_multiplier'] = live_engine.profit_target_multiplier
        
        # Optionnel: Sauvegarder dans trading_config.py
        try:
            import trading_config
            for param, value in updated_params.items():
                if param == 'max_positions':
                    trading_config.MAX_SIMULTANEOUS_POSITIONS = value
                elif param == 'position_size_pct':
                    trading_config.MAX_SIMULTANEOUS_POSITIONS = value * 100
                elif param == 'min_confidence':
                    trading_config.MIN_CONFIDENCE_THRESHOLD = value * 100
                elif param == 'trailing_stop_distance':
                    trading_config.TRAILING_STOP_DISTANCE = value * 100
                elif param == 'profit_target_multiplier':
                    trading_config.PROFIT_TARGET_MULTIPLIER = value
        except ImportError:
            # trading_config pas disponible, continuer quand même
            pass
            
        return {
            "status": "success",
            "message": "Configuration mise à jour",
            "updated_params": updated_params,
            "timestamp": datetime.now().isoformat()
        }
        
    except Exception as e:
        raise HTTPException(status_code=500, detail=f"Erreur mise à jour config: {str(e)}")

@app.get("/api/logs")
async def get_logs(lines: int = 100):
    """Récupère les dernières lignes du fichier de log du trading engine"""
    try:
        import os
        
        log_file = "logs/trading_engine.log"
        
        # Vérifier si le fichier existe
        if not os.path.exists(log_file):
            return {
                "success": False, 
                "logs": [],
                "message": "Fichier de log non trouvé"
            }
        
        # Lire les dernières lignes du fichier
        with open(log_file, 'r', encoding='utf-8') as f:
            all_lines = f.readlines()
        
        # Prendre les dernières 'lines' lignes
        recent_lines = all_lines[-lines:] if len(all_lines) > lines else all_lines
        
        # Nettoyer les lignes (enlever les \n)
        clean_lines = [line.strip() for line in recent_lines if line.strip()]
        
        return {
            "success": True,
            "logs": clean_lines,
            "total_lines": len(clean_lines),
            "file_size": os.path.getsize(log_file) if os.path.exists(log_file) else 0,
            "timestamp": datetime.now().isoformat()
        }
        
    except Exception as e:
        return {
            "success": False,
            "logs": [],
            "error": str(e),
            "timestamp": datetime.now().isoformat()
        }

if __name__ == "__main__":
    print("=" * 60)
    print("SERVEUR UNIFIE CRYPTO TRADING")
    print("=" * 60)
    print(f"URL: http://{APP_HOST}:{APP_PORT}")
    print(f"Authentification: {AUTH_USERNAME} / {AUTH_PASSWORD}")
    print("Fonctionnalités:")
    print("   - Dashboard avec signaux paginés")
    print("   - Moteur de trading live intégré")
    print("   - Portfolio Hyperliquid + Simulation")
    print("   - Suivi PnL temps réel")
    print("   - API complète unifiée")
    print("=" * 60)
    
    # Afficher les dates de dernière modification des fichiers principaux
    print("DATES DE DERNIERE MODIFICATION DES FICHIERS:")
    print(f"  unified_trading_server.py: {get_file_modification_time('unified_trading_server.py')}")
    print(f"  real_time_trading_engine.py: {get_file_modification_time('real_time_trading_engine.py')}")
    print(f"  hyperliquid_client.py: {get_file_modification_time('hyperliquid_client.py')}")
    print(f"  trading_config.py: {get_file_modification_time('trading_config.py')}")
    print("=" * 60)
    
    uvicorn.run("unified_trading_server:app", host=APP_HOST, port=APP_PORT, reload=False)