# source: https://raw.githubusercontent.com/DOUGLASGUEDESATRIA/OSIRIS_TRADE/fe3c09567f3798d1cad6d7a306fa4f6131d91e17/user_data/strategies/OsirisDailyV1.py
"""
OSIRIS DAILY V1 — Session-Based Intraday for Consistent Daily Profit
=====================================================================
Built from exhaustive 1h data analysis across 10 pairs (Jan 2024 — Mar 2026).

CORE EDGE: Specific hours have structural directional bias.
When combined with trend filter (SMA20), the edge multiplies.

SIGNALS (trend-filtered, Mon-Fri):
  LONG  at 21h UTC if Bull trend  → Sharpe 5.06, WR 54.6%, WinDays 60%
  LONG  at 3-4h UTC if Bull trend → Sharpe 3.39, WR 52.4%, WinDays 57%
  SHORT at 13h UTC if Bear trend  → Sharpe 4.02, WR 61.6%, WinDays 62%
  SHORT at 23h UTC if Bear trend  → Sharpe 2.03, WR 54.5%, WinDays 54%

DOW-SPECIFIC (no trend filter needed):
  Mon 07h LONG,  Tue 13h SHORT,  Wed 21h LONG,
  Thu 19h SHORT,  Thu 21h LONG,  Fri 07h SHORT

EXECUTION: Enter at candle open, exit after 1h (next candle close).
No SL/TP — the hourly bias IS the edge. Holding longer hurts.
"""

import logging
import numpy as np
from datetime import datetime
from pandas import DataFrame

from freqtrade.strategy import IStrategy

logger = logging.getLogger(__name__)


class Github_DOUGLASGUEDESATRIA_OSIRIS_TRADE__OsirisDailyV1__20260329_060843(IStrategy):
    """Github_DOUGLASGUEDESATRIA_OSIRIS_TRADE__OsirisDailyV1__20260329_060843 — session-based intraday, Mon-Fri."""

    INTERFACE_VERSION = 3
    timeframe = "1h"
    can_short = True

    # No fixed ROI — custom exit handles everything
    minimal_roi = {"0": 100}

    # Emergency stoploss (should never trigger — we exit after 1-2 candles)
    stoploss = -0.05

    trailing_stop = False
    process_only_new_candles = True
    startup_candle_count: int = 25

    # ─── Trade management ────────────────────────────────
    max_entry_position_adjustment = 0

    def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        # Trend: SMA20 on close (20 hourly candles = ~1 day)
        dataframe['sma20'] = dataframe['close'].rolling(20).mean()
        dataframe['trend_bull'] = (dataframe['close'] > dataframe['sma20']).astype(int)

        # Time features
        dataframe['hour'] = dataframe['date'].dt.hour
        dataframe['dow'] = dataframe['date'].dt.dayofweek  # 0=Mon

        return dataframe

    def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        """
        DOW-specific signals. FT enters at OPEN of NEXT candle after signal.
        So to trade hour X, we must signal at hour X-1.
        """
        # ═══ LONGS ═══
        long_mask = np.zeros(len(dataframe), dtype=bool)

        # Mon 07h Long → signal at hour 6
        long_mask |= (
            (dataframe['dow'] == 0) & (dataframe['hour'] == 6)
        ).values
        # Wed 21h Long → signal at hour 20
        long_mask |= (
            (dataframe['dow'] == 2) & (dataframe['hour'] == 20)
        ).values
        # Thu 21h Long → signal at hour 20
        long_mask |= (
            (dataframe['dow'] == 3) & (dataframe['hour'] == 20)
        ).values

        dataframe.loc[long_mask, 'enter_long'] = 1

        # ═══ SHORTS ═══
        short_mask = np.zeros(len(dataframe), dtype=bool)

        # Tue 13h Short → signal at hour 12
        short_mask |= (
            (dataframe['dow'] == 1) & (dataframe['hour'] == 12)
        ).values
        # Thu 19h Short → signal at hour 18
        short_mask |= (
            (dataframe['dow'] == 3) & (dataframe['hour'] == 18)
        ).values
        # Fri 07h Short → signal at hour 6
        short_mask |= (
            (dataframe['dow'] == 4) & (dataframe['hour'] == 6)
        ).values

        dataframe.loc[short_mask, 'enter_short'] = 1

        return dataframe

    def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        # No indicator-based exits — use custom_exit
        return dataframe

    def custom_exit(self, pair: str, trade, current_time: datetime,
                    current_rate: float, current_profit: float,
                    **kwargs) -> str | bool:
        """Exit after 1 candle (1 hour hold)."""
        trade_duration_hours = (current_time - trade.open_date_utc).total_seconds() / 3600

        # Exit after 1h (1 candle)
        if trade_duration_hours >= 1.0:
            return "1h_exit"

        return False
