# source: https://raw.githubusercontent.com/shatianming5/Agent_market/8dc940a702171d651cabe59aea11f778550390e4/workspace/strategies/auto_v101.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

import pandas as pd


class Github_shatianming5_Agent_market__auto_v101__20260330_190736(IStrategy):
    INTERFACE_VERSION = 3

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

    stoploss = -0.028
    minimal_roi = {"0": 0.02, "24": 0.012, "72": 0.006}
    use_exit_signal = True
    exit_profit_only = True
    ignore_roi_if_entry_signal = False

    rsi_len = 14
    bb_len = 20
    bb_k = 2.0
    atr_len = 14
    ema_len = 50
    vol_len = 20

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

        df = dataframe
        for c in ("open", "high", "low", "close", "volume"):
            if c not in df.columns:
                df[c] = 0.0

        close = df["close"].astype(float)
        high = df["high"].astype(float)
        low = df["low"].astype(float)
        vol = df["volume"].astype(float)

        delta = close.diff()
        gain = delta.clip(lower=0.0)
        loss = (-delta).clip(lower=0.0)
        alpha = 1.0 / float(self.rsi_len)
        avg_gain = gain.ewm(alpha=alpha, adjust=False).mean()
        avg_loss = loss.ewm(alpha=alpha, adjust=False).mean()
        rs = avg_gain / avg_loss.replace(0.0, np.nan)
        df["rsi"] = (100.0 - (100.0 / (1.0 + rs))).fillna(0.0)

        mid = close.rolling(self.bb_len, min_periods=2).mean()
        std = close.rolling(self.bb_len, min_periods=2).std(ddof=0)
        upper = mid + (self.bb_k * std)
        lower = mid - (self.bb_k * std)
        df["bb_mid"] = mid
        df["bb_upper"] = upper
        df["bb_lower"] = lower
        df["bb_width"] = ((upper - lower) / mid.replace(0.0, np.nan)).fillna(0.0)

        prev_close = close.shift(1)
        tr1 = (high - low).abs()
        tr2 = (high - prev_close).abs()
        tr3 = (low - prev_close).abs()
        tr = pd.concat([tr1, tr2, tr3], axis=1).max(axis=1)
        df["atr"] = tr.ewm(alpha=(1.0 / float(self.atr_len)), adjust=False).mean().fillna(0.0)

        df["ema"] = close.ewm(span=self.ema_len, adjust=False).mean()
        df["ema_slope24"] = (df["ema"] - df["ema"].shift(24)).fillna(0.0)

        vol_ma = vol.rolling(self.vol_len, min_periods=2).mean()
        df["vol_ratio"] = (vol / vol_ma.replace(0.0, np.nan)).fillna(0.0)

        df["ret_24"] = (close / prev_close.shift(23).replace(0.0, np.nan) - 1.0).replace([np.inf, -np.inf], np.nan).fillna(0.0)

        return df

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

        df = dataframe
        for c in ("rsi", "bb_lower", "bb_mid", "bb_width", "atr", "ema", "ema_slope24", "vol_ratio"):
            if c not in df.columns:
                df[c] = 0.0

        close = df["close"].astype(float)
        rsi = df["rsi"].astype(float)
        bbl = df["bb_lower"].astype(float)
        bbm = df["bb_mid"].astype(float)
        atr = df["atr"].astype(float)
        ema = df["ema"].astype(float)
        slope24 = df["ema_slope24"].astype(float)
        vratio = df["vol_ratio"].astype(float)

        oversold = (close < (bbl * 1.002)) & (rsi < 30.5)
        rebound = (close > close.shift(1)) | (rsi > rsi.shift(1))
        regime_ok = (slope24 > (-1.15 * atr)) | (close > (ema * 0.985))
        liquidity_ok = (df["volume"] > 0) & (vratio > 0.6)
        not_too_far = close > (bbm * 0.94)

        enter = oversold & rebound & regime_ok & liquidity_ok & not_too_far

        df.loc[enter, "enter_long"] = 1
        return df

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

        df = dataframe
        for c in ("rsi", "bb_mid", "bb_upper", "atr", "ema", "ema_slope24"):
            if c not in df.columns:
                df[c] = 0.0

        close = df["close"].astype(float)
        rsi = df["rsi"].astype(float)
        bbm = df["bb_mid"].astype(float)
        bbu = df["bb_upper"].astype(float)
        atr = df["atr"].astype(float)
        slope24 = df["ema_slope24"].astype(float)

        mean_revert_done = (close > bbm) & (rsi > 55.0)
        big_pop = (close > (bbu * 0.998)) | (rsi > 62.0)
        risk_off = (slope24 < (-1.6 * atr)) & (rsi < 45.0)

        exit_sig = (df["volume"] > 0) & (mean_revert_done | big_pop | risk_off)
        df.loc[exit_sig, "exit_long"] = 1
        return df

    def custom_exit(self, pair, trade, current_time, current_rate, current_profit, **kwargs):
        try:
            age_h = (current_time - trade.open_date_utc).total_seconds() / 3600.0
        except Exception:
            return None

        if age_h >= 18.0 and current_profit > 0.008:
            return "time_tp"
        if age_h >= 30.0 and current_profit > -0.004:
            return "time_flat"
        if age_h >= 48.0:
            return "time_max"
        return None