"""
Market Regime Detection Module
==============================
Analyse le marché global (BTC + Top Altcoins) pour détecter le régime actuel
et ajuster automatiquement l'exposition du bot.

Régimes:
- BULL_STRONG: Marché très haussier, exposition maximale
- BULL_WEAK: Marché haussier modéré
- NEUTRAL: Marché latéral
- CORRECTION: Correction en cours, réduire l'exposition
- BEAR: Marché baissier, exposition minimale
"""

import json
import logging
import os
import time
import requests
from datetime import datetime, timedelta
from typing import Dict, Optional, Tuple, List
import numpy as np

logger = logging.getLogger('MarketRegime')

# Fichier de persistance de l'état du régime (survit aux redémarrages)
_MODULE_DIR = os.path.dirname(os.path.abspath(__file__))
REGIME_STATE_FILE = os.path.join(_MODULE_DIR, 'data', 'spy_regime_state.json')

# URL de l'API Binance publique (pas besoin de clés)
BINANCE_API_URL = "https://api.binance.com/api/v3"

# ════════════════════════════════════════════════════════════════════════════
# CONFIGURATION DES RÉGIMES
# ════════════════════════════════════════════════════════════════════════════

REGIME_CONFIG = {
    'BULL_STRONG': {
        'max_positions': 15,  # 🔧 FIX 30/01: Réduit de 20 à 15 (limite exposition)
        'min_score': 50,  # 🔧 FIX 08/02: Restauré — smart_criteria valide déjà la qualité
        'position_size_pct': 100,  # 100% de la taille normale
        'take_profit_multiplier': 1.2,  # TP plus ambitieux
        'description': '🚀 Marché BULL FORT - Exposition maximale'
    },
    'BULL_WEAK': {
        'max_positions': 10,  # 🔧 FIX 01/03 v2: 12→10 (limiter exposition en marché incertain)
        'min_score': 65,  # 🔧 FIX 01/03 v2: 60→65 (exiger meilleure qualité)
        'position_size_pct': 85,  # 🔧 FIX 01/03 v2: 90→85
        'take_profit_multiplier': 1.0,
        'description': '📈 Marché BULL MODÉRÉ - Exposition élevée'
    },
    'NEUTRAL': {
        'max_positions': 7,  # 🔧 FIX 03/03: 5→7 — capturer plus d'opportunités en NEUTRAL
        'min_score': 72,  # 🔧 FIX 03/03: 75→72 — signaux 73-74 ne doivent pas être ratés
        'position_size_pct': 65,  # 🔧 FIX 03/03: 60→65 (taille légèrement augmentée)
        'take_profit_multiplier': 0.90,  # TP plus conservateur
        'description': '➡️ Marché NEUTRE - Exposition MODÉRÉE (creux-rebonds de qualité)'
    },
    'CORRECTION': {
        'max_positions': 3,  # Ultra-conservateur
        'min_score': 82,  # Seuls les meilleurs signaux
        'position_size_pct': 40,
        'take_profit_multiplier': 0.75,
        'description': '⚠️ CORRECTION en cours - Exposition MINIMALE'
    },
    'EARLY_RECOVERY': {
        'max_positions': 10,  # 🔧 FIX 03/03: 4→10 — REPRISE = OPPORTUNITÉ, pas prudence!
        'min_score': 63,   # 🔧 FIX 03/03: 70→63 — capter les rebonds dès le début
        'position_size_pct': 75,  # 🔧 FIX 03/03: 50→75 — la reprise mérite de s'exposer
        'take_profit_multiplier': 1.0,  # 🔧 FIX 03/03: 0.85→1.0 — TP normal en reprise
        'description': '🔄 REPRISE EN COURS - Exposition ACTIVE (opportunités de rebond)'
    },
    'BEAR': {
        'max_positions': 2,  # 🔧 FIX 01/03 v2: 5→2 (quasi-arrêt du trading)
        'min_score': 85,  # 🔧 FIX 02/03: 90→85 (score 90 inatteignable avec pénalité BEAR)
        'position_size_pct': 25,  # 🔧 FIX 01/03 v2: 30→25
        'take_profit_multiplier': 0.5,
        'description': '🔴 Marché BEAR - Trading quasi-arrêté'
    }
}

# Top altcoins pour l'analyse de marché (hors stablecoins) - Liste étendue pour meilleure détection
TOP_ALTCOINS = [
    # Top 10 par market cap
    'ETHUSDT', 'BNBUSDT', 'SOLUSDT', 'XRPUSDT', 'ADAUSDT',
    'DOGEUSDT', 'AVAXUSDT', 'DOTUSDT', 'LINKUSDT', 'MATICUSDT',
    # Top 11-20 - important pour détecter les rotations
    'LTCUSDT', 'ATOMUSDT', 'UNIUSDT', 'ETCUSDT', 'XLMUSDT',
    'NEARUSDT', 'APTUSDT', 'FILUSDT', 'ARBUSDT', 'OPUSDT',
    # Top 21-30 - altcoins plus volatils, bons indicateurs de sentiment
    'AAVEUSDT', 'ICPUSDT', 'VETUSDT', 'FTMUSDT', 'ALGOUSDT',
    'SANDUSDT', 'MANAUSDT', 'AXSUSDT', 'GALAUSDT', 'ROSEUSDT'
]


