#!/usr/bin/env python3
"""
STRATÉGIE BEAR - Zones Baissières
Optimisée pour tendances baissières fortes

Logique:
1. Détecte tendance baissière (prix < MAs décroissantes)
2. Entre SHORT sur rallies techniques (dead cat bounces)
3. Short les rejets de résistances
4. Stop loss serré (rallies baissiers = courts)
5. Take profit rapide (tendance reprend vite)
"""

import pandas as pd
import numpy as np
from typing import Optional, Dict
from dataclasses import dataclass


@dataclass
class BearSignal:
    """Signal de trading baissier"""
    side: str  # Toujours 'SHORT' pour bear
    entry_price: float
    stop_loss: float
    take_profit: float
    confidence: float  # 0-100
    reason: str
    leverage: int


class BearDetector:
    """Détecteur de conditions baissières"""

    @staticmethod
    def calculate_trend_strength(df: pd.DataFrame, lookback: int = 50) -> float:
        """
        Calcule force de la tendance baissière

        Returns:
            Score 0-100 (plus élevé = plus baissier)
        """
        if len(df) < lookback:
            return 0.0

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

        # 1. Pente linéaire négative
        x = np.arange(len(closes))
        slope = np.polyfit(x, closes, 1)[0]
        slope_score = min(100, max(0, -slope / closes[-1] * 10000))  # Pente négative = score positif

        # 2. Prix vs MAs
        ma20 = window['close'].rolling(20).mean().iloc[-1]
        ma50 = window['close'].rolling(50).mean().iloc[-1] if len(window) >= 50 else ma20
        price = closes[-1]

        below_ma20 = 1 if price < ma20 else 0
        below_ma50 = 1 if price < ma50 else 0
        ma_descending = 1 if ma20 < ma50 else 0

        ma_score = (below_ma20 + below_ma50 + ma_descending) * 33.33

        # 3. Lower highs / lower lows
        highs = window['high'].values
        lows = window['low'].values

        recent_high = max(highs[-10:])
        prev_high = max(highs[-30:-10]) if len(highs) >= 30 else recent_high

        recent_low = min(lows[-10:])
        prev_low = min(lows[-30:-10]) if len(lows) >= 30 else recent_low

        lh_score = 50 if recent_high < prev_high else 0  # Lower high
        ll_score = 50 if recent_low < prev_low else 0  # Lower low

        structure_score = lh_score + ll_score

        # Score final (moyenne pondérée)
        total = (slope_score * 0.4 + ma_score * 0.3 + structure_score * 0.3)

        return min(100, max(0, total))

    @staticmethod
    def is_bear_market(df: pd.DataFrame) -> bool:
        """
        Détermine si on est en marché baissier
        """
        strength = BearDetector.calculate_trend_strength(df)
        return strength >= 60.0  # Threshold baissier


