#!/usr/bin/env python3
"""
STRATÉGIE V10 RANGE ADAPTED
Adaptation de la stratégie inverse_strategy_v10 pour le backtest_adaptive

Logique:
1. Détecte les conditions RANGE (ATR < 0.3%, touches S/R)
2. Trade les rebonds sur support/résistance
3. RSI extrêmes pour confirmer épuisement
4. Bollinger Bands pour zones d'entrée
"""

import pandas as pd
import numpy as np
from typing import Optional


class V10RangeDetector:
    """Détecteur RANGE de la stratégie V10"""

    @staticmethod
    def calculate_atr(high, low, close, period=14):
        """Calcule ATR"""
        tr1 = high - low
        tr2 = abs(high - close.shift())
        tr3 = abs(low - close.shift())
        tr = pd.concat([tr1, tr2, tr3], axis=1).max(axis=1)
        atr = tr.rolling(window=period).mean()
        return atr

    @staticmethod
    def is_range_market(df: pd.DataFrame, lookback: int = 100) -> bool:
        """
        Détecte si marché en RANGE

        Critères:
        - ATR < 0.3% du prix
        - Touches répétées de S/R (2+ de chaque)
        """
        if len(df) < lookback:
            return False

        window = df.iloc[-lookback:]
        closes = window['close'].values

        # 1. Volatilité via ATR
        atr = V10RangeDetector.calculate_atr(
            window['high'], window['low'], window['close'], period=14
        )
        avg_atr = atr.iloc[-20:].mean()
        price = closes[-1]
        atr_pct = (avg_atr / price) * 100

        # Range si ATR < threshold (par défaut 0.3%, assouppli à 0.35%)
        threshold = 0.3  # Valeur par défaut
        if atr_pct >= threshold:
            return False

        # 2. Support/Resistance touches
        period_high = closes.max()
        period_low = closes.min()
        range_size = period_high - period_low

        # Compter touches dans top/bottom 5%
        top_zone = period_high - range_size * 0.05
        bottom_zone = period_low + range_size * 0.05

        touches_top = sum(1 for c in closes[-50:] if c >= top_zone)
        touches_bottom = sum(1 for c in closes[-50:] if c <= bottom_zone)

        # Range confirmé si touches répétées
        return touches_top >= 2 and touches_bottom >= 2


