#!/usr/bin/env python3
"""
Stratégie Probabiliste FINALE - Optimisée pour BNB 1m
Corrections: Calibration ajustée pour générer des signaux valides
"""

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


@dataclass
class FinalProbabilisticSignal:
    """Signal probabiliste final"""
    symbol: str
    side: str
    entry_price: float
    stop_loss: float
    take_profit: float
    leverage: int
    confidence: float
    probability: float  # Calibrée réaliste
    expected_value: float
    reason: str
    trend_context: str  # 'WITH_TREND', 'NEUTRAL', 'AGAINST_TREND'


class FinalProbabilisticStrategy:
    """
    Stratégie Probabiliste Finale Optimisée

    CALIBRATION FIXÉE:
    - calibration_factor augmenté à 0.80 (au lieu de 0.60)
    - min_probability réduit à 0.50 (réaliste pour 50%+ win rate)
    - Z-threshold ajusté à 1.8 (plus de signaux)
    """

    def __init__(
        self,
        leverage: int = 100,
        sl_pct: float = 0.05,  # 0.05% SL
        tp_pct: float = 0.10,  # 0.10% TP (ratio 2:1)
        lookback_window: int = 100,
        z_threshold: float = 1.6,  # Réduit encore à 1.6
        min_probability: float = 0.48,  # Réduit à 0.48
        min_ev: float = 0.03,  # FIXÉ: 3% au lieu de 15%!
        trend_filter_enabled: bool = True,
        calibration_factor: float = 0.80  # Augmenté de 0.60 à 0.80
    ):
        self.leverage = leverage
        self.sl_pct = sl_pct / 100
        self.tp_pct = tp_pct / 100
        self.lookback_window = lookback_window
        self.z_threshold = z_threshold
        self.min_probability = min_probability
        self.min_ev = min_ev
        self.trend_filter_enabled = trend_filter_enabled
        self.calibration_factor = calibration_factor

    def analyze(self, symbol: str, df: pd.DataFrame) -> Optional[FinalProbabilisticSignal]:
        """Analyse probabiliste optimisée"""
        if len(df) < self.lookback_window + 50:
            return None

        current_price = df['close'].iloc[-1]

        # Statistiques
        stats = self._calculate_statistics(df)

        # Détection tendance
        trend = self._detect_trend(df) if self.trend_filter_enabled else 'NEUTRAL'

        # Z-score
        z_score = (current_price - stats['mean']) / stats['std']

        # LONG signal
        if z_score < -self.z_threshold:
            # Bloquer si tendance baissière forte
            if self.trend_filter_enabled and trend == 'STRONG_DOWN':
                return None

            signal = self._create_long_signal(symbol, current_price, z_score, stats, trend)
            if signal and self._validate_signal(signal):
                return signal

        # SHORT signal
        elif z_score > self.z_threshold:
            # Bloquer si tendance haussière forte
            if self.trend_filter_enabled and trend == 'STRONG_UP':
                return None

            signal = self._create_short_signal(symbol, current_price, z_score, stats, trend)
            if signal and self._validate_signal(signal):
                return signal

        return None

    def _detect_trend(self, df: pd.DataFrame) -> str:
        """Détecte tendance forte"""
        ema_20 = df['close'].ewm(span=20, adjust=False).mean().iloc[-1]
        ema_50 = df['close'].ewm(span=50, adjust=False).mean().iloc[-1]

        recent_prices = df['close'].iloc[-20:].values
        trend_pct = ((recent_prices[-1] - recent_prices[0]) / recent_prices[0]) * 100

        if ema_20 > ema_50 * 1.002 and trend_pct > 0.5:
            return 'STRONG_UP'
        elif ema_20 < ema_50 * 0.998 and trend_pct < -0.5:
            return 'STRONG_DOWN'
        else:
            return 'NEUTRAL'

    def _calculate_statistics(self, df: pd.DataFrame) -> Dict:
        """Calcule stats"""
        window = df['close'].iloc[-self.lookback_window:].values

        return {
            'mean': np.mean(window),
            'std': np.std(window),
            'cv': np.std(window) / np.mean(window),
            'kurtosis': scipy_stats.kurtosis(window)
        }

    def _create_long_signal(
        self, symbol: str, current_price: float, z_score: float, stats: Dict, trend: str
    ) -> Optional[FinalProbabilisticSignal]:
        """Crée signal LONG"""

        # Probabilité brute
        raw_prob = scipy_stats.norm.cdf(abs(z_score))

        # Calibration améliorée
        calibrated_prob = raw_prob * self.calibration_factor

        # Bonus tendance
        if trend == 'STRONG_UP':
            calibrated_prob = min(calibrated_prob * 1.15, 0.75)
        elif trend == 'NEUTRAL':
            calibrated_prob = min(calibrated_prob * 1.05, 0.70)

        # Limite réaliste 45-75%
        calibrated_prob = max(0.45, min(calibrated_prob, 0.75))

        # SL/TP adaptatifs
        sl_pct = self._adjust_sl_for_volatility(stats['cv'])
        tp_pct = sl_pct * 2.0

        sl_price = current_price * (1 - sl_pct)
        tp_price = current_price * (1 + tp_pct)

        # Expected Value
        win_amount = tp_pct * self.leverage
        loss_amount = sl_pct * self.leverage
        ev = (calibrated_prob * win_amount) - ((1 - calibrated_prob) * loss_amount)

        # Confidence
        confidence = self._calculate_confidence(z_score, calibrated_prob, stats, trend)

        trend_ctx = 'WITH_TREND' if trend == 'STRONG_UP' else ('NEUTRAL' if trend == 'NEUTRAL' else 'AGAINST_TREND')

        reason = (
            f"MR LONG | Z:{z_score:.2f} ({abs(z_score):.1f}σ) | "
            f"P:{calibrated_prob*100:.0f}% | EV:{ev:.0f}% | {trend_ctx}"
        )

        return FinalProbabilisticSignal(
            symbol=symbol,
            side='LONG',
            entry_price=current_price,
            stop_loss=sl_price,
            take_profit=tp_price,
            leverage=self.leverage,
            confidence=confidence,
            probability=calibrated_prob,
            expected_value=ev,
            reason=reason,
            trend_context=trend_ctx
        )

    def _create_short_signal(
        self, symbol: str, current_price: float, z_score: float, stats: Dict, trend: str
    ) -> Optional[FinalProbabilisticSignal]:
        """Crée signal SHORT"""

        raw_prob = scipy_stats.norm.cdf(z_score)
        calibrated_prob = raw_prob * self.calibration_factor

        if trend == 'STRONG_DOWN':
            calibrated_prob = min(calibrated_prob * 1.15, 0.75)
        elif trend == 'NEUTRAL':
            calibrated_prob = min(calibrated_prob * 1.05, 0.70)

        calibrated_prob = max(0.45, min(calibrated_prob, 0.75))

        sl_pct = self._adjust_sl_for_volatility(stats['cv'])
        tp_pct = sl_pct * 2.0

        sl_price = current_price * (1 + sl_pct)
        tp_price = current_price * (1 - tp_pct)

        win_amount = tp_pct * self.leverage
        loss_amount = sl_pct * self.leverage
        ev = (calibrated_prob * win_amount) - ((1 - calibrated_prob) * loss_amount)

        confidence = self._calculate_confidence(z_score, calibrated_prob, stats, trend)

        trend_ctx = 'WITH_TREND' if trend == 'STRONG_DOWN' else ('NEUTRAL' if trend == 'NEUTRAL' else 'AGAINST_TREND')

        reason = (
            f"MR SHORT | Z:{z_score:.2f} ({z_score:.1f}σ) | "
            f"P:{calibrated_prob*100:.0f}% | EV:{ev:.0f}% | {trend_ctx}"
        )

        return FinalProbabilisticSignal(
            symbol=symbol,
            side='SHORT',
            entry_price=current_price,
            stop_loss=sl_price,
            take_profit=tp_price,
            leverage=self.leverage,
            confidence=confidence,
            probability=calibrated_prob,
            expected_value=ev,
            reason=reason,
            trend_context=trend_ctx
        )

    def _adjust_sl_for_volatility(self, cv: float) -> float:
        """Ajuste SL selon volatilité"""
        base_sl = self.sl_pct

        if cv > 0.04:
            return base_sl * 1.5
        elif cv > 0.03:
            return base_sl * 1.2
        elif cv < 0.015:
            return base_sl * 0.8
        else:
            return base_sl

    def _calculate_confidence(self, z_score: float, probability: float, stats: Dict, trend: str) -> float:
        """Calcule confidence"""
        confidence = 0

        # Z-score
        if abs(z_score) > 2.5:
            confidence += 30
        elif abs(z_score) > 2.0:
            confidence += 25
        elif abs(z_score) > 1.8:
            confidence += 20

        # Probabilité
        if probability > 0.65:
            confidence += 25
        elif probability > 0.60:
            confidence += 20
        elif probability > 0.55:
            confidence += 15
        elif probability >= 0.50:
            confidence += 10

        # Volatilité modérée
        if 0.015 < stats['cv'] < 0.04:
            confidence += 15

        # Distribution normale
        if abs(stats['kurtosis']) < 3:
            confidence += 10

        # Tendance
        if trend == 'STRONG_UP' or trend == 'STRONG_DOWN':
            confidence += 10
        elif trend == 'NEUTRAL':
            confidence += 5

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

    def _validate_signal(self, signal: FinalProbabilisticSignal) -> bool:
        """Valide signal"""
        if signal.probability < self.min_probability:
            return False

        if signal.expected_value < self.min_ev:
            return False

        if signal.confidence < 40:
            return False

        return True

    def get_optimizable_params(self) -> Dict:
        """Paramètres optimisables"""
        return {
            'leverage': (50, 150),
            'sl_pct': (0.04, 0.08),
            'tp_pct': (0.08, 0.16),
            'lookback_window': (50, 150),
            'z_threshold': (1.5, 2.2),
            'min_probability': (0.45, 0.60),
            'min_ev': (0.10, 0.25),
            'calibration_factor': (0.75, 0.90)
        }


if __name__ == '__main__':
    print("🎯 STRATÉGIE PROBABILISTE FINALE - OPTIMISÉE")
    print()
    print("✅ CALIBRATION FIXÉE:")
    print("   • calibration_factor: 0.80 (au lieu de 0.60)")
    print("   • min_probability: 0.50 (réaliste)")
    print("   • Z-threshold: 1.8 (plus de signaux)")
    print()
    print("🎲 PROBABILITÉS ATTENDUES:")
    print("   • Théorique: 98-99%")
    print("   • Calibrée: 50-70% (réaliste)")
    print("   • Win rate cible: 55-65%")
