# source: https://raw.githubusercontent.com/khoand3012/quant-bot/33c60f10a4fb7e5683770ce81c035fa35c897ba2/core/user_data/strategies/swing_v1.py
# directory_url: https://github.com/khoand3012/quant-bot/blob/master/core/user_data/strategies/
# User: khoand3012
# Repository: quant-bot
# --------------------from __future__ import annotations

try:
    from freqtrade.strategy import IStrategy
except ModuleNotFoundError:
    # Lets this module import (and its pure helpers be unit-tested) without a full
    # freqtrade install. In the freqtrade Docker image the real IStrategy is used.
    IStrategy = object  # type: ignore


def atr_stop_price(rate: float, atr: float, mult: float) -> float:
    """Price of an ATR-based stop `mult` ATRs below entry `rate`. Never above entry."""
    return rate - max(0.0, mult) * max(0.0, atr)


class Github_khoand3012_quant_bot__swing_v1__20261006_140411(IStrategy):
    """v1 hypothesis: RSI mean-reversion with a trend filter and an ATR stop, plus a
    momentum entry (fast EMA crossing the trend EMA) so the bot also trades breakouts
    rather than only waiting for dips. Every manifest tunable is read from config so
    M3 can change it via a restart; the EMA lengths and momentum RSI band are fixed."""

    timeframe = "1h"
    can_short = False
    startup_candle_count = 200
    # native config keys (stoploss / minimal_roi / trailing_*) come from config.json;
    # this attribute is only a safety floor — the real stop is custom_stoploss below.
    stoploss = -0.99
    use_custom_stoploss = True

    def _p(self, key: str, default):
        return self.config.get("strategy_params", {}).get(key, default)

    # Fixed (not in the manifest): shorter EMAs than v1's EMA100 so the trend filter
    # admits more bars and the momentum entry reacts within a day or two on 1h candles.
    ema_fast_len = 20
    ema_slow_len = 50
    momentum_rsi_band = (50, 70)    # momentum entries need strength, but not overbought

    def populate_indicators(self, df, metadata: dict):
        import pandas_ta as pta
        df["rsi"] = pta.rsi(df["close"], length=14)
        df["ema_fast"] = pta.ema(df["close"], length=self.ema_fast_len)
        df["ema_slow"] = pta.ema(df["close"], length=self.ema_slow_len)
        df["atr"] = pta.atr(df["high"], df["low"], df["close"], length=14)
        return df

    def populate_entry_trend(self, df, metadata: dict):
        rsi_buy = self._p("rsi_buy_threshold", 40)
        uptrend = df["close"] > df["ema_slow"]
        has_volume = df["volume"] > 0

        # 1) mean-reversion: RSI crossing up through the threshold, only in an uptrend
        rsi_rebound = (
            (df["rsi"] > rsi_buy)
            & (df["rsi"].shift(1) <= rsi_buy)
            & uptrend
            & has_volume
        )
        # 2) momentum: fast EMA crossing above the trend EMA with healthy, non-overbought RSI
        lo, hi = self.momentum_rsi_band
        ema_cross = (
            (df["ema_fast"] > df["ema_slow"])
            & (df["ema_fast"].shift(1) <= df["ema_slow"].shift(1))
            & df["rsi"].between(lo, hi)
            & has_volume
        )
        df.loc[rsi_rebound, ["enter_long", "enter_tag"]] = (1, "rsi_rebound")
        df.loc[ema_cross & ~rsi_rebound, ["enter_long", "enter_tag"]] = (1, "ema_cross")
        return df

    def populate_exit_trend(self, df, metadata: dict):
        # exits handled by minimal_roi, trailing stop, and custom_stoploss
        return df

    def custom_stoploss(self, pair, trade, current_time, current_rate,
                        current_profit, **kwargs) -> float:
        mult = self._p("atr_stop_multiplier", 2.0)
        df, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe)
        if df is None or len(df) == 0:
            return self.stoploss
        atr = float(df["atr"].iloc[-1])
        stop_price = atr_stop_price(trade.open_rate, atr, mult)
        # convert to a ratio relative to current rate for freqtrade
        return (stop_price / current_rate) - 1.0
