# source: https://raw.githubusercontent.com/remiotore/freqtrade/44beaeb6a420cd8e9f2e4ea93e11d6cfa192ee03/strategies/BreakoutSpecialist.py
from freqtrade.strategy import IStrategy
from pandas import DataFrame
import talib.abstract as ta
import freqtrade.vendor.qtpylib.indicators as qtpylib
import numpy  # noqa


class Github_remiotore_freqtrade__BreakoutSpecialist__20260111_210550(IStrategy):
    """
    Github_remiotore_freqtrade__BreakoutSpecialist__20260111_210550
    author@: PiP Repunzel

    How to use it?

    > freqtrade download-data --timeframes 5m --timerange=20200101-20250117
    > freqtrade backtesting --export trades -s Github_remiotore_freqtrade__BreakoutSpecialist__20260111_210550 --timeframe 5m --timerange=20200101-20250117
    > freqtrade plot-dataframe -s Github_remiotore_freqtrade__BreakoutSpecialist__20260111_210550 --indicators1 resistance support --timeframe 5m --timerange=20200101-20250117

    """

    # Minimal ROI designed for the strategy.
    # This attribute will be overridden if the config file contains "minimal_roi"
    minimal_roi = {
        "0": 0.1
    }

    # This attribute will be overridden if the config file contains "stoploss"
    stoploss = -0.1  # Dynamic stoploss with ATR will also be applied

    # Optimal timeframe for the strategy
    timeframe = '5m'

    # trailing stoploss
    trailing_stop = True

    def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        """
        Populate indicators for breakout detection and trend validation.
        """
        atr_period = 10  # Adjusted for better balance of noise and accuracy in shorter timeframes
        atr = ta.ATR(dataframe, timeperiod=atr_period)
        dataframe['atr'] = atr
        dataframe['resistance'] = dataframe['high'].rolling(20).max()
        dataframe['support'] = dataframe['low'].rolling(20).min()
        dataframe['donchian_high'] = dataframe['high'].rolling(20).max()
        dataframe['donchian_low'] = dataframe['low'].rolling(20).min()
        adx_threshold_base = 20  # Base ADX threshold
        adx_dynamic_adjustment = dataframe['atr'] / dataframe['close'] * 100  # Adjust ADX based on recent volatility
        dataframe['adx_threshold'] = adx_threshold_base + adx_dynamic_adjustment
        dataframe['adx'] = ta.ADX(dataframe)
        dataframe['adx_signal'] = dataframe['adx'] > dataframe['adx_threshold']
        dataframe['supertrend'] = qtpylib.supertrend(dataframe, period=10, multiplier=3)
        dataframe['volume_mean'] = dataframe['volume'].rolling(10).mean()  # Adjusted rolling window for shorter timeframe
        dataframe['trailing_stop'] = dataframe['close'] - (atr * 3)  # Increased multiplier to reduce premature stops
        return dataframe

    def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        """
        Buy when breakout conditions are met.
        """
        dataframe.loc[
            (
                # Breakout above resistance
                (dataframe['close'] > dataframe['resistance']) &
                # Volume confirms breakout
                (dataframe['volume'] > dataframe['volume_mean'] * 1.5) &
                # Ensure strong trend (ADX threshold)
                (dataframe['adx_signal'] == True)
            ),
            'buy'
        ] = 1

        # Optional re-entry on pullback
        dataframe.loc[
            (
                (dataframe['close'] > dataframe['resistance'] * 0.98) &
                (dataframe['close'] < dataframe['resistance'] * 1.02) &
                (dataframe['volume'] > dataframe['volume_mean'])
            ),
            'buy'
        ] = 1

        return dataframe

    def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        """
        Sell when trend weakens or trailing stop is hit.
        """
        dataframe.loc[
            (
                (dataframe['close'] < dataframe['trailing_stop']) |  # Trailing stop triggered
                (dataframe['adx'] < dataframe['adx_threshold'] * 0.75) |  # Adjusted ADX threshold for weakening trend
                (dataframe['close'] > dataframe['entry_price'] * 1.2)  # Take profit
            ),
            'sell'
        ] = 1

        return dataframe
