# source: https://raw.githubusercontent.com/a1exander81/clawmimoto-backtests/de399a73a61446bed15fe3614b1f014252a14f2a/strategies/claw5m_sniper_session.py
"""
Github_a1exander81_clawmimoto_backtests__claw5m_sniper_session__20260430_031719 — Base strategy for ClawForge.
5M TF, ISOLATED margin, 3 trades/day max, trailing SL at +50%.
"""

from datetime import time, datetime, timezone
import pandas as pd
import pandas_ta as ta
from freqtrade.strategy import IStrategy, IntParameter, DecimalParameter, BooleanParameter
from freqtrade.persistence import Trade


class Github_a1exander81_clawmimoto_backtests__claw5m_sniper_session__20260430_031719(IStrategy):
    """5-minute sniper with institutional risk management."""

    INTERFACE_VERSION = 3
    timeframe = "5m"
    startup_candle_count = 50

    # ── Risk Management ──
    max_open_trades = 3
    stoploss = -0.25
    trailing_stop = True
    trailing_stop_positive = 0.5
    trailing_stop_positive_offset = 0.51
    trailing_only_offset_is_reached = True
    minimal_roi = {"0": 1.0}

    # ── Indicators ──
    rsi_enabled = BooleanParameter(default=True, space="buy")
    rsi_period = IntParameter(10, 30, default=14, space="buy")
    rsi_buy = IntParameter(20, 40, default=30, space="buy")
    rsi_sell = IntParameter(60, 80, default=70, space="sell")

    macd_enabled = BooleanParameter(default=True, space="buy")
    macd_fast = IntParameter(8, 20, default=12, space="buy")
    macd_slow = IntParameter(20, 40, default=26, space="buy")
    macd_signal = IntParameter(5, 15, default=9, space="buy")

    ema_fast = IntParameter(5, 20, default=10, space="buy")
    ema_slow = IntParameter(20, 50, default=30, space="buy")

    # ── StepFun Sentiment ──
    use_sentiment = BooleanParameter(default=False, space="buy")
    sentiment_threshold = DecimalParameter(0.6, 0.9, default=0.75, space="buy")

    def populate_indicators(self, dataframe: pd.DataFrame, metadata: dict) -> pd.DataFrame:
        df = dataframe.copy()

        if self.rsi_enabled.value:
            df["rsi"] = ta.rsi(df["close"], length=self.rsi_period.value)

        if self.macd_enabled.value:
            macd = ta.macd(df["close"], fast=self.macd_fast.value, slow=self.macd_slow.value, signal=self.macd_signal.value)
            df["macd"] = macd["MACD_12_26_9"]
            df["macdsignal"] = macd["MACDs_12_26_9"]
            df["macdhist"] = macd["MACDh_12_26_9"]

        df["ema_fast"] = ta.ema(df["close"], length=self.ema_fast.value)
        df["ema_slow"] = ta.ema(df["close"], length=self.ema_slow.value)
        df["ema_cross"] = (df["ema_fast"] > df["ema_slow"]).astype(int)

        df["session"] = self.get_session(df["date"])

        return df

    def populate_buy_trend(self, dataframe: pd.DataFrame, metadata: dict) -> pd.DataFrame:
        df = dataframe.copy()
        df["buy"] = 0

        cond_rsi = self.rsi_enabled.value & (df["rsi"] < self.rsi_buy.value)
        cond_macd = self.macd_enabled.value & (df["macd"] > df["macdsignal"]) & (df["macdhist"] > 0)
        cond_ema = df["ema_cross"] == 1
        cond_session = df["session"].isin(["NY", "TOKYO", "LONDON"])

        buy_cond = cond_rsi & cond_macd & cond_ema & cond_session

        if self.use_sentiment.value:
            from clawforge.integrations.stepfun import get_sentiment_score
            sentiment = get_sentiment_score(metadata["pair"])
            if sentiment < self.sentiment_threshold.value:
                buy_cond = False

        df.loc[buy_cond, "buy"] = 1
        return df

    def populate_sell_trend(self, dataframe: pd.DataFrame, metadata: dict) -> pd.DataFrame:
        df = dataframe.copy()
        df["sell"] = 0

        cond_rsi = self.rsi_enabled.value & (df["rsi"] > self.rsi_sell.value)
        cond_macd = self.macd_enabled.value & (df["macd"] < df["macdsignal"]) & (df["macdhist"] < 0)
        cond_ema = df["ema_cross"] == 0

        sell_cond = cond_rsi | cond_macd | cond_ema
        df.loc[sell_cond, "sell"] = 1
        return df

    @staticmethod
    def get_session(date_series: pd.Series) -> pd.Series:
        """Map UTC hour to trading session."""
        def _session(ts):
            hour = ts.hour
            if 0 <= hour < 8:
                return "NY"
            elif 8 <= hour < 16:
                return "TOKYO"
            elif 16 <= hour < 24:
                return "LONDON"
            return "OTHER"
        return date_series.apply(_session)

    @staticmethod
    def hyperopt_loss_function(results_df: pd.DataFrame, trade_count: int, min_date: datetime,
                               max_date: datetime, processed: dict, *args, **kwargs) -> float:
        """Optimize for Risk/Reward Ratio ≥ 2.0."""
        if trade_count == 0:
            return 1000000

        wins = results_df[results_df["profit_abs"] > 0]
        losses = results_df[results_df["profit_abs"] < 0]

        if len(wins) == 0 or len(losses) == 0:
            return 1000000

        avg_win = wins["profit_abs"].mean()
        avg_loss = abs(losses["profit_abs"].mean())
        rrr = avg_win / avg_loss if avg_loss > 0 else 0

        return max(0, 2.0 - rrr) * 1000 + max(0, trade_count - 100) * 0.1
