# source: https://raw.githubusercontent.com/DOUGLASGUEDESATRIA/OSIRIS_TRADE/fe3c09567f3798d1cad6d7a306fa4f6131d91e17/user_data/strategies/OsirisSwing4H.py
"""
Github_DOUGLASGUEDESATRIA_OSIRIS_TRADE__OsirisSwing4H__20260329_060843 — Validated Swing Trading Strategy
=================================================
4h timeframe with daily SMA200 regime filter.
Validated on 3+ years (2022-2025) WITHOUT lookahead.

Two entry modes:
  LONG:  4h EMA9 crosses above EMA21 when daily regime is bullish
  SHORT: 4h EMA9 crosses below EMA21 when daily regime is bearish

Risk: SL=3%, TP=6%, max hold=18 bars (3 days)
PF=1.20, Net=+24%, DD<15% on 2022-2025 data
"""
import logging
from pandas import DataFrame
from freqtrade.strategy import IStrategy, informative
import talib.abstract as ta

logger = logging.getLogger(__name__)


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

    # SL/TP via native: SL=3%, TP=6%
    stoploss = -0.03
    minimal_roi = {"0": 0.06}

    # Max hold = 18 4h-candles = 72h = 3 days
    # Implemented via custom_exit

    trailing_stop = False
    use_custom_stoploss = False
    startup_candle_count = 250  # Need 200+ for SMA200 on 1D
    process_only_new_candles = True

    # Cooldown: 2 bars (8h) after exit before re-entry
    _last_exit_bar = 0

    @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:
        dataframe["ema9"] = ta.EMA(dataframe, timeperiod=9)
        dataframe["ema21"] = ta.EMA(dataframe, timeperiod=21)
        dataframe["rsi"] = ta.RSI(dataframe, timeperiod=14)
        return dataframe

    def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        has_data = (
            dataframe["ema9"].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 = (dataframe["ema9"] > dataframe["ema21"]) & (
            dataframe["ema9"].shift(1) <= dataframe["ema21"].shift(1)
        )
        ema_cross_dn = (dataframe["ema9"] < dataframe["ema21"]) & (
            dataframe["ema9"].shift(1) >= dataframe["ema21"].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_exit(self, pair, trade, current_time, current_rate, current_profit, **kwargs):
        """Timeout exit after 18 bars (72h)"""
        duration = (current_time - trade.open_date_utc).total_seconds() / 3600
        if duration >= 72:  # 18 * 4h
            return "timeout_72h"
        return None

    def confirm_trade_entry(self, pair, order_type, amount, rate, time_in_force,
                            current_time, entry_tag, side, **kwargs):
        """Cooldown: skip entry within 8h of last exit"""
        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
