# source: https://raw.githubusercontent.com/RoboticAutomations/freqtradestrategies/52c4bd40414645f079a980cf4486dc7af4061efb/AIRA%20-%20open%20code%20v2/strategies/elliott-wave/EI4_t4c0s_QA3.py
# directory_url: https://github.com/RoboticAutomations/freqtradestrategies/blob/main/AIRA - open code v2/strategies/elliott-wave/
# User: RoboticAutomations
# Repository: freqtradestrategies
# --------------------import logging
from functools import reduce
from datetime import datetime
from typing import Optional
from typing import Dict, List
import pandas as pd
import numpy as np
import math
import technical.indicators as ftt
import talib.abstract as ta
from pandas import DataFrame
from technical import qtpylib
from freqtrade.persistence import Trade
from datetime import datetime, timedelta

from freqtrade.strategy import CategoricalParameter, DecimalParameter, IntParameter, IStrategy, merge_informative_pair


logger = logging.getLogger(__name__)

def EWO(dataframe, ema_length=5, ema2_length=3):
    df = dataframe.copy()
    ema1 = ta.EMA(df, timeperiod=ema_length)
    ema2 = ta.EMA(df, timeperiod=ema2_length)
    emadif = (ema1 - ema2) / df['close'] * 100
    return emadif