class BearStrategy:
    """
    Stratégie pour zones baissières
    SHORT uniquement, sur rallies et rejets
    """

    def __init__(self, leverage: int = 200):
        self.leverage = leverage

        # Paramètres optimisables
        self.params = {
            'min_trend_strength': 60.0,  # Force tendance minimum
            'rsi_rally_min': 55,  # RSI min pour short rally
            'rsi_overbought': 70,  # RSI surachat pour short
            'volume_spike_threshold': 1.3,  # Volume pour rejets
            'sl_atr_multiplier': 1.5,  # SL serré (rallies courts)
            'tp_atr_multiplier': 3.5,  # TP rapide
            'trailing_pct': 0.08,  # 8% trailing stop
            'min_confidence': 65,
        }

    def analyze(self, symbol: str, df: pd.DataFrame) -> Optional[BearSignal]:
        """
        Analyse et génère signal SHORT si conditions baissières

        Args:
            symbol: Symbole du token
            df: DataFrame avec OHLCV

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

        # 1. Vérifier conditions baissières
        trend_strength = BearDetector.calculate_trend_strength(df)

        if trend_strength < self.params['min_trend_strength']:
            return None  # Pas assez baissier

        # 2. Calculer indicateurs
        close = df['close'].iloc[-1]

        # RSI
        delta = df['close'].diff()
        gain = delta.where(delta > 0, 0).rolling(14).mean()
        loss = -delta.where(delta < 0, 0).rolling(14).mean()
        rs = gain / loss
        rsi = 100 - (100 / (1 + rs))
        current_rsi = rsi.iloc[-1]

        # ATR
        tr1 = df['high'] - df['low']
        tr2 = abs(df['high'] - df['close'].shift())
        tr3 = abs(df['low'] - df['close'].shift())
        tr = pd.concat([tr1, tr2, tr3], axis=1).max(axis=1)
        atr = tr.rolling(14).mean().iloc[-1]

        # Volume
        avg_volume = df['volume'].rolling(20).mean().iloc[-1]
        current_volume = df['volume'].iloc[-1]
        volume_ratio = current_volume / avg_volume if avg_volume > 0 else 1.0

        # MAs
        ma20 = df['close'].rolling(20).mean().iloc[-1]
        ma50 = df['close'].rolling(50).mean().iloc[-1]

        # 3. Détecter type d'entrée
        signal = None
        reason = ""
        confidence = 50

        # A) RALLY SHORT (RSI élevé, prix proche résistance MA20)
        if current_rsi > self.params['rsi_rally_min']:
            distance_to_ma20 = abs(close - ma20) / ma20

            if close < ma20 and distance_to_ma20 < 0.015:  # Prix proche mais sous MA20
                reason = f"RALLY SHORT vers MA20 (RSI {current_rsi:.0f})"
                confidence = 60 + (current_rsi - 50)

                # Entry
                entry = close
                sl = entry + (atr * self.params['sl_atr_multiplier'])
                tp = entry - (atr * self.params['tp_atr_multiplier'])

                signal = BearSignal(
                    side='SHORT',
                    entry_price=entry,
                    stop_loss=sl,
                    take_profit=tp,
                    confidence=min(100, confidence),
                    reason=reason,
                    leverage=self.leverage
                )

        # B) RESISTANCE REJECTION (prix rejeté + volume)
        if not signal and current_rsi > self.params['rsi_overbought']:
            # Résistance = plus haut des 10 dernières périodes
            recent_resistance = df['high'].iloc[-10:-1].max()

            # Détection rejet: bougie actuelle proche résistance mais fermeture basse
            current_high = df['high'].iloc[-1]
            distance_to_resistance = abs(current_high - recent_resistance) / recent_resistance

            if distance_to_resistance < 0.01 and close < df['open'].iloc[-1]:  # Bougie rouge
                if volume_ratio >= self.params['volume_spike_threshold']:
                    reason = f"REJET résistance ${recent_resistance:.2f} (Vol x{volume_ratio:.1f})"
                    confidence = 70 + min(20, (current_rsi - 70) / 2)

                    # Entry au rejet
                    entry = close
                    sl = recent_resistance * 1.01  # SL juste au-dessus résistance
                    tp = entry - (atr * self.params['tp_atr_multiplier'])

                    signal = BearSignal(
                        side='SHORT',
                        entry_price=entry,
                        stop_loss=sl,
                        take_profit=tp,
                        confidence=min(100, confidence),
                        reason=reason,
                        leverage=self.leverage
                    )

        # C) BREAKDOWN CONTINUATION (trending fort baissier + RSI moyen)
        if not signal and 30 < current_rsi < 50 and trend_strength > 75:
            if close < ma20 < ma50:  # Structure baissière claire
                reason = f"BREAKDOWN continuation (Trend {trend_strength:.0f})"
                confidence = 65 + (trend_strength - 75) / 2

                entry = close
                sl = ma20 * 1.02  # SL au-dessus MA20
                tp = entry - (atr * self.params['tp_atr_multiplier'])

                signal = BearSignal(
                    side='SHORT',
                    entry_price=entry,
                    stop_loss=sl,
                    take_profit=tp,
                    confidence=min(100, confidence),
                    reason=reason,
                    leverage=self.leverage
                )

        # 4. Filtrer par confidence minimum
        if signal and signal.confidence < self.params['min_confidence']:
            return None

        return signal

    def get_optimizable_params(self) -> Dict:
        """Retourne les paramètres optimisables"""
        return {
            'min_trend_strength': (50, 80),
            'rsi_rally_min': (50, 65),
            'rsi_overbought': (65, 80),
            'volume_spike_threshold': (1.2, 2.0),
            'sl_atr_multiplier': (1.0, 2.5),
            'tp_atr_multiplier': (2.5, 5.0),
            'trailing_pct': (0.05, 0.15),
            'min_confidence': (60, 75),
        }


if __name__ == '__main__':
    # Test rapide
    print("🐻 Bear Strategy - Test")
    print("=" * 80)
    print("Stratégie spécialisée pour zones BAISSIÈRES")
    print()
    print("Entrées:")
    print("  • RALLY SHORT (dead cat bounce vers MA20)")
    print("  • REJET de résistance + volume")
    print("  • BREAKDOWN continuation")
    print()
    print("Paramètres par défaut:")
    strategy = BearStrategy()
    for k, v in strategy.params.items():
        print(f"  {k}: {v}")
