# source: https://raw.githubusercontent.com/xyd945/first-duck-trade/9e85ef5d07e5e3b71afc1604b1023b6350b42e82/user_data/strategies/MomentumTrendStrategy.py
"""
Github_xyd945_first_duck_trade__MomentumTrendStrategy__20260524_092701 — EMA crossover trend-following strategy.

Designed to complement LiquiditySweepStrategy (which works in ranging markets)
by capturing directional moves in trending markets.

Entry (Long):
  1. EMA 20 crosses above EMA 50 (golden cross)
  2. ADX > 25 (confirming trend strength)
  3. Close above EMA 200 (higher timeframe trend alignment)
  4. Volume above average (institutional participation)

Exit (Long):
  1. EMA 20 crosses below EMA 50 (death cross)
  2. RSI > 80 (overbought)
  3. ATR-based trailing stop

Custom Stoploss:
  Dynamic stoploss at 2x ATR below entry price.
"""

from freqtrade.strategy import IStrategy, IntParameter, DecimalParameter, BooleanParameter
from pandas import DataFrame
import pandas_ta as ta
import numpy as np


class Github_xyd945_first_duck_trade__MomentumTrendStrategy__20260524_092701(IStrategy):
    """
    Trend-following strategy using EMA crossovers with ADX confirmation.
    Best in trending/breakout regimes. Complements LiquiditySweepStrategy.
    """

    INTERFACE_VERSION = 3

    # ==========================================================================
    # Strategy Settings
    # ==========================================================================
    minimal_roi = {
        "0": 0.20,    # 20% at any time
        "120": 0.10,  # 10% after 2 hours
        "360": 0.05,  # 5% after 6 hours
    }

    stoploss = -0.06  # -6% fallback
    use_custom_stoploss = True
    timeframe = '1h'
    startup_candle_count = 250

    # ==========================================================================
    # HYPEROPT PARAMETERS
    # ==========================================================================

    # EMA periods
    ema_fast = IntParameter(10, 30, default=20, space='buy', optimize=True)
    ema_slow = IntParameter(40, 80, default=50, space='buy', optimize=True)
    ema_trend = IntParameter(150, 250, default=200, space='buy', optimize=True)

    # ADX filter
    adx_threshold = IntParameter(20, 35, default=25, space='buy', optimize=True)

    # Volume filter
    use_vol_filter = BooleanParameter(default=True, space='buy', optimize=True)
    vol_ma_length = IntParameter(10, 50, default=20, space='buy', optimize=True)
    vol_multiplier = DecimalParameter(1.0, 2.5, default=1.2, decimals=1, space='buy', optimize=True)

    # ATR for stoploss
    atr_length = IntParameter(10, 30, default=14, space='buy', optimize=True)
    atr_multiplier = DecimalParameter(1.5, 3.5, default=2.0, decimals=1, space='buy', optimize=True)

    # RSI exit
    rsi_exit_threshold = IntParameter(70, 90, default=80, space='sell', optimize=True)

    # ==========================================================================
    # INDICATORS
    # ==========================================================================

    def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:

        # EMAs
        dataframe['ema_fast'] = ta.ema(dataframe['close'], length=self.ema_fast.value)
        dataframe['ema_slow'] = ta.ema(dataframe['close'], length=self.ema_slow.value)
        dataframe['ema_trend'] = ta.ema(dataframe['close'], length=self.ema_trend.value)

        # ADX
        adx_result = ta.adx(dataframe['high'], dataframe['low'], dataframe['close'], length=14)
        dataframe['adx'] = adx_result['ADX_14']

        # RSI
        dataframe['rsi'] = ta.rsi(dataframe['close'], length=14)

        # ATR for dynamic stoploss
        dataframe['atr'] = ta.atr(
            dataframe['high'], dataframe['low'], dataframe['close'],
            length=self.atr_length.value
        )

        # Volume MA
        dataframe['vol_ma'] = dataframe['volume'].rolling(
            window=self.vol_ma_length.value
        ).mean()

        # EMA crossover signals (shifted by 1 to detect the cross candle)
        dataframe['ema_cross_up'] = (
            (dataframe['ema_fast'] > dataframe['ema_slow']) &
            (dataframe['ema_fast'].shift(1) <= dataframe['ema_slow'].shift(1))
        )
        dataframe['ema_cross_down'] = (
            (dataframe['ema_fast'] < dataframe['ema_slow']) &
            (dataframe['ema_fast'].shift(1) >= dataframe['ema_slow'].shift(1))
        )

        return dataframe

    # ==========================================================================
    # ENTRY LOGIC
    # ==========================================================================

    def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        conditions = (
            # EMA golden cross occurred within last 3 candles
            (dataframe['ema_cross_up'] |
             dataframe['ema_cross_up'].shift(1) |
             dataframe['ema_cross_up'].shift(2)) &

            # ADX confirms trend strength
            (dataframe['adx'] > self.adx_threshold.value) &

            # Price above long-term trend
            (dataframe['close'] > dataframe['ema_trend']) &

            # Volume filter
            (
                ~self.use_vol_filter.value |
                (dataframe['volume'] > dataframe['vol_ma'] * self.vol_multiplier.value)
            ) &

            # Safety check
            (dataframe['volume'] > 0)
        )

        dataframe.loc[conditions, 'enter_long'] = 1
        return dataframe

    # ==========================================================================
    # EXIT LOGIC
    # ==========================================================================

    def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        # EMA death cross
        death_cross = dataframe['ema_cross_down']

        # RSI overbought
        rsi_exit = dataframe['rsi'] > self.rsi_exit_threshold.value

        dataframe.loc[
            (death_cross | rsi_exit) &
            (dataframe['volume'] > 0),
            'exit_long'
        ] = 1

        return dataframe

    # ==========================================================================
    # CUSTOM STOPLOSS (ATR-based)
    # ==========================================================================

    def custom_stoploss(
        self,
        pair: str,
        trade: 'Trade',
        current_time: 'datetime',
        current_rate: float,
        current_profit: float,
        after_fill: bool,
        **kwargs
    ) -> float:
        dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe)

        if dataframe.empty:
            return -1

        # Get ATR at entry time
        entry_candle = dataframe.loc[dataframe['date'] <= trade.open_date_utc]
        if entry_candle.empty:
            return -1

        atr_value = entry_candle.iloc[-1]['atr']
        if pd.isna(atr_value) or atr_value <= 0:
            return -1

        # Stop at entry_price - (ATR * multiplier)
        stop_price = trade.open_rate - (atr_value * self.atr_multiplier.value)
        stoploss_ratio = (stop_price - trade.open_rate) / trade.open_rate

        if stoploss_ratio >= 0:
            return -1

        return stoploss_ratio
