# source: https://raw.githubusercontent.com/remiotore/freqtrade/44beaeb6a420cd8e9f2e4ea93e11d6cfa192ee03/strategies/EnhancedCandlestickStrategy_20250117.py
import numpy as np
import pandas as pd
from pandas import DataFrame
import talib.abstract as ta
from freqtrade.strategy import IStrategy
from freqtrade.strategy import CategoricalParameter, DecimalParameter, IntParameter

class Github_remiotore_freqtrade__EnhancedCandlestickStrategy_20250117__20260111_210550(IStrategy):
    """
    Estratégia de trading com padrões de candlestick e indicadores técnicos ajustados
    para melhorar a assertividade e os lucros.
    """

    ########################################################################
    #                         PARÂMETROS HYPEROPT
    ########################################################################
    can_short = True
    timeframe = CategoricalParameter(
        ["5m", "15m", "1h"], default="5m", space="buy", optimize=True
    )

    leverage_level = IntParameter(1, 20, default=5, space="buy", optimize=True)
    bullish_threshold = IntParameter(5, 20, default=10, space="buy", optimize=True)
    bearish_threshold = IntParameter(-20, -5, default=-10, space="sell", optimize=True)

    stoploss_param = DecimalParameter(
        -0.25, -0.02, default=-0.05, decimals=2, space="sell", optimize=True
    )

    trailing_stop_param = CategoricalParameter(
        [True, False], default=True, space="sell", optimize=True
    )
    trailing_stop_positive_param = DecimalParameter(
        0.001, 0.05, default=0.02, decimals=3, space="sell", optimize=True
    )
    trailing_stop_positive_offset_param = DecimalParameter(
        0.001, 0.10, default=0.03, decimals=3, space="sell", optimize=True
    )
    trailing_only_offset_is_reached_param = CategoricalParameter(
        [True, False], default=True, space="sell", optimize=True
    )

    ema_short_period = IntParameter(10, 100, default=50, space="buy", optimize=True)
    ema_long_period = IntParameter(100, 300, default=200, space="buy", optimize=True)
    rsi_period = IntParameter(10, 30, default=14, space="buy", optimize=True)
    adx_period = IntParameter(10, 30, default=14, space="buy", optimize=True)
    atr_period = IntParameter(10, 30, default=14, space="buy", optimize=True)

    ########################################################################
    #                         CONSTRUTOR
    ########################################################################
    def __init__(self, config: dict) -> None:
        super().__init__(config)
        self.stoploss = float(self.stoploss_param.value)
        self.trailing_stop = bool(self.trailing_stop_param.value)
        self.trailing_stop_positive = float(self.trailing_stop_positive_param.value)
        self.trailing_stop_positive_offset = float(
            self.trailing_stop_positive_offset_param.value
        )
        self.trailing_only_offset_is_reached = bool(
            self.trailing_only_offset_is_reached_param.value
        )

    ########################################################################
    #                         INDICADORES
    ########################################################################
    def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        """
        Adiciona indicadores técnicos ao dataframe.
        """
        dataframe["ema_short"] = ta.EMA(dataframe, timeperiod=self.ema_short_period.value)
        dataframe["ema_long"] = ta.EMA(dataframe, timeperiod=self.ema_long_period.value)
        dataframe["rsi"] = ta.RSI(dataframe, timeperiod=self.rsi_period.value)
        dataframe["adx"] = ta.ADX(dataframe, timeperiod=self.adx_period.value)
        dataframe["atr"] = ta.ATR(dataframe, timeperiod=self.atr_period.value)

        # Padrões de candlestick mais relevantes
        dataframe["engulfing"] = ta.CDLENGULFING(dataframe)
        dataframe["hammer"] = ta.CDLHAMMER(dataframe)
        dataframe["shooting_star"] = ta.CDLSHOOTINGSTAR(dataframe)

        return dataframe

    ########################################################################
    #                         ENTRADA
    ########################################################################
    def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        """
        Define as condições de entrada com base nos indicadores e padrões.
        """
        dataframe["enter_long"] = 0
        dataframe["enter_short"] = 0

        # Condições para entradas long
        long_condition = (
            (dataframe["ema_short"] > dataframe["ema_long"]) &
            (dataframe["rsi"] < 30) &  # RSI em sobrevenda
            (dataframe["adx"] > 20) &  # Tendência moderada ou forte
            (
                (dataframe["engulfing"] > 0) |  # Padrão engolfo de alta
                (dataframe["hammer"] > 0)  # Padrão martelo
            )
        )

        # Condições para entradas short
        short_condition = (
            (dataframe["ema_short"] < dataframe["ema_long"]) &
            (dataframe["rsi"] > 70) &  # RSI em sobrecompra
            (dataframe["adx"] > 20) &  # Tendência moderada ou forte
            (
                (dataframe["engulfing"] < 0) |  # Padrão engolfo de baixa
                (dataframe["shooting_star"] < 0)  # Padrão estrela cadente
            )
        )

        dataframe.loc[long_condition, "enter_long"] = 1
        dataframe.loc[short_condition, "enter_short"] = 1

        return dataframe

    ########################################################################
    #                         SAÍDA
    ########################################################################
    def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        """
        Define as condições de saída com base em indicadores e trailing stop.
        """
        dataframe["exit_long"] = 0
        dataframe["exit_short"] = 0

        # Condições para saída de long
        exit_long_condition = (
            (dataframe["rsi"] > 70) |  # RSI indicando sobrecompra
            (dataframe["close"] > dataframe["ema_long"])  # Preço muito acima da média
        )

        # Condições para saída de short
        exit_short_condition = (
            (dataframe["rsi"] < 30) |  # RSI indicando sobrevenda
            (dataframe["close"] < dataframe["ema_long"])  # Preço muito abaixo da média
        )

        dataframe.loc[exit_long_condition, "exit_long"] = 1
        dataframe.loc[exit_short_condition, "exit_short"] = 1

        return dataframe

    ########################################################################
    #                         ALAVANCAGEM
    ########################################################################
    def leverage(
        self, pair: str, current_time, current_rate, proposed_leverage, max_leverage, side, **kwargs
    ) -> float:
        """
        Ajusta a alavancagem com base na volatilidade (ATR).
        """
        dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe)
        atr = dataframe["atr"].iloc[-1]

        if atr > 0.01:  # Exemplo: ajuste conforme a volatilidade
            return max(1, max_leverage / 2)
        return min(max_leverage, proposed_leverage)
