#!/usr/bin/env python3
"""
Stratégie de Scalping Probabiliste/Mathématique
Approche basée sur statistiques et probabilités au lieu d'indicateurs techniques
"""

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

logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)


@dataclass
class ProbabilisticSignal:
    """Signal basé sur probabilités"""
    symbol: str
    side: str  # 'LONG' ou 'SHORT'
    entry_price: float
    stop_loss: float
    take_profit: float
    leverage: int
    confidence: float  # 0-100
    probability: float  # Probabilité de réussite (0-1)
    expected_value: float  # Espérance mathématique
    reason: str


class ProbabilisticScalpingStrategy:
    """
    Stratégie de scalping basée sur des approches mathématiques/probabilistes

    Principes:
    1. MEAN REVERSION: Prix dévie de sa moyenne → probabilité de retour
    2. Z-SCORE: Mesure combien le prix est "extrême" statistiquement
    3. DISTRIBUTION: Analyse des mouvements historiques pour prédire probabilités
    4. EXPECTED VALUE: Trade seulement si EV > 0
    5. KELLY CRITERION: Taille de position optimale

    Au lieu de "RSI < 30 = oversold", on calcule:
    "Le prix est à 2.5 écarts-types sous la moyenne → 98% de probabilité de retour"
    """

    def __init__(
        self,
        leverage: int = 1000,
        sl_pct: float = 0.03,  # 0.03% SL
        tp_pct: float = 0.10,  # 0.10% TP
        lookback_window: int = 100,  # Fenêtre pour calculs statistiques
        z_threshold: float = 2.0,  # Seuil Z-score pour signal
        min_probability: float = 0.65,  # Probabilité minimale (65%)
        min_ev: float = 0.30  # Expected Value minimum (30%)
    ):
        self.leverage = leverage
        self.sl_pct = sl_pct / 100
        self.tp_pct = tp_pct / 100

        # Paramètres statistiques
        self.lookback_window = lookback_window
        self.z_threshold = z_threshold
        self.min_probability = min_probability
        self.min_ev = min_ev

    def analyze(self, symbol: str, df: pd.DataFrame) -> Optional[ProbabilisticSignal]:
        """
        Analyse probabiliste du marché

        Returns:
            ProbabilisticSignal ou None
        """
        if len(df) < self.lookback_window + 20:
            return None

        # Prix actuel
        current_price = df['close'].iloc[-1]

        # ========== 1. CALCUL DES STATISTIQUES ==========
        stats = self._calculate_statistics(df)

        # ========== 2. Z-SCORE (distance de la moyenne) ==========
        z_score = (current_price - stats['mean']) / stats['std']

        # ========== 3. DÉTECTION MEAN REVERSION ==========
        # Si le prix est trop loin de la moyenne, il va probablement revenir

        # LONG: Prix trop bas → probabilité de remontée
        if z_score < -self.z_threshold:
            signal = self._create_long_signal(
                symbol, current_price, z_score, stats
            )
            if signal and self._validate_signal(signal):
                return signal

        # SHORT: Prix trop haut → probabilité de baisse
        elif z_score > self.z_threshold:
            signal = self._create_short_signal(
                symbol, current_price, z_score, stats
            )
            if signal and self._validate_signal(signal):
                return signal

        return None

    def _calculate_statistics(self, df: pd.DataFrame) -> Dict:
        """
        Calcule statistiques sur la fenêtre lookback
        """
        window = df['close'].iloc[-self.lookback_window:].values

        # Statistiques de base
        mean = np.mean(window)
        std = np.std(window)
        median = np.median(window)

        # Rendements (log returns pour propriétés gaussiennes)
        returns = np.diff(np.log(window))
        returns_mean = np.mean(returns)
        returns_std = np.std(returns)

        # Percentiles de distribution
        percentile_25 = np.percentile(window, 25)
        percentile_75 = np.percentile(window, 75)
        percentile_5 = np.percentile(window, 5)
        percentile_95 = np.percentile(window, 95)

        # Coefficient de variation (volatilité relative)
        cv = (std / mean) if mean != 0 else 0

        # Skewness et Kurtosis (forme de la distribution)
        skewness = scipy_stats.skew(window)
        kurtosis = scipy_stats.kurtosis(window)

        return {
            'mean': mean,
            'std': std,
            'median': median,
            'returns_mean': returns_mean,
            'returns_std': returns_std,
            'percentile_25': percentile_25,
            'percentile_75': percentile_75,
            'percentile_5': percentile_5,
            'percentile_95': percentile_95,
            'cv': cv,
            'skewness': skewness,
            'kurtosis': kurtosis
        }

    def _create_long_signal(
        self,
        symbol: str,
        current_price: float,
        z_score: float,
        stats: Dict
    ) -> Optional[ProbabilisticSignal]:
        """
        Crée signal LONG basé sur probabilités
        """
        # Prix trop bas (z_score négatif élevé) → Mean reversion LONG

        # 1. Calculer probabilité de remontée (basée sur distribution normale)
        # P(prix remonte) = CDF du Z-score (plus c'est bas, plus ça va remonter)
        probability = scipy_stats.norm.cdf(abs(z_score))  # Probabilité de retour vers moyenne

        # 2. SL/TP
        sl_price = current_price * (1 - self.sl_pct)
        tp_price = current_price * (1 + self.tp_pct)

        # 3. Expected Value (espérance mathématique)
        # EV = (Probabilité de gain × Gain) - (Probabilité de perte × Perte)
        win_amount = self.tp_pct * self.leverage  # En pourcentage du capital
        loss_amount = self.sl_pct * self.leverage

        expected_value = (probability * win_amount) - ((1 - probability) * loss_amount)

        # 4. Confidence (basée sur plusieurs facteurs)
        confidence = self._calculate_confidence(z_score, probability, stats)

        # 5. Raison détaillée
        reason = (
            f"Mean Reversion LONG | "
            f"Z-score: {z_score:.2f} (prix {abs(z_score):.1f}σ sous moyenne) | "
            f"P(remontée): {probability*100:.1f}% | "
            f"EV: {expected_value:.1f}%"
        )

        return ProbabilisticSignal(
            symbol=symbol,
            side='LONG',
            entry_price=current_price,
            stop_loss=sl_price,
            take_profit=tp_price,
            leverage=self.leverage,
            confidence=confidence,
            probability=probability,
            expected_value=expected_value,
            reason=reason
        )

    def _create_short_signal(
        self,
        symbol: str,
        current_price: float,
        z_score: float,
        stats: Dict
    ) -> Optional[ProbabilisticSignal]:
        """
        Crée signal SHORT basé sur probabilités
        """
        # Prix trop haut (z_score positif élevé) → Mean reversion SHORT

        # 1. Probabilité de baisse
        probability = scipy_stats.norm.cdf(z_score)

        # 2. SL/TP
        sl_price = current_price * (1 + self.sl_pct)
        tp_price = current_price * (1 - self.tp_pct)

        # 3. Expected Value
        win_amount = self.tp_pct * self.leverage
        loss_amount = self.sl_pct * self.leverage

        expected_value = (probability * win_amount) - ((1 - probability) * loss_amount)

        # 4. Confidence
        confidence = self._calculate_confidence(z_score, probability, stats)

        # 5. Raison
        reason = (
            f"Mean Reversion SHORT | "
            f"Z-score: {z_score:.2f} (prix {z_score:.1f}σ au-dessus moyenne) | "
            f"P(baisse): {probability*100:.1f}% | "
            f"EV: {expected_value:.1f}%"
        )

        return ProbabilisticSignal(
            symbol=symbol,
            side='SHORT',
            entry_price=current_price,
            stop_loss=sl_price,
            take_profit=tp_price,
            leverage=self.leverage,
            confidence=confidence,
            probability=probability,
            expected_value=expected_value,
            reason=reason
        )

    def _calculate_confidence(
        self,
        z_score: float,
        probability: float,
        stats: Dict
    ) -> float:
        """
        Calcule score de confiance basé sur plusieurs facteurs statistiques
        """
        confidence = 0

        # 1. Z-score absolu (plus c'est extrême, mieux c'est)
        if abs(z_score) > 3.0:
            confidence += 40
        elif abs(z_score) > 2.5:
            confidence += 30
        elif abs(z_score) > 2.0:
            confidence += 20

        # 2. Probabilité élevée
        if probability > 0.75:
            confidence += 30
        elif probability > 0.70:
            confidence += 25
        elif probability > 0.65:
            confidence += 20

        # 3. Volatilité (coefficient de variation)
        # Volatilité modérée est mieux (ni trop faible ni trop forte)
        cv = stats['cv']
        if 0.01 < cv < 0.05:
            confidence += 15
        elif 0.005 < cv < 0.08:
            confidence += 10

        # 4. Distribution proche de la normale (kurtosis faible)
        if abs(stats['kurtosis']) < 3:
            confidence += 15

        return min(confidence, 100)

    def _validate_signal(self, signal: ProbabilisticSignal) -> bool:
        """
        Valide le signal selon critères probabilistes
        """
        # 1. Probabilité minimale
        if signal.probability < self.min_probability:
            return False

        # 2. Expected Value positif et significatif
        if signal.expected_value < self.min_ev:
            return False

        # 3. Confidence minimale
        if signal.confidence < 50:
            return False

        return True

    def get_optimizable_params(self) -> Dict:
        """Paramètres optimisables"""
        return {
            'leverage': (500, 1000),
            'sl_pct': (0.02, 0.05),
            'tp_pct': (0.05, 0.20),
            'lookback_window': (50, 200),
            'z_threshold': (1.5, 3.0),
            'min_probability': (0.60, 0.75),
            'min_ev': (0.20, 0.50)
        }


