# source: https://raw.githubusercontent.com/shatianming5/Agent_market/2154e7914e43a9585737ea9303cd4bd7f45995bc/workspace/strategies/auto_v203.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_v203__20260330_151458(IStrategy):
    INTERFACE_VERSION = 3

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

    minimal_roi = {"0": 0.02, "240": 0.006, "720": 0.0}
    stoploss = -0.05
    use_exit_signal = True

    trailing_stop = True
    trailing_stop_positive = 0.015
    trailing_stop_positive_offset = 0.02
    trailing_only_offset_is_reached = True

    rsi_period = 14
    bb_period = 20
    bb_std = 2.0
    ema_fast = 20
    ema_slow = 50

    rsi_buy_1 = 38.0
    rsi_buy_2 = 45.0
    rsi_sell = 68.0

    min_bb_width = 0.006
    min_vol_ratio = 0.7
    max_downtrend_dist = 0.97

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

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

        ret1 = close.pct_change()
        dataframe["ret1"] = ret1.replace([np.inf, -np.inf], np.nan).ffill().fillna(0.0)

        delta = close.diff()
        gain = delta.clip(lower=0.0)
        loss = (-delta).clip(lower=0.0)
        alpha = 1.0 / float(self.rsi_period)
        avg_gain = gain.ewm(alpha=alpha, adjust=False).mean()
        avg_loss = loss.ewm(alpha=alpha, adjust=False).mean()
        rs = avg_gain / (avg_loss + 1e-10)
        dataframe["rsi"] = (100.0 - (100.0 / (1.0 + rs))).replace([np.inf, -np.inf], np.nan).ffill().fillna(0.0)
        dataframe["rsi_chg"] = dataframe["rsi"].diff().replace([np.inf, -np.inf], np.nan).ffill().fillna(0.0)

        sma = close.rolling(self.bb_period).mean()
        std = close.rolling(self.bb_period).std(ddof=0)
        dataframe["bb_middle"] = sma
        dataframe["bb_upper"] = sma + self.bb_std * std
        dataframe["bb_lower"] = sma - self.bb_std * std
        dataframe["bb_width"] = ((dataframe["bb_upper"] - dataframe["bb_lower"]) / (sma + 1e-10)).replace(
            [np.inf, -np.inf], np.nan
        ).ffill().fillna(0.0)

        dataframe["vol_sma"] = vol.rolling(20).mean()
        dataframe["vol_ratio"] = (vol / (dataframe["vol_sma"] + 1e-10)).replace([np.inf, -np.inf], np.nan).ffill().fillna(0.0)

        dataframe["ema_fast"] = close.ewm(span=self.ema_fast, adjust=False).mean()
        dataframe["ema_slow"] = close.ewm(span=self.ema_slow, adjust=False).mean()

        dataframe["bb_pos"] = ((close - dataframe["bb_lower"]) / (dataframe["bb_upper"] - dataframe["bb_lower"] + 1e-10)).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

        base = (
            (dataframe["volume"] > 0)
            & (dataframe["bb_width"] > self.min_bb_width)
            & (dataframe["vol_ratio"] > self.min_vol_ratio)
            & (dataframe["close"] > dataframe["ema_slow"] * self.max_downtrend_dist)
        )

        overshoot = dataframe["close"] < (dataframe["bb_lower"] * 1.0015)

        entry_a = base & overshoot & (dataframe["rsi"] < self.rsi_buy_1)
        entry_b = base & overshoot & (dataframe["rsi"] < self.rsi_buy_2) & (dataframe["ret1"] < -0.006)
        entry_c = base & overshoot & (dataframe["rsi"] < (self.rsi_buy_2 + 2.0)) & (dataframe["rsi_chg"] > 0.0) & (
            dataframe["bb_pos"] < 0.08
        )

        cond = entry_a | entry_b | entry_c
        dataframe.loc[cond, ["enter_long", "enter_tag"]] = (1, "rsi_bb_mr_trail_v203")
        return dataframe

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

        base = dataframe["volume"] > 0

        tp_soft = (dataframe["close"] > dataframe["bb_middle"]) & (dataframe["rsi"] > 55.0) & (dataframe["bb_pos"] > 0.45)
        tp_strong = (dataframe["close"] > dataframe["bb_upper"] * 0.995) | (dataframe["rsi"] > self.rsi_sell)
        fail_bounce = (dataframe["close"] < dataframe["bb_lower"] * 0.992) & (dataframe["rsi"] < 28.0)

        dataframe.loc[base & (tp_soft | tp_strong | fail_bounce), "exit_long"] = 1
        return dataframe