#!/usr/bin/env python3
"""
STRATÉGIE BULL - Zones Haussières
Optimisée pour tendances haussières fortes

Logique:
1. Détecte tendance haussière (prix > MAs croissantes)
2. Entre sur pullbacks vers supports
3. Breakouts de résistances avec volume
4. Stop loss large (laisser respirer)
5. Take profit agressif sur momentum
"""

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


@dataclass
class BullSignal:
    """Signal de trading haussier"""
    side: str  # Toujours 'LONG' pour bull
    entry_price: float
    stop_loss: float
    take_profit: float
    confidence: float  # 0-100
    reason: str
    leverage: int


class BullDetector:
    """Détecteur de conditions haussières"""

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

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

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

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

        # 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]

        above_ma20 = 1 if price > ma20 else 0
        above_ma50 = 1 if price > ma50 else 0
        ma_ascending = 1 if ma20 > ma50 else 0

        ma_score = (above_ma20 + above_ma50 + ma_ascending) * 33.33

        # 3. Higher highs / higher 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

        hh_score = 50 if recent_high > prev_high else 0
        hl_score = 50 if recent_low > prev_low else 0

        structure_score = hh_score + hl_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_bull_market(df: pd.DataFrame) -> bool:
        """
        Détermine si on est en marché haussier
        """
        strength = BullDetector.calculate_trend_strength(df)
        return strength >= 60.0  # Threshold haussier


class BullStrategy:
    """
    Stratégie pour zones haussières
    LONG uniquement, sur pullbacks et breakouts
    """

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

        # Paramètres optimisables
        self.params = {
            'min_trend_strength': 60.0,  # Force tendance minimum
            'rsi_pullback_max': 45,  # RSI max pour pullback entry
            'rsi_breakout_min': 55,  # RSI min pour breakout entry
            'volume_spike_threshold': 1.5,  # Volume pour breakouts
            'sl_atr_multiplier': 2.0,  # SL large (laisser respirer)
            'tp_atr_multiplier': 4.0,  # TP agressif
            'trailing_pct': 0.10,  # 10% trailing stop
            'min_confidence': 65,
        }

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

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

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

        # 1. Vérifier conditions haussières
        trend_strength = BullDetector.calculate_trend_strength(df)

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

        # 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) PULLBACK ENTRY (RSI bas, prix proche MA20)
        if current_rsi < self.params['rsi_pullback_max']:
            distance_to_ma20 = abs(close - ma20) / ma20

            if distance_to_ma20 < 0.015:  # < 1.5% de la MA20
                reason = f"PULLBACK à MA20 (RSI {current_rsi:.0f})"
                confidence = 60 + (self.params['rsi_pullback_max'] - current_rsi)

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

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

        # B) BREAKOUT ENTRY (prix casse résistance + volume)
        if not signal and current_rsi > self.params['rsi_breakout_min']:
            # Résistance = plus haut des 20 dernières périodes
            resistance = df['high'].iloc[-20:-1].max()

            if close > resistance and volume_ratio >= self.params['volume_spike_threshold']:
                reason = f"BREAKOUT résistance ${resistance:.2f} (Vol x{volume_ratio:.1f})"
                confidence = 70 + min(20, (current_rsi - 50) / 2)

                # Entry au breakout
                entry = close
                sl = resistance * 0.98  # SL juste sous résistance cassée
                tp = entry + (atr * self.params['tp_atr_multiplier'])

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

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

                entry = close
                sl = ma20 * 0.98  # SL sous MA20
                tp = entry + (atr * self.params['tp_atr_multiplier'])

                signal = BullSignal(
                    side='LONG',
                    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_pullback_max': (35, 50),
            'rsi_breakout_min': (50, 65),
            'volume_spike_threshold': (1.2, 2.5),
            'sl_atr_multiplier': (1.5, 3.0),
            'tp_atr_multiplier': (3.0, 6.0),
            'trailing_pct': (0.05, 0.20),
            'min_confidence': (60, 75),
        }


if __name__ == '__main__':
    # Test rapide
    print("🐂 Bull Strategy - Test")
    print("=" * 80)
    print("Stratégie spécialisée pour zones HAUSSIÈRES")
    print()
    print("Entrées:")
    print("  • PULLBACK à support (MA20)")
    print("  • BREAKOUT de résistance + volume")
    print("  • MOMENTUM continuation")
    print()
    print("Paramètres par défaut:")
    strategy = BullStrategy()
    for k, v in strategy.params.items():
        print(f"  {k}: {v}")