class MarketRegimeDetector:
    """
    Détecte le régime de marché actuel en analysant BTC et les top altcoins.
    """
    
    def __init__(self, binance_client=None):
        """
        Initialise le détecteur de régime.
        
        Args:
            binance_client: Client Binance pour récupérer les données
        """
        self.client = binance_client
        self.current_regime = 'NEUTRAL'
        self.regime_config = REGIME_CONFIG[self.current_regime]
        self.last_update = None
        self.update_interval = 30  # 🔧 FIX 03/03: 60s→30s (plus réactif aux retournements)
        self.max_cache_time = 45   # 🔧 FIX 03/03: 90s→45s (force refresh rapide)
        self.regime_history = []
        self.market_data = {}
        self.last_score = 50  # Score précédent pour détecter les chutes drastiques
        self.score_history = []  # Historique des 5 derniers scores pour détecter les tendances
        # 🔴 FIX 04/03: Mode FAST adaptatif — quand marché chute, réduire update_interval à 10s
        # Asymétrie intentionnelle: on détecte les crashs VITE, reprises après confirmation
        self._fast_mode = False           # Actif si chute rapide détectée
        self._fast_mode_until = 0.0       # Timestamp de fin du mode fast
        self._bear_detected_at = 0.0      # Timestamp de la 1ère détection BEAR (pour urgence)
        
        # Restaurer l'état persisté si disponible (évite de démarrer NEUTRAL après restart)
        self._try_load_state()
        logger.info("🎯 MarketRegimeDetector initialisé")
    
    def _try_load_state(self):
        """Charge l'état du régime depuis le fichier si récent (< 15 min).
        Permet au bot de reprendre en BEAR après un redémarrage au lieu de defaulter à NEUTRAL.
        Seuls les régimes restrictifs (BEAR/CORRECTION) sont restaurés — NEUTRAL est le défaut."""
        try:
            if not os.path.exists(REGIME_STATE_FILE):
                return
            with open(REGIME_STATE_FILE, 'r') as f:
                state = json.load(f)
            age = time.time() - state.get('last_update_ts', 0)
            MAX_AGE = 900  # 15 minutes — au-delà le marché a pu changer
            if age > MAX_AGE:
                logger.info(f"📦 État régime expiré ({age:.0f}s > {MAX_AGE}s) — démarrage en NEUTRAL")
                return
            saved_regime = state.get('current_regime', 'NEUTRAL')
            # Restaurer uniquement les régimes restrictifs (NEUTRAL est déjà le défaut)
            if saved_regime in ('BEAR', 'CORRECTION', 'EARLY_RECOVERY'):
                self.current_regime = saved_regime
                self.regime_config = dict(REGIME_CONFIG[saved_regime])
                self._fast_mode = state.get('_fast_mode', False)
                self._fast_mode_until = state.get('_fast_mode_until', 0.0)
                self._bear_detected_at = state.get('_bear_detected_at', 0.0)
                # last_update = il y a 25s → le cache expire dans ~5s → reload rapide avec données fraîches
                self.last_update = datetime.now() - timedelta(seconds=25)
                logger.warning(f"♻️ Régime {saved_regime} restauré depuis fichier (âge: {age:.0f}s) — refresh dans ~5s")
            else:
                logger.info(f"📦 État régime {saved_regime} non restauré (NEUTRAL par défaut)")
        except Exception as e:
            logger.warning(f"⚠️ Impossible de charger le régime sauvegardé: {e}")

    def _save_state(self):
        """Persiste l'état du régime sur disque après chaque changement.
        Utilisé pour survivre aux redémarrages sans perdre le contexte BEAR/CORRECTION."""
        try:
            state = {
                'current_regime': self.current_regime,
                '_fast_mode': self._fast_mode,
                '_fast_mode_until': self._fast_mode_until,
                '_bear_detected_at': self._bear_detected_at,
                'last_update_ts': time.time(),
                'saved_at': datetime.now().isoformat()
            }
            tmp = REGIME_STATE_FILE + '.tmp'
            with open(tmp, 'w') as f:
                json.dump(state, f, indent=2)
            os.replace(tmp, REGIME_STATE_FILE)
        except Exception as e:
            logger.warning(f"⚠️ Impossible de sauvegarder l'état du régime: {e}")

    def set_client(self, binance_client):
        """Définit le client Binance."""
        self.client = binance_client
    
    def _get_klines_from_api(self, symbol: str, interval: str = '1h', limit: int = 50) -> Optional[List]:
        """Récupère les klines directement depuis l'API publique Binance."""
        try:
            url = f"{BINANCE_API_URL}/klines"
            params = {
                'symbol': symbol,
                'interval': interval,
                'limit': limit
            }
            response = requests.get(url, params=params, timeout=10)
            if response.status_code == 200:
                return response.json()
            else:
                logger.debug(f"Erreur API Binance {symbol}: {response.status_code}")
                return None
        except Exception as e:
            logger.debug(f"Erreur requête klines {symbol}: {e}")
            return None
    
    def _get_klines(self, symbol: str, interval: str = '1h', limit: int = 50) -> Optional[List]:
        """Récupère les klines pour un symbole (client ou API directe)."""
        # Si on a un client, l'utiliser en priorité
        if self.client:
            try:
                if hasattr(self.client, 'get_klines_production'):
                    klines = self.client.get_klines_production(symbol=symbol, interval=interval, limit=limit)
                else:
                    klines = self.client.get_klines(symbol=symbol, interval=interval, limit=limit)
                if klines:
                    return klines
            except Exception as e:
                logger.debug(f"Erreur client klines {symbol}: {e}")
        
        # Fallback: utiliser l'API publique Binance directement
        return self._get_klines_from_api(symbol, interval, limit)
    
    def _calculate_indicators(self, klines: List) -> Dict:
        """
        Calcule les indicateurs techniques à partir des klines.
        
        Returns:
            Dict avec EMA, RSI, momentum, etc.
        """
        if not klines or len(klines) < 26:
            return {}
        
        try:
            closes = np.array([float(k[4]) for k in klines])
            highs = np.array([float(k[2]) for k in klines])
            lows = np.array([float(k[3]) for k in klines])
            volumes = np.array([float(k[5]) for k in klines])
            
            # EMAs
            ema9 = self._ema(closes, 9)
            ema21 = self._ema(closes, 21)
            ema50 = self._ema(closes, 50) if len(closes) >= 50 else ema21
            
            # RSI
            rsi = self._rsi(closes, 14)
            
            # Momentum multi-périodes (CRITIQUE pour détecter les retournements)
            # Momentum 3h (court terme - détecte les rebonds temporaires)
            if len(closes) >= 3:
                momentum_3h = ((closes[-1] - closes[-3]) / closes[-3]) * 100
            else:
                momentum_3h = 0
            
            # Momentum 5h (moyen terme - détecte les crashs en cours)
            if len(closes) >= 5:
                momentum_5h = ((closes[-1] - closes[-5]) / closes[-5]) * 100
            else:
                momentum_5h = 0
            
            # 🔧 FIX 01/03: Momentum 12h (aligné avec le graphique dashboard)
            # Le graphique montre les 12 dernières heures mais le score l'ignorait
            if len(closes) >= 12:
                momentum_12h = ((closes[-1] - closes[-12]) / closes[-12]) * 100
            else:
                momentum_12h = 0
            
            # Momentum 24h (long terme)
            if len(closes) >= 24:
                momentum_24h = ((closes[-1] - closes[-24]) / closes[-24]) * 100
            else:
                momentum_24h = ((closes[-1] - closes[0]) / closes[0]) * 100
            
            # ATR pour la volatilité
            atr = self._atr(highs, lows, closes, 14)
            atr_pct = (atr / closes[-1]) * 100  # ATR en % du prix
            
            # Volume moyen
            vol_avg = np.mean(volumes[-10:])
            vol_current = volumes[-1]
            vol_ratio = vol_current / vol_avg if vol_avg > 0 else 1
            
            # Tendance EMA - ASSOUPLIE pour mieux détecter les hausses
            # Bullish: ema9 au-dessus de ema21 OU momentum positif significatif
            # On ne demande plus l'alignement parfait des 3 EMAs
            ema9_above_21 = ema9 > ema21
            ema21_above_50 = ema21 > ema50
            ema9_below_21 = ema9 < ema21
            ema21_below_50 = ema21 < ema50
            
            # Score de tendance basé sur plusieurs facteurs
            # 🔧 FIX 01/03: Intégrer momentum 3h, 5h, 12h dans trend_score
            # Avant: seul momentum_24h était pris en compte → incohérence graphique/tendance
            trend_score = 0
            if ema9_above_21:
                trend_score += 2  # EMA9 au-dessus de EMA21 = signal haussier
            if ema21_above_50:
                trend_score += 1  # EMA21 au-dessus de EMA50 = tendance de fond haussière
            
            # 🔧 FIX 01/03: Momentum pondéré (récent prioritaire) au lieu du 24h seul
            # Poids: 3h=40%, 5h=25%, 12h=20%, 24h=15%
            weighted_trend_mom = (momentum_3h * 0.40) + (momentum_5h * 0.25) + (momentum_12h * 0.20) + (momentum_24h * 0.15)
            
            if weighted_trend_mom > 1:
                trend_score += 1  # Momentum pondéré fort positif
            elif weighted_trend_mom > 0.3:
                trend_score += 0.5  # Momentum pondéré léger positif
            
            if ema9_below_21:
                trend_score -= 2
            if ema21_below_50:
                trend_score -= 1
            
            if weighted_trend_mom < -1:
                trend_score -= 1  # Momentum pondéré fort négatif
            elif weighted_trend_mom < -0.3:
                trend_score -= 0.5  # Momentum pondéré léger négatif
            
            # 🔧 FIX 01/03: Pénalité supplémentaire si 12h en baisse significative
            # Ceci aligne la tendance avec ce que montre le graphique du dashboard
            if momentum_12h < -1.0:
                trend_score -= 1  # Baisse significative sur 12h
            elif momentum_12h < -0.5:
                trend_score -= 0.5  # Baisse légère sur 12h
            
            # Détermination de la tendance basée sur le score
            if trend_score >= 2:
                ema_trend = 'BULLISH'
            elif trend_score <= -2:
                ema_trend = 'BEARISH'
            else:
                ema_trend = 'NEUTRAL'
            
            # Distance au prix
            price = closes[-1]
            ema9_dist = ((price - ema9) / ema9) * 100
            ema21_dist = ((price - ema21) / ema21) * 100
            
            return {
                'price': price,
                'ema9': ema9,
                'ema21': ema21,
                'ema50': ema50,
                'ema_trend': ema_trend,
                'ema9_dist': ema9_dist,
                'ema21_dist': ema21_dist,
                'rsi': rsi,
                'momentum_3h': momentum_3h,
                'momentum_5h': momentum_5h,
                'momentum_12h': momentum_12h,  # 🔧 FIX 01/03: momentum 12h (aligné graphique)
                'momentum_24h': momentum_24h,
                'atr_pct': atr_pct,
                'vol_ratio': vol_ratio
            }
            
        except Exception as e:
            logger.error(f"Erreur calcul indicateurs: {e}")
            return {}
    
    def _ema(self, data: np.ndarray, period: int) -> float:
        """Calcule l'EMA."""
        if len(data) < period:
            return float(data[-1])
        
        multiplier = 2 / (period + 1)
        ema = data[0]
        for price in data[1:]:
            ema = (price - ema) * multiplier + ema
        return float(ema)
    
    def _rsi(self, data: np.ndarray, period: int = 14) -> float:
        """Calcule le RSI."""
        if len(data) < period + 1:
            return 50.0
        
        deltas = np.diff(data)
        gains = np.where(deltas > 0, deltas, 0)
        losses = np.where(deltas < 0, -deltas, 0)
        
        avg_gain = np.mean(gains[-period:])
        avg_loss = np.mean(losses[-period:])
        
        if avg_loss == 0:
            return 100.0
        
        rs = avg_gain / avg_loss
        return float(100 - (100 / (1 + rs)))
    
    def _atr(self, highs: np.ndarray, lows: np.ndarray, closes: np.ndarray, period: int = 14) -> float:
        """Calcule l'ATR."""
        if len(closes) < period + 1:
            return float(np.mean(highs - lows))
        
        tr_list = []
        for i in range(1, len(closes)):
            tr = max(
                highs[i] - lows[i],
                abs(highs[i] - closes[i-1]),
                abs(lows[i] - closes[i-1])
            )
            tr_list.append(tr)
        
        return float(np.mean(tr_list[-period:]))
    
    def analyze_btc(self) -> Dict:
        """
        Analyse BTC comme baromètre du marché.
        
        Returns:
            Dict avec les indicateurs BTC et un score de sentiment
        """
        klines = self._get_klines('BTCUSDT', '1h', 50)
        if not klines:
            return {'sentiment_score': 50, 'status': 'NO_DATA'}
        
        indicators = self._calculate_indicators(klines)
        if not indicators:
            return {'sentiment_score': 50, 'status': 'CALC_ERROR'}
        
        # Calcul du score de sentiment BTC (0-100)
        score = 50  # Base neutre
        
        # Tendance EMA (+/- 15 points)
        if indicators['ema_trend'] == 'BULLISH':
            score += 15
        elif indicators['ema_trend'] == 'BEARISH':
            score -= 15
        
        # Momentum 24h - AMÉLIORÉ pour forte sensibilité aux mouvements extrêmes
        momentum = indicators['momentum_24h']
        if momentum >= 5:
            score += 25  # Forte hausse
        elif momentum >= 3:
            score += 20
        elif momentum >= 1:
            score += 10
        elif momentum <= -10:
            score -= 35  # CRASH majeur (-10% ou pire)
        elif momentum <= -5:
            score -= 28  # Chute significative
        elif momentum <= -3:
            score -= 20
        elif momentum <= -1:
            score -= 10
        
        # 🚨 NOUVEAU: Momentum 3h et 5h (court terme) - critique pour détecter les retournements
        momentum_3h = indicators.get('momentum_3h', 0)
        momentum_5h = indicators.get('momentum_5h', 0)
        
        # Momentum 3h - très sensible aux changements récents
        if momentum_3h <= -2:
            score -= 15  # Chute rapide en 3h
        elif momentum_3h <= -1:
            score -= 10
        elif momentum_3h <= -0.5:
            score -= 5
        elif momentum_3h < 0:
            score -= 2  # Tout momentum négatif = pénalité légère
        elif momentum_3h >= 1:
            score += 5  # Hausse récente = bonus
        
        # Momentum 5h - confirme ou infirme la tendance
        if momentum_5h <= -3:
            score -= 15  # Crash en cours
        elif momentum_5h <= -1:
            score -= 8
        elif momentum_5h <= -0.3:
            score -= 3
        elif momentum_5h >= 1:
            score += 5
        
        # 🔧 FIX 01/03: Momentum 12h — aligné avec le graphique du dashboard
        # Le graphique montre la variation 12h mais le score l'ignorait → incohérence
        # Si le graphique est rouge (12h négatif) mais le score dit BULLISH, c'est trompeur
        momentum_12h = indicators.get('momentum_12h', 0)
        if momentum_12h <= -3:
            score -= 15  # Baisse forte sur 12h
        elif momentum_12h <= -1.5:
            score -= 10  # Baisse significative
        elif momentum_12h <= -0.5:
            score -= 5   # Baisse légère
        elif momentum_12h >= 2:
            score += 8   # Forte hausse 12h
        elif momentum_12h >= 1:
            score += 4   # Hausse 12h
        
        # RSI - MODIFIÉ: RSI bas = survente = OPPORTUNITÉ si momentum positif
        rsi = indicators['rsi']
        if 55 <= rsi <= 70:
            score += 10  # Zone bullish idéale
        elif 70 < rsi <= 80:
            score += 3   # Légèrement surachat
        elif rsi > 80:
            score -= 5   # Très surachat, risque de correction
        elif rsi < 20:
            # RSI extrême < 20: Pénalité SEULEMENT si momentum récent négatif
            # Si momentum positif = rebond depuis survente = OPPORTUNITÉ!
            if momentum_3h < 0 and momentum_5h < 0:
                score -= 15  # Réduit de -20 à -15 ET seulement si momentum négatif
            else:
                score += 5   # BONUS si momentum positif = rebond depuis survente!
        elif rsi < 30:
            # RSI 20-30: Légère pénalité sauf si rebond
            if momentum_3h >= 0 or momentum_5h >= 0:
                score += 2   # Petit bonus si rebond
            else:
                score -= 8   # Réduit de -12 à -8
        elif rsi < 40:
            score -= 5   # Réduit de -8 à -5
        
        # Distance EMA9 (+/- 5 points)
        if indicators['ema9_dist'] > 1:
            score += 5  # Prix au-dessus de EMA9
        elif indicators['ema9_dist'] < -1:
            score -= 5  # Prix sous EMA9

        # ═══ FAST SENSOR 5min: Détection quasi-temps réel des retournements ═══
        # Les klines 1h ont un lag de 0–60 min sur le mouvement en cours.
        # Les klines 5min permettent de capter les croisements EMA et reprises
        # en moins de 15 minutes au lieu d'attendre la clôture de la bougie 1h.
        fast_signal_adj = 0
        mom_10min = 0.0
        mom_20min = 0.0
        mom_30min = 0.0
        try:
            klines_5m = self._get_klines_from_api('BTCUSDT', '5m', 12)  # 12×5min = 1h
            if klines_5m and len(klines_5m) >= 6:
                c5 = np.array([float(k[4]) for k in klines_5m])

                # Momentum très court terme (bougie 5min)
                mom_10min = ((c5[-1] - c5[-2]) / c5[-2]) * 100 if len(c5) >= 2 else 0
                mom_20min = ((c5[-1] - c5[-4]) / c5[-4]) * 100 if len(c5) >= 4 else 0
                mom_30min = ((c5[-1] - c5[-6]) / c5[-6]) * 100 if len(c5) >= 6 else 0

                # EMA5 et EMA9 sur 5min (proxy du croisement EMA visible sur chart)
                ema5_now = self._ema(c5,       min(5, len(c5)))
                ema9_now = self._ema(c5,       min(9, len(c5)))
                ema5_prev = self._ema(c5[:-1], min(5, len(c5) - 1)) if len(c5) > 5 else ema5_now
                ema9_prev = self._ema(c5[:-1], min(9, len(c5) - 1)) if len(c5) > 9 else ema9_now

                # Détection croisement EMA (EMA9 croise EMA5, proxy du cross EMA9/21 sur 1min)
                cross_up   = (ema9_prev <= ema5_prev) and (ema9_now > ema5_now)
                cross_down = (ema9_prev >= ema5_prev) and (ema9_now < ema5_now)

                # Détection retournement haussier: on baissait sur 30min, on remonte sur 10min
                was_falling = mom_30min < -0.25
                now_rising  = mom_10min > 0.15
                reversal_up = was_falling and now_rising

                # Détection retournement baissier: on montait, on redécend
                was_rising  = mom_30min > 0.25
                now_falling = mom_10min < -0.15
                reversal_down = was_rising and now_falling

                if reversal_up:
                    fast_signal_adj += 20  # Retournement haussier = signal critique
                    logger.info(f"   🔄 FAST SENSOR: Retournement haussier BTC "
                               f"(30m:{mom_30min:+.2f}% → 10m:{mom_10min:+.2f}%)")
                elif cross_up:
                    fast_signal_adj += 14
                    logger.info(f"   📈 FAST SENSOR: EMA cross UP 5min BTC (mom10m:{mom_10min:+.2f}%)")
                elif mom_10min > 0.30 and mom_20min > 0.15:
                    fast_signal_adj += 10   # Hausse consistante
                elif mom_10min > 0.15:
                    fast_signal_adj += 5
                elif mom_10min > 0.0:
                    fast_signal_adj += 2

                if reversal_down:
                    fast_signal_adj -= 18  # Retournement baissier
                    logger.info(f"   🔄 FAST SENSOR: Retournement baissier BTC "
                               f"(30m:{mom_30min:+.2f}% → 10m:{mom_10min:+.2f}%)")
                elif cross_down:
                    fast_signal_adj -= 12
                    logger.info(f"   📉 FAST SENSOR: EMA cross DOWN 5min BTC (mom10m:{mom_10min:+.2f}%)")
                elif mom_10min < -0.30 and mom_20min < -0.15:
                    fast_signal_adj -= 10
                elif mom_10min < -0.15:
                    fast_signal_adj -= 5

                logger.info(f"   ⚡ 5min BTC: mom10m={mom_10min:+.2f}% "
                           f"mom20m={mom_20min:+.2f}% mom30m={mom_30min:+.2f}%"
                           f" adj={fast_signal_adj:+d}")
        except Exception as e:
            logger.debug(f"Fast sensor 5min indisponible: {e}")

        # Appliquer le fast signal au score BTC
        score = score + fast_signal_adj

        indicators['sentiment_score'] = max(0, min(100, score))
        indicators['fast_signal_adj'] = fast_signal_adj
        indicators['mom_10min'] = round(mom_10min, 3)
        indicators['mom_20min'] = round(mom_20min, 3)
        indicators['mom_30min'] = round(mom_30min, 3)
        indicators['status'] = 'OK'

        return indicators
    
    def analyze_altcoins(self) -> Dict:
        """
        Analyse les top altcoins pour avoir une vue globale du marché.
        
        Returns:
            Dict avec stats agrégées des altcoins
        """
        bullish_count = 0
        bearish_count = 0
        total_momentum = 0
        total_momentum_3h = 0  # NOUVEAU: momentum court terme
        total_momentum_5h = 0  # NOUVEAU: momentum moyen terme
        total_momentum_12h = 0  # 🔧 FIX 01/03: momentum 12h (aligné graphique)
        analyzed = 0
        
        for symbol in TOP_ALTCOINS:
            klines = self._get_klines(symbol, '1h', 30)
            if not klines:
                continue
            
            indicators = self._calculate_indicators(klines)
            if not indicators:
                continue
            
            analyzed += 1
            total_momentum += indicators.get('momentum_24h', 0)
            total_momentum_3h += indicators.get('momentum_3h', 0)
            total_momentum_5h += indicators.get('momentum_5h', 0)
            total_momentum_12h += indicators.get('momentum_12h', 0)  # 🔧 FIX 01/03
            
            if indicators.get('ema_trend') == 'BULLISH':
                bullish_count += 1
            elif indicators.get('ema_trend') == 'BEARISH':
                bearish_count += 1
        
        if analyzed == 0:
            return {'altcoin_score': 50, 'status': 'NO_DATA'}
        
        bullish_pct = (bullish_count / analyzed) * 100
        avg_momentum = total_momentum / analyzed
        avg_momentum_3h = total_momentum_3h / analyzed  # NOUVEAU
        avg_momentum_5h = total_momentum_5h / analyzed  # NOUVEAU
        avg_momentum_12h = total_momentum_12h / analyzed  # 🔧 FIX 01/03: momentum 12h
        
        # Score altcoins (0-100) - AMÉLIORÉ pour mieux refléter le marché
        score = 50
        
        # 🔧 FIX 01/03: Momentum pondéré incluant 12h pour cohérence avec graphiques
        # 3h=35%, 5h=25%, 12h=25%, 24h=15%
        weighted_momentum = (avg_momentum_3h * 0.35) + (avg_momentum_5h * 0.25) + (avg_momentum_12h * 0.25) + (avg_momentum * 0.15)
        
        # % d'altcoins bullish - POINTS AJUSTÉS
        if bullish_pct >= 70:
            score += 25
        elif bullish_pct >= 50:
            score += 15
        elif bullish_pct >= 30:
            score += 5  # Même 30% bullish est positif
        elif bullish_pct <= 10:
            score -= 25  # Moins de 10% bullish = BEAR absolu
        elif bullish_pct <= 15:
            score -= 18  # 10-15% bullish = très baissier
        elif bullish_pct <= 20:
            score -= 12  # Moins de 20% bullish = vraiment baissier
        elif bullish_pct < 30:
            score -= 5
        
        # 🚨 MODIFIÉ: Utiliser momentum PONDÉRÉ au lieu de 24h
        # Ceci permet de détecter les reprises du marché même si le 24h est négatif
        if weighted_momentum >= 5:
            score += 30  # Forte hausse extrême
        elif weighted_momentum >= 3:
            score += 25  # Forte hausse
        elif weighted_momentum >= 2:
            score += 20
        elif weighted_momentum >= 1:
            score += 12
        elif weighted_momentum >= 0.3:
            score += 6  # Légère hausse
        elif weighted_momentum <= -10:
            score -= 40  # CRASH altcoins
        elif weighted_momentum <= -5:
            score -= 32  # Chute majeure
        elif weighted_momentum <= -3:
            score -= 25
        elif weighted_momentum <= -2:
            score -= 20
        elif weighted_momentum <= -1:
            score -= 12
        elif weighted_momentum <= -0.3:
            score -= 6
        
        # 🚨 BONUS/MALUS supplémentaires sur momentum 3h et 5h (en plus du pondéré)
        # Ces bonus s'appliquent EN PLUS du momentum pondéré pour amplifier les signaux récents
        if avg_momentum_3h <= -2:
            score -= 15  # Chute rapide récente
        elif avg_momentum_3h <= -1:
            score -= 8
        elif avg_momentum_3h <= -0.5:
            score -= 4
        elif avg_momentum_3h >= 1.5:
            score += 12  # Forte hausse récente = bonus important
        elif avg_momentum_3h >= 1:
            score += 8  # Hausse récente = bonus
        elif avg_momentum_3h >= 0.5:
            score += 4  # Légère hausse = petit bonus
        
        # 🚨 Momentum moyen terme (5h) - Bonus/malus additionnels
        if avg_momentum_5h <= -3:
            score -= 12
        elif avg_momentum_5h <= -1.5:
            score -= 6
        elif avg_momentum_5h <= -0.5:
            score -= 3
        elif avg_momentum_5h >= 2:
            score += 10  # Forte hausse moyen terme
        elif avg_momentum_5h >= 1:
            score += 5
        elif avg_momentum_5h >= 0.5:
            score += 2
        
        # 🚨 RÈGLE DE COHÉRENCE: Si momentum récents (3h ET 5h) sont négatifs, pénalité
        # MAIS si momentum récents sont POSITIFS, même avec 24h négatif = BONUS (reprise)
        if avg_momentum_3h < 0 and avg_momentum_5h < 0:
            score -= 10  # Tendance baissière confirmée sur plusieurs périodes
        elif avg_momentum_3h > 0 and avg_momentum_5h > 0 and avg_momentum < -2:
            score += 15  # 🚀 REPRISE DÉTECTÉE: momentum récent positif malgré 24h négatif!
        
        return {
            'altcoin_score': max(0, min(100, score)),
            'bullish_count': bullish_count,
            'bearish_count': bearish_count,
            'neutral_count': analyzed - bullish_count - bearish_count,
            'avg_momentum': round(avg_momentum, 2),
            'avg_momentum_3h': round(avg_momentum_3h, 2),
            'avg_momentum_5h': round(avg_momentum_5h, 2),
            'avg_momentum_12h': round(avg_momentum_12h, 2),  # 🔧 FIX 01/03: momentum 12h
            'weighted_momentum': round(weighted_momentum, 2),
            'bullish_pct': round(bullish_pct, 1),
            'analyzed': analyzed,
            'status': 'OK'
        }
    
    def detect_regime(self, force_update: bool = False) -> Tuple[str, Dict]:
        """
        Détecte le régime de marché actuel.
        
        Args:
            force_update: Force la mise à jour même si l'intervalle n'est pas écoulé
            
        Returns:
            Tuple (regime_name, regime_config)
        """
        now = datetime.now()
        
        # Vérifier si une mise à jour est nécessaire
        if not force_update and self.last_update:
            elapsed = (now - self.last_update).total_seconds()
            # 🔴 FIX 04/03: Intervalle adaptatif — FAST MODE si marché en chute
            # En mode normal: update_interval=30s | En chute: 10s pour capter le bear VITE
            _fast_mode_active = (self._fast_mode and time.time() < self._fast_mode_until)
            effective_interval = 10 if _fast_mode_active else self.update_interval
            effective_max_cache = 15 if _fast_mode_active else self.max_cache_time
            if _fast_mode_active:
                logger.debug(f"⚡ FAST MODE actif: interval={effective_interval}s (marché en chute)")
            # FORCER une mise à jour si le cache est trop vieux
            if elapsed > effective_max_cache:
                logger.warning(f"⚠️ Cache régime expiré ({elapsed:.0f}s > {effective_max_cache}s) - Mise à jour forcée")
                force_update = True
            elif elapsed < effective_interval:
                # Cache encore valide
                logger.debug(f"📦 Cache régime valide: {self.current_regime} (âge: {elapsed:.0f}s)")
                return self.current_regime, self.regime_config
            else:
                logger.info(f"🔄 Rafraîchissement régime programmé (âge: {elapsed:.0f}s ≥ {effective_interval}s)")
        
        logger.info("🔍 Analyse du régime de marché...")
        
        # Analyser BTC
        btc_data = self.analyze_btc()
        btc_score = btc_data.get('sentiment_score', 50)
        btc_momentum_24h = btc_data.get('momentum_24h', 0)
        btc_momentum_5h  = btc_data.get('momentum_5h', 0)   # CRITIQUE: momentum moyen terme
        btc_momentum_3h  = btc_data.get('momentum_3h', 0)   # CRITIQUE: momentum court terme
        btc_mom_10min    = btc_data.get('mom_10min', 0)      # 🆕 Fast sensor 5min
        btc_mom_30min    = btc_data.get('mom_30min', 0)      # 🆕 Fast sensor 5min
        btc_fast_adj     = btc_data.get('fast_signal_adj', 0)  # 🆕 Ajustement fast sensor
        
        # � Momentum pondéré incluant fast sensor 5min (priorité maximale au très court terme)
        # Poids: 10min=30%, 3h=30%, 5h=25%, 24h=15%
        # Le fast sensor capte les retournements que les 1h-klines ratent
        btc_weighted_crash = (
            (btc_mom_10min * 0.30) +
            (btc_momentum_3h * 0.30) +
            (btc_momentum_5h * 0.25) +
            (btc_momentum_24h * 0.15)
        )
        
        # 🔴 FIX 04/03: Seuil crash abaissé -4.0 → -2.5 (détection PLUS TÔTE)
        # Avant: marché devait baisser de ~4% pondéré avant déclenchement BEAR
        # Maintenant: -2.5% pondéré suffit — évite les achats pendant la chute
        # Activer FAST MODE pour les 5 prochaines minutes
        if btc_weighted_crash < -2.5:  # Crash pondéré — seuil abaissé (was -4.0)
            logger.warning(f"🚨 CRASH BTC détecté! Momentum pondéré={btc_weighted_crash:.2f}% (3h={btc_momentum_3h:.2f}% 5h={btc_momentum_5h:.2f}%)")
            # Activer FAST MODE: vérifier toutes les 10s pendant 5 min
            self._fast_mode = True
            self._fast_mode_until = time.time() + 300  # 5 min en mode surveillance renforcée
            self._bear_detected_at = time.time()
            new_regime = 'BEAR'
            self.current_regime = new_regime
            self.regime_config = dict(REGIME_CONFIG[new_regime])
            self.last_update = now
            self.market_data = {
                'btc': btc_data,
                'altcoins': {'altcoin_score': 20, 'status': 'CRASH_DETECTED'},
                'global_score': 15,
                'timestamp': now.isoformat()
            }
            logger.error(f"📊 Régime: {self.regime_config['description']} (score=15) [FAST MODE 5min]")
            self._save_state()
            return self.current_regime, self.regime_config
        
        # Analyser les altcoins
        altcoin_data = self.analyze_altcoins()
        altcoin_score = altcoin_data.get('altcoin_score', 50)
        altcoin_weighted_momentum = altcoin_data.get('weighted_momentum', 0)
        altcoin_bullish_pct = altcoin_data.get('bullish_pct', 50)
        
        # 🚨 Détection crash altcoins (utiliser momentum pondéré)
        # 🔴 FIX 04/03: Seuil abaissé -8.0 → -5.0 (détection plus tôt)
        if altcoin_weighted_momentum < -5.0:  # Crash altcoins pondéré (was -8.0)
            logger.warning(f"🚨 CRASH ALTCOINS! Momentum pondéré={altcoin_weighted_momentum:.2f}% Bullish={altcoin_bullish_pct:.1f}%")
            new_regime = 'BEAR'
            self.current_regime = new_regime
            self.regime_config = dict(REGIME_CONFIG[new_regime])
            self.last_update = now
            self.market_data = {
                'btc': btc_data,
                'altcoins': altcoin_data,
                'global_score': 20,
                'timestamp': now.isoformat()
            }
            logger.error(f"📊 Régime: {self.regime_config['description']} (score=20)")
            self._save_state()
            return self.current_regime, self.regime_config
        
        # � Momentum pondéré final incluant fast sensor
        # Poids: 10min=30%, 3h=30%, 5h=25%, 24h=15%
        btc_weighted_momentum = (
            (btc_mom_10min * 0.30) +
            (btc_momentum_3h * 0.30) +
            (btc_momentum_5h * 0.25) +
            (btc_momentum_24h * 0.15)
        )
        
        # Utiliser le momentum pondéré pour la détection (priorité au récent)
        if btc_weighted_momentum < -3.0:  # Forte chute pondérée
            logger.warning(f"🚨 BTC EN CHUTE PONDÉRÉE ({btc_weighted_momentum:.2f}%) - Score altcoins ignoré!")
            logger.warning(f"   → Mom3h={btc_momentum_3h:.2f}% Mom5h={btc_momentum_5h:.2f}% Mom24h={btc_momentum_24h:.2f}%")
            global_score = btc_score * 0.9  # BTC = 90%
        elif btc_weighted_momentum < -1.0:  # Légère baisse pondérée
            logger.warning(f"⚠️ BTC en baisse pondérée ({btc_weighted_momentum:.2f}%) - Pondération BTC augmentée")
            logger.warning(f"   → Mom3h={btc_momentum_3h:.2f}% Mom5h={btc_momentum_5h:.2f}% Mom24h={btc_momentum_24h:.2f}%")
            global_score = (btc_score * 0.8) + (altcoin_score * 0.2)  # BTC pèse 80%
        else:
            # Score global normal (BTC pèse 60%, altcoins 40%)
            global_score = (btc_score * 0.6) + (altcoin_score * 0.4)
            logger.info(f"✅ BTC momentum pondéré positif ({btc_weighted_momentum:.2f}%)")
            logger.info(f"   → Mom3h={btc_momentum_3h:.2f}% Mom5h={btc_momentum_5h:.2f}% Mom24h={btc_momentum_24h:.2f}%")
        
        # Détecter les chutes de score importantes
        score_drop = self.last_score - global_score
        if score_drop > 15:
            logger.warning(f"⚠️ Chute de score: {self.last_score:.1f} → {global_score:.1f} (-{score_drop:.1f})")
        elif global_score - self.last_score > 15:
            logger.info(f"📈 Hausse de score: {self.last_score:.1f} → {global_score:.1f} (+{global_score - self.last_score:.1f})")
        
        # Sauvegarder le score
        self.last_score = global_score
        
        # � Conditions BULL inclut fast sensor: si 10min monte = autoriser BULL même si 3h négatif
        btc_allows_bull = (
            btc_weighted_momentum >= 0 or
            (btc_mom_10min > 0.2 and btc_fast_adj > 0)  # Fast sensor positif = signal de reprise
        )
        altcoins_allow_bull = (altcoin_bullish_pct >= 40 and altcoin_weighted_momentum >= -1.0)

        # 🆕 EARLY_RECOVERY élargi: fonctionne aussi depuis NEUTRAL et BULL_WEAK
        # Avant: seul BEAR/CORRECTION → EARLY_RECOVERY. Mais si on est en NEUTRAL et BTC reparcourt,
        # on ne capte pas la fenêtre positive. Maintenant: retournement 5min suffit.
        is_recovering = (
            (
                btc_momentum_3h > 0.1 and
                btc_momentum_5h > -0.5 and
                altcoin_weighted_momentum > -1.0 and
                self.current_regime in ('BEAR', 'CORRECTION')
            ) or (
                # Fast-path: retournement 5min clair depuis n'importe quel régime
                btc_mom_10min > 0.2 and
                btc_mom_30min < -0.2 and  # Était en baisse avant
                btc_fast_adj >= 14 and    # Fast sensor a détecté signal fort (cross ou reversal)
                self.current_regime in ('BEAR', 'CORRECTION', 'NEUTRAL')
            )
        )
        
        # Détection du régime basée sur le score global ET les conditions de marché
        if global_score >= 75 and btc_allows_bull and altcoins_allow_bull:
            new_regime = 'BULL_STRONG'
        elif global_score >= 60 and btc_allows_bull and altcoins_allow_bull:
            new_regime = 'BULL_WEAK'
        elif global_score >= 60:
            # Score haut mais conditions BULL non remplies
            new_regime = 'NEUTRAL'
            if not btc_allows_bull:
                logger.info(f"   → NEUTRAL: BTC momentum={btc_weighted_momentum:.2f}% négatif")
            elif not altcoins_allow_bull:
                logger.info(f"   → NEUTRAL: Altcoins faibles (Bullish={altcoin_bullish_pct:.1f}%)")
        elif global_score >= 45:
            new_regime = 'NEUTRAL'
        elif is_recovering:
            # 🆕 FIX 02/03: Transition rapide BEAR/CORRECTION → EARLY_RECOVERY
            # Permet de trader pendant la reprise sans attendre que le 24h devienne positif
            new_regime = 'EARLY_RECOVERY'
            logger.warning(f"🔄 REPRISE DÉTECTÉE: BTC Mom3h={btc_momentum_3h:+.2f}% Mom5h={btc_momentum_5h:+.2f}% → EARLY_RECOVERY")
        elif global_score >= 30:
            new_regime = 'CORRECTION'
        else:
            new_regime = 'BEAR'
        
        # Stocker les données
        self.market_data = {
            'btc': btc_data,
            'altcoins': altcoin_data,
            'global_score': round(global_score, 1),
            'btc_rsi': round(btc_data.get('rsi', 50), 1),  # 🔧 FIX 07/02: RSI BTC pour dashboard
            'rsi_warning': 'OVERBOUGHT' if btc_data.get('rsi', 50) > 70 else ('OVERSOLD' if btc_data.get('rsi', 50) < 30 else 'NORMAL'),
            'timestamp': now.isoformat()
        }
        
        # Logger le changement de régime
        if new_regime != self.current_regime:
            logger.warning(f"📊 CHANGEMENT: {self.current_regime} → {new_regime} (score={global_score:.1f})")
            logger.info(f"   BTC: score={btc_score} momentum={btc_weighted_momentum:.2f}%")
            logger.info(f"   Altcoins: score={altcoin_score} bullish={altcoin_bullish_pct:.1f}% momentum={altcoin_weighted_momentum:.2f}%")
            
            # Historique
            self.regime_history.append({
                'from': self.current_regime,
                'to': new_regime,
                'score': global_score,
                'timestamp': now.isoformat()
            })
            self.regime_history = self.regime_history[-50:]
        else:
            logger.info(f"📊 Régime: {new_regime} (score={global_score:.1f}, BTC={btc_weighted_momentum:.2f}%, Alt={altcoin_weighted_momentum:.2f}%)")
        
        self.current_regime = new_regime
        # 🔧 FIX 07/02: Copie du config pour modulation RSI (ne modifie pas l'original)
        self.regime_config = dict(REGIME_CONFIG[new_regime])
        
        # 🚨 MODULATION RSI GRADUELLE: Ajustement progressif selon RSI BTC
        # 🔧 FIX 25/02: BULL_STRONG = aucune réduction RSI (trend fort > RSI lag)
        # Les réductions RSI ne s'appliquent qu'aux autres régimes
        btc_rsi = btc_data.get('rsi', 50)
        is_strong_bull = (new_regime == 'BULL_STRONG')
        original_max = self.regime_config['max_positions']
        
        if is_strong_bull:
            # En BULL_STRONG, on fait confiance au trend — pas de réduction RSI
            logger.info(f"   ✅ BULL_STRONG: RSI {btc_rsi:.1f} — aucune réduction (trend fort)")
        elif btc_rsi > 90:
            # RSI > 90 = Extrême
            self.regime_config['max_positions'] = max(3, int(original_max * 0.5))  # -50%
            self.regime_config['min_score'] = min(90, self.regime_config['min_score'] + 5)
            logger.warning(f"   ⚠️ BTC RSI {btc_rsi:.1f} EXTRÊME → max_positions {original_max}→{self.regime_config['max_positions']}, min_score={self.regime_config['min_score']}")
        elif btc_rsi > 82:
            # RSI 82-90 = Très suracheté
            self.regime_config['max_positions'] = max(3, int(original_max * 0.6))  # -40%
            self.regime_config['min_score'] = min(90, self.regime_config['min_score'] + 5)
            logger.warning(f"   ⚠️ BTC RSI {btc_rsi:.1f} SURACHETÉ → max_positions {original_max}→{self.regime_config['max_positions']}, min_score={self.regime_config['min_score']}")
        elif btc_rsi > 75:
            # RSI 75-82 = Suracheté
            self.regime_config['max_positions'] = max(3, int(original_max * 0.65))  # -35%
            self.regime_config['min_score'] = min(90, self.regime_config['min_score'] + 3)
            logger.warning(f"   ⚠️ BTC RSI {btc_rsi:.1f} suracheté → max_positions {original_max}→{self.regime_config['max_positions']}")
        elif btc_rsi > 70:
            # RSI 70-75 = Suracheté modéré
            self.regime_config['max_positions'] = max(4, int(original_max * 0.80))  # -20%
            logger.info(f"   ⚠️ BTC RSI {btc_rsi:.1f} suracheté modéré → max_positions {original_max}→{self.regime_config['max_positions']}")
        elif btc_rsi < 25:
            # RSI < 25 = Très survendu → Marché en panique, réduire positions
            self.regime_config['max_positions'] = max(3, int(original_max * 0.5))
            self.regime_config['min_score'] = min(90, self.regime_config['min_score'] + 5)
            logger.warning(f"   ⚠️ BTC RSI {btc_rsi:.1f} SURVENDU → max_positions {original_max}→{self.regime_config['max_positions']}, min_score={self.regime_config['min_score']}")
        
        # ⚡ BOOST REPRISE RAPIDE: Fast sensor 5min détecte une reprise en cours
        # Si le capteur 5min confirme un retournement haussier (EMA cross ou reversal fort),
        # booster les positions pour ne pas rater le début de la reprise.
        # Ce boost s'applique APRÈS la modulation RSI (peut partiellement compenser RSI conservateur)
        btc_fast_adj = btc_data.get('fast_signal_adj', 0)
        # 🔴 FIX 04/03: EARLY_RECOVERY plus sensible — seuils abaissés pour anticiper reprise
        # Avant: fast_adj>=10, mom>0.15 → trop strict, rate les petites reprises
        # Après: fast_adj>=8, mom>0.10 → capte les débuts de rebond plus tôt
        if new_regime in ('NEUTRAL', 'EARLY_RECOVERY') and btc_fast_adj >= 8 and btc_mom_10min > 0.10:
            # Fast sensor positif : reversal ou crossover EMA 5min détecté
            boost_pos = 5 if new_regime == 'EARLY_RECOVERY' else 3
            boost_score = 10 if new_regime == 'EARLY_RECOVERY' else 5
            cur_max = self.regime_config['max_positions']
            cur_score = self.regime_config['min_score']
            self.regime_config['max_positions'] = min(13, cur_max + boost_pos)
            self.regime_config['min_score'] = max(55, cur_score - boost_score)
            # Désactiver FAST MODE si on est en reprise (normaliser la fréquence)
            if self._fast_mode and btc_mom_10min > 0.20:
                self._fast_mode = False
                logger.info(f"   ✅ FAST MODE désactivé: reprise confirmée (mom_10m={btc_mom_10min:+.3f}%)")
            logger.info(f"   ⚡ BOOST REPRISE (regime={new_regime}, fast_adj={btc_fast_adj:+.0f}, mom_10m={btc_mom_10min:+.3f}%) → max_pos {cur_max}→{self.regime_config['max_positions']}, min_score {cur_score}→{self.regime_config['min_score']}")
        elif new_regime == 'CORRECTION' and btc_fast_adj >= 10 and btc_mom_10min > 0.15:
            # 🔴 FIX 04/03: CORRECTION + retournement — seuil abaissé (was 14/0.2)
            cur_max = self.regime_config['max_positions']
            cur_score = self.regime_config['min_score']
            self.regime_config['max_positions'] = min(6, cur_max + 3)
            self.regime_config['min_score'] = max(68, cur_score - 10)
            logger.info(f"   ⚡ BOOST CORRECTION→REPRISE (fast_adj={btc_fast_adj:+.0f}, mom_10m={btc_mom_10min:+.3f}%) → max_pos {cur_max}→{self.regime_config['max_positions']}, min_score {cur_score}→{self.regime_config['min_score']}")
        
        self.last_update = now
        self._save_state()
        logger.info(f"   → Max positions: {self.regime_config['max_positions']}")
        logger.info(f"   → Score IA min: {self.regime_config['min_score']}")
        
        return self.current_regime, self.regime_config
    
    def get_current_regime(self) -> Tuple[str, Dict]:
        """Retourne le régime actuel sans forcer de mise à jour."""
        return self.current_regime, self.regime_config
    
    def get_max_positions(self) -> int:
        """Retourne le nombre max de positions selon le régime."""
        return self.regime_config['max_positions']
    
    def get_min_score(self) -> int:
        """Retourne le score IA minimum selon le régime."""
        return self.regime_config['min_score']
    
    def get_position_size_multiplier(self) -> float:
        """Retourne le multiplicateur de taille de position."""
        return self.regime_config['position_size_pct'] / 100.0
    
    def get_tp_multiplier(self) -> float:
        """Retourne le multiplicateur de Take Profit."""
        return self.regime_config['take_profit_multiplier']
    
    def should_allow_new_position(self, current_positions: int, signal_score: int) -> Tuple[bool, str]:
        """
        Vérifie si une nouvelle position est autorisée selon le régime.
        
        Args:
            current_positions: Nombre de positions actuelles
            signal_score: Score du signal d'achat
            
        Returns:
            Tuple (allowed, reason)
        """
        # Mettre à jour le régime si nécessaire
        self.detect_regime()
        
        max_pos = self.regime_config['max_positions']
        min_score = self.regime_config['min_score']
        
        if current_positions >= max_pos:
            return False, f"Régime {self.current_regime}: Max positions atteint ({current_positions}/{max_pos})"
        
        if signal_score < min_score:
            return False, f"Régime {self.current_regime}: Score insuffisant ({signal_score} < {min_score})"
        
        return True, f"Régime {self.current_regime}: Position autorisée"
    
    def get_status(self, force_fresh: bool = True) -> Dict:
        """
        Retourne le statut complet pour le dashboard.
        
        Args:
            force_fresh: Si True, force une mise à jour des données avant de retourner le statut
        
        Returns:
            Dict avec toutes les infos du régime
        """
        # Toujours mettre à jour le régime pour avoir des données fraîches
        if force_fresh:
            try:
                self.detect_regime(force_update=True)
            except Exception as e:
                logger.warning(f"Erreur mise à jour régime dans get_status: {e}")
        
        return {
            'regime': self.current_regime,
            'config': self.regime_config,
            'market_data': self.market_data,
            'last_update': self.last_update.isoformat() if self.last_update else None,
            'history': self.regime_history[-10:]  # 10 derniers changements
        }


