# source: https://raw.githubusercontent.com/shamarmoore/TradingEdge/5433eed3626dc7044682f28e30ff11674a7ba528/strategies/TrendFollower.py
"""
Github_shamarmoore_TradingEdge__TrendFollower__20260329_033453 – Freqtrade strategy for BULL_TREND regimes.

Buys pullbacks within confirmed bull trends.
"""
from __future__ import annotations

import sys
import pandas as pd

try:
    from freqtrade.strategy import IStrategy, DecimalParameter, IntParameter
    _FREQTRADE_AVAILABLE = True
except ImportError:
    _FREQTRADE_AVAILABLE = False

    class IStrategy:  # type: ignore[no-redef]
        """Minimal stub so the module is importable without Freqtrade."""
        stoploss: float = -0.05
        minimal_roi: dict = {"0": 0.08}
        timeframe: str = "4h"
        trailing_stop: bool = False
        trailing_stop_positive: float | None = None
        trailing_stop_positive_offset: float = 0.0
        trailing_only_offset_is_reached: bool = False
        process_only_new_candles: bool = True
        use_exit_signal: bool = True
        exit_profit_only: bool = False
        can_short: bool = False
        startup_candle_count: int = 200

        def __init__(self, config: dict | None = None):
            self.config = config or {}

        def populate_indicators(self, dataframe, metadata):  # pragma: no cover
            return dataframe

        def populate_entry_trend(self, dataframe, metadata):  # pragma: no cover
            return dataframe

        def populate_exit_trend(self, dataframe, metadata):  # pragma: no cover
            return dataframe

    class DecimalParameter:  # type: ignore[no-redef]
        def __init__(self, *args, **kwargs):
            self.value = kwargs.get("default", args[1] if len(args) > 1 else 0)

    class IntParameter:  # type: ignore[no-redef]
        def __init__(self, *args, **kwargs):
            self.value = kwargs.get("default", args[1] if len(args) > 1 else 0)


import sys, os
sys.path.insert(0, os.path.join(os.path.dirname(__file__), ".."))

from strategies.helpers.indicators import (
    ema,
    rsi,
    macd,
    volume_sma,
)
from strategies.RegimeDetector import RegimeDetector


class Github_shamarmoore_TradingEdge__TrendFollower__20260329_033453(IStrategy):
    """
    Trend-following strategy active only in BULL_TREND regime.

    Entry logic (all must be true):
        1. Regime == BULL_TREND
        2. RSI_14 crosses below 40 (pullback signal)
        3. Price within 1 % of EMA_21
        4. MACD histogram decreasing but MACD line above signal
        5. Volume on pullback candle below 20-bar average

    Exit logic:
        - Hard stop: -5 %
        - minimal_roi: 8 %
        - Trailing stop activates after 4 % gain
    """

    stoploss = -0.05
    minimal_roi = {"0": 0.08}
    timeframe = "4h"

    trailing_stop = True
    trailing_stop_positive = 0.04
    trailing_stop_positive_offset = 0.05
    trailing_only_offset_is_reached = True

    process_only_new_candles = True
    use_exit_signal = True
    exit_profit_only = False
    can_short = False
    startup_candle_count = 200

    def __init__(self, config: dict | None = None):
        if _FREQTRADE_AVAILABLE:
            super().__init__(config)  # type: ignore[call-arg]
        else:
            self.config = config or {}
        self._regime_detector = RegimeDetector()

    def populate_indicators(self, dataframe: pd.DataFrame, metadata: dict) -> pd.DataFrame:
        # Regime indicators
        dataframe = self._regime_detector.add_indicators(dataframe)
        raw_regime = self._regime_detector.detect_regime(dataframe)
        dataframe["regime"] = self._regime_detector.apply_hysteresis(raw_regime, candles=3)

        # Strategy indicators
        dataframe["ema_21"] = ema(dataframe["close"], 21)
        dataframe["rsi_14"] = rsi(dataframe["close"], 14)

        macd_line, signal_line, histogram = macd(dataframe["close"])
        dataframe["macd"] = macd_line
        dataframe["macd_signal"] = signal_line
        dataframe["macd_hist"] = histogram
        dataframe["macd_hist_prev"] = histogram.shift(1)

        dataframe["vol_avg_20"] = volume_sma(dataframe["volume"], 20)
        dataframe["rsi_prev"] = dataframe["rsi_14"].shift(1)

        return dataframe

    def populate_entry_trend(self, dataframe: pd.DataFrame, metadata: dict) -> pd.DataFrame:
        dataframe["enter_long"] = 0
        dataframe["enter_tag"] = ""

        # Distance from EMA_21 in percent
        ema21_dist = ((dataframe["close"] - dataframe["ema_21"]) / dataframe["ema_21"]).abs()

        conditions = (
            (dataframe["regime"] == "BULL_TREND")
            & (dataframe["rsi_14"] < 40)         # RSI pulled back
            & (dataframe["rsi_prev"] >= 40)       # crossed below 40 (was above previous candle)
            & (ema21_dist <= 0.01)                 # within 1 % of EMA_21
            & (dataframe["macd"] > dataframe["macd_signal"])  # MACD line above signal
            & (dataframe["macd_hist"] < dataframe["macd_hist_prev"])  # histogram decreasing
            & (dataframe["volume"] < dataframe["vol_avg_20"])  # low volume pullback
        )

        dataframe.loc[conditions, "enter_long"] = 1
        dataframe.loc[conditions, "enter_tag"] = "trend_pullback"
        return dataframe

    def populate_exit_trend(self, dataframe: pd.DataFrame, metadata: dict) -> pd.DataFrame:
        dataframe["exit_long"] = 0
        dataframe["exit_tag"] = ""

        # Exit on regime change away from BULL_TREND
        exit_conditions = dataframe["regime"] != "BULL_TREND"
        dataframe.loc[exit_conditions, "exit_long"] = 1
        dataframe.loc[exit_conditions, "exit_tag"] = "regime_change"
        return dataframe
