# source: https://raw.githubusercontent.com/4tie/fortiesr/d5cc40760e3cbaff381058a0ea8733d2be469578/user_data/strategies/AutoQuantRobustV4.py
from freqtrade.strategy import IStrategy, IntParameter, DecimalParameter
from pandas import DataFrame
import talib.abstract as ta
import freqtrade.vendor.qtpylib.indicators as qtpylib


class Github_4tie_fortiesr__AutoQuantRobustV4__20260709_213639(IStrategy):
    """
    Github_4tie_fortiesr__AutoQuantRobustV4__20260709_213639 - A robust trend-following strategy with multiple filters.
    
    Strategy Logic:
    1. Trend Filter: EMA trend direction (200 EMA)
    2. Momentum Confirmation: RSI overbought/oversold + MACD crossover
    3. Volatility Filter: ATR-based volatility check
    4. Volume Filter: Volume spike confirmation
    5. Risk Control: ATR-based dynamic stoploss
    
    This strategy is designed to avoid overfitting by using proven indicators
    with sensible default ranges for optimization.
    """
    
    INTERFACE_VERSION: int = 3
    
    # ROI Table - realistic profit targets
    minimal_roi = {
        "0": 0.06,   # 6% profit target immediately
        "60": 0.04,  # 4% after 1 hour
        "240": 0.02, # 2% after 4 hours
        "720": 0.01, # 1% after 12 hours
        "1440": 0,   # exit after 24 hours if not stopped
    }
    
    # Stoploss - ATR-based will be calculated dynamically
    stoploss = -0.10  # 10% default, will be adjusted by ATR
    
    # Timeframe - 1h for good balance of signal frequency and noise reduction
    timeframe = "1h"
    
    # Trailing stop - enabled for trend-following
    trailing_stop = True
    trailing_stop_positive = 0.015  # 1.5% trailing stop when in profit
    trailing_stop_positive_offset = 0.025  # Activate trailing stop at 2.5% profit
    trailing_only_offset_is_reached = True
    
    # Process only new candles for efficiency
    process_only_new_candles = True
    startup_candle_count = 200  # Need enough data for indicators
    
    # Can short - disabled for simplicity and risk management
    can_short = False
    
    # ── Tunable Parameters for Optimizer ──
    
    # Trend Filter Parameters
    trend_ema_period = IntParameter(100, 200, default=200, space="buy", optimize=True)
    
    # Momentum Parameters  
    rsi_period = IntParameter(10, 20, default=14, space="buy", optimize=True)
    rsi_oversold = IntParameter(20, 35, default=30, space="buy", optimize=True)
    rsi_overbought = IntParameter(65, 80, default=70, space="sell", optimize=True)
    
    macd_fast = IntParameter(8, 15, default=12, space="buy", optimize=True)
    macd_slow = IntParameter(20, 30, default=26, space="buy", optimize=True)
    macd_signal = IntParameter(5, 10, default=9, space="buy", optimize=True)
    
    # Volatility Filter Parameters
    atr_period = IntParameter(10, 20, default=14, space="buy", optimize=True)
    atr_multiplier = DecimalParameter(1.0, 3.0, default=1.5, decimals=1, space="buy", optimize=True)
    
    # Volume Filter Parameters
    volume_multiplier = DecimalParameter(1.0, 3.0, default=1.5, decimals=1, space="buy", optimize=True)
    volume_period = IntParameter(15, 30, default=20, space="buy", optimize=True)
    
    # Stoploss Parameters
    atr_stoploss_multiplier = DecimalParameter(1.5, 3.0, default=2.0, decimals=1, space="stoploss", optimize=True)
    
    def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        """
        Calculate all indicators needed for the strategy.
        """
        
        # Trend Filter - EMA
        dataframe["ema_trend"] = ta.EMA(dataframe, timeperiod=self.trend_ema_period.value)
        
        # Momentum Indicators
        dataframe["rsi"] = ta.RSI(dataframe, timeperiod=self.rsi_period.value)
        
        macd = ta.MACD(dataframe, fastperiod=self.macd_fast.value, 
                       slowperiod=self.macd_slow.value, 
                       signalperiod=self.macd_signal.value)
        dataframe["macd"] = macd["macd"]
        dataframe["macd_signal"] = macd["macdsignal"]
        dataframe["macd_hist"] = macd["macdhist"]
        
        # Volatility Indicator - ATR
        dataframe["atr"] = ta.ATR(dataframe, timeperiod=self.atr_period.value)
        
        # Volume Indicator
        dataframe["volume_mean"] = dataframe["volume"].rolling(window=self.volume_period.value).mean()
        dataframe["volume_ratio"] = dataframe["volume"] / dataframe["volume_mean"]
        
        # Dynamic ATR-based stoploss
        dataframe["atr_stoploss"] = dataframe["atr"] * self.atr_stoploss_multiplier.value
        
        return dataframe

    def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        """
        Entry conditions with multiple filters for robustness.
        """
        # Condition 1: Trend Filter - Price above long-term EMA (uptrend)
        trend_up = dataframe["close"] > dataframe["ema_trend"]
        
        # Condition 2: Momentum - RSI oversold (buying opportunity)
        rsi_oversold = dataframe["rsi"] < self.rsi_oversold.value
        
        # Condition 3: Momentum - MACD crossover (bullish signal)
        macd_bullish = (
            (dataframe["macd"] > dataframe["macd_signal"]) &  # MACD above signal
            (dataframe["macd_hist"] > 0)  # Positive histogram
        )
        
        # Condition 4: Volatility Filter - Not extremely volatile (avoid panic buys)
        volatility_ok = dataframe["atr"] < (dataframe["close"] * 0.05)  # ATR less than 5% of price
        
        # Condition 5: Volume Filter - Volume spike (confirmation)
        volume_spike = dataframe["volume_ratio"] > self.volume_multiplier.value
        
        # Combine all conditions
        dataframe.loc[
            trend_up & 
            rsi_oversold & 
            macd_bullish & 
            volatility_ok & 
            volume_spike,
            "enter_long"
        ] = 1
        
        return dataframe

    def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        """
        Exit conditions with proper profit taking and loss cutting.
        """
        # Condition 1: Momentum - RSI overbought (take profit)
        rsi_overbought = dataframe["rsi"] > self.rsi_overbought.value
        
        # Condition 2: Momentum - MACD bearish crossover (exit signal)
        macd_bearish = (
            (dataframe["macd"] < dataframe["macd_signal"]) &  # MACD below signal
            (dataframe["macd_hist"] < 0)  # Negative histogram
        )
        
        # Condition 3: Trend Break - Price below trend EMA
        trend_break = dataframe["close"] < dataframe["ema_trend"]
        
        # Combine exit conditions (any one triggers exit)
        dataframe.loc[
            rsi_overbought | 
            macd_bearish | 
            trend_break,
            "exit_long"
        ] = 1
        
        return dataframe

    def custom_stoploss(self, pair: str, trade, current_time, current_rate, 
                        current_profit, **kwargs) -> float:
        """
        Custom ATR-based stoploss for dynamic risk management.
        """
        dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe)
        last_candle = dataframe.iloc[-1].squeeze()
        
        # Use ATR-based stoploss if available, otherwise use default
        if "atr_stoploss" in last_candle and not last_candle["atr_stoploss"] == 0:
            atr_stop = -(last_candle["atr_stoploss"] / last_candle["close"])
            # Use the wider of default stoploss and ATR stoploss
            return max(self.stoploss, atr_stop)
        
        return self.stoploss
