# source: https://raw.githubusercontent.com/manasipatel090-commits/my-freqtrade-strategy/e6cb6057673cbb31cd2bdbfc9305c8a0c7500b1e/CombinedStrategy.py
from freqtrade.strategy import IStrategy, IntParameter, DecimalParameter, merge_informative_pair
from pandas import DataFrame
import talib.abstract as ta


class Github_manasipatel090_commits_my_freqtrade_strategy__CombinedStrategy__20260320_062358(IStrategy):
    """
    Combined TrendFollow + Scalping Strategy
    ----------------------------------------
    Entry : EMA crossover + MACD + ADX (trend)
            confirmed by BB position + Stochastic (timing)
    Filter: 1h trend direction
    Exit  : Trailing stop + ROI
    """

    INTERFACE_VERSION = 3
    timeframe = "5m"
    can_short = True

    # ── ROI ────────────────────────────────────────────────────────────────
    minimal_roi = {
        "0":   0.05,
        "60":  0.03,
        "120": 0.02
    }

    # ── Risk management ────────────────────────────────────────────────────
    stoploss = -0.02
    trailing_stop = True
    trailing_stop_positive = 0.01
    trailing_stop_positive_offset = 0.015
    trailing_only_offset_is_reached = True

    # ── Buy parameters (TrendFollow) ───────────────────────────────────────
    ema_fast = IntParameter(5, 20, default=10, space="buy")
    ema_slow = IntParameter(15, 40, default=29, space="buy")
    rsi_long_min = IntParameter(30, 60, default=53, space="buy")
    rsi_long_max = IntParameter(55, 80, default=60, space="buy")
    adx_threshold = IntParameter(10, 35, default=15, space="buy")

    # ── Buy parameters (Scalping confirmation) ─────────────────────────────
    stoch_long_max = IntParameter(30, 60, default=45, space="buy")
    bb_zone = DecimalParameter(0.3, 0.8, default=0.6, space="buy")

    # ── Sell parameters ────────────────────────────────────────────────────
    ema_fast_short = IntParameter(5, 20, default=10, space="sell")
    ema_slow_short = IntParameter(15, 40, default=29, space="sell")
    rsi_short_min = IntParameter(40, 65, default=46, space="sell")
    rsi_short_max = IntParameter(55, 80, default=63, space="sell")
    adx_threshold_short = IntParameter(10, 35, default=15, space="sell")
    stoch_short_min = IntParameter(50, 80, default=55, space="sell")

    startup_candle_count: int = 100

    def informative_pairs(self):
        pairs = self.dp.current_whitelist()
        return [(pair, "1h") for pair in pairs]

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

        # ── EMA ────────────────────────────────────────────────────────────
        dataframe["ema_fast"] = ta.EMA(dataframe, timeperiod=self.ema_fast.value)
        dataframe["ema_slow"] = ta.EMA(dataframe, timeperiod=self.ema_slow.value)
        dataframe["ema_fast_short"] = ta.EMA(dataframe, timeperiod=self.ema_fast_short.value)
        dataframe["ema_slow_short"] = ta.EMA(dataframe, timeperiod=self.ema_slow_short.value)

        # ── MACD ───────────────────────────────────────────────────────────
        macd = ta.MACD(dataframe, fastperiod=12, slowperiod=26, signalperiod=9)
        dataframe["macd_hist"] = macd["macdhist"]

        # ── RSI ────────────────────────────────────────────────────────────
        dataframe["rsi"] = ta.RSI(dataframe, timeperiod=14)

        # ── ADX ────────────────────────────────────────────────────────────
        dataframe["adx"] = ta.ADX(dataframe, timeperiod=14)

        # ── Bollinger Bands ────────────────────────────────────────────────
        bollinger = ta.BBANDS(dataframe, timeperiod=20, nbdevup=2.0, nbdevdn=2.0)
        dataframe["bb_upper"] = bollinger["upperband"]
        dataframe["bb_lower"] = bollinger["lowerband"]
        dataframe["bb_mid"]   = bollinger["middleband"]
        dataframe["bb_range"] = dataframe["bb_upper"] - dataframe["bb_lower"]
        dataframe["bb_pos"]   = (
            (dataframe["close"] - dataframe["bb_lower"]) / dataframe["bb_range"]
        )

        # ── Stochastic ─────────────────────────────────────────────────────
        stoch = ta.STOCH(dataframe, fastk_period=14, slowk_period=3, slowd_period=3)
        dataframe["stoch_k"] = stoch["slowk"]
        dataframe["stoch_d"] = stoch["slowd"]

        # ── 1h trend filter ────────────────────────────────────────────────
        informative_1h = self.dp.get_pair_dataframe(
            pair=metadata["pair"], timeframe="1h"
        )
        informative_1h["ema_1h_fast"] = ta.EMA(informative_1h, timeperiod=9)
        informative_1h["ema_1h_slow"] = ta.EMA(informative_1h, timeperiod=21)
        informative_1h["trend_up"] = (
            informative_1h["ema_1h_fast"] > informative_1h["ema_1h_slow"]
        ).astype(int)

        dataframe = merge_informative_pair(
            dataframe, informative_1h, self.timeframe, "1h", ffill=True
        )

        return dataframe

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

        # ── LONG entry ─────────────────────────────────────────────────────
        # TrendFollow conditions
        trend_long = (
            (dataframe["trend_up_1h"] == 1) &
            (dataframe["ema_fast"] > dataframe["ema_slow"]) &
            (dataframe["ema_fast"].shift(1) <= dataframe["ema_slow"].shift(1)) &
            (dataframe["macd_hist"] > 0) &
            (dataframe["macd_hist"].shift(1) <= 0) &
            (dataframe["rsi"] >= self.rsi_long_min.value) &
            (dataframe["rsi"] <= self.rsi_long_max.value) &
            (dataframe["adx"] > self.adx_threshold.value)
        )

        # Scalping confirmation — price in lower BB zone, stoch not overbought
        # LONG - scalping confirmation

        scalp_confirm_long = (
            (dataframe["bb_pos"] <= self.bb_zone.value + 0.3) &
            (dataframe["stoch_k"] <= self.stoch_long_max.value + 20)
        )

        dataframe.loc[
            (trend_long & scalp_confirm_long & (dataframe["volume"] > 0)),
            ["enter_long", "enter_tag"]
        ] = 1, "combined_long"

        # ── SHORT entry ────────────────────────────────────────────────────
        # TrendFollow conditions
        trend_short = (
            (dataframe["trend_up_1h"] == 0) &
            (dataframe["ema_fast_short"] < dataframe["ema_slow_short"]) &
            (dataframe["ema_fast_short"].shift(1) >= dataframe["ema_slow_short"].shift(1)) &
            (dataframe["macd_hist"] < 0) &
            (dataframe["macd_hist"].shift(1) >= 0) &
            (dataframe["rsi"] >= self.rsi_short_min.value) &
            (dataframe["rsi"] <= self.rsi_short_max.value) &
            (dataframe["adx"] > self.adx_threshold_short.value)
        )

        # Scalping confirmation — price in upper BB zone, stoch not oversold
      # SHORT - scalping confirmation
        scalp_confirm_short = (
            (dataframe["bb_pos"] >= 1 - self.bb_zone.value - 0.3) &
            (dataframe["stoch_k"] >= self.stoch_short_min.value - 20)
        )

        dataframe.loc[
            (trend_short & scalp_confirm_short & (dataframe["volume"] > 0)),
            ["enter_short", "enter_tag"]
        ] = 1, "combined_short"

        return dataframe

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