# Zeus Strategy: First Generation of GodStra Strategy with maximum
# AVG/MID profit in USDT
# Author: @Mablue (Masoud Azizi)
# github: https://github.com/mablue/
# IMPORTANT: INSTALL TA BEFOUR RUN(pip install ta)
# freqtrade hyperopt --hyperopt-loss SharpeHyperOptLoss --spaces buy sell roi --strategy Zeus
# --- Do not remove these libs ---
from datetime import timedelta, datetime
from freqtrade.persistence import Trade
from freqtrade.strategy import (BooleanParameter, CategoricalParameter, DecimalParameter, stoploss_from_open,
                                IntParameter, IStrategy, merge_informative_pair, informative, stoploss_from_absolute)
import pandas as pd
import numpy as np
from pandas import DataFrame
from typing import Optional, Union, Tuple

import logging
import configparser
from technical import pivots_points
# --------------------------------

# Add your lib to import here test git
import ta
import talib.abstract as talib
import freqtrade.vendor.qtpylib.indicators as qtpylib
import requests
from datetime import timezone, timedelta

logger = logging.getLogger(__name__)

from tabulate import tabulate


def pprint_df(dframe):
    print(tabulate(dframe, headers='keys', tablefmt='psql', showindex=False))


def normalize(df):
    df = (df - df.min()) / (df.max() - df.min())
    return df


def get_limit_from_config(section, pair):
    file_path = '/HOME/home/souti/freqtrade2/user_data/strategies/Zeus_8_3_2_B_4_2.txt'
    # Créez un objet ConfigParser
    config = configparser.ConfigParser()

    try:
        # Lisez le fichier avec les valeurs
        config.read(file_path)

        # Vérifiez si la section existe
        if config.has_section(section):
            # Obtenez les valeurs à partir de la section et de la clé (pair)
            limit = config.get(section, pair)
            return limit
        else:
            raise ValueError(f"La section '{section}' n'existe pas dans le fichier de configuration.")
    except Exception as e:
        print(f"Erreur lors de la lecture du fichier de configuration : {e}")
        return None