# Instance globale
_market_regime_instance = None

def reset_market_regime_detector():
    """Réinitialise le singleton (utile pour les tests ou redémarrage)."""
    global _market_regime_instance
    _market_regime_instance = None

def get_market_regime_detector(binance_client=None, force_new=False) -> MarketRegimeDetector:
    """
    Retourne l'instance singleton du détecteur de régime.
    
    Args:
        binance_client: Client Binance (optionnel, pour initialisation)
        force_new: Force la création d'une nouvelle instance
        
    Returns:
        MarketRegimeDetector instance
    """
    global _market_regime_instance
    
    if force_new:
        _market_regime_instance = None
    
    if _market_regime_instance is None:
        _market_regime_instance = MarketRegimeDetector(binance_client)
    elif binance_client and not _market_regime_instance.client:
        _market_regime_instance.set_client(binance_client)
    
    return _market_regime_instance


# Test standalone
if __name__ == '__main__':
    logging.basicConfig(level=logging.INFO)
    
    print("=" * 60)
    print("TEST MARKET REGIME DETECTOR")
    print("=" * 60)
    
    # Test sans client Binance (données simulées)
    detector = MarketRegimeDetector()
    
    print("\nConfiguration des régimes:")
    for regime, config in REGIME_CONFIG.items():
        print(f"  {regime}:")
        print(f"    - Max positions: {config['max_positions']}")
        print(f"    - Min score: {config['min_score']}")
        print(f"    - Position size: {config['position_size_pct']}%")
    
    print("\n✅ Module prêt à être intégré")
