# source: https://raw.githubusercontent.com/ertugrul59/FreqtradeStra/daad6db4b7b386d32b713cf00c8c740c38e524e4/user_data/strategies/BollingerEMAMFIStrategy.py
from freqtrade.strategy import IStrategy, IntParameter
from pandas import DataFrame
import talib.abstract as ta
import numpy as np


class Github_ertugrul59_FreqtradeStra__BollingerEMAMFIStrategy__20240908_175806(IStrategy):
    INTERFACE_VERSION = 3

    # Leverage settings (applied in trade size and stoploss)
    leverage_value = 2

    # Risk management - Stoploss and ROI
    stoploss = -0.05
    # minimal_roi = {"0": 1}

    timeframe = '1h'
    inf_timeframe = '1d'

    # EMA and Bollinger Bands Parameters
    ema_length = 500
    bb_length = 50
    bb_dev = 2.0  # Ensure this is a float
    mfi_length = 14
    rsi_length = 14
    divergence_window = 5  # Number of candles to look back for divergence
    min_rr_ratio = 3.0  # Minimum 1:2 risk/reward ratio

    can_short = True

    # Hyperopt parameters
    buy_mfi_value = IntParameter(10,
                                 50,
                                 default=30,
                                 space="buy",
                                 optimize=True)
    sell_mfi_value = IntParameter(50,
                                  90,
                                  default=70,
                                  space="sell",
                                  optimize=True)

    plot_config = {
        'main_plot': {
            'ema_500': {
                'color': 'red'
            },
            'bb_upperband': {
                'color': 'green'
            },
            'bb_lowerband': {
                'color': 'green'
            },
            'bb_middleband': {
                'color': 'blue'
            },
        },
        'subplots': {
            "MFI": {
                'mfi': {
                    'color': 'purple'
                },
            },
            "RSI": {
                'rsi': {
                    'color': 'orange'
                },
            },
        }
    }

    def populate_indicators(self, dataframe: DataFrame,
                            metadata: dict) -> DataFrame:
        # EMA 500
        dataframe['ema_500'] = ta.EMA(dataframe['close'],
                                      timeperiod=self.ema_length)

        # Bollinger Bands
        dataframe['bb_upperband'], dataframe['bb_middleband'], dataframe[
            'bb_lowerband'] = ta.BBANDS(
                dataframe['close'],
                timeperiod=self.bb_length,
                # nbdevup=self.bb_dev,
                # nbdevdn=self.bb_dev
            )

        # MFI (Money Flow Index)
        dataframe['mfi'] = ta.MFI(dataframe, timeperiod=self.mfi_length)

        # RSI
        dataframe['rsi'] = ta.RSI(dataframe['close'],
                                  timeperiod=self.rsi_length)

        # MFI Reversal for long positions (upward reversal)
        dataframe['mfi_reversal_long'] = np.where(
            (dataframe['mfi'].shift(1) < dataframe['mfi']) &
            (dataframe['mfi'].shift(2) > dataframe['mfi'].shift(1)) &
            (dataframe['mfi'] < self.buy_mfi_value.value), 1, 0)

        # MFI Reversal for short positions (downward reversal)
        dataframe['mfi_reversal_short'] = np.where(
            (dataframe['mfi'].shift(1) > dataframe['mfi']) &
            (dataframe['mfi'].shift(2) < dataframe['mfi'].shift(1)) &
            (dataframe['mfi'] > self.sell_mfi_value.value), 1, 0)

        # Bullish RSI Divergence
        dataframe['bullish_divergence'] = self.detect_bullish_divergence(
            dataframe)

        # Bearish RSI Divergence
        dataframe['bearish_divergence'] = self.detect_bearish_divergence(
            dataframe)

        return dataframe

    def detect_bullish_divergence(self, dataframe: DataFrame) -> np.array:
        divergence = np.zeros(len(dataframe))
        for i in range(self.divergence_window, len(dataframe)):
            if (dataframe['close'].iloc[i] < dataframe['close'].iloc[i-self.divergence_window]) and \
               (dataframe['mfi'].iloc[i] > dataframe['mfi'].iloc[i-self.divergence_window]):
                divergence[i] = 1
        return divergence

    def detect_bearish_divergence(self, dataframe: DataFrame) -> np.array:
        divergence = np.zeros(len(dataframe))
        for i in range(self.divergence_window, len(dataframe)):
            if (dataframe['close'].iloc[i] > dataframe['close'].iloc[i-self.divergence_window]) and \
               (dataframe['mfi'].iloc[i] < dataframe['mfi'].iloc[i-self.divergence_window]):
                divergence[i] = 1
        return divergence

    def risk_reward_check(self, entry_price: float, stop_price: float,
                          target_price: float) -> bool:
        risk = abs(entry_price - stop_price)
        reward = abs(target_price - entry_price)
        return reward / risk >= self.min_rr_ratio

    def populate_entry_trend(self, dataframe: DataFrame,
                             metadata: dict) -> DataFrame:
        dataframe.loc[
            ((dataframe['close'] > dataframe['ema_500']) &
             (dataframe['close'].shift(1) < dataframe['bb_lowerband']) &
             (dataframe['close'] > dataframe['bb_lowerband']) &
             # (dataframe['mfi_reversal_long'] == 1) &
             #  (dataframe['bullish_divergence'] == 1) &
             (self.
              risk_reward_check(dataframe['close'], dataframe['bb_lowerband'],
                                dataframe['bb_middleband']))),
            'enter_long'] = 1

        dataframe.loc[
            ((dataframe['close'] < dataframe['ema_500']) &
             (dataframe['close'].shift(1) > dataframe['bb_upperband']) &
             (dataframe['close'] < dataframe['bb_upperband']) &
             # (dataframe['mfi_reversal_short'] == 1) &
             #  (dataframe['bearish_divergence'] == 1) &
             (self.
              risk_reward_check(dataframe['close'], dataframe['bb_upperband'],
                                dataframe['bb_middleband']))),
            'enter_short'] = 1

        return dataframe

    def populate_exit_trend(self, dataframe: DataFrame,
                            metadata: dict) -> DataFrame:
        # Your exit logic here if needed
        return dataframe

    def custom_exit(self, pair: str, trade: 'Trade', current_time: 'datetime',
                    current_rate: float, current_profit: float, **kwargs):
        dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe)
        last_candle = dataframe.iloc[-1]

        if trade.trade_direction == 'long':
            if current_rate >= last_candle['bb_middleband']:
                return 'long_exit_signal'
        elif trade.trade_direction == 'short':
            if current_rate <= last_candle['bb_middleband']:
                return 'short_exit_signal'

        return None  # Continue holding

    def leverage(self, *args, **kwargs) -> float:
        return self.leverage_value
