# source: https://raw.githubusercontent.com/shatianming5/Agent_market/8c0fefbce46f5910f461758b1284d50b395381d7/workspace/strategies/auto_v401.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_v401__20260330_154001(IStrategy):
    INTERFACE_VERSION = 3

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

    minimal_roi = {"0": 0.03, "12": 0.015, "36": 0.0}
    stoploss = -0.05
    use_exit_signal = True
    exit_profit_only = False
    ignore_roi_if_entry_signal = False

    @staticmethod
    def _rsi(close, period: int = 14):
        d = close.diff()
        gain = d.clip(lower=0.0)
        loss = (-d).clip(lower=0.0)
        avg_gain = gain.ewm(alpha=1.0 / period, adjust=False).mean()
        avg_loss = loss.ewm(alpha=1.0 / period, adjust=False).mean()
        rs = avg_gain / (avg_loss + 1e-12)
        return 100.0 - (100.0 / (1.0 + rs))

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

        close = dataframe["close"].astype(float)
        low = dataframe["low"].astype(float)
        volume = dataframe["volume"].astype(float)

        rsi = self._rsi(close, 14)
        n = 20
        mid = close.rolling(n).mean()
        sd = close.rolling(n).std(ddof=0)
        upper = mid + 2.0 * sd
        lower = mid - 2.0 * sd
        bb_width = (upper - lower) / (mid.replace(0.0, np.nan))

        vol_mean = volume.rolling(n).mean()
        vol_ratio = volume / (vol_mean.replace(0.0, np.nan))

        ret1 = close.pct_change()
        ret20 = close / close.shift(20) - 1.0
        vol_ann = ret1.rolling(20).std(ddof=0) * np.sqrt(24.0 * 365.0)

        w = 10
        low_w = low.rolling(w).min()
        low_prev = low.shift(w).rolling(w).min()
        rsi_low_w = rsi.rolling(w).min()
        rsi_low_prev = rsi.shift(w).rolling(w).min()

        div1 = (low_w < (low_prev * 0.999)) & (rsi_low_w > (rsi_low_prev + 0.5)) & (close <= (low_w * 1.01))
        div2 = (low < (low.shift(5) * 0.999)) & (rsi > (rsi.shift(5) + 0.5)) & (rsi < 45.0)

        dataframe["rsi"] = rsi
        dataframe["bb_middle"] = mid
        dataframe["bb_upper"] = upper
        dataframe["bb_lower"] = lower
        dataframe["bb_width"] = bb_width
        dataframe["volume_ratio"] = vol_ratio
        dataframe["ret1"] = ret1
        dataframe["ret20"] = ret20
        dataframe["vol_ann"] = vol_ann
        dataframe["div_bull"] = (div1 | div2).astype(int)

        dataframe = dataframe.replace([np.inf, -np.inf], np.nan).ffill().fillna(0.0)
        return dataframe

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

        rsi = dataframe["rsi"]
        close = dataframe["close"]
        lower = dataframe["bb_lower"]
        mid = dataframe["bb_middle"]

        regime_ok = dataframe["ret20"] <= 0.03  # soft filter: only skip strong rebound
        vol_ok = (dataframe["volume"] > 0) & (dataframe["volume_ratio"] > 0.5)
        vol_guard = dataframe["vol_ann"] < 0.60

        oversold = (rsi < 38.0) | (close < (lower * 1.02))
        bb_pressure = close < (mid * 0.995)
        divergence = dataframe["div_bull"] > 0

        enter = regime_ok & vol_ok & vol_guard & divergence & oversold & bb_pressure
        dataframe.loc[enter, "enter_long"] = 1
        return dataframe

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

        close = dataframe["close"]
        rsi = dataframe["rsi"]
        mid = dataframe["bb_middle"]
        upper = dataframe["bb_upper"]

        take_mid = (close > mid) & (rsi > 48.0)
        take_upper = close > (upper * 0.995)
        rebound_spike = (dataframe["ret1"] > 0.012) & (close > mid) & (rsi > 52.0)
        exit_cond = (dataframe["volume"] > 0) & (take_mid | take_upper | rebound_spike)

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