# --- Do not remove these libs ---
from freqtrade.strategy.interface import IStrategy
from freqtrade.strategy import timeframe_to_minutes
from freqtrade.strategy import (BooleanParameter, CategoricalParameter, DecimalParameter, RealParameter, IntParameter)
from pandas import DataFrame
from technical.util import resample_to_interval, resampled_merge
from functools import reduce
import numpy  # noqa
import requests  # Necesario para hacer la solicitud HTTP
import pandas as pd  # Necesario para manipular los datos históricos
from datetime import datetime  # Necesario para calcular la duración de la operación
# --------------------------------
import talib.abstract as ta
import freqtrade.vendor.qtpylib.indicators as qtpylib


class StrategyScalpingFast2(IStrategy):
    """
        Based on ReinforcedSmoothScalp
        https://github.com/freqtrade/freqtrade-strategies/blob/master/user_data/strategies/berlinguyinca/ReinforcedSmoothScalp.py
        this strategy is based around the idea of generating a lot of potentatils buys and make tiny profits on each trade

        we recommend to have at least 60 parallel trades at any time to cover non avoidable losses
    """
    INTERFACE_VERSION = 2
    
    # Buy hyperspace params:
    #buy_params = {
    #    "mfi-value": 19,
    #    "fastd-value": 29,
    #    "fastk-value": 19,
    #    "adx-value": 30,
    #    "mfi-enabled": False,
    #    "fastd-enabled": False,
    #    "adx-enabled": False,
    #    "fastk-enabled": False,
    #}
    
    # Parámetros para el índice de Miedo y Codicia
    #fear_threshold = IntParameter(0, 50, default=20, space='buy', optimize=True) 
    
    buy_stochrsi_k_max = IntParameter(0, 60, default=20, space='buy', optimize=True)
    mfi_value = IntParameter(9, 29, default=19, space='buy', optimize=False)
    fastd_value = IntParameter(19, 39, default=29, space='buy', optimize=False)
    fastk_value = IntParameter(9, 29, default=19, space='buy', optimize=False)
    adx_value = IntParameter(20, 40, default=30, space='buy', optimize=False)
    mfi_enabled = BooleanParameter(default=False, space='buy', optimize=False)
    fastd_enabled = BooleanParameter(default=False, space='buy', optimize=False)
    adx_enabled = BooleanParameter(default=False, space='buy', optimize=False)
    fastk_enabled = BooleanParameter(default=False, space='buy', optimize=False)

    sell_mfi_value = IntParameter(79, 99, default=89, space='sell', optimize=False)
    sell_fastd_value = IntParameter(62, 82, default=72, space='sell', optimize=False)
    sell_fastk_value = IntParameter(58, 78, default=68, space='sell', optimize=False)
    sell_adx_value = IntParameter(76, 96, default=86, space='sell', optimize=False)
    sell_cci_value = IntParameter(147, 167, default=157, space='sell', optimize=False)
    sell_mfi_enabled = BooleanParameter(default=True, space='sell', optimize=False)
    sell_fastd_enabled = BooleanParameter(default=True, space='sell', optimize=False)
    sell_adx_enabled = BooleanParameter(default=True, space='sell', optimize=False)
    sell_cci_enabled = BooleanParameter(default=False, space='sell', optimize=False)
    sell_fastk_enabled = BooleanParameter(default=False, space='sell', optimize=False)
    # Añadir la variable optimizable para el límite de tiempo de venta
    time_limit_sell = IntParameter(30, 1200, default=300, space='sell', optimize=True)  # Límite en minutos

    # Define the parameter spaces
    cooldown_lookback = IntParameter(2, 48, default=5, space="protection", optimize=True)
    stop_duration = IntParameter(12, 200, default=5, space="protection", optimize=True)
    use_stop_protection = BooleanParameter(default=True, space="protection", optimize=True)
    max_drawdown_enabled = BooleanParameter(default=True, space="protection", optimize=True)
    max_drawdown_lookback_period_candles = IntParameter(38, 58, default=48, space="protection", optimize=True)
    max_drawdown_trade_limit = IntParameter(10, 30, default=20, space="protection", optimize=True)
    max_drawdown_stop_duration_candles = IntParameter(2, 22, default=12, space="protection", optimize=True)
    max_drawdown_max_allowed_drawdown = DecimalParameter(0, 1, default=0.2, space="protection", optimize=True)

    # Sell hyperspace params:
    #sell_params = {
    #    "sell-mfi-value": 89,
    #    "sell-fastd-value": 72,
    #    "sell-fastk-value": 68,
    #    "sell-adx-value": 86,
    #    "sell-cci-value": 157,
    #    "sell-mfi-enabled": True,
    #    "sell-fastd-enabled": True,
    #    "sell-adx-enabled": True,
    #    "sell-cci-enabled": False,
    #    "sell-fastk-enabled": False,
    #}

    # ROI table:
    minimal_roi = {
        "0": 0.082,
        "18": 0.06,
        "51": 0.012,
        "123": 0
    }
    use_sell_signal = True
    # Stoploss:
    stoploss = -0.326
    # Minimal ROI designed for the strategy.
    # This attribute will be overridden if the config file contains "minimal_roi"
    #minimal_roi = {
    #    "0": 0.02
    #}
    # Optimal stoploss designed for the strategy
    # This attribute will be overridden if the config file contains "stoploss"
    # should not be below 3% loss

    #stoploss = -0.1
    # Optimal timeframe for the strategy
    # the shorter the better
    timeframe = '1m'

    # resample factor to establish our general trend. Basically don't buy if a trend is not given
    resample_factor = 5
    
    fear_greed_data = None  # Variable de clase para almacenar los datos del índice

    @property
    def protections(self):
        prot = []

        prot.append({
            "method": "CooldownPeriod",
            "stop_duration_candles": self.cooldown_lookback.value
        })
        if self.max_drawdown_enabled.value:
            prot.append({
                "method": "MaxDrawdown",
                "lookback_period_candles": self.max_drawdown_lookback_period_candles.value,
                "trade_limit": self.max_drawdown_trade_limit.value,
                "stop_duration_candles": self.max_drawdown_stop_duration_candles.value,
                "max_allowed_drawdown": self.max_drawdown_max_allowed_drawdown.value
            })
        if self.use_stop_protection.value:
            prot.append({
                "method": "StoplossGuard",
                "lookback_period_candles": 24 * 3,
                "trade_limit": 4,
                "stop_duration_candles": self.stop_duration.value,
                "only_per_pair": False
            })

        return prot
        
    #def load_fear_greed_data(self) -> pd.DataFrame:
        #url = "https://api.alternative.me/fng/?limit=365&format=json"  # URL de la API para obtener los datos históricos
        #response = requests.get(url)
        #data = response.json()
        
        # Convertir los datos a un DataFrame
        #df = pd.DataFrame(data['data'])
        #df['timestamp'] = pd.to_numeric(df['timestamp'])  # Asegurarse de que sea numérico
        #df['timestamp'] = pd.to_datetime(df['timestamp'], unit='s')
        #df.set_index('timestamp', inplace=True)
        #df['value'] = df['value'].astype(int)  # Asegurar que el valor sea un entero
        #return df

    def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        # Cargar los datos históricos si aún no se han cargado
        #if self.fear_greed_data is None:
            #self.fear_greed_data = self.load_fear_greed_data()
            
        tf_res = timeframe_to_minutes(self.timeframe) * self.resample_factor
        df_res = resample_to_interval(dataframe, tf_res)
        df_res['sma'] = ta.SMA(df_res, 50, price='close')
        dataframe = resampled_merge(dataframe, df_res, fill_na=True)
        dataframe['resample_sma'] = dataframe[f'resample_{tf_res}_sma']

        dataframe['ema_high'] = ta.EMA(dataframe, timeperiod=5, price='high')
        dataframe['ema_close'] = ta.EMA(dataframe, timeperiod=5, price='close')
        dataframe['ema_low'] = ta.EMA(dataframe, timeperiod=5, price='low')
        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['cci'] = ta.CCI(dataframe, timeperiod=20)
        dataframe['rsi'] = ta.RSI(dataframe, timeperiod=14)
        dataframe['mfi'] = ta.MFI(dataframe)

        # required for graphing
        bollinger = qtpylib.bollinger_bands(dataframe['close'], window=20, stds=2)
        dataframe['bb_lowerband'] = bollinger['lower']
        dataframe['bb_upperband'] = bollinger['upper']
        dataframe['bb_middleband'] = bollinger['mid']
        
        #stoch_rsi = ta.STOCHRSI(dataframe, timeperiod=28)
        #dataframe['stochrsi_k'] = stoch_rsi['fastk']
        #dataframe['stochrsi_d'] = stoch_rsi['fastd']

        # Calcula la media móvil de 9 periodos
        #dataframe['sma9'] = ta.SMA(dataframe, timeperiod=9)
        
        # Calcula la media móvil de 50 periodos
        #dataframe['sma50'] = ta.SMA(dataframe, timeperiod=50)
        
        # Calcula la media móvil de 200 periodos
        #dataframe['sma200'] = ta.SMA(dataframe, timeperiod=200)
        
        # Añadir el índice de Miedo y Codicia al dataframe
        #dataframe['fear_greed_index'] = dataframe['date'].map(self.fear_greed_data['value']).ffill()

        return dataframe

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

        # Evitar la compra si hay demasiado miedo en el mercado
        #conditions.append(dataframe['fear_greed_index'] > self.fear_threshold.value)  # Comprar solo si el índice está por encima del umbral

        conditions.append(dataframe["volume"] > 0)
        conditions.append(dataframe['open'] < dataframe['ema_low'])
        conditions.append(dataframe['resample_sma'] < dataframe['close'])

        if self.adx_enabled.value:
            conditions.append(dataframe["adx"] < self.adx_value.value)
        if self.mfi_enabled.value:
            conditions.append(dataframe['mfi'] < self.mfi_value.value)
        if self.fastk_enabled.value:
            conditions.append(dataframe['fastk'] < self.fastk_value.value)
        if self.fastd_enabled.value:
            conditions.append(dataframe['fastd'] < self.fastd_value.value)
        if self.fastk_enabled.value == True & self.fastd_enabled.value == True:
            conditions.append(qtpylib.crossed_above(dataframe['fastk'], dataframe['fastd']))
        
        # Stochastic RSI conditions
        #conditions.append(dataframe['stochrsi_k'] < self.buy_stochrsi_k_max.value)
        #conditions.append(qtpylib.crossed_above(dataframe['stochrsi_k'], dataframe['stochrsi_d']))
        
        # |
        # # try to get some sure things independent of resample
        # ((dataframe['rsi'] - dataframe['mfi']) < 10) &
        # (dataframe['mfi'] < 30) &
        # (dataframe['cci'] < -200)
        if conditions:
            dataframe.loc[reduce(lambda x, y: x & y, conditions), "buy"] = 1       
            
        #dataframe['buy'] = False
        return dataframe

    def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        conditions = []
        conditions.append(dataframe['open'] >= dataframe['ema_high'])

        if self.sell_fastd_enabled.value | self.sell_fastk_enabled.value:
            conditions.append((qtpylib.crossed_above(dataframe['fastk'], self.sell_fastk_value.value)) |
                              (qtpylib.crossed_above(dataframe['fastd'], self.sell_fastd_value.value)))
        if self.sell_cci_enabled.value == True:
            conditions.append(dataframe['cci'] > 100)
        if self.sell_mfi_enabled.value == True:
            conditions.append(dataframe['mfi'] > self.sell_mfi_value.value)
        if self.sell_adx_enabled.value == True:
            conditions.append(dataframe["adx"] < self.sell_adx_value.value)            

        # BEGIN - Añadir condición para vender si la operación ha estado abierta por más del tiempo límite
        #if 'trade_date_open' in dataframe.columns:
            #current_time = datetime.utcnow()
            #dataframe['trade_duration'] = (current_time - dataframe['trade_date_open']).dt.total_seconds() / 60.0
            #conditions.append(dataframe['trade_duration'] > self.time_limit_sell.value)
        # BEGIN - Añadir condición para vender si la operación ha estado abierta por más del tiempo límite

        if conditions:
            dataframe.loc[reduce(lambda x, y: x & y, conditions), "sell"] = 1              
        return dataframe