# source: https://raw.githubusercontent.com/djienne/YOUTUBE_STRATEGIES_FREQTRADE/f5f4339d51624b1105fdf00e58d848223b269f38/Strategies/Claude.py
# Importations nécessaires
import numpy as np
import pandas as pd
import pandas_ta as ta
from functools import reduce 
from freqtrade.strategy import IStrategy, DecimalParameter, IntParameter, CategoricalParameter

# Définition de la classe de stratégie
class Github_djienne_YOUTUBE_STRATEGIES_FREQTRADE__Claude__20241027_081933(IStrategy):
    """
    Stratégie optimisée pour Freqtrade
    Utilise plusieurs indicateurs avec des paramètres optimisables via hyperopt
    Timeframe: 1h
    """
    
    # Paramètres généraux de la stratégie
    timeframe = '1h'
    stoploss = -0.32  # Stop loss à -10%
    trailing_stop = True
    trailing_stop_positive = 0.314
    trailing_stop_positive_offset = 0.395
    trailing_only_offset_is_reached = True

    # Optional order type mapping.
    order_types = {
        "entry": "market",
        "exit": "market",
        "stoploss": "market",
        "stoploss_on_exchange": False,
    }

    # Optional order time in force.
    order_time_in_force = {"entry": "GTC", "exit": "GTC"}
    
    # Paramètres optimisables pour les indicateurs
    rsi_period = IntParameter(10, 30, default=11, space='buy', optimize=True)
    rsi_oversold = IntParameter(10, 40, default=26, space='buy', optimize=True)
    rsi_overbought = IntParameter(60, 90, default=80, space='sell', optimize=True)
    
    macd_fast = IntParameter(5, 20, default=20, space='buy', optimize=True)
    macd_slow = IntParameter(21, 40, default=20, space='buy', optimize=True)
    macd_signal = IntParameter(5, 15, default=5, space='buy', optimize=True)
    
    ema_short = IntParameter(5, 20, default=11, space='buy', optimize=True)
    ema_long = IntParameter(20, 40, default=30, space='buy', optimize=True)
    
    atr_period = IntParameter(10, 30, default=29, space='buy', optimize=True)
    atr_multiplier = DecimalParameter(1.0, 5.0, default=1.1, decimals=1, space='buy', optimize=True)
    
    # Paramètre pour choisir la méthode d'entrée
    entry_method = CategoricalParameter(['rsi', 'macd', 'ema_cross'], default='macd', space='buy', optimize=True)

    def populate_indicators(self, dataframe: pd.DataFrame, metadata: dict) -> pd.DataFrame:
        """
        Calcule les indicateurs pour la stratégie
        """
        # RSI
        dataframe['rsi'] = ta.rsi(dataframe['close'], length=self.rsi_period.value)
        
        # MACD
        macd = ta.macd(dataframe['close'], fast=self.macd_fast.value, slow=self.macd_slow.value, signal=self.macd_signal.value)
        dataframe['macd'] = macd['MACD_' + str(self.macd_fast.value) + '_' + str(self.macd_slow.value) + '_' + str(self.macd_signal.value)]
        dataframe['macdsignal'] = macd['MACDs_' + str(self.macd_fast.value) + '_' + str(self.macd_slow.value) + '_' + str(self.macd_signal.value)]
        
        # EMA
        dataframe['ema_short'] = ta.ema(dataframe['close'], length=self.ema_short.value)
        dataframe['ema_long'] = ta.ema(dataframe['close'], length=self.ema_long.value)
        
        # ATR pour le trailing stop
        dataframe['atr'] = ta.atr(dataframe['high'], dataframe['low'], dataframe['close'], length=self.atr_period.value)
        
        return dataframe

    def populate_entry_trend(self, dataframe: pd.DataFrame, metadata: dict) -> pd.DataFrame:
        """
        Définit les conditions d'entrée en position
        """
        conditions = []
        
        if self.entry_method.value == 'rsi':
            conditions.append(dataframe['rsi'] > self.rsi_overbought.value)
        elif self.entry_method.value == 'macd':
            conditions.append((dataframe['macd'] > dataframe['macdsignal']) & 
                              (dataframe['macd'].shift(1) <= dataframe['macdsignal'].shift(1)))
        elif self.entry_method.value == 'ema_cross':
            conditions.append((dataframe['ema_short'] > dataframe['ema_long']) & 
                              (dataframe['ema_short'].shift(1) <= dataframe['ema_long'].shift(1)))
        
        conditions.append(dataframe['volume'] > 0)  # S'assurer qu'il y a du volume
        
        if conditions:
            dataframe.loc[
                reduce(lambda x, y: x & y, conditions),
                'enter_long'] = 1
        
        return dataframe

    def populate_exit_trend(self, dataframe: pd.DataFrame, metadata: dict) -> pd.DataFrame:
        """
        Définit les conditions de sortie de position
        """
        conditions = []
        
        conditions.append(dataframe['rsi'] < self.rsi_oversold.value)
        conditions.append(dataframe['volume'] > 0)  # S'assurer qu'il y a du volume
        
        if conditions:
            dataframe.loc[
                reduce(lambda x, y: x & y, conditions),
                'exit_long'] = 1
        
        return dataframe

    def custom_stoploss(self, pair: str, trade: 'Trade', current_time: 'datetime',
                        current_rate: float, current_profit: float, **kwargs) -> float:
        """
        Implémente un stop loss personnalisé basé sur l'ATR
        """
        dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe)
        last_candle = dataframe.iloc[-1]
        
        # Calcul du stop loss basé sur l'ATR
        atr_stop = last_candle['close'] - (last_candle['atr'] * self.atr_multiplier.value)
        
        # Convertir en pourcentage
        atr_stop_percent = (atr_stop - trade.open_rate) / trade.open_rate
        
        # Retourner le maximum entre le stop loss par défaut et le stop loss ATR
        return max(self.stoploss, atr_stop_percent)

# Note: La fonction reduce est importée de functools, assurez-vous de l'ajouter aux importations si nécessaire