# source: https://raw.githubusercontent.com/shatianming5/Agent_market/487b93170d1291655acc04636d2e8eadc42e6da4/workspace/strategies/auto_v103.py
from __future__ import annotations
import sys
from pathlib import Path
import numpy as np
from pandas import DataFrame
_ROOT = Path(__file__).resolve().parents[2]
if str(_ROOT / "src") not in sys.path:
    sys.path.insert(0, str(_ROOT / "src"))
    sys.path.insert(0, str(_ROOT))
from freqtrade.strategy import IStrategy


class Github_shatianming5_Agent_market__auto_v103__20260330_144927(IStrategy):
    INTERFACE_VERSION = 3

    timeframe = "1h"
    can_short = False
    process_only_new_candles = True
    startup_candle_count: int = 60

    use_exit_signal = True
    exit_profit_only = True
    ignore_roi_if_entry_signal = False

    minimal_roi = {
        "0": 0.015,
        "24": 0.010,
        "48": 0.0,
    }
    stoploss = -0.025

    @staticmethod
    def _ema(s, period: int):
        return s.ewm(span=period, adjust=False, min_periods=1).mean()

    @staticmethod
    def _rsi(close, period: int):
        d = close.diff()
        up = d.clip(lower=0.0)
        dn = (-d).clip(lower=0.0)
        au = up.ewm(alpha=1.0 / float(period), adjust=False, min_periods=period).mean()
        ad = dn.ewm(alpha=1.0 / float(period), adjust=False, min_periods=period).mean()
        rs = au / (ad + 1e-12)
        return 100.0 - (100.0 / (1.0 + rs))

    @staticmethod
    def _atr(df: DataFrame, period: int):
        high = df["high"]
        low = df["low"]
        close = df["close"]
        prev_close = close.shift(1)
        tr = np.maximum(high - low, np.maximum((high - prev_close).abs(), (low - prev_close).abs()))
        return tr.ewm(alpha=1.0 / float(period), adjust=False, min_periods=period).mean()

    def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        if dataframe is None or dataframe.empty:
            return dataframe

        close = dataframe["close"]
        vol = dataframe["volume"]

        mid = close.rolling(20, min_periods=20).mean()
        std = close.rolling(20, min_periods=20).std(ddof=0)
        lower = mid - 2.0 * std
        upper = mid + 2.0 * std
        dataframe["bb_mid"] = mid
        dataframe["bb_lower"] = lower
        dataframe["bb_upper"] = upper
        dataframe["bb_width"] = (upper - lower) / (mid + 1e-12)
        dataframe["z"] = (close - mid) / (std + 1e-12)

        dataframe["rsi14"] = self._rsi(close, 14)
        dataframe["rsi2"] = self._rsi(close, 2)

        dataframe["ema20"] = self._ema(close, 20)
        dataframe["ema50"] = self._ema(close, 50)
        dataframe["ema200"] = self._ema(close, 200)
        ema200 = dataframe["ema200"]
        dataframe["ema200_slope_24"] = (ema200 / (ema200.shift(24) + 1e-12)) - 1.0

        atr14 = self._atr(dataframe, 14)
        dataframe["atr14"] = atr14
        dataframe["atr_pct"] = atr14 / (close + 1e-12)

        vma20 = vol.rolling(20, min_periods=20).mean()
        dataframe["vol_ratio"] = vol / (vma20 + 1e-12)

        dataframe["ret1"] = close.pct_change(1)
        dataframe["ret3"] = close.pct_change(3)

        return dataframe

    def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        if dataframe is None or dataframe.empty:
            return dataframe

        vol = dataframe["volume"]
        close = dataframe["close"]

        base = vol > 0

        oversold = (
            (dataframe["ret1"] <= -0.010)
            | (close <= dataframe["bb_lower"] * 0.998)
            | (dataframe["z"] <= -1.8)
        )

        rsi_ok = (dataframe["rsi14"] < 42.0) | (dataframe["rsi2"] < 18.0)

        vol_ok = dataframe["vol_ratio"] >= 0.80

        atr_ok = dataframe["atr_pct"] >= 0.0018

        rebound = (close > dataframe["open"]) | (dataframe["rsi2"] > dataframe["rsi2"].shift(1))

        trend_ok = (dataframe["ema200_slope_24"] > -0.020) | (close > dataframe["ema200"] * 0.88)

        enter = (base & oversold & rsi_ok & vol_ok & atr_ok & rebound & trend_ok).fillna(False)

        dataframe.loc[enter, "enter_long"] = 1
        dataframe.loc[enter, "enter_tag"] = "capitulation_rebound"
        return dataframe

    def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        if dataframe is None or dataframe.empty:
            return dataframe

        vol = dataframe["volume"]
        close = dataframe["close"]

        base = vol > 0

        mean_revert = (close >= dataframe["bb_mid"]) | (dataframe["ret3"] >= 0.010)
        momentum_cool = (dataframe["rsi14"] >= 55.0) | (close >= dataframe["ema50"])

        exit_cond = (base & mean_revert & momentum_cool).fillna(False)

        dataframe.loc[exit_cond, "exit_long"] = 1
        dataframe.loc[exit_cond, "exit_tag"] = "mean_revert_takeprofit"
        return dataframe