class Zeus_8_3_2_B_4_2(IStrategy):
    levels = [1, 2, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20]

    # ROI table:
    minimal_roi = {
        "0": 0.564,
        "567": 0.273,
        "2814": 0.12,
        "7675": 0
    }

    # Stoploss:
    stoploss = -1  # 0.256
    # Custom stoploss
    use_custom_stoploss = True

    # Buy hypers
    timeframe = '5m'

    max_open_trades = 5
    max_amount = 40

    # DCA config
    position_adjustment_enable = True

    plot_config = {
        "main_plot": {
            "min200": {
                "color": "#86c932"
            },
            "max50": {
                "color": "white"
            },
            "max200": {
                "color": "yellow"
            },
            "bb_lowerband": {
                "color": "#da59a6"},
            "bb_upperband": {
                "color": "#da59a6",
            }
        },
        "subplots": {
            "Rsi": {
                "rsi": {
                    "color": "pink"
                }
            },
            "Percent": {
                "max_min": {
                    "color": "#74effc"
                }
            }
        }
    }

    # 20 20 40   60 100 160 260 420
    # 50 50 100 300 500
    # fibo = [1, 1, 2, 3, 5, 8, 13, 21]
    # my fibo
    # 50 50 50 100 100 150 200 250 350 450 600 1050
    fibo = [1, 1, 1, 2, 2, 3, 4, 5, 7, 9, 12, 16, 21]
    baisse = [1, 2, 3, 5, 7, 10, 14, 19, 26, 35, 47, 63, 84]
    # Ma suite	1	1	1	2	2	3	4	5	7	9	12	16	21
    # Mise	50	50	50	100	100	150	200	250	350	450	600	800	1050
    # Somme Mises	50	100	150	250	350	500	700	950	1300	1750	2350	3150	4200
    # baisse	1	2	3	5	7	10	14	19	26	35	47	63	84

    trades = list()
    max_profit_pairs = {}

    profit_b_no_change = BooleanParameter(default=True, space="sell")
    profit_b_quick_lost = BooleanParameter(default=True, space="sell")
    profit_b_sma5 = BooleanParameter(default=True, space="sell")
    profit_b_sma10 = BooleanParameter(default=True, space="sell")
    profit_b_sma20 = BooleanParameter(default=True, space="sell")
    profit_b_quick_gain = BooleanParameter(default=True, space="sell")
    profit_b_quick_gain_3 = BooleanParameter(default=True, space="sell")
    profit_b_old_sma10 = BooleanParameter(default=True, space="sell")
    profit_b_very_old_sma10 = BooleanParameter(default=True, space="sell")
    profit_b_over_rsi = BooleanParameter(default=True, space="sell")
    profit_b_short_loss = BooleanParameter(default=True, space="sell")

    sell_b_percent = DecimalParameter(0, 0.02, decimals=3, default=0.01, space='sell')
    sell_b_percent3 = DecimalParameter(0, 0.02, decimals=3, default=0.01, space='sell')
    sell_b_candels = IntParameter(0, 48, default=12, space='sell')

    sell_b_too_old_day = IntParameter(0, 10, default=300, space='sell')
    sell_b_too_old_percent = DecimalParameter(0, 0.02, decimals=3, default=0.01, space='sell')

    sell_b_profit_no_change = DecimalParameter(0, 0.02, decimals=3, default=0.005, space='sell')
    sell_b_profit_percent12 = DecimalParameter(0, 0.002, decimals=4, default=0.001, space='sell')

    sell_b_RSI = IntParameter(70, 98, default=88, space='sell')
    sell_b_RSI2 = IntParameter(70, 98, default=88, space='sell')
    sell_b_RSI3 = IntParameter(70, 98, default=80, space='sell')

    sell_b_RSI2_percent = DecimalParameter(0, 0.02, decimals=3, default=0.01, space='sell')
    # sell_b_expected_profit = DecimalParameter(0, 0.01, decimals=3,  default=0.01, space='sell')

    profit_h_no_change = BooleanParameter(default=True, space="sell")
    profit_h_quick_lost = BooleanParameter(default=True, space="sell")
    profit_h_sma5 = BooleanParameter(default=True, space="sell")
    profit_h_sma10 = BooleanParameter(default=True, space="sell")
    profit_h_sma20 = BooleanParameter(default=True, space="sell")
    profit_h_quick_gain = BooleanParameter(default=True, space="sell")
    profit_h_quick_gain_3 = BooleanParameter(default=True, space="sell")
    profit_h_old_sma10 = BooleanParameter(default=True, space="sell")
    profit_h_very_old_sma10 = BooleanParameter(default=True, space="sell")
    profit_h_over_rsi = BooleanParameter(default=True, space="sell")
    profit_h_short_loss = BooleanParameter(default=True, space="sell")

    sell_h_percent = DecimalParameter(0, 0.02, decimals=3, default=0.01, space='sell')
    sell_h_percent3 = DecimalParameter(0, 0.02, decimals=3, default=0.01, space='sell')
    sell_h_candels = IntParameter(0, 48, default=12, space='sell')

    sell_h_too_old_day = IntParameter(0, 10, default=300, space='sell')
    sell_h_too_old_percent = DecimalParameter(0, 0.02, decimals=3, default=0.01, space='sell')

    sell_h_profit_no_change = DecimalParameter(0, 0.02, decimals=3, default=0.005, space='sell')
    sell_h_profit_percent12 = DecimalParameter(0, 0.002, decimals=4, default=0.001, space='sell')

    sell_h_RSI = IntParameter(70, 98, default=88, space='sell')
    sell_h_RSI2 = IntParameter(70, 98, default=88, space='sell')
    sell_h_RSI3 = IntParameter(70, 98, default=80, space='sell')

    sell_h_RSI2_percent = DecimalParameter(0, 0.02, decimals=3, default=0.01, space='sell')

    protection_percent_buy_lost = IntParameter(1, 10, default=5, space='protection')
    # protection_nb_buy_lost = IntParameter(1, 2, default=2, space='protection')

    protection_fibo = IntParameter(1, 10, default=2, space='protection')

    # trailing stoploss hyperopt parameters
    # hard stoploss profit
    sell_allow_decrease = DecimalParameter(0.005, 0.02, default=0.2, decimals=2, space='sell', optimize=True, load=True)

    pHSL = DecimalParameter(-0.200, -0.040, default=-0.08, decimals=3, space='sell', optimize=False, load=True)
    # profit threshold 1, trigger point, SL_1 is used
    pPF_1 = DecimalParameter(0.008, 0.020, default=0.016, decimals=3, space='sell', optimize=True, load=True)
    pSL_1 = DecimalParameter(0.008, 0.020, default=0.011, decimals=3, space='sell', optimize=True, load=True)

    # profit threshold 2, SL_2 is used
    pPF_2 = DecimalParameter(0.040, 0.100, default=0.080, decimals=3, space='sell', optimize=True, load=True)
    pSL_2 = DecimalParameter(0.020, 0.070, default=0.040, decimals=3, space='sell', optimize=True, load=True)

    def min_max_scaling(self, series: pd.Series) -> pd.Series:
        """Normaliser les données en les ramenant entre 0 et 100."""
        return 100 * (series - series.min()) / (series.max() - series.min())

    def z_score_scaling(self, series: pd.Series) -> pd.Series:
        """Normaliser les données en utilisant Z-Score Scaling."""
        return (series - series.mean()) / series.std()

    def confirm_trade_entry(self, pair: str, order_type: str, amount: float, rate: float, time_in_force: str,
                            current_time: datetime, entry_tag: Optional[str], **kwargs) -> bool:
        # count_buys = 0
        # trade = self.getTrade(pair)
        # if trade:
        #     filled_buys = trade.select_filled_orders('buy')
        #     count_buys = len(filled_buys)

        print('entry_tag' + str(entry_tag))
        dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe)
        last_candle = dataframe.iloc[-1].squeeze()
        # last_candle_12 = dataframe.iloc[-13].squeeze()
        limit = get_limit_from_config('Achats', pair)

        # allow_to_buy = True #(not self.stop_all) #& (not self.all_down)
        allow_to_buy = True  # (rate <= float(limit)) | (entry_tag == 'force_entry')
        # allow_to_buy = rate <= dataframe['lbp_3']
        self.trades = list()
        dispo = round(self.wallets.get_available_stake_amount())
        logger.info(f"BUY {pair} allow_to_buy {allow_to_buy} limit={limit} Buy {entry_tag} {current_time} dispo={dispo}")

        return allow_to_buy

    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, **kwargs, ) -> bool:
        # allow_to_sell = (minutes > 30)
        limit = get_limit_from_config('Ventes', pair)
        dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe)
        last_candle = dataframe.iloc[-1].squeeze()

        allow_to_sell = (last_candle['percent'] < 0)  # rate > float(limit)
        string = ""

        if allow_to_sell:
            self.trades = list()
            logger.info('Sell ' + exit_reason + ' ' + str(current_time) + ' ' + pair + " dispo=" + str(
                round(self.wallets.get_available_stake_amount()))  # "+ str(amount) + ' ' + str(rate)
                        + " open_rate=" + str(trade.open_rate) + " rate=" + str(rate) + " profit=" + str(
                trade.calc_profit(rate, amount))
                        + " " + string)
            # del self.max_profit_pairs[pair]
        else:
            logger.info('Cancel Sell ' + exit_reason + ' ' + str(current_time) + ' ' + pair)
        return (allow_to_sell) | (exit_reason == 'force_exit')

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

        dataframe, _ = self.dp.get_analyzed_dataframe(pair=pair, timeframe=self.timeframe)
        current_candle = dataframe.iloc[-1].squeeze()
        adjusted_stake_amount = self.adjust_stake_amount(pair, current_candle)

        logger.info(f"{pair} adjusted_stake_amount{adjusted_stake_amount}")

        # Use default stake amount.
        return adjusted_stake_amount
    #
    # def custom_stoploss(self, pair: str, trade: 'Trade', current_time: datetime,
    #                     current_rate: float, current_profit: float, **kwargs) -> float:
    #
    #     # # hard stoploss profit
    #     # HSL = self.pHSL.value
    #     # PF_1 = self.pPF_1.value
    #     # SL_1 = self.pSL_1.value
    #     # PF_2 = self.pPF_2.value
    #     # SL_2 = self.pSL_2.value
    #     #
    #     # # For profits between PF_1 and PF_2 the stoploss (sl_profit) used is linearly interpolated
    #     # # between the values of SL_1 and SL_2. For all profits above PL_2 the sl_profit value
    #     # # rises linearly with current profit, for profits below PF_1 the hard stoploss profit is used.
    #     #
    #     # if current_profit > PF_2:
    #     #     sl_profit = SL_2 + (current_profit - PF_2)
    #     # elif current_profit > PF_1:
    #     #     sl_profit = SL_1 + ((current_profit - PF_1) * (SL_2 - SL_1) / (PF_2 - PF_1))
    #     # else:
    #     #     sl_profit = HSL
    #
    #     #print(f"entry_tag={trade.entry_tag} max={trade.max_rate} min={trade.min_rate} ")
    #     if current_profit > 0.0125:
    #         sl_profit = 0.75 * current_profit  # 75% du profit en cours
    #     else:
    #         sl_profit = self.pHSL.value  # Hard stop-loss
    #     stoploss = stoploss_from_open(sl_profit, current_profit)
    #     return stoploss

    #
    #     dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe)
    #     last_candle = dataframe.iloc[-1].squeeze()
    #     limit = get_limit_from_config('Ventes', pair)
    #
    #     print(pair + " " + str(current_rate) + " " + str(limit))
    #
    #     #stop_loss = self.adjust_stop_loss(dataframe.iloc[-1])
    #
    #     #if current_rate < float(limit):
    #     #    return -1
    #
    #     # "pHSL": -0.99,
    #     # "pPF_1": 0.022,
    #     # "pSL_1": 0.021,
    #     # "pPF_2": 0.08,
    #     # "pSL_2": 0.04,
    #     #
    #     # hard stoploss profit
    #     HSL = self.pHSL.value
    #     PF_1 = self.pPF_1.value
    #     SL_1 = self.pSL_1.value
    #     PF_2 = self.pPF_2.value
    #     SL_2 = self.pSL_2.value
    #
    #     # For profits between PF_1 and PF_2 the stoploss (sl_profit) used is linearly interpolated
    #     # between the values of SL_1 and SL_2. For all profits above PL_2 the sl_profit value
    #     # rises linearly with current profit, for profits below PF_1 the hard stoploss profit is used.
    #
    #     #                   0.04
    #     if current_profit > PF_2:
    #         #           0.04 + (current_profit - 0.08)
    #         sl_profit = SL_2 + (current_profit - PF_2)
    #     #                   0.022
    #     elif current_profit > PF_1:
    #         #           0.021 + ((current_profit - 0.022) * (0.04 - 0.021) / (0.08 - 0.022))
    #         sl_profit = SL_1 + ((current_profit - PF_1) * (SL_2 - SL_1) / (PF_2 - PF_1))
    #     else:
    #         sl_profit = HSL
    #
    #     slfo = stoploss_from_open(sl_profit, current_profit)
    #     print('current_profit=' + str(current_profit) + ' stop from open=' + str(slfo))
    #     return slfo

    def custom_exit(self, pair: str, trade: Trade, current_time, current_rate, current_profit, **kwargs):

        dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe)
        last_candle = dataframe.iloc[-1].squeeze()

        # self.analyze_conditions(pair, dataframe)

        # print("---------------" + pair + "----------------")
        expected_profit = self.expectedProfit(pair, last_candle)

        dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe)
        last_candle = dataframe.iloc[-1]

        # Calcul du prix cible basé sur l'ATR
        atr_take_profit = trade.open_rate + (last_candle['atr'] * 2)  # Prendre profit à 2x l'ATR

        # logger.info(f"{pair} Custom exit atr_take_profit={atr_take_profit:.4f}")
        # if current_rate >= atr_take_profit:
        #     return 'sell_atr_take_profit'

        if (last_candle['percent3'] < -0.002) & (last_candle['percent12'] < 0) & (
                current_profit > last_candle['min_max200'] / 2):
            self.trades = list()
            return 'min_max200'
        if (last_candle['percent12'] <= -0.01) & (current_profit >= expected_profit):
            self.trades = list()
            return 'profit'

    def informative_pairs(self):
        # get access to all pairs available in whitelist.
        pairs = self.dp.current_whitelist()
        informative_pairs = [(pair, '1d') for pair in pairs]
        informative_pairs += [(pair, '1h') for pair in pairs]

        return informative_pairs

    def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        # Add all ta features
        pair = metadata['pair']

        dataframe['achats'] = get_limit_from_config('Achats', pair)
        dataframe['ventes'] = get_limit_from_config('Ventes', pair)

        heikinashi = qtpylib.heikinashi(dataframe)
        dataframe['haopen'] = heikinashi['open']
        dataframe['haclose'] = heikinashi['close']
        dataframe['hapercent'] = (dataframe['haclose'] - dataframe['haopen']) / dataframe['haclose']

        dataframe['close_02'] = dataframe['haclose'] * 1.02

        dataframe['pct_change'] = dataframe['close'].pct_change(5)
        dataframe['min'] = talib.MIN(dataframe['close'], timeperiod=200)
        dataframe['min12'] = talib.MIN(dataframe['close'], timeperiod=12)

        dataframe['min50'] = talib.MIN(dataframe['close'], timeperiod=50)
        dataframe['min200'] = talib.MIN(dataframe['close'], timeperiod=200)

        dataframe['max50'] = talib.MAX(dataframe['close'], timeperiod=50)
        dataframe['max144'] = talib.MAX(dataframe['close'], timeperiod=144)
        dataframe['min_max50'] = (dataframe['max50'] - dataframe['min50']) / dataframe['min50']

        dataframe['max200'] = talib.MAX(dataframe['close'], timeperiod=200)
        dataframe['min_max200'] = (dataframe['max200'] - dataframe['min200']) / dataframe['min200']
        dataframe['max200_diff'] = (dataframe['max200'] - dataframe['close']) / dataframe['close']
        dataframe['max50_diff'] = (dataframe['max50'] - dataframe['close']) / dataframe['close']

        dataframe['sma5'] = talib.SMA(dataframe, timeperiod=5)
        dataframe['sma10'] = talib.SMA(dataframe, timeperiod=10)
        dataframe['sma20'] = talib.SMA(dataframe, timeperiod=20)
        dataframe["percent"] = (dataframe["close"] - dataframe["open"]) / dataframe["open"]
        dataframe["percent3"] = (dataframe["close"] - dataframe["open"].shift(3)) / dataframe["open"].shift(3)
        dataframe["percent5"] = (dataframe["close"] - dataframe["open"].shift(5)) / dataframe["open"].shift(5)
        dataframe["percent12"] = (dataframe["close"] - dataframe["open"].shift(12)) / dataframe["open"].shift(12)
        dataframe["percent24"] = (dataframe["close"] - dataframe["open"].shift(24)) / dataframe["open"].shift(24)
        dataframe["percent48"] = (dataframe["close"] - dataframe["open"].shift(48)) / dataframe["open"].shift(48)
        dataframe["percent_max_144"] = (dataframe["close"] - dataframe["max144"]) / dataframe["close"]

        dataframe['sma10_s2'] = dataframe['sma10'].shift(1)
        dataframe['sma20_s2'] = dataframe['sma20'].shift(1)
        dataframe['percent12_s2'] = dataframe['percent12'].shift(1)

        dataframe['sma5_s5'] = dataframe['sma5'].shift(4)
        dataframe['sma10_s5'] = dataframe['sma10'].shift(4)
        dataframe['sma20_s5'] = dataframe['sma20'].shift(4)
        # print(metadata['pair'])
        dataframe['rsi'] = talib.RSI(dataframe['close'], length=14)

        # Bollinger Bands
        bollinger = qtpylib.bollinger_bands(qtpylib.typical_price(dataframe), window=20, stds=2)
        dataframe['bb_lowerband'] = bollinger['lower']
        dataframe['bb_middleband'] = bollinger['mid']
        dataframe['bb_upperband'] = bollinger['upper']
        dataframe["bb_percent"] = (
                (dataframe["close"] - dataframe["bb_lowerband"]) /
                (dataframe["bb_upperband"] - dataframe["bb_lowerband"])
        )

        # Normalization

        dataframe['average_line'] = dataframe['close'].mean()
        dataframe['average_line_50'] = talib.MIDPOINT(dataframe['close'], timeperiod=50)

        dataframe['average_line_288'] = talib.MIDPOINT(dataframe['close'], timeperiod=288)
        dataframe['average_line_288_098'] = dataframe['average_line_288'] * 0.98
        dataframe['average_line_288_099'] = dataframe['average_line_288'] * 0.99
        # Sort the close prices to find the 4 lowest values
        sorted_close_prices = dataframe['close'].tail(576).sort_values()
        lowest_4 = sorted_close_prices.head(20)

        dataframe['lowest_4_average'] = lowest_4.mean()
        # Propagate this mean value across the entire dataframe
        # dataframe['lowest_4_average'] = dataframe['lowest_4_average'].iloc[0]

        # # Sort the close prices to find the 4 highest values
        sorted_close_prices = dataframe['close'].tail(288).sort_values(ascending=False)
        highest_4 = sorted_close_prices.head(20)

        # # Calculate the mean of the 4 highest values
        dataframe['highest_4_average'] = highest_4.mean()

        # # Propagate this mean value across the entire dataframe
        # dataframe['highest_4_average'] = dataframe['highest_4_average'].iloc[0]

        dataframe['volatility'] = talib.STDDEV(dataframe['close'], timeperiod=144) / dataframe['close']
        dataframe['atr'] = talib.ATR(dataframe['high'], dataframe['low'], dataframe['close'], timeperiod=144) / \
                           dataframe['close']
        # dataframe['pct_average'] = (dataframe['highest_4_average'] - dataframe['close']) / dataframe['lowest_4_average']
        # dataframe['highest_4_average_1'] = dataframe['highest_4_average'] * 0.99
        # dataframe['highest_4_average_2'] = dataframe['highest_4_average'] * 0.98
        # dataframe['highest_4_average_3'] = dataframe['highest_4_average'] * 0.97
        # dataframe['highest_4_average_4'] = dataframe['highest_4_average'] * 0.96
        # dataframe['highest_4_average_5'] = dataframe['highest_4_average'] * 0.95

        # Normaliser les données de 'close'
        # normalized_close = self.min_max_scaling(dataframe['close'])
        ################### INFORMATIVE 1h
        informative = self.dp.get_pair_dataframe(pair=metadata['pair'], timeframe="1h")
        informative['volatility'] = talib.STDDEV(informative['close'], timeperiod=14) / informative['close']
        informative['atr'] = (talib.ATR(informative['high'], informative['low'], informative['close'], timeperiod=14)) / informative['close']
        dataframe = merge_informative_pair(dataframe, informative, self.timeframe, "1h", ffill=True)

        ################### INFORMATIVE 1d
        informative = self.dp.get_pair_dataframe(pair=metadata['pair'], timeframe="1d")
        sorted_close_prices = informative['close'].tail(365).sort_values()
        lowest_4 = sorted_close_prices.head(4)
        informative['lowest_4'] = lowest_4.mean()

        sorted_close_prices = informative['close'].tail(365).sort_values(ascending=False)
        highest_4 = sorted_close_prices.head(4)
        informative['highest_4'] = highest_4.mean()

        last_14_days = informative.tail(14)

        # Récupérer le minimum et le maximum de la colonne 'close' des 14 derniers jours
        min_14_days = last_14_days['close'].min()
        max_14_days = last_14_days['close'].max()
        informative['lowest'] = min_14_days
        informative['highest'] = max_14_days
        informative['pct_min_max'] = (max_14_days - min_14_days) / min_14_days
        informative['mid_min_max'] = min_14_days + (max_14_days - min_14_days) / 2
        informative['middle'] = informative['lowest_4'] + (informative['highest_4'] - informative['lowest_4']) / 2
        informative['mid_min_max_0.98'] = informative['mid_min_max'] * 0.98
        dataframe = merge_informative_pair(dataframe, informative, self.timeframe, "1d", ffill=True)

        dataframe['count_buys'] = 0

        dataframe['last_price'] = dataframe['close']
        dataframe['first_price'] = dataframe['close']
        dataframe['mid_price'] = (dataframe['last_price'] + dataframe['first_price']) / 2
        dataframe['close01'] = dataframe.iloc[-1]['close'] * 1.01
        dataframe['amount'] = 0
        dataframe['limit'] = dataframe['close']
        count_buys = 0
        if self.dp:
            if self.dp.runmode.value in ('live', 'dry_run'):
                self.getOpenTrades()

                for trade in self.trades:
                    if trade.pair != pair:
                        continue
                    print(trade)
                    filled_buys = trade.select_filled_orders('buy')
                    dataframe['count_buys'] = len(filled_buys)
                    count = 0
                    amount = 0
                    for buy in filled_buys:
                        if count == 0:
                            dataframe['first_price'] = buy.price
                            dataframe['close01'] = buy.price * 1.01

                        # Order(id=2396, trade=1019, order_id=29870026652, side=buy, filled=0.00078, price=63921.01,
                        # status=closed, date=2024-08-26 02:20:11)
                        dataframe['last_price'] = buy.price
                        print(buy)
                        count = count + 1
                        amount += buy.price * buy.filled
                    dataframe['mid_price'] = (dataframe['last_price'] + dataframe['first_price']) / 2
                    count_buys = count
                    dataframe['limit'] = dataframe['last_price'] * (1 - self.baisse[count] / 100)
                    dataframe['amount'] = amount
                    print(f"amount= {amount}")

                # trades = Trade.get_trades([Trade.is_open is False]).all()
                trades = Trade.get_trades_proxy(is_open=False, pair=metadata['pair'])
                if trades:
                    trade = trades[-1]
                    print('closed trade pair is : ')
                    print(trade)
                    dataframe['expected_profit'] = (1 + self.expectedProfit(pair, dataframe.iloc[-1])) * dataframe[
                        'last_price']
                    dataframe['lbp'] = dataframe['last_price']
                    dataframe['lbp_3'] = dataframe['lbp'] * 0.97  # 3
                    dataframe['lbp_6'] = dataframe['lbp'] * 0.94  # 6
                    dataframe['lbp_9'] = dataframe['lbp'] * 0.90  # 10
                    dataframe['lbp_12'] = dataframe['lbp'] * 0.85  # 15
                    dataframe['lbp_20'] = dataframe['lbp'] * 0.8  # 20
                    dataframe['fbp'] = trade.open_rate
                # else:
                #     last_trade = self.get_trades(pair=pair).order_by('-close_date').first()
                #     filled_buys = last_trade.select_filled_orders('buy')
                #     print(last_trade)
                #     for buy in filled_buys:
                #         print(filled_buys)

        dataframe['buy_level'] = dataframe['lowest_4_average'] * (1 - self.levels[count_buys] / 100)
        # ----------------------------------------------------------
        # Calcul de la variation entre deux bougies successives
        dataframe['price_change'] = dataframe['close'].diff()

        # Marquer les bougies en baisse
        dataframe['is_down'] = dataframe['price_change'] < 0

        # Identifier les blocs consécutifs de baisses
        # dataframe['drop_id'] = (dataframe['is_down'] != dataframe['is_down'].shift(1)).cumsum()
        dataframe['drop_id'] = np.where(dataframe['is_down'],
                                        (dataframe['is_down'] != dataframe['is_down'].shift(12)).cumsum(), np.nan)

        # Identifier uniquement les blocs de baisse
        dataframe['drop_id'] = dataframe['drop_id'].where(dataframe['is_down'])
        # # Grouper par les chutes détectées
        # drop_info = dataframe.groupby('drop_id').agg(
        #     start=('close', 'first'),  # Prix au début de la chute
        #     end=('close', 'last'),  # Prix à la fin de la chute
        #     start_index=('close', 'idxmin'),  # Début de la chute (index)
        #     end_index=('close', 'idxmax'),  # Fin de la chute (index)
        # )
        #
        # # Calcul de l'ampleur de la chute en %
        # drop_info['drop_amplitude_pct'] = ((drop_info['end'] - drop_info['start']) / drop_info['start']) * 100
        # # Filtrer les chutes avec une amplitude supérieure à 3%
        # drop_info = drop_info[drop_info['drop_amplitude_pct'] < -3]

        # **************

        # Identifier le prix de début et de fin de chaque chute
        drop_stats = dataframe.groupby('drop_id').agg(
            start_price=('close', 'first'),  # Prix au début de la chute
            end_price=('close', 'last'),  # Prix à la fin de la chute
        )

        # Calculer l'amplitude en %
        drop_stats['amplitude_pct'] = ((drop_stats['end_price'] - drop_stats['start_price']) / drop_stats[
            'start_price']) * 100
        # drop_stats = drop_stats[drop_stats['amplitude_pct'] < -1]
        # Associer les amplitudes calculées à chaque drop_id dans dataframe
        dataframe = dataframe.merge(drop_stats[['amplitude_pct']], on='drop_id', how='left')
        # Remplir les lignes sans drop_id par 0
        dataframe['amplitude_pct'] = dataframe['amplitude_pct'].fillna(0)
        dataframe['amplitude_pct_60'] = dataframe['amplitude_pct'].rolling(60).sum()
        # ----------------------------------------------------------

        self.getBinanceOrderBook(pair, dataframe)

        return dataframe

    def getOpenTrades(self):
        # if len(self.trades) == 0:
        print('search open trades')
        self.trades = Trade.get_open_trades()
        return self.trades

    # def getTrade(self, pair):
    #     trades = self.getOpenTrades()
    #     trade_for_pair = None
    #     for trade in trades:
    #         if trade.pair == pair:
    #             trade_for_pair = trade
    #             break
    #     return trade_for_pair

    def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        pair = metadata['pair']

        # self.getOpenTrades()
        expected_profit = self.expectedProfit(pair, dataframe.iloc[-1])
        # self.getBinanceOrderBook(pair, dataframe)
        last_candle = dataframe.iloc[-1].squeeze()
        # limit = last_candle['first_price'] * (1 - self.baisse[last_candle['count_buys']] / 100)

        # self.updateLastValue(dataframe, 'expected_profit', expected_profit)
        print("---------------" + pair + "----------------")
        print('adjust stake amount ' + str(self.adjust_stake_amount(pair, dataframe.iloc[-1])))
        # print('adjust exit price ' + str(self.adjust_exit_price(dataframe.iloc[-1])))
        print('calcul expected_profit ' + str(expected_profit))

        buy_level = dataframe['buy_level']  # self.get_buy_level(pair, dataframe)

        dataframe.loc[
            (
                    (dataframe['close'] <= dataframe['min200'] * 1.002)
                    & (dataframe['percent_max_144'] <= -0.012)
                    & (dataframe['haopen'] < buy_level)
                    & (dataframe['open'] < dataframe['average_line_288'])
                    & (dataframe['min50'].shift(3) == dataframe['min50'])
            ), ['buy', 'enter_tag']] = (1, 'buy_fractal')

        dataframe.loc[
            (
                    (dataframe['max200_diff'].shift(4) >= 0.015)
                    & (dataframe['close'] <= dataframe['lowest_4_average'] * 1.002)
                    & (dataframe['close'] <= dataframe['min200'] * 1.002)
                    & (dataframe['max50_diff'].shift(4) >= 0.01)
                    & (dataframe['haclose'] < dataframe['bb_middleband'])
                    & (dataframe['close'] < buy_level)
                    & (dataframe['open'] < dataframe['average_line_288'])
                    & (dataframe['min50'].shift(3) == dataframe['min50'])
            ), ['buy', 'enter_tag']] = (1, 'buy_max_diff_015')

        dataframe.loc[
            (
                    (dataframe['max200_diff'] >= 0.018)
                    & (dataframe['close'] <= dataframe['lowest_4_average'] * 1.002)
                    & (dataframe['max50_diff'] >= 0.009)
                    & (dataframe['close'] <= dataframe['min200'] * 1.002)
                    & (dataframe['haclose'] < dataframe['bb_middleband'])
                    & (dataframe['close'] < buy_level)
                    & (dataframe['open'] < dataframe['average_line_288'])
                    & (dataframe['min50'].shift(3) == dataframe['min50'])
            ), ['buy', 'enter_tag']] = (1, 'buy_max_diff_018')

        # dataframe.loc[
        #     (
        #             (dataframe['max200_diff'] >= 0.018)
        #             & (dataframe['open'] < dataframe['average_line_288'])
        #             & (dataframe['close'] < dataframe['min12'] * 1.002)
        #             & (dataframe['min12'].shift(2) == dataframe['min12'])
        #     ), ['buy', 'enter_tag']] = (1, 'buy_min_max200')
        dataframe.loc[
            (
                    (dataframe['max200_diff'] >= 0.025)
                    & (dataframe['percent12'] < -0.002)
                    & (dataframe['pct_change'] < 0)
                    & (dataframe['open'] < dataframe['average_line_288_099'])
                    & (dataframe['open'] < dataframe['average_line_50'])
                    & (dataframe['count_buys'] == 0 |
                       ((dataframe['count_buys'] > 0) & (dataframe['close'] <= dataframe['limit']))
                       )
                    & (dataframe['percent'] >= -0.0005)
                    & (dataframe['min12'].shift(2) == dataframe['min12'])
            ), ['enter_long', 'enter_tag']] = (1, 'buy_min_max200_2')
        dataframe.loc[
            (
                    ((dataframe['count_buys'] > 0) & (dataframe['close'] <= dataframe['limit']))
                    & (dataframe['close'] < dataframe['min200'] * 1.002)
                    # & (dataframe['percent'] >= -0.0005)
                    & (
                            (dataframe['min12'].shift(2) == dataframe['min12']) |
                            (dataframe['min200'].shift(60) >= dataframe['min200'] * 1.03)
                    )
            ), ['enter_long', 'enter_tag']] = (1, 'buy_count_buy')

        dataframe.loc[
            (
                    (
                            (dataframe['percent12'] < -0.015) |
                            (dataframe['percent24'] < -0.022) |
                            (dataframe['percent48'] < -0.030)
                    )
                    & (dataframe['count_buys'] == 0)
                    & (dataframe['close'] <= dataframe['min50'] * 1.002)
                    & (dataframe['open'] < dataframe['average_line_50'])
                    & (
                            (dataframe['close'] < dataframe['min12'] * 1.002)
                            | (dataframe['percent12'] < -0.022)
                            | (dataframe['percent24'] < -0.022)
                    )
                    & (
                            (dataframe['min50'].shift(2) == dataframe['min50'])
                            | (dataframe['percent12'] < -0.022)
                            | (dataframe['percent24'] < -0.022)
                    )
            ), ['enter_long', 'enter_tag']] = (1, 'buy_0_percent12')
        dataframe.loc[
            (
                # (dataframe['percent12'] < -0.015)
                    ((dataframe['count_buys'] > 0) & (dataframe['close'] <= dataframe['limit']))
                    & (dataframe['open'] < dataframe['average_line_50'])
                    & (dataframe['close'] < dataframe['min12'] * 1.002)
                    & (dataframe['min12'].shift(2) == dataframe['min12'])
            ), ['enter_long', 'enter_tag']] = (1, 'buy_percent12')

        dataframe.loc[
            (
                    (dataframe['percent_max_144'] <= -0.02)
                    & (dataframe['close'] <= dataframe['lowest_4_average'] * 1.002)
                    & (dataframe['haopen'] < buy_level)
                    & (dataframe['min50'].shift(3) == dataframe['min50'])
                    & (dataframe['close'] <= dataframe['min50'] * 1.002)
                    & (dataframe['open'] < dataframe['average_line_288'])
            ), ['enter_long', 'enter_tag']] = (1, 'buy_percent_max_144')
        dataframe.loc[
            (
                    (dataframe['close'] <= dataframe['min200'] * 1.002)
                    & (dataframe['min_max200'] > 0.015)
                    & (dataframe['pct_change'] < 0)
                    & (dataframe['haopen'] < buy_level)
                    & (dataframe['open'] < dataframe['average_line_288'])
            ), ['enter_long', 'enter_tag']] = (1, 'buy_min_max_200')
        dataframe.loc[
            (
                    (dataframe['percent_max_144'] <= -0.02)
                    & (dataframe['close'] <= dataframe['lowest_4_average'] * 1.002)
                    & (dataframe['close_02'] < dataframe['max144'])
                    & (dataframe['haopen'] < buy_level)
                    & (dataframe['close'] <= dataframe['average_line_288_099'])
                    & (dataframe['min50'].shift(3) == dataframe['min50'])
                    & (dataframe['close'] <= dataframe['min50'] * 1.002)
            ), ['enter_long', 'enter_tag']] = (1, 'buy_close_02')
        # dataframe.loc[
        #     (
        #             (dataframe['close'] <= dataframe['lowest_4_average'] * 1.002)
        #             & (dataframe['haopen'] >= dataframe['lbp_3'])
        #             & (dataframe['haclose'] <= dataframe['lbp_3'])
        #             & (dataframe['haopen'] < buy_level)
        #     ), ['enter_long', 'enter_tag']] = (1, 'buy_lbp_3')
        dataframe.loc[
            (
                    (dataframe['close'] <= dataframe['lowest_4_average'] * 1.002)
                    & (dataframe['haopen'] >= dataframe['average_line_288_098'])
                    & (dataframe['haclose'] <= dataframe['average_line_288_098'])
                    & (dataframe['haopen'] < buy_level)
            ), ['enter_long', 'enter_tag']] = (1, 'buy_average_line_288_098')
        dataframe.loc[
            (
                    (dataframe['close'].shift(2) <= dataframe['min200'])
                    & (dataframe['pct_change'] < 0)
                    & (dataframe['min200'].shift(2) == dataframe['min200'])
                    & (dataframe['close'] < dataframe['lowest_4_average'])
                    & (dataframe['count_buys'] == 0 |
                       ((dataframe['count_buys'] > 0) & (dataframe['close'] <= dataframe['limit']))
                       )
            ), ['enter_long', 'enter_tag']] = (1, 'buy_min200')

        dataframe['test'] = np.where(dataframe['enter_long'] == 1, dataframe['close'] * 1.01, np.nan)

        return dataframe

    # def get_buy_level(self, pair, dataframe):
    #     limit = get_limit_from_config('Achats', pair)
    #
    #     filled_buys = {}
    #     for trade in self.trades:
    #         if trade.pair != pair:
    #             continue
    #         filled_buys = trade.select_filled_orders('enter_long')
    #     print('populate_buy_trend filled_buys : ' + str(len(filled_buys)))
    #     # Affichez les valeurs
    #     print(pair, limit)
    #     # BUY_LEVELS = {
    #     #    'BTC/USDT': [int(btc_limit), 42600, 41000, 40000, 39000, 38000, 37000, 36000, 35000],
    #     #    'ETH/USDT': [int(eth_limit), 2290, 1900, 1800, 1700, 1600, 1500, 1400, 1300],
    #     #    'ETC/USDT': [int(eth_limit), 2290, 1900, 1800, 1700, 1600, 1500, 1400, 1300],
    #     #    'DOGE/USDT': [int(eth_limit), 2290, 1900, 1800, 1700, 1600, 1500, 1400, 1300],
    #     #    # Ajoutez d'autres paires avec leurs niveaux d'achat ici...
    #     # }
    #     count_of_buys = len(filled_buys)
    #     buy_level = dataframe['lbp'] * (1 - self.levels[count_of_buys] / 100)  # float(limit) #BUY_LEVELS.get(pair, [])[0]  #dataframe['lbp_3'] #"
    #     return buy_level

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

    def adjust_trade_position(self, trade: Trade, current_time: datetime,
                              current_rate: float, current_profit: float, min_stake: float,
                              max_stake: float, **kwargs):
        dataframe, _ = self.dp.get_analyzed_dataframe(trade.pair, self.timeframe)
        # print(dataframe)
        last_candle = dataframe.iloc[-1].squeeze()
        last_candle_12 = dataframe.iloc[-13].squeeze()

        if (len(dataframe) < 1):
            return None
        pair = trade.pair
        if pair not in ('BTC/USDT', 'DOGE/USDT', 'ETH/USDT'):
            return None
        max_buys = 7

        filled_buys = trade.select_filled_orders('buy')
        count_of_buys = len(filled_buys)
        if count_of_buys >= max_buys:
            return None

        # if 'buy' in last_candle:
        #     condition = (last_candle['buy'] == 1)
        # else:
        #     condition = False
        # self.protection_nb_buy_lost.value
        # limits = ['lbp_3', 'lbp_6', 'lbp_9', 'lbp_12', 'lbp_20']
        # limit = last_candle[limits[count_of_buys]]
        limit = last_candle['limit']
        stake_amount = min(200, self.adjust_stake_amount(pair, last_candle) * self.fibo[count_of_buys])

        # print("Adjust " + trade.pair + " time=" + str(current_time) + ' rate=' + str(current_rate) + " buys=" + str(count_of_buys) + " limit=" + str(limit) + " stake=" + str(stake_amount))
        # logger.info(
        #     f"Adjust price={trade.pair} buy={condition} rate={current_rate:.4f} buys={count_of_buys} limit={limit:.4f} stake={stake_amount:.4f}")
        current_time_utc = current_time.astimezone(timezone.utc)
        open_date = trade.open_date.astimezone(timezone.utc)
        days_since_open = (current_time_utc - open_date).days

        if (days_since_open > count_of_buys) & (0 < count_of_buys <= max_buys) & (current_rate <= limit) & (last_candle['enter_long'] == 1):
            try:

                # This then calculates current safety order size
                # stake_amount = stake_amount * pow(1.5, count_of_buys)
                # print("Effective Adjust " + trade.pair + " time=" + str(current_time) + ' rate=' + str(current_rate) + " buys=" + str(count_of_buys) + " limit=" + str(limit) + " stake=" + str(stake_amount))
                logger.info(
                    f"Adjust {current_time} price={trade.pair} rate={current_rate:.4f} buys={count_of_buys} limit={limit:.4f} stake={stake_amount:.4f}")

                return stake_amount
            except Exception as exception:
                print(exception)
                return None
        return None

    def adjust_stake_amount(self, pair: str, dataframe: DataFrame):
        # Calculer le minimum des 14 derniers jours
        current_price = dataframe['close']

        # trade = self.getTrade(pair)
        # if trade:
        #     current_price = trade.open_rate
        base_stake_amount = self.config.get('stake_amount', 50)  # Montant de base configuré

        # Calculer le max des 14 derniers jours
        min_14_days_4 = dataframe['lowest_4_1d']
        max_14_days_4 = dataframe['highest_4_1d']
        percent_4 = 1 - (current_price - min_14_days_4) / (max_14_days_4 - min_14_days_4)
        factor_4 = 1 / ((current_price - min_14_days_4) / (max_14_days_4 - min_14_days_4))
        max_min_4 = max_14_days_4 / min_14_days_4

        # min_14_days = dataframe['lowest_1d']
        # max_14_days = dataframe['highest_1d']
        # percent = 1 - (current_price - min_14_days) / (max_14_days - min_14_days)
        # factor = 1 / ((current_price - min_14_days) / (max_14_days - min_14_days))
        # max_min = max_14_days / min_14_days
        # Stack amount ajusté price=2473.47 min_max=0.15058074985054215 percent=0.8379141364642171 amount=20.0

        adjusted_stake_amount = max(base_stake_amount / 2.5, min(75, base_stake_amount * percent_4))
        # if pair in ('BTC/USDT', 'ETH/USDT'):
        #     if percent_4 > 0.5:
        #         adjusted_stake_amount = 300

        # adjusted_stake_amount_2 = max(base_stake_amount / 2.5, min(75, base_stake_amount * percent))

        # print(
        #     f"Stack amount ajusté price={current_price} max_min={max_min_4:.4f} min_14={min_14_days_4:.4f} max_14={max_14_days_4:.4f} factor={factor_4:.4f} percent={percent_4:.4f} amount={adjusted_stake_amount:.4f}")
        # print(f"Stack amount ajusté price={current_price} max_min={max_min:.4f} min_14={min_14_days:.4f} max_14={max_14_days:.4f} factor={factor:.4f} percent={percent:.4f} amount={adjusted_stake_amount_2:.4f}")

        return adjusted_stake_amount

    # def adjust_exit_price(self, dataframe: DataFrame):
    #     # Calculer le max des 14 derniers jours
    #     min_14_days = dataframe['lowest_1d']
    #     max_14_days = dataframe['highest_1d']
    #     entry_price = dataframe['fbp']
    #     current_price = dataframe['close']
    #     percent = 0.5 * (max_14_days - min_14_days) / min_14_days
    #     exit_price = (1 + percent) * entry_price
    #
    #     print(f"Exit price ajusté price={current_price:.4f} max_14={max_14_days:.4f} exit_price={exit_price:.4f}")
    #
    #     return exit_price

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

        # Utiliser l'ATR pour ajuster le stoploss
        atr_stoploss = current_rate - (last_candle['atr'] * 1.5)  # Stoploss à 1.5x l'ATR

        # Retourner le stoploss dynamique en pourcentage du prix actuel
        return (atr_stoploss / current_rate) - 1

    def expectedProfit(self, pair: str, dataframe: DataFrame):

        current_price = dataframe['last_price']  # dataframe['close']

        # trade = self.getTrade(pair)
        # if trade:
        #     current_price = trade.open_rate

        # Calculer le max des 14 derniers jours
        min_14_days = dataframe['lowest_1d']
        max_14_days = dataframe['highest_1d']
        percent = (max_14_days - current_price) / (min_14_days)

        min_max = dataframe['pct_min_max_1d']  # (max_14_days - min_14_days) / min_14_days
        expected_profit = min(0.1, max(0.01, dataframe['min_max200'] * 0.5))

        # print(
        #     f"Expected profit price={current_price:.4f} min_max={min_max:.4f} min_14={min_14_days:.4f} max_14={max_14_days:.4f} percent={percent:.4f} expected_profit={expected_profit:.4f}")

        # self.analyze_conditions(pair, dataframe)
        return expected_profit

    # def adjust_exit_price(self, dataframe: DataFrame):
    #     # Calculer le max des 14 derniers jours
    #     min_14_days = dataframe['lowest_1d']
    #     max_14_days = dataframe['highest_1d']
    #     entry_price = dataframe['fbp']
    #     current_price = dataframe['close']
    #     percent = 0.5 * (max_14_days - min_14_days) / min_14_days
    #     exit_price = (1 + percent) * entry_price
    #
    #     print(f"Exit price ajusté price={current_price} max_14={max_14_days} exit_price={exit_price}")
    #
    #     return exit_price

    # def adjust_entry_price(self, dataframe: DataFrame):
    #     # Calculer le max des 14 derniers jours
    #     min_14_days = dataframe['lowest_1d']
    #     max_14_days = dataframe['highest_1d']
    #     current_price = dataframe['close']
    #     percent = 0.5 * (max_14_days - min_14_days) / min_14_days
    #     entry_price = (1 + percent) * entry_price
    #
    #     print(f"Entry price ajusté price={current_price} max_14={max_14_days} exit_price={entry_price}")
    #
    #     return entry_price

    # def adjust_stake_amount(self, dataframe: DataFrame):
    #     # Calculer le minimum des 14 derniers jours
    #     middle = dataframe['middle_1d']
    #
    #     # Récupérer la dernière cotation actuelle (peut être le dernier point de la série)
    #     current_price = dataframe['close']
    #
    #     # Calculer l'écart entre la cotation actuelle et le minimum des 14 derniers jours
    #     difference = middle - current_price
    #     # Ajuster la stake_amount en fonction de l'écart
    #     # Par exemple, augmenter la stake_amount proportionnellement à l'écart
    #     base_stake_amount = self.config.get('stake_amount', 100)  # Montant de base configuré
    #
    #     multiplier = 1 - (difference / current_price)  # Exemple de logique d'ajustement
    #
    #     adjusted_stake_amount = max(base_stake_amount / 2.5, base_stake_amount * multiplier)
    #
    #     # difference = 346.07000000000016
    #     # price = 2641.75
    #     # min_14 = 2295.68
    #     # amount = 56.5500141951358
    #
    #     print(f"Stack amount ajusté difference={difference} price={current_price} middle={middle} multiplier={multiplier} amount={adjusted_stake_amount}")
    #
    #     return adjusted_stake_amount

    def analyze_conditions(self, pair: str, row: DataFrame):
        dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe)

        if dataframe is None or dataframe.empty:
            return
        if row is None or row.empty:
            return
        # Créer un tableau pour stocker les résultats de l'analyse
        results = []
        # row = dataframe.iloc[-1].squeeze()
        # result = {'triggered': False, 'conditions_failed': []}
        try:
            buy_level = row['buy_level']
        except Exception as exception:
            print(exception)
            return None
        # Première condition : 'buy_fractal'
        print('------------------------------------------------')
        print('Test buy fractal ' + pair + ' buy_level=' + str(buy_level))
        if not (
                (row['close'] <= (row['min200'] * 1.002)) and
                (row['percent_max_144'] <= -0.012) and
                (row['haopen'] < buy_level) and
                (row['open'] < row['average_line_288']) and
                (dataframe['min50'].shift(3).iloc[-1] == row['min50'])
        ):
            failed_conditions = []
            if row['close'] > (row['min200'] * 1.002):
                print('close > min200 * 1.002')
            if row['percent_max_144'] > -0.012:
                print('percent_max_144 > -0.012')
            if row['haopen'] >= buy_level:
                print('haopen >= buy_level')
            if row['open'] >= row['average_line_288']:
                print('open >= average_line_288')
            if dataframe['min50'].shift(3).iloc[-1] != row['min50']:
                print('min50.shift(3) != min50')
            # result['conditions_failed'].append({'buy_fractal': failed_conditions})
        print('------------------------------------------------')
        print('Test buy_max_diff_015 ' + pair + ' buy_level=' + str(buy_level))
        # Deuxième condition : 'buy_max_diff_015'
        if not (
                (dataframe['max200_diff'].shift(4).iloc[-1] >= 0.015) and
                (row['close'] <= row['lowest_4_average'] * 1.002) and
                (row['close'] <= row['min200'] * 1.002) and
                (dataframe['max50_diff'].shift(4).iloc[-1] >= 0.01) and
                (row['haclose'] < row['bb_middleband']) and
                (row['close'] < buy_level) and
                (row['open'] < row['average_line_288']) and
                (dataframe['min50'].shift(3).iloc[-1] == row['min50'])
        ):
            if dataframe['max200_diff'].shift(4).iloc[-1] < 0.015:
                print('max200_diff.shift(4) < 0.015')
            if row['close'] > row['lowest_4_average'] * 1.002:
                print('close > lowest_4_average * 1.002')
            if row['close'] > row['min200'] * 1.002:
                print('close > min200 * 1.002')
            if dataframe['max50_diff'].shift(4).iloc[-1] < 0.01:
                print('max50_diff.shift(4) < 0.01')
            if row['haclose'] >= row['bb_middleband']:
                print('haclose >= bb_middleband')
            if row['close'] >= buy_level:
                print('close >= buy_level')
            if row['open'] >= row['average_line_288']:
                print('open >= average_line_288')
            if dataframe['min50'].shift(3).iloc[-1] != row['min50']:
                print('min50.shift(3) != min50')
        print('------------------------------------------------')
        print('Test buy_min_max_200 ' + pair + ' buy_level=' + str(buy_level))
        if not (
                (row['close'] <= row['min200'] * 1.002)
                and (row['min_max200'] > 0.015)
                and (row['haopen'] < buy_level)
                and (row['open'] < row['average_line_288'])
        ):
            if row['close'] > row['min200'] * 1.002:
                print('close > row[min200] * 1.002')
            if row['min_max200'] <= 0.015:
                print('row[min_max200] <= 0.015')
            if row['haopen'] < buy_level:
                print('row[haopen] < buy_level')
            if row['open'] < row['average_line_288']:
                print('row[open] >= row[average_line_288]')
        print('------------------------------------------------')

        # Ajouter le résultat à la liste des résultats
        # results.append(result)

        # print(result)

    def getBinanceOrderBook(self, pair, dataframe: DataFrame):
        """Fetch the order book (depth) from Binance."""
        # print(dataframe)
        last_candle = dataframe.iloc[-1].squeeze()
        symbol = pair.replace('/', '')

        try:
            url = f"https://api.binance.com/api/v3/depth?symbol={symbol}&limit=5000"
            response = requests.get(url)
            data = response.json()

            # Extract bids and asks from the order book
            asks, bids = self.calculateSMA(20, data['asks'], data['bids'])  # Ventes List of [price, quantity]
            # bids = data['bids']
            # asks = data['asks']  # Achats List of [price, quantity]

            # Process the depth data as you need it
            # bid_volume = sum([float(bid[1]) for bid in bids])  # Sum of all bid volumes
            # $ * nb / $ => nb
            bid_volume = sum([float(bid[0]) * float(bid[1]) / float(last_candle['close']) for bid in bids[:10]])
            # ask_volume = sum([float(ask[1]) for ask in asks])  # Sum of all ask volumes
            ask_volume = sum([float(ask[0]) * float(ask[1]) / float(last_candle['close']) for ask in asks[:10]])

            # Example: add the difference in volumes as an indicator
            if bid_volume and ask_volume:
                self.updateLastValue(dataframe, 'depth_bid_ask_diff', round(bid_volume - ask_volume, 2))
            else:
                self.updateLastValue(dataframe, 'depth_bid_ask_diff', 0)

            # probabilité baisse hausse sur les n premiers élements
            for start in [0, 50, 100, 150]:
                self.updateLastValue(dataframe, 'prob_hausse_' + str(start + 50),
                                     self.calculateProbaNb(asks, bids, start, start + 50))
                # dataframe['prob_hausse_' + str(nb)] = self.calculateProbaNb(asks, bids, nb)
            # Analyse des prix moyens pondérés par les volumes (VWAP) :
            #
            # Le VWAP (Volume Weighted Average Price) peut être utilisé pour comprendre la pression directionnelle.
            # Si le VWAP basé sur les ordres d'achat est plus élevé que celui des ordres de vente,
            # cela peut indiquer une probabilité de hausse.
            nb = 50

            bid_vwap = sum([float(bid[0]) * float(bid[1]) for bid in bids[:nb]]) / sum(
                [float(bid[1]) for bid in bids[:nb]])
            ask_vwap = sum([float(ask[0]) * float(ask[1]) for ask in asks[:nb]]) / sum(
                [float(ask[1]) for ask in asks[:nb]])

            if bid_vwap > ask_vwap:
                self.updateLastValue(dataframe, 'vwap_hausse',
                                     round(100 * (bid_vwap - ask_vwap) / (bid_vwap + ask_vwap), 2))
            else:
                self.updateLastValue(dataframe, 'vwap_hausse',
                                     - round(100 * (ask_vwap - bid_vwap) / (bid_vwap + ask_vwap), 2))

            current_price = last_candle['close']  # le prix actuel du marché

            # Calcul du seuil de variation de 1%
            lower_threshold = current_price * 0.99
            upper_threshold = current_price * 1.01

            # Volumes d'achat (bids) sous 1% du prix actuel
            bid_volume_1percent = sum(
                [float(bid[1]) for bid in bids if current_price >= float(bid[0]) >= lower_threshold])

            # Volumes de vente (asks) au-dessus de 1% du prix actuel
            ask_volume_1percent = sum(
                [float(ask[1]) for ask in asks if current_price <= float(ask[0]) <= upper_threshold])

            # Estimation de la probabilité basée sur le déséquilibre des volumes
            total_volume = bid_volume_1percent + ask_volume_1percent
            if total_volume > 0:
                prob_hausse = bid_volume_1percent / total_volume
                prob_baisse = ask_volume_1percent / total_volume
            else:
                prob_hausse = prob_baisse = 0

            self.updateLastValue(dataframe, 'proba_hausse_1%', round(prob_hausse * 100, 2))
            self.updateLastValue(dataframe, 'proba_baisse_1%', round(prob_baisse * 100, 2))
            print(f"Probabilité de hausse de 1%: {prob_hausse * 100:.2f}%")
            print(f"Probabilité de baisse de 1%: {prob_baisse * 100:.2f}%")

            self.calculateResistance(pair, asks, dataframe)
            self.calculateSupport(pair, bids, dataframe)

            dataframe['r_s'] = 100 * (dataframe['r_min'] - dataframe['s_min']) / dataframe['s_min']

        except Exception as e:
            logger.error(f"Error fetching order book: {e}")
            return None, None

    def calculateProbaNb(self, asks, bids, start, nb):
        top_bids = sum([float(bid[1]) for bid in bids[start:nb]])
        top_asks = sum([float(ask[1]) for ask in asks[start:nb]])
        if top_bids > top_asks:
            proba = round(100 * (top_bids - top_asks) / (top_bids + top_asks), 2)
        else:
            proba = - round(100 * (top_asks - top_bids) / (top_bids + top_asks), 2)
        return proba

    def calculateResistance(self, pair, asks, dataframe: DataFrame):
        last_candle = dataframe.iloc[-1].squeeze()

        # Filtrage +-5%
        current_price = float(last_candle['close'])
        lower_bound = current_price * 0.95
        upper_bound = current_price * 1.05
        ask_prices = [float(ask[0]) for ask in asks]
        ask_volumes = [float(ask[1]) for ask in asks]
        ask_df = pd.DataFrame({'price': ask_prices, 'volume': ask_volumes})
        filtered_ask_df = ask_df[(ask_df['price'] >= lower_bound) & (ask_df['price'] <= upper_bound)]
        # Trier le DataFrame sur la colonne 'volume' en ordre décroissant
        sorted_ask_df = filtered_ask_df.sort_values(by='volume', ascending=False)

        # Ne garder que les 3 premières lignes (les 3 plus gros volumes)
        top_3_asks = sorted_ask_df.head(3)
        print(top_3_asks)

        # Convertir les ordres de vente en numpy array pour faciliter le traitement
        asks_array = np.array(filtered_ask_df, dtype=float)

        # Détecter les résistances : on peut définir qu'une résistance est un niveau de prix où la quantité est élevée
        # Ex: seuil de résistance à partir des 10% des plus grosses quantités
        resistance_threshold = np.percentile(asks_array[:, 1], 90)
        resistances = asks_array[asks_array[:, 1] >= resistance_threshold]

        # Afficher les résistances trouvées
        # print(f"{pair} Niveaux de résistance détectés:")
        # for resistance in resistances:
        #     print(f"{pair} Prix: {resistance[0]}, Quantité: {resistance[1]}")

        # Exemple : somme des quantités sur des plages de prix
        # Intervalles de 10 USDT
        step = last_candle['close'] / 100
        price_intervals = np.arange(asks_array[:, 0].min(), asks_array[:, 0].max(), step=step)

        for start_price in price_intervals:
            end_price = start_price + step
            mask = (asks_array[:, 0] >= start_price) & (asks_array[:, 0] < end_price)
            volume_in_range = asks_array[mask, 1].sum()
            amount = volume_in_range * end_price
            print(
                f"Prix entre {start_price:.6f} et {end_price:.6f}: Volume total = {volume_in_range:.2f} amount={amount:.2f}")

        # Trier les asks par quantité en ordre décroissant
        asks_sorted = asks_array[asks_array[:, 1].argsort()][::-1]

        # Sélectionner les trois plus gros resistances
        top_3_resistances = asks_sorted[:3]

        # Afficher les trois plus gros resistances
        print("Les trois plus grosses resistances détectées : ")
        self.updateLastValue(dataframe, 'r3', top_3_resistances[0][0])
        self.updateLastValue(dataframe, 'r2', top_3_resistances[1][0])
        self.updateLastValue(dataframe, 'r1', top_3_resistances[2][0])
        self.updateLastValue(dataframe, 'r_min',
                             min(top_3_resistances[0][0], top_3_resistances[1][0], top_3_resistances[2][0]))
        for resistance in top_3_resistances:
            print(f"{pair} Prix: {resistance[0]}, Quantité: {resistance[1]}")

    def calculateSupport(self, pair, bids, dataframe: DataFrame):
        last_candle = dataframe.iloc[-1].squeeze()

        # Convert to pandas DataFrame to apply moving average
        current_price = float(last_candle['close'])
        lower_bound = current_price * 0.95
        upper_bound = current_price * 1.05
        bid_prices = [float(bid[0]) for bid in bids]
        bid_volumes = [float(bid[1]) for bid in bids]
        bid_df = pd.DataFrame({'price': bid_prices, 'volume': bid_volumes})
        filtered_bid_df = bid_df[(bid_df['price'] >= lower_bound) & (bid_df['price'] <= upper_bound)]
        # Trier le DataFrame sur la colonne 'volume' en ordre décroissant
        sorted_bid_df = filtered_bid_df.sort_values(by='volume', ascending=False)

        # Ne garder que les 3 premières lignes (les 3 plus gros volumes)
        top_3_bids = sorted_bid_df.head(3)
        print(top_3_bids)

        # Convertir les ordres d'achat en numpy array pour faciliter le traitement
        bids_array = np.array(filtered_bid_df, dtype=float)

        # Détecter les supports : on peut définir qu'un support est un niveau de prix où la quantité est élevée
        # Ex: seuil de support à partir des 10% des plus grosses quantités
        support_threshold = np.percentile(bids_array[:, 1], 90)
        supports = bids_array[bids_array[:, 1] >= support_threshold]

        # Afficher les supports trouvés
        # print(f"{pair} Niveaux de support détectés:")
        # for support in supports:
        #     print(f"{pair} Prix: {support[0]}, Quantité: {support[1]}")

        step = last_candle['close'] / 100
        # Exemple : somme des quantités sur des plages de prix pour les bids
        price_intervals = np.arange(bids_array[:, 0].min(), bids_array[:, 0].max(), step=step)  # Intervalles de 10 USDT

        for start_price in price_intervals:
            end_price = start_price + step
            mask = (bids_array[:, 0] >= start_price) & (bids_array[:, 0] < end_price)
            volume_in_range = bids_array[mask, 1].sum()
            amount = volume_in_range * end_price
            print(
                f"Prix entre {start_price:.6f} et {end_price:.6f}: Volume total = {volume_in_range:.2f} amount={amount:.2f}")

        # Trier les bids par quantité en ordre décroissant
        bids_sorted = bids_array[bids_array[:, 1].argsort()][::-1]

        # Sélectionner les trois plus gros supports
        top_3_supports = bids_sorted[:3]

        # Afficher les trois plus gros supports
        print("Les trois plus gros supports détectés:")

        self.updateLastValue(dataframe, 's1', top_3_supports[0][0])
        self.updateLastValue(dataframe, 's2', top_3_supports[1][0])
        self.updateLastValue(dataframe, 's3', top_3_supports[2][0])
        self.updateLastValue(dataframe, 's_min', max(top_3_supports[0][0], top_3_supports[1][0], top_3_supports[2][0]))

        for support in top_3_supports:
            print(f"{pair} Prix: {support[0]}, Quantité: {support[1]}")

    def updateLastValue(self, df: DataFrame, col, value):
        if col in df.columns:
            print(f"update last col {col} {value}")
            df.iloc[-1, df.columns.get_loc(col)] = value
        else:
            print(f"update all col {col} {value}")
            df[col] = value

    # def update_last_record(self, dataframe: DataFrame, new_data):
    #     # Vérifiez si de nouvelles données ont été reçues
    #     if new_data is not None:
    #         # Ne mettez à jour que la dernière ligne du dataframe
    #         last_index = dataframe.index[-1]  # Sélectionne le dernier enregistrement
    #         dataframe.loc[last_index] = new_data  # Met à jour le dernier enregistrement avec les nouvelles données
    #     return dataframe

    def calculateSMA(self, nb, asks, bids):
        # Prepare data for plotting
        bid_prices = [float(bid[0]) for bid in bids]
        bid_volumes = [float(bid[1]) for bid in bids]

        ask_prices = [float(ask[0]) for ask in asks]
        ask_volumes = [float(ask[1]) for ask in asks]

        # Convert to pandas DataFrame to apply moving average
        bid_df = pd.DataFrame({'price': bid_prices, 'volume': bid_volumes})
        ask_df = pd.DataFrame({'price': ask_prices, 'volume': ask_volumes})

        # Apply a rolling window to calculate a 10-value simple moving average (SMA)
        bid_df['volume_sma'] = bid_df['volume'].rolling(window=nb).mean()
        ask_df['volume_sma'] = ask_df['volume'].rolling(window=nb).mean()

        # Pour bid_df
        bid_df = bid_df.dropna(subset=['volume_sma'])
        bids_with_sma = list(zip(bid_df['price'], bid_df['volume_sma']))

        # Pour ask_df
        ask_df = ask_df.dropna(subset=['volume_sma'])
        asks_with_sma = list(zip(ask_df['price'], ask_df['volume_sma']))

        # print(bids_with_sma)
        # print(asks_with_sma)

        return asks_with_sma, bids_with_sma
