# source: https://raw.githubusercontent.com/DOUGLASGUEDESATRIA/OSIRIS_TRADE/fe3c09567f3798d1cad6d7a306fa4f6131d91e17/user_data/strategies/OsirisSwing4HV2.py
"""
OsirisSwing4H V2 — Hyperoptable Swing Strategy
================================================
Based on OsirisSwing4H V1 (+22.65% on 2022-2025).

SL/TP both intra-candle via custom_stoploss + stoploss_from_open.
Hyperopt optimizes entry EMA params, SL%, TP%, and max hold.
"""
import logging
from pandas import DataFrame
from freqtrade.strategy import IStrategy, DecimalParameter, IntParameter, informative
import talib.abstract as ta

logger = logging.getLogger(__name__)


def stoploss_from_open(open_relative_stop, current_profit, is_short=False):
    if current_profit == 0:
        return 1
    if is_short:
        return -1 + ((1 - open_relative_stop) / (1 - current_profit))
    return 1 - ((1 + open_relative_stop) / (1 + current_profit))


class Github_DOUGLASGUEDESATRIA_OSIRIS_TRADE__OsirisSwing4HV2__20260329_060843(IStrategy):
    INTERFACE_VERSION = 3
    can_short = True
    timeframe = "4h"

    # Safety net stoploss (never hit — custom_stoploss handles real SL)
    stoploss = -0.15
    use_custom_stoploss = True
    trailing_stop = False
    minimal_roi = {"0": 100}  # Disabled — TP via custom_stoploss

    startup_candle_count = 250
    process_only_new_candles = True

    # ── Hyperopt: Entry ─────────────────────────────────────────────
    ema_fast = IntParameter(5, 15, default=9, space="buy", optimize=True)
    ema_slow = IntParameter(15, 30, default=21, space="buy", optimize=True)

    # ── Hyperopt: Exit ──────────────────────────────────────────────
    sl_pct = DecimalParameter(2.0, 6.0, default=3.0, decimals=1, space="sell", optimize=True)
    tp_pct = DecimalParameter(3.0, 12.0, default=6.0, decimals=1, space="sell", optimize=True)
    max_hold_bars = IntParameter(6, 30, default=18, space="sell", optimize=True)

    @informative("1d")
    def populate_indicators_1d(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        dataframe["sma50"] = ta.SMA(dataframe, timeperiod=50)
        dataframe["sma200"] = ta.SMA(dataframe, timeperiod=200)
        return dataframe

    def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        for p in range(5, 31):
            dataframe[f"ema{p}"] = ta.EMA(dataframe, timeperiod=p)
        dataframe["rsi"] = ta.RSI(dataframe, timeperiod=14)
        return dataframe

    def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        fast = int(self.ema_fast.value)
        slow = int(self.ema_slow.value)

        if fast >= slow:
            return dataframe

        ema_f = dataframe[f"ema{fast}"]
        ema_s = dataframe[f"ema{slow}"]

        has_data = (
            ema_f.notna()
            & dataframe["sma200_1d"].notna()
            & (dataframe["volume"] > 0)
        )

        # Daily regime
        daily_bull = (
            (dataframe["close"] > dataframe["sma200_1d"])
            & (dataframe["sma50_1d"] > dataframe["sma200_1d"])
        )
        daily_bear = (
            (dataframe["close"] < dataframe["sma200_1d"])
            & (dataframe["sma50_1d"] < dataframe["sma200_1d"])
        )

        # 4h EMA cross
        ema_cross_up = (ema_f > ema_s) & (ema_f.shift(1) <= ema_s.shift(1))
        ema_cross_dn = (ema_f < ema_s) & (ema_f.shift(1) >= ema_s.shift(1))

        # LONG: EMA cross up in bull regime
        dataframe.loc[
            has_data & ema_cross_up & daily_bull,
            ["enter_long", "enter_tag"],
        ] = (1, "ema_cross_bull")

        # SHORT: EMA cross down in bear regime
        dataframe.loc[
            has_data & ema_cross_dn & daily_bear,
            ["enter_short", "enter_tag"],
        ] = (1, "ema_cross_bear")

        return dataframe

    def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        dataframe["exit_long"] = 0
        dataframe["exit_short"] = 0
        return dataframe

    def custom_stoploss(self, pair, trade, current_time,
                        current_rate, current_profit, **kwargs) -> float:
        """Intra-candle SL and TP via stoploss_from_open."""
        sl = float(self.sl_pct.value) / 100
        tp = float(self.tp_pct.value) / 100
        is_short = trade.is_short

        # TP reached → lock profit with tight trail from open
        if current_profit >= tp:
            return stoploss_from_open(tp - 0.001, current_profit, is_short=is_short)

        # Normal SL from entry
        return stoploss_from_open(-sl, current_profit, is_short=is_short)

    def custom_exit(self, pair, trade, current_time, current_rate, current_profit, **kwargs):
        max_h = int(self.max_hold_bars.value)
        duration_hours = (current_time - trade.open_date_utc).total_seconds() / 3600
        if duration_hours >= max_h * 4:
            return "timeout"
        return None

    def confirm_trade_entry(self, pair, order_type, amount, rate, time_in_force,
                            current_time, entry_tag, side, **kwargs):
        if hasattr(self, '_last_exit_time') and self._last_exit_time:
            hours_since = (current_time - self._last_exit_time).total_seconds() / 3600
            if hours_since < 8:
                return False
        return True

    def confirm_trade_exit(self, pair, trade, order_type, amount, rate,
                           time_in_force, exit_reason, current_time, **kwargs):
        self._last_exit_time = current_time
        return True

    def leverage(self, pair, current_time, current_rate, proposed_leverage,
                 max_leverage, entry_tag, side, **kwargs) -> float:
        return 1.0