class Github_RoboticAutomations_freqtradestrategies__EI4_t4c0s_QA3__20260915_233254(IStrategy):

    """
          ______   __          __              __    __   ______   __    __        __     __    __             ______            
     /      \ /  |       _/  |            /  |  /  | /      \ /  \  /  |      /  |   /  |  /  |           /      \           
    /$$$$$$  |$$ |____  / $$ |    _______ $$ | /$$/ /$$$$$$  |$$  \ $$ |     _$$ |_  $$ |  $$ |  _______ /$$$$$$  |  _______ 
    $$ |  $$/ $$      \ $$$$ |   /       |$$ |/$$/  $$ ___$$ |$$$  \$$ |    / $$   | $$ |__$$ | /       |$$$  \$$ | /       |
    $$ |      $$$$$$$  |  $$ |  /$$$$$$$/ $$  $$<     /   $$< $$$$  $$ |    $$$$$$/  $$    $$ |/$$$$$$$/ $$$$  $$ |/$$$$$$$/ 
    $$ |   __ $$ |  $$ |  $$ |  $$ |      $$$$$  \   _$$$$$  |$$ $$ $$ |      $$ | __$$$$$$$$ |$$ |      $$ $$ $$ |$$      \ 
    $$ \__/  |$$ |  $$ | _$$ |_ $$ \_____ $$ |$$  \ /  \__$$ |$$ |$$$$ |      $$ |/  |     $$ |$$ \_____ $$ \$$$$ | $$$$$$  |
    $$    $$/ $$ |  $$ |/ $$   |$$       |$$ | $$  |$$    $$/ $$ | $$$ |______$$  $$/      $$ |$$       |$$   $$$/ /     $$/ 
     $$$$$$/  $$/   $$/ $$$$$$/  $$$$$$$/ $$/   $$/  $$$$$$/  $$/   $$//      |$$$$/       $$/  $$$$$$$/  $$$$$$/  $$$$$$$/  
                                                                       $$$$$$/                                               
Here be stonks
1. freqtrade hyperopt --hyperopt-loss SharpeHyperOptLoss --strategy RL_kdog_spot --freqaimodel ReinforcementLearnerSpot --spaces roi stoploss --timerange "$(date --date='-1 week' '+%Y%m%d')"-"$(date '+%Y%m%d')" -e 1000
2. freqtrade trade --logfile ./logs --freqaimodel ReinforcementLearnerSpot --strategy RL_kdog_spot
    """

    minimal_roi = {"0": 0.1, "2400": -1}

    plot_config = {
        "main_plot": {},
        "subplots": {
            "prediction": {"prediction": {"color": "blue"}},
            "target_roi": {
                "target_roi": {"color": "brown"},
            },
            "do_predict": {
                "do_predict": {"color": "brown"},
            },
            "&-action": {
                "&-action": {"color": "green"},
            },
        },
    }
    timeframe = '5m'
    # position_adjustment_enable = True
    # max_entry_position_adjustment = 2
    # max_dca_multiplier = 4
    process_only_new_candles = True
    stoploss = -0.3
    use_exit_signal = True
    startup_candle_count: int = 300
    can_short = False
    # Specific variables
    linear_roi_offset = DecimalParameter(0.00, 0.02, default=0.005, space="sell", optimize=False, load=True)
    max_roi_time_long = IntParameter(0, 800, default=400, space="sell", optimize=False, load=True)

    # Buy hyperspace params:
    buy_params = {
        "base_nb_candles_buy": 12,
        "rsi_buy": 58,
        "low_offset": 0.987,
        "lambo2_ema_14_factor": 0.981,
        "lambo2_enabled": True,
        "lambo2_rsi_14_limit": 39,
        "lambo2_rsi_4_limit": 44,
        "buy_adx": 20,
        "buy_fastd": 20,
        "buy_fastk": 22,
        "buy_ema_cofi": 0.98,
        "buy_ewo_high": 4.179
    }

    # Sell hyperspace params:
    sell_params = {
        "base_nb_candles_sell": 22,
        "high_offset": 1.014,
        "high_offset_2": 1.01
    }

    @property
    def protections(self):
        return [
            {
                "method": "CooldownPeriod",
                "stop_duration_candles": 5
            },
            {
                "method": "MaxDrawdown",
                "lookback_period_candles": 48,
                "trade_limit": 20,
                "stop_duration_candles": 4,
                "max_allowed_drawdown": 0.2
            },
            {
                "method": "StoplossGuard",
                "lookback_period_candles": 24,
                "trade_limit": 4,
                "stop_duration_candles": 2,
                "only_per_pair": False
            },
            {
                "method": "LowProfitPairs",
                "lookback_period_candles": 6,
                "trade_limit": 2,
                "stop_duration_candles": 60,
                "required_profit": 0.02
            },
            {
                "method": "LowProfitPairs",
                "lookback_period_candles": 24,
                "trade_limit": 4,
                "stop_duration_candles": 2,
                "required_profit": 0.01
            }
        ]

    # ROI table:
    minimal_roi = {
        "0": 0.99,
        
    }

    # Stoploss:
    stoploss = -0.99

    # SMAOffset
    base_nb_candles_buy = IntParameter(8, 20, default=buy_params['base_nb_candles_buy'], space='buy', optimize=False)
    base_nb_candles_sell = IntParameter(8, 20, default=sell_params['base_nb_candles_sell'], space='sell', optimize=False)
    low_offset = DecimalParameter(0.985, 0.995, default=buy_params['low_offset'], space='buy', optimize=True)
    high_offset = DecimalParameter(1.005, 1.015, default=sell_params['high_offset'], space='sell', optimize=True)
    high_offset_2 = DecimalParameter(1.010, 1.020, default=sell_params['high_offset_2'], space='sell', optimize=True)

    # lambo2
    lambo2_ema_14_factor = DecimalParameter(0.8, 1.2, decimals=3,  default=buy_params['lambo2_ema_14_factor'], space='buy', optimize=True)
    lambo2_rsi_4_limit = IntParameter(5, 60, default=buy_params['lambo2_rsi_4_limit'], space='buy', optimize=True)
    lambo2_rsi_14_limit = IntParameter(5, 60, default=buy_params['lambo2_rsi_14_limit'], space='buy', optimize=True)

    # Protection
    fast_ewo = 50
    slow_ewo = 200

    rsi_buy = IntParameter(30, 70, default=buy_params['rsi_buy'], space='buy', optimize=False)

    #cofi
    is_optimize_cofi = False
    buy_ema_cofi = DecimalParameter(0.96, 0.98, default=0.97 , optimize = is_optimize_cofi)
    buy_fastk = IntParameter(20, 30, default=20, optimize = is_optimize_cofi)
    buy_fastd = IntParameter(20, 30, default=20, optimize = is_optimize_cofi)
    buy_adx = IntParameter(20, 30, default=30, optimize = is_optimize_cofi)
    buy_ewo_high = DecimalParameter(2, 12, default=3.553, optimize = is_optimize_cofi)

    atr_length = IntParameter(10, 30, default=14, space='buy', optimize=True)
    increment = DecimalParameter(low=1.0005, high=1.001, default=1.0007, decimals=4 ,space='buy', optimize=True, load=True)

    use_custom_stoploss = True
    process_only_new_candles = True
    # Custom Entry
    last_entry_price = None
    #     # This is called when placing the initial order (opening trade)
    # def informative_pairs(self):
    #     whitelist_pairs = self.dp.current_whitelist()
    #     corr_pairs = self.config["freqai"]["feature_parameters"]["include_corr_pairlist"]
    #     informative_pairs = []
    #     for tf in self.config["freqai"]["feature_parameters"]["include_timeframes"]:
    #         for pair in whitelist_pairs:
    #             informative_pairs.append((pair, tf))
    #         for pair in corr_pairs:
    #             if pair in whitelist_pairs:
    #                 continue  # avoid duplication
    #             informative_pairs.append((pair, tf))
    #     return informative_pairs


    # def feature_engineering_expand_all(self, dataframe, period, **kwargs):
    #     dataframe["%-rsi-period"] = ta.RSI(dataframe, timeperiod=period)
    #     dataframe["%-mfi-period"] = ta.MFI(dataframe, timeperiod=period)
    #     dataframe["%-rocr-period"] = ta.ROCR(dataframe, timeperiod=period)
    #     dataframe["%-chop-period"] = qtpylib.chopiness(dataframe, period)
    #     dataframe["%-linear-period"] = ta.LINEARREG_ANGLE(
    #         dataframe['close'], timeperiod=period)
    #     dataframe["%-atr-period"] = ta.ATR(dataframe, timeperiod=period)
    #     dataframe["%-atr-periodp"] = dataframe["%-atr-period"] / \
    #         dataframe['close'] * 1000

        # return dataframe

    def feature_engineering_expand_basic(self, dataframe, metadata, **kwargs):
        dataframe["%-raw_volume"] = dataframe["volume"]
        dataframe["%-obv"] = ta.OBV(dataframe)

        return dataframe

    def feature_engineering_standard(self, dataframe: DataFrame, **kwargs) -> DataFrame:
        # The following features are necessary for RL models

        for val in self.base_nb_candles_buy.range:
            dataframe[f'ma_buy_{val}'] = ta.EMA(dataframe, timeperiod=val)

        # Calculate all ma_sell values
        for val in self.base_nb_candles_sell.range:
            dataframe[f'ma_sell_{val}'] = ta.EMA(dataframe, timeperiod=val)

        dataframe['%-ma_lo'] = dataframe[f'ma_buy_{self.base_nb_candles_buy.value}'] * (self.low_offset.value)
        dataframe['%-hi'] = dataframe[f'ma_sell_{self.base_nb_candles_sell.value}'] * (self.high_offset.value)
        dataframe['%-ma_hi_2'] = dataframe[f'ma_sell_{self.base_nb_candles_sell.value}'] * (self.high_offset_2.value)

        dataframe['%-hma_50'] = qtpylib.hull_moving_average(dataframe['close'], window=50)
        # HMA-BUY SQUEEZE
        dataframe['%-HMA_SQZ'] = (((dataframe[f'ma_buy_{self.base_nb_candles_buy.value}'] - dataframe['%-hma_50']) 
            / dataframe[f'ma_buy_{self.base_nb_candles_buy.value}']) * 100)


        dataframe['zero'] = 0
        # Elliot
        dataframe['%-EWO'] = EWO(dataframe, self.fast_ewo, self.slow_ewo)
        dataframe.loc[dataframe['%-EWO'] > 0, "EWO_UP"] = dataframe['%-EWO']
        dataframe.loc[dataframe['%-EWO'] < 0, "EWO_DN"] = dataframe['%-EWO']
        dataframe['EWO_UP'].ffill()
        dataframe['EWO_DN'].ffill()
        dataframe['%-EWO_MEAN_UP'] = dataframe['EWO_UP'].mean()
        dataframe['%-EWO_MEAN_DN'] = dataframe['EWO_DN'].mean()
        dataframe['%-EWO_UP_FIB'] = dataframe['%-EWO_MEAN_UP'] * 1.618
        dataframe['%-EWO_DN_FIB'] = dataframe['%-EWO_MEAN_DN'] * 1.618

        # RSI
        dataframe['%-rsi'] = ta.RSI(dataframe, timeperiod=14)
        dataframe['%-rsi_fast'] = ta.RSI(dataframe, timeperiod=4)
        dataframe['%-rsi_slow'] = ta.RSI(dataframe, timeperiod=20)

        #lambo2
        dataframe['%-ema_14'] = ta.EMA(dataframe, timeperiod=14)
        dataframe['%-rsi_4'] = ta.RSI(dataframe, timeperiod=4)
        dataframe['%-rsi_14'] = ta.RSI(dataframe, timeperiod=14)

        # Cofi
        stoch_fast = ta.STOCHF(dataframe, 5, 3, 0, 3, 0)
        dataframe['%-fastd'] = stoch_fast['fastd']
        dataframe['%-fastk'] = stoch_fast['fastk']
        dataframe['%-adx'] = ta.ADX(dataframe)
        dataframe['%-ema_8'] = ta.EMA(dataframe, timeperiod=8)

        dataframe['%-OHLC4'] = (dataframe['open'] + dataframe['high'] + dataframe['low'] + dataframe['close']) / 4

        # Check how far we are from min and max 
        dataframe['%-max'] = dataframe['%-OHLC4'].rolling(4).max() / dataframe['%-OHLC4'] - 1
        dataframe['%-min'] = abs(dataframe['%-OHLC4'].rolling(4).min() / dataframe['%-OHLC4'] - 1)

        dataframe['%-max_l'] = dataframe['%-OHLC4'].rolling(120).max() / dataframe['%-OHLC4'] - 1
        dataframe['%-min_l'] = abs(dataframe['%-OHLC4'].rolling(120).min() / dataframe['%-OHLC4'] - 1)

        # Apply rolling window operation to the 'OHLC4'column
        rolling_window = dataframe['%-OHLC4'].rolling(16) 
        rolling_max = rolling_window.max()
        rolling_min = rolling_window.min()

        # Calculate the peak-to-peak value on the resulting rolling window data
        ptp_value = rolling_window.apply(lambda x: np.ptp(x))

        # Assign the calculated peak-to-peak value to the DataFrame column
        dataframe['%-move'] = ptp_value / dataframe['%-OHLC4']
        dataframe['%-move_mean'] = dataframe['%-move'].mean()
        dataframe['%-move_mean_x'] = dataframe['%-move'].mean() * 1.6
        dataframe['%-exit_mean'] = rolling_min * (1 + dataframe['%-move_mean'])
        dataframe['%-exit_mean_x'] = rolling_min * (1 + dataframe['%-move_mean_x'])
        dataframe['%-enter_mean'] = rolling_max * (1 - dataframe['%-move_mean'])
        dataframe['%-enter_mean_x'] = rolling_max * (1 - dataframe['%-move_mean_x'])
        dataframe['%-atr_pcnt'] = (ta.ATR(dataframe, timeperiod=5) / dataframe['%-OHLC4'])
        dataframe[f"%-raw_close"] = dataframe["close"]
        dataframe[f"%-raw_open"] = dataframe["open"]
        dataframe[f"%-raw_high"] = dataframe["high"]
        dataframe[f"%-raw_low"] = dataframe["low"]

        return dataframe


    def set_freqai_targets(self, dataframe, **kwargs):
        dataframe['OHLC4'] = (dataframe['open'] + dataframe['high'] + dataframe['low'] + dataframe['close']) / 4

        # Apply rolling window operation to the 'OHLC4'column
        rolling_window = dataframe['OHLC4'].rolling(16) 
        rolling_max = rolling_window.max()
        rolling_min = rolling_window.min()

        # Calculate the peak-to-peak value on the resulting rolling window data
        ptp_value = rolling_window.apply(lambda x: np.ptp(x))

        # Assign the calculated peak-to-peak value to the DataFrame column
        dataframe['&s-move'] = ptp_value / dataframe['OHLC4']
      
        return dataframe

    def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:

        dataframe = self.freqai.start(dataframe, metadata, self)
       # Calculate all ma_buy values
        for val in self.base_nb_candles_buy.range:
            dataframe[f'ma_buy_{val}'] = ta.EMA(dataframe, timeperiod=val)

        # Calculate all ma_sell values
        for val in self.base_nb_candles_sell.range:
            dataframe[f'ma_sell_{val}'] = ta.EMA(dataframe, timeperiod=val)

        dataframe['ma_lo'] = dataframe[f'ma_buy_{self.base_nb_candles_buy.value}'] * (self.low_offset.value)
        dataframe['ma_hi'] = dataframe[f'ma_buy_{self.base_nb_candles_buy.value}'] * (self.high_offset.value)
        dataframe['ma_hi_2'] = dataframe[f'ma_buy_{self.base_nb_candles_buy.value}'] * (self.high_offset_2.value)

        dataframe['hma_50'] = qtpylib.hull_moving_average(dataframe['close'], window=50)
        # HMA-BUY SQUEEZE
        dataframe['HMA_SQZ'] = (((dataframe[f'ma_buy_{self.base_nb_candles_buy.value}'] - dataframe['hma_50']) 
            / dataframe[f'ma_buy_{self.base_nb_candles_buy.value}']) * 100)


        dataframe['zero'] = 0
        # Elliot
        dataframe['EWO'] = EWO(dataframe, self.fast_ewo, self.slow_ewo)
        dataframe.loc[dataframe['EWO'] > 0, "EWO_UP"] = dataframe['EWO']
        dataframe.loc[dataframe['EWO'] < 0, "EWO_DN"] = dataframe['EWO']
        dataframe['EWO_UP'].ffill()
        dataframe['EWO_DN'].ffill()
        dataframe['EWO_MEAN_UP'] = dataframe['EWO_UP'].mean()
        dataframe['EWO_MEAN_DN'] = dataframe['EWO_DN'].mean()
        dataframe['EWO_UP_FIB'] = dataframe['EWO_MEAN_UP'] * 1.618
        dataframe['EWO_DN_FIB'] = dataframe['EWO_MEAN_DN'] * 1.618

        # RSI
        dataframe['rsi'] = ta.RSI(dataframe, timeperiod=14)
        dataframe['rsi_fast'] = ta.RSI(dataframe, timeperiod=4)
        dataframe['rsi_slow'] = ta.RSI(dataframe, timeperiod=20)

        #lambo2
        dataframe['ema_14'] = ta.EMA(dataframe, timeperiod=14)
        dataframe['rsi_4'] = ta.RSI(dataframe, timeperiod=4)
        dataframe['rsi_14'] = ta.RSI(dataframe, timeperiod=14)

        # Cofi
        stoch_fast = ta.STOCHF(dataframe, 5, 3, 0, 3, 0)
        dataframe['fastd'] = stoch_fast['fastd']
        dataframe['fastk'] = stoch_fast['fastk']
        dataframe['adx'] = ta.ADX(dataframe)
        dataframe['ema_8'] = ta.EMA(dataframe, timeperiod=8)

        dataframe['OHLC4'] = (dataframe['open'] + dataframe['high'] + dataframe['low'] + dataframe['close']) / 4

        # Check how far we are from min and max 
        dataframe['max'] = dataframe['OHLC4'].rolling(4).max() / dataframe['OHLC4'] - 1
        dataframe['min'] = abs(dataframe['OHLC4'].rolling(4).min() / dataframe['OHLC4'] - 1)

        dataframe['max_l'] = dataframe['OHLC4'].rolling(120).max() / dataframe['OHLC4'] - 1
        dataframe['min_l'] = abs(dataframe['OHLC4'].rolling(120).min() / dataframe['OHLC4'] - 1)

        # Apply rolling window operation to the 'OHLC4'column
        rolling_window = dataframe['OHLC4'].rolling(16) 
        rolling_max = rolling_window.max()
        rolling_min = rolling_window.min()

        # Calculate the peak-to-peak value on the resulting rolling window data
        ptp_value = rolling_window.apply(lambda x: np.ptp(x))

        # Assign the calculated peak-to-peak value to the DataFrame column
        dataframe['move'] = ptp_value / dataframe['OHLC4']
        dataframe['move_mean'] = dataframe['move'].mean()
        dataframe['move_mean_x'] = dataframe['move'].mean() * 1.6
        dataframe['exit_mean'] = rolling_min * (1 + dataframe['move_mean'])
        dataframe['exit_mean_x'] = rolling_min * (1 + dataframe['move_mean_x'])
        dataframe['enter_mean'] = rolling_max * (1 - dataframe['move_mean'])
        dataframe['enter_mean_x'] = rolling_max * (1 - dataframe['move_mean_x'])
        dataframe['atr_pcnt'] = (ta.ATR(dataframe, timeperiod=5) / dataframe['OHLC4'])


        return dataframe

    def populate_entry_trend(self, df: DataFrame, metadata: dict) -> DataFrame:

        lambo2 = (
            (df['close'] < (df['ema_14'] * self.lambo2_ema_14_factor.value)) &
            (df['rsi_4'] < int(self.lambo2_rsi_4_limit.value)) &
            (df['rsi_14'] < int(self.lambo2_rsi_14_limit.value)) &
            (df['atr_pcnt'] > df['min_l']) &
            (df['do_predict'] == 1) &
            # (df["&-action"] == 1) &
            (df['volume'] > 0) 
        )
        df.loc[lambo2, 'enter_long'] = 1
        df.loc[lambo2, 'enter_tag'] = 'lambo '

        buy1ewo = (
            (df['rsi_fast'] < 35 ) &
            (df['close'] < df['ma_lo']) &
            (df['EWO'] > df['EWO_MEAN_UP']) &
            (df['close'] < df['enter_mean_x']) &
            (df['close'].shift() < df['enter_mean_x'].shift()) &
            (df['rsi'] < self.rsi_buy.value) &
            (df['atr_pcnt'] > df['min']) &
            (df['do_predict'] == 1) &
            # (df["&-action"] == 1) &
            (df['volume'] > 0) 
        )
        df.loc[buy1ewo, 'enter_long'] = 1
        df.loc[buy1ewo, 'enter_tag'] = 'buy1ewo'

        buy2ewo = (
            (df['rsi_fast'] < 35) &
            (df['close'] < df['ma_lo']) &
            (df['EWO'] < df['EWO_DN_FIB']) &
            (df['atr_pcnt'] > df['min']) &
            (df['do_predict'] == 1) &
            # (df["&-action"] == 1) &
            (df['volume'] > 0) 
        )
        df.loc[buy2ewo, 'enter_long'] = 1
        df.loc[buy2ewo, 'enter_tag'] = 'buy2ewo'

        is_cofi = (
            (df['open'] < df['ema_8'] * self.buy_ema_cofi.value) &
            (qtpylib.crossed_above(df['fastk'], df['fastd'])) &
            (df['fastk'] < self.buy_fastk.value) &
            (df['fastd'] < self.buy_fastd.value) &
            (df['adx'] > self.buy_adx.value) &
            (df['EWO'] > df['EWO_MEAN_UP']) &
            (df['atr_pcnt'] > df['min']) &
            (df['do_predict'] == 1) &
            # (df["&-action"] == 1) &
            (df['volume'] > 0) 
            )
        df.loc[is_cofi, 'enter_long'] = 1
        df.loc[is_cofi, 'enter_tag'] = 'cofi'

        # is_rl = (
        #     (df["&-action"].shift() != 1) &
        #     (df['do_predict'] == 1) &
        #     (df["&-action"] == 1) &
        #     (df['volume'] > 0) 
        #     )
        # df.loc[is_cofi, 'enter_long'] = 1
        # df.loc[is_cofi, 'enter_tag'] = 'RL Entry'

        return df

    def populate_exit_trend(self, df: DataFrame, metadata: dict) -> DataFrame:

        condition5 = (
                (df['close'] > df['hma_50']) &
                (df['close'] > df['ma_hi_2']) &
                (df['close'] > df['exit_mean_x']) &
                (df['rsi'] > 50 ) &
                (df['do_predict'] == 1) &
                # (df["&-action"] == 2) &
                (df['volume'] > 0 ) &
                (df['rsi_fast']>df['rsi_slow'])

            )
        df.loc[condition5, 'exit_long'] = 1
        df.loc[condition5, 'exit_tag'] = 'Close > Offset Hi 2'


        
        condition6 = (
                (df['close'] < df['hma_50']) &
                (df['close'] > df['ma_hi']) &
                (df['volume'] > 0) &
                (df['do_predict'] == 1) &
                # (df["&-action"] == 2) &
                (df['rsi_fast']>df['rsi_slow'])

            )
        df.loc[condition6, 'exit_long'] = 1
        df.loc[condition6, 'exit_tag'] = 'Close > Offset Hi 1'

        # condition7 = (

        #         (df['volume'] > 0) &
        #         (df['do_predict'] == 1) &
        #         (df["&-action"] == 2) &
        #         (df["&-action"].shift() != 2) 

        #     )
        # df.loc[condition7, 'exit_long'] = 1
        # df.loc[condition7, 'exit_tag'] = 'RL Exit'

        return df

    # def custom_stake_amount(self, pair: str, current_time: datetime, current_rate: float,
    #                         proposed_stake: float, min_stake: float, max_stake: float,
    #                         entry_tag: Optional[str], **kwargs) -> float:

    #     # We need to leave most of the funds for possible further DCA orders
    #     # This also applies to fixed stakes
    #     return proposed_stake / self.max_dca_multiplier

    # def adjust_trade_position(self, trade: Trade, current_time: datetime,
    #                           current_rate: float, current_profit: float, min_stake: float,
    #                           max_stake: float, **kwargs):
    #     """
    #     Custom trade adjustment logic, returning the stake amount that a trade should be increased.
    #     This means extra buy orders with additional fees.

    #     :param trade: trade object.
    #     :param current_time: datetime object, containing the current datetime
    #     :param current_rate: Current buy rate.
    #     :param current_profit: Current profit (as ratio), calculated based on current_rate.
    #     :param min_stake: Minimal stake size allowed by exchange.
    #     :param max_stake: Balance available for trading.
    #     :param **kwargs: Ensure to keep this here so updates to this won't break your strategy.
    #     :return float: Stake amount to adjust your trade
    #     """

    #     #if current_profit > -0.01:
    #         #return None
    #     # Don't rebuy for trades on hold
    #     is_backtest = self.dp.runmode.value == 'backtest'
    #     if (trade.open_date_utc.replace(tzinfo=None) < datetime(2022, 4, 6) and not is_backtest):
    #         return None
    #     dataframe, _ = self.dp.get_analyzed_dataframe(trade.pair, self.timeframe)
    #     if(len(dataframe) < 2):
    #         return None
    #     last_candle = dataframe.iloc[-1].squeeze()
    #     previous_candle = dataframe.iloc[-2].squeeze()

    #     filled_buys = trade.select_filled_orders()
    #     count_of_buys = trade.nr_of_successful_entries
    #     try:
    #         stake_amount = filled_buys[0].cost
    #         stake_amount = stake_amount * (1 + (count_of_buys * 0.25))
    #         if (
    #                 ((last_candle['do_predict'] == 1)
    #                 and (last_candle['&-action'] == 2)
    #                 and count_of_buys == 1)
    #                 or ((previous_candle['do_predict'] == 1)
    #                 and (previous_candle['&-action'] == 2)
    #                 and count_of_buys == 1)
    #         ):
    #             return stake_amount
    #         if (
    #                 ((last_candle['do_predict'] == 1)
    #                 and (last_candle['&-action'] == 3)
    #                 and count_of_buys == 2)
    #                 or ((previous_candle['do_predict'] == 1)
    #                 and (previous_candle['&-action'] == 3)
    #                 and count_of_buys == 2)
    #         ):
    #             return stake_amount
    #     except Exception as exception:
    #         logger.warning(f"{exception}")

    #     return None


    ### Trailing Stop ###
    def custom_stoploss(self, pair: str, trade: 'Trade', current_time: datetime,
                        current_rate: float, current_profit: float, **kwargs) -> float:


        dataframe, _ = self.dp.get_analyzed_dataframe(pair=pair, timeframe=self.timeframe)
        current_candle = dataframe.iloc[-1].squeeze()
        SLT1 = current_candle['move_mean']
        SL1 = 0.013
        SLT2 = current_candle['move_mean_x']
        SL2 = current_candle['move_mean_x'] - current_candle['move_mean']
        display_profit = current_profit * 100
        slt1 = SLT1 * 100
        sl1 = SL1 * 100
        slt2 = SLT2 * 100
        sl2 = SL2 * 100


        if current_candle['max_l'] > .003: #ignore stoploss if setting new highs
            if SLT2 is not None and current_profit > SLT2:
                self.dp.send_msg(f'*** {pair} *** Profit {display_profit:.2f}% - {slt2:.2f}%/{sl2:.2f}% activated')
                logger.info(f'*** {pair} *** Profit {display_profit:.2f}% - {slt2:.2f}%/{sl2:.2f}% activated')
                return SL2
            if SLT1 is not None and current_profit > SLT1:
                self.dp.send_msg(f'*** {pair} *** Profit {display_profit:.2f}% - {SLT1:.2f}%/{SL1:.2f}% activated')
                logger.info(f'*** {pair} *** Profit {display_profit:.2f}% - {slt1:.2f}%/{sl1:.2f}% activated')
                return SL1

        else:
            if SLT1 is not None and current_profit > SL1:
                self.dp.send_msg(f'*** {pair} *** Profit {display_profit:.2f}% SWINGING FOR THE MOON!!!')
                logger.info(f'*** {pair} *** Profit {display_profit:.2f}% SWINGING FOR THE MOON!!!')
                return 0.99

        return self.stoploss


    def custom_entry_price(self, pair: str, trade: Optional['Trade'], current_time: datetime, proposed_rate: float,
                           entry_tag: Optional[str], side: str, **kwargs) -> float:

        dataframe, last_updated = self.dp.get_analyzed_dataframe(pair=pair,
                                                                timeframe=self.timeframe)

        entry_price = (dataframe['close'].iat[-1] + dataframe['open'].iat[-1] + proposed_rate + proposed_rate) / 4
        logger.info(f"{pair} Using Entry Price: {entry_price} | close: {dataframe['close'].iat[-1]} open: {dataframe['open'].iat[-1]} proposed_rate: {proposed_rate}") 

        # Check if there is a stored last entry price and if it matches the proposed entry price
        if self.last_entry_price is not None and abs(entry_price - self.last_entry_price) < 0.0001:  # Tolerance for floating-point comparison
            entry_price *= self.increment.value # Increment by 0.2%
            logger.info(f"{pair} Incremented entry price: {entry_price} based on previous entry price : {self.last_entry_price}.")

        # Update the last entry price
        self.last_entry_price = entry_price

        return entry_price


    def confirm_trade_exit(self, pair: str, trade: Trade, order_type: str, amount: float,
                           rate: float, time_in_force: str, exit_reason: str,
                           current_time: datetime, **kwargs) -> bool:
        
        dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe)
        last_candle = dataframe.iloc[-1].squeeze()

        if exit_reason == 'roi' and (last_candle['max_l'] < 0.003):
            return False

        # Handle freak events

        if exit_reason == 'Down Trend Soon' and trade.calc_profit_ratio(rate) < 0.003:
            logger.info(f"{trade.pair} Waiting for Profit")
            self.dp.send_msg(f'{trade.pair} Waiting for Profit')
            return False

        if exit_reason == 'roi' and trade.calc_profit_ratio(rate) < 0.003:
            logger.info(f"{trade.pair} ROI is below 0")
            self.dp.send_msg(f'{trade.pair} ROI is below 0')
            return False

        if exit_reason == 'partial_exit' and trade.calc_profit_ratio(rate) < 0:
            logger.info(f"{trade.pair} partial exit is below 0")
            self.dp.send_msg(f'{trade.pair} partial exit is below 0')
            return False

        if exit_reason == 'trailing_stop_loss' and trade.calc_profit_ratio(rate) < 0:
            logger.info(f"{trade.pair} trailing stop price is below 0")
            self.dp.send_msg(f'{trade.pair} trailing stop price is below 0')
            return False

        return True

    def custom_sell(self, pair: str, trade: 'Trade', current_time: 'datetime', current_rate: float, current_profit: float, **kwargs):
        # Sell any positions at a loss if they are held for more than 7 days.
        if current_profit < -0.04 and (current_time - trade.open_date_utc).days >= 4:
            return 'unclog'
    
    # Sell signal
    use_sell_signal = True
    sell_profit_only = True
    sell_profit_offset = 0.01
    ignore_roi_if_buy_signal = False

    def pct_change(a, b):
        return (b - a) / a