class V10RangeStrategy:
    """
    Stratégie V10 RANGE adaptée

    Trade les rebonds sur support/résistance en conditions de range
    Utilise RSI + Bollinger Bands pour confirmer épuisement
    """

    def __init__(self, leverage: int = 150):
        self.leverage = leverage
        self.params = {
            'atr_period': 14,
            'rsi_period': 14,
            'rsi_oversold': 30,  # OPTIMISÉ: Épuisement vendeur réel
            'rsi_overbought': 70,  # OPTIMISÉ: Épuisement acheteur réel
            'bb_period': 20,
            'bb_std': 2.0,
            'min_confidence': 75,  # OPTIMISÉ: Haute qualité seulement
            'sl_atr_multiplier': 1.0,  # OPTIMISÉ: 1.2 → 1.0 (SL plus serré, meilleur PF)
            'tp_atr_multiplier': 4.0,  # OPTIMISÉ: 3.0 → 4.0 (TP plus large, +71% PnL, PF 3.85)
            'lookback_range': 100,
            'range_atr_threshold': 0.30,  # OPTIMISÉ: Vraie consolidation uniquement
        }

    def _is_range_market_flexible(self, df: pd.DataFrame) -> bool:
        """Version flexible de la détection range avec seuil configurable"""
        lookback = self.params['lookback_range']
        if len(df) < lookback:
            return False

        window = df.iloc[-lookback:]
        closes = window['close'].values

        # ATR avec seuil flexible
        atr = V10RangeDetector.calculate_atr(
            window['high'], window['low'], window['close'], period=14
        )
        avg_atr = atr.iloc[-20:].mean()
        price = closes[-1]
        atr_pct = (avg_atr / price) * 100

        # Range si ATR < threshold configurable
        if atr_pct >= self.params['range_atr_threshold']:
            return False

        # Support/Resistance touches
        period_high = closes.max()
        period_low = closes.min()
        range_size = period_high - period_low

        top_zone = period_high - range_size * 0.05
        bottom_zone = period_low + range_size * 0.05

        touches_top = sum(1 for c in closes[-50:] if c >= top_zone)
        touches_bottom = sum(1 for c in closes[-50:] if c <= bottom_zone)

        # Range confirmé
        return touches_top >= 2 and touches_bottom >= 2

    def calculate_rsi(self, prices: pd.Series, period: int = 14) -> pd.Series:
        """Calcule RSI"""
        delta = prices.diff()
        gain = (delta.where(delta > 0, 0)).rolling(window=period).mean()
        loss = (-delta.where(delta < 0, 0)).rolling(window=period).mean()
        rs = gain / loss
        rsi = 100 - (100 / (1 + rs))
        return rsi

    def calculate_bollinger_bands(self, prices: pd.Series, period: int = 20, std: float = 2.0):
        """Calcule Bollinger Bands"""
        sma = prices.rolling(window=period).mean()
        rolling_std = prices.rolling(window=period).std()
        upper = sma + (rolling_std * std)
        lower = sma - (rolling_std * std)
        return upper, sma, lower

    def analyze(self, symbol: str, df: pd.DataFrame) -> Optional[dict]:
        """
        Analyse et génère un signal RANGE

        Returns:
            Signal dict ou None
        """
        if len(df) < 100:
            return None

        # 1. Vérifier si marché en RANGE (avec seuil assouppli)
        # On passe le seuil ATR via une vérification custom
        is_range = self._is_range_market_flexible(df)

        if not is_range:
            return None

        # 2. Calculer indicateurs
        closes = df['close']
        rsi = self.calculate_rsi(closes, self.params['rsi_period'])
        bb_upper, bb_middle, bb_lower = self.calculate_bollinger_bands(
            closes, self.params['bb_period'], self.params['bb_std']
        )
        atr = V10RangeDetector.calculate_atr(
            df['high'], df['low'], df['close'], self.params['atr_period']
        )

        current_price = closes.iloc[-1]
        current_rsi = rsi.iloc[-1]
        current_bb_upper = bb_upper.iloc[-1]
        current_bb_lower = bb_lower.iloc[-1]
        current_atr = atr.iloc[-1]

        # 3. Trouver support/résistance
        lookback = self.params['lookback_range']
        period_high = closes.iloc[-lookback:].max()
        period_low = closes.iloc[-lookback:].min()
        range_size = period_high - period_low

        # Zones de rebond (5% du range)
        resistance_zone = period_high - range_size * 0.05
        support_zone = period_low + range_size * 0.05

        # 4. Générer signal
        signal = None
        confidence = 0

        # LONG sur support (épuisement vendeur)
        if current_price <= support_zone:
            # Conditions:
            # - RSI oversold
            # - Prix touche BB lower
            # - En zone de support

            confluences = []

            if current_rsi <= self.params['rsi_oversold']:
                confluences.append('RSI_OVERSOLD')
                confidence += 35

            if current_price <= current_bb_lower:
                confluences.append('BB_LOWER_TOUCH')
                confidence += 30

            # Distance au support
            dist_to_support = abs(current_price - period_low) / period_low
            if dist_to_support < 0.005:  # < 0.5%
                confluences.append('NEAR_SUPPORT')
                confidence += 20

            # Volume spike (si disponible)
            if 'volume' in df.columns:
                avg_vol = df['volume'].iloc[-20:-1].mean()
                current_vol = df['volume'].iloc[-1]
                if current_vol > avg_vol * 1.5:
                    confluences.append('VOLUME_SPIKE')
                    confidence += 15

            if confidence >= self.params['min_confidence']:
                signal = {
                    'side': 'LONG',
                    'confidence': min(confidence, 100),
                    'confluences': confluences
                }

        # SHORT sur résistance (épuisement acheteur)
        elif current_price >= resistance_zone:
            # Conditions:
            # - RSI overbought
            # - Prix touche BB upper
            # - En zone de résistance

            confluences = []

            if current_rsi >= self.params['rsi_overbought']:
                confluences.append('RSI_OVERBOUGHT')
                confidence += 35

            if current_price >= current_bb_upper:
                confluences.append('BB_UPPER_TOUCH')
                confidence += 30

            # Distance à la résistance
            dist_to_resistance = abs(current_price - period_high) / period_high
            if dist_to_resistance < 0.005:  # < 0.5%
                confluences.append('NEAR_RESISTANCE')
                confidence += 20

            # Volume spike
            if 'volume' in df.columns:
                avg_vol = df['volume'].iloc[-20:-1].mean()
                current_vol = df['volume'].iloc[-1]
                if current_vol > avg_vol * 1.5:
                    confluences.append('VOLUME_SPIKE')
                    confidence += 15

            if confidence >= self.params['min_confidence']:
                signal = {
                    'side': 'SHORT',
                    'confidence': min(confidence, 100),
                    'confluences': confluences
                }

        if not signal:
            return None

        # 5. Calculer SL/TP basés sur ATR
        sl_distance = current_atr * self.params['sl_atr_multiplier']
        tp_distance = current_atr * self.params['tp_atr_multiplier']

        if signal['side'] == 'LONG':
            stop_loss = current_price - sl_distance
            take_profit = current_price + tp_distance
        else:  # SHORT
            stop_loss = current_price + sl_distance
            take_profit = current_price - tp_distance

        # 6. Créer objet signal complet
        return {
            'symbol': symbol,
            'side': signal['side'],
            'entry_price': current_price,
            'stop_loss': stop_loss,
            'take_profit': take_profit,
            'confidence': signal['confidence'],
            'score': signal['confidence'],  # Alias pour compatibilité
            'strategy_used': 'V10_RANGE_REVERSAL',
            'strategy': 'V10_RANGE_REVERSAL',  # Alias pour compatibilité
            'confluences': signal['confluences'],
            'atr': current_atr,
            'rsi': current_rsi,
            'is_range_market': True,
            'leverage': self.leverage,  # Leverage de la stratégie
            'risk_pct': 0.02,  # 2% risk par défaut
        }


if __name__ == '__main__':
    import logging
    logging.basicConfig(level=logging.INFO)

    # Test rapide
    from apollo_client import ApolloDegenClient

    client = ApolloDegenClient()
    df = client.get_klines('BTCUSDT', '5m', limit=500)

    strategy = V10RangeStrategy(leverage=150)
    signal = strategy.analyze('BTCUSDT', df)

    if signal:
        print(f"\n✅ SIGNAL V10 RANGE:")
        print(f"   Side: {signal['side']}")
        print(f"   Confidence: {signal['confidence']}%")
        print(f"   Entry: ${signal['entry_price']:,.2f}")
        print(f"   SL: ${signal['stop_loss']:,.2f}")
        print(f"   TP: ${signal['take_profit']:,.2f}")
        print(f"   Confluences: {', '.join(signal['confluences'])}")
        print(f"   RSI: {signal['rsi']:.1f}")
    else:
        print("\n❌ Pas de signal (marché pas en range ou pas d'épuisement)")
