# source: https://raw.githubusercontent.com/Chris6622/class-AdvancedFuturesSwingStrategy-IStrategy-/786cfa2c323feab11c072706b06cb9e4243af820/user_data/strategies/blackflag_ewo_eth.py
# --- Do not remove these libs ---
from freqtrade.strategy import IStrategy
from pandas import DataFrame
import talib.abstract as ta
import numpy as np
import freqtrade.vendor.qtpylib.indicators as qtpylib
from datetime import datetime
from freqtrade.persistence import Trade
from typing import Any

# --- Import Strategy Parameters ---
from freqtrade.strategy import (
    DecimalParameter,
    IntParameter,
    RealParameter,
)

# --------------------------------

class Github_Chris6622_class_AdvancedFuturesSwingStrategy_IStrategy___blackflag_ewo_eth__20260208_084829(IStrategy):
    """
    Final, robust version of the futures strategy with fully hyperoptable parameters.
    """
    # Strategy interface version - Required
    INTERFACE_VERSION = 3

    # --- Order Type Configuration ---
    order_types = {
        'entry': 'market',
        'exit': 'market',
        'stoploss': 'market',
        'stoploss_on_exchange': False
    }

    # --- Hyperoptable Parameters (defined inline) ---
    leverage_opt = RealParameter(1.0, 5.0, default=1.4, step=0.1, space="buy", optimize=True)
    stoploss_opt = RealParameter(-0.15, -0.05, default=-0.10, space="stoploss", optimize=True)

    # ROI table parameters
    roi_t1 = IntParameter(10, 240, default=120, space="roi", optimize=True)
    roi_t2 = IntParameter(240, 720, default=480, space="roi", optimize=True)
    roi_p1 = DecimalParameter(0.01, 0.10, default=0.05, decimals=2, space="roi", optimize=True)
    roi_p2 = DecimalParameter(0.01, 0.05, default=0.02, decimals=2, space="roi", optimize=True)

    # Indicator Parameters
    bf_atr_period = IntParameter(10, 50, default=27, space="buy", optimize=True)
    bf_multiplier = DecimalParameter(2.0, 7.0, default=2.4, decimals=1, space="buy", optimize=True)
    ewo_fast_ma = IntParameter(3, 10, default=10, space="buy", optimize=True)
    ewo_slow_ma = IntParameter(20, 50, default=38, space="buy", optimize=True)
    adx_period = IntParameter(10, 30, default=19, space="buy", optimize=True)
    adx_threshold = IntParameter(15, 35, default=22, space="buy", optimize=True)
    entry_pullback_pct = DecimalParameter(0.005, 0.03, default=0.03, decimals=3, space="buy", optimize=True)

    # --- Static Strategy Settings (will be overridden by hyperopt values at runtime) ---
    timeframe = '1h'
    stoploss = -0.299
    minimal_roi = {
        "0": 0.551,
        "445": 0.156,
        "590": 0.052,
        "1391": 0
    }

    # Trailing stop:
    trailing_stop = True
    trailing_stop_positive = 0.243
    trailing_stop_positive_offset = 0.272
    trailing_only_offset_is_reached = False
    
    # --- Dynamic Leverage Method ---
    def leverage(self, pair: str, current_time: datetime, current_rate: float,
                 proposed_leverage: float, max_leverage: float, side: str,
                 **kwargs) -> float:
        """
        Calculate leverage for a trade.
        """
        return self.leverage_opt.value

    def bot_loop_start(self, **kwargs) -> None:
        """
        Called once at startup.
        Assigns hyperopt values to core strategy attributes.
        """
        super().bot_loop_start(**kwargs)
        self.stoploss = self.stoploss_opt.value

        # --- FIX: Use integers for ROI dictionary keys ---
        self.minimal_roi = {
            0: self.roi_p1.value + self.roi_p2.value,
            self.roi_t2.value: self.roi_p1.value,
            self.roi_t1.value + self.roi_t2.value: 0
        }

    # --- Strategy Logic ---
    def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        dataframe['bf_atr'] = ta.ATR(dataframe, timeperiod=self.bf_atr_period.value)
        dataframe['bf_midline'] = ta.SMA(
            (dataframe['high'] + dataframe['low']) / 2,
            timeperiod=self.bf_atr_period.value
        )
        dataframe['bf_upperband'] = dataframe['bf_midline'] + (self.bf_multiplier.value * dataframe['bf_atr'])
        dataframe['bf_lowerband'] = dataframe['bf_midline'] - (self.bf_multiplier.value * dataframe['bf_atr'])
        dataframe['bf_trend'] = 0
        dataframe['bf_support'] = np.nan
        dataframe['bf_resistance'] = np.nan
        for i in range(1, len(dataframe)):
            if dataframe.loc[i-1, 'bf_trend'] == 1:
                if dataframe.loc[i, 'close'] < dataframe.loc[i-1, 'bf_lowerband']:
                    dataframe.loc[i, 'bf_trend'] = -1
                else:
                    dataframe.loc[i, 'bf_trend'] = 1
            elif dataframe.loc[i-1, 'bf_trend'] == -1:
                if dataframe.loc[i, 'close'] > dataframe.loc[i-1, 'bf_upperband']:
                    dataframe.loc[i, 'bf_trend'] = 1
                else:
                    dataframe.loc[i, 'bf_trend'] = -1
            else:
                if dataframe.loc[i, 'close'] > dataframe.loc[i-1, 'bf_midline']:
                    dataframe.loc[i, 'bf_trend'] = 1
                else:
                    dataframe.loc[i, 'bf_trend'] = -1

            if dataframe.loc[i, 'bf_trend'] == 1:
                dataframe.loc[i, 'bf_support'] = dataframe.loc[i, 'bf_lowerband']
            else:
                dataframe.loc[i, 'bf_resistance'] = dataframe.loc[i, 'bf_upperband']

        dataframe['ewo'] = ta.EMA(dataframe, timeperiod=self.ewo_fast_ma.value) - \
                         ta.EMA(dataframe, timeperiod=self.ewo_slow_ma.value)
        
        dataframe['adx'] = ta.ADX(dataframe, timeperiod=self.adx_period.value)
        return dataframe

    def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        long_pullback_zone = dataframe['bf_support'] * (1 + self.entry_pullback_pct.value)
        short_pullback_zone = dataframe['bf_resistance'] * (1 - self.entry_pullback_pct.value)

        dataframe.loc[
            (
                (dataframe['adx'] > self.adx_threshold.value) &
                (dataframe['bf_trend'] == 1) &
                (dataframe['ewo'] > 0) &
                (dataframe['low'] <= long_pullback_zone) &
                (dataframe['close'] > dataframe['open']) &
                (dataframe['volume'] > 0)
            ),
            'enter_long'] = 1

        dataframe.loc[
            (
                (dataframe['adx'] > self.adx_threshold.value) &
                (dataframe['bf_trend'] == -1) &
                (dataframe['ewo'] < 0) &
                (dataframe['high'] >= short_pullback_zone) &
                (dataframe['close'] < dataframe['open']) &
                (dataframe['volume'] > 0)
            ),
            'enter_short'] = 1
        return dataframe

    def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        dataframe.loc[
            (qtpylib.crossed_below(dataframe['ewo'], 0)),
            'exit_long'] = 1

        dataframe.loc[
            (qtpylib.crossed_above(dataframe['ewo'], 0)),
            'exit_short'] = 1
        return dataframe

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

        if trade.is_long:
            support_level = last_candle['bf_support']
            if support_level and support_level > 0:
                return (current_rate - support_level) / current_rate
        
        elif not trade.is_long:
            resistance_level = last_candle['bf_resistance']
            if resistance_level and resistance_level > 0:
                return (resistance_level - current_rate) / current_rate
        
        return 1