if __name__ == '__main__':
    print("=" * 80)
    print("🎯 STRATÉGIE DE SCALPING PROBABILISTE/MATHÉMATIQUE")
    print("=" * 80)
    print()
    print("📊 APPROCHE MATHÉMATIQUE:")
    print("  ✓ Mean Reversion (retour à la moyenne)")
    print("  ✓ Z-Score (distance statistique en écarts-types)")
    print("  ✓ Distribution Gaussienne (probabilités)")
    print("  ✓ Expected Value (espérance mathématique)")
    print("  ✓ Confidence Score (multi-facteurs statistiques)")
    print()
    print("🔢 EXEMPLE DE SIGNAL:")
    print("  • Prix actuel: $100,000")
    print("  • Moyenne 100 bougies: $100,500")
    print("  • Écart-type: $200")
    print("  • Z-score: -2.5 (prix à 2.5σ sous moyenne)")
    print("  • Probabilité de remontée: 99.4%")
    print("  • EV avec 1000x: +80%")
    print("  → Signal LONG validé")
    print()
    print("⚡ AVANTAGES vs INDICATEURS TECHNIQUES:")
    print("  • Quantifie VRAIMENT la probabilité (pas juste 'oversold')")
    print("  • Expected Value = décision rationnelle")
    print("  • S'adapte automatiquement à la volatilité")
    print("  • Pas de paramètres arbitraires (14 périodes RSI?)")
    print()

    strategy = ProbabilisticScalpingStrategy(
        leverage=1000,
        sl_pct=0.03,
        tp_pct=0.10,
        z_threshold=2.0,
        min_probability=0.65
    )

    print(f"Paramètres optimisables: {len(strategy.get_optimizable_params())}")
    print()
    print("=" * 80)
