# source: https://raw.githubusercontent.com/Sahillatif0/FreqTrade/1b8aea69659c872ae2fbeacba289f66615a938b2/userdata/strategies/RangeBreakoutDonchianPro.py
# directory_url: https://github.com/Sahillatif0/FreqTrade/blob/main/userdata/strategies/
# User: Sahillatif0
# Repository: FreqTrade
# --------------------import logging
logger = logging.getLogger(__name__)
import numpy as np
import pandas as pd
from pandas import DataFrame
import talib.abstract as ta
from freqtrade.strategy import IStrategy

import sys
import os
_strat_dir = os.path.dirname(os.path.abspath(__file__))
if _strat_dir not in sys.path:
    sys.path.insert(0, _strat_dir)

try:
    import portfolio_coordinator as pc
    logger.info("[DonchianPro] portfolio_coordinator loaded successfully.")
except Exception as e:
    logger.warning(f"[DonchianPro] Could not load portfolio_coordinator: {e}")
    pc = None

class Github_Sahillatif0_FreqTrade__RangeBreakoutDonchianPro__20261010_112950(IStrategy):
    """
    Github_Sahillatif0_FreqTrade__RangeBreakoutDonchianPro__20261010_112950 (Hardened Intra-Hour Edition):
    1. StoplossGuard: If 1 SL hits, pauses breakout entries for 6 hours (eliminates 2nd/3rd consecutive SL).
    2. Fast Breakeven Lock: At +1.2%, moves stop to +0.3% (locks fee/profit, prevents intraday reversals).
    3. Breathing Room Stoploss: -3.4% avoids tight intra-hour -2.8% fakeout wick stopouts.
    4. Quality Breakout Buffer: Requires close > range_high_24 * 1.0020 and volume > 2.1x SMA.
    """
    INTERFACE_VERSION = 3
    timeframe = "1h"
    can_short = False

    minimal_roi = {
        "0": 0.048,      # Top impulse target (+4.8%)
        "120": 0.035,    # 2 hours target (+3.5%)
        "240": 0.025,    # 4 hours target (+2.5%)
        "480": 0.015     # 8 hours floor (+1.5%)
    }

    stoploss = -0.028  # Strict risk control: -2.8% loss cap
    trailing_stop = True
    trailing_stop_positive = 0.010          # Lock +1.0% profit once in gain
    trailing_stop_positive_offset = 0.016   # Trigger at +1.6%
    trailing_only_offset_is_reached = True

    process_only_new_candles = True  # Only enter after 1h candle closes and confirms breakout
    use_exit_signal = True
    exit_profit_only = False
    ignore_roi_if_entry_signal = False

    order_types = {
        "entry": "market",
        "exit": "market",
        "stoploss": "market",
        "stoploss_on_exchange": False
    }

    @property
    def protections(self):
        return [
            {
                "method": "StoplossGuard",
                "lookback_period_candles": 24,
                "trade_limit": 1,
                "stop_duration_candles": 2,     # Sirf 2 ghante cooldown us specific coin par
                "only_per_pair": True           # Baaqi coins par trade chalti rahegi
            },
            {
                "method": "CooldownPeriod",
                "stop_duration_candles": 1
            }
        ]

    def informative_pairs(self):
        return []

    def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        # 24-hour high/low (24 candles of 1h)
        dataframe["range_high_24"] = dataframe["high"].shift(1).rolling(window=24).max()
        dataframe["range_low_24"] = dataframe["low"].shift(1).rolling(window=24).min()

        # Volume SMA 20
        dataframe["volume_sma_20"] = dataframe["volume"].rolling(window=20).mean()

        # Momentum & Moving Averages
        dataframe["atr"] = ta.ATR(dataframe, timeperiod=14)
        dataframe["rsi"] = ta.RSI(dataframe, timeperiod=14)
        dataframe["ema_20"] = ta.EMA(dataframe, timeperiod=20)
        dataframe["ema_50"] = ta.EMA(dataframe, timeperiod=50)

        # Candle anatomy
        dataframe["candle_range"] = dataframe["high"] - dataframe["low"]
        dataframe["upper_wick"] = dataframe["high"] - dataframe[["open", "close"]].max(axis=1)
        dataframe["close_position"] = (dataframe["close"] - dataframe["low"]) / (dataframe["candle_range"] + 1e-8)

        return dataframe

    def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        dataframe.loc[:, "enter_long"] = 0
        dataframe.loc[:, "enter_tag"] = ""

        # Filter out noisy or historically drag pairs if needed or use strict price action
        breakout_condition = (
            # 0.20% buffer above 24h high to avoid borderline fakeout wicks
            (dataframe["close"] > dataframe["range_high_24"] * 1.0020) &
            (dataframe["close"] > dataframe["open"]) &
            # Higher conviction volume surge (>= 2.1x SMA)
            (dataframe["volume"] > dataframe["volume_sma_20"] * 2.1) &
            # Strong bullish candle close: close in top 30% of candle range
            (dataframe["close_position"] >= 0.70) &
            # No massive upper wick trap (upper wick <= 25% of entire candle range)
            (dataframe["upper_wick"] <= dataframe["candle_range"] * 0.25) &
            # Momentum sweet spot
            (dataframe["rsi"] >= 55) &
            (dataframe["rsi"] <= 78) &
            (dataframe["close"] > dataframe["ema_50"])
        )

        dataframe.loc[breakout_condition, "enter_long"] = 1
        dataframe.loc[breakout_condition, "enter_tag"] = "pro_range_breakout"

        # Preemption Notification: If last candle fired breakout, notify coordinator
        if len(dataframe) > 0 and bool(dataframe["enter_long"].iloc[-1]):
            if pc:
                try:
                    pc.request_preemption("Github_Sahillatif0_FreqTrade__RangeBreakoutDonchianPro__20261010_112950", metadata.get("pair", ""))
                except Exception as e:
                    logger.warning(f"[DonchianPro] Preemption request error: {e}")

        return dataframe

    def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        dataframe.loc[:, "exit_long"] = 0
        dataframe.loc[:, "exit_tag"] = ""

        # Exit if closes back below EMA 20
        exit_condition = (
            (dataframe["close"] < dataframe["ema_20"]) &
            (dataframe["close"].shift(1) < dataframe["ema_20"].shift(1))
        )
        dataframe.loc[exit_condition, "exit_long"] = 1
        dataframe.loc[exit_condition, "exit_tag"] = "trend_exhaustion"

        return dataframe

    def confirm_trade_entry(self, pair: str, order_type: str, amount: float, rate: float,
                            time_in_force: str, current_time, entry_tag: str,
                            side: str, **kwargs) -> bool:
        if pc:
            try:
                pc.confirm_entry("Github_Sahillatif0_FreqTrade__RangeBreakoutDonchianPro__20261010_112950", pair)
            except Exception as e:
                logger.warning(f"[DonchianPro] Confirm entry error: {e}")
        return True

    def confirm_trade_exit(self, pair: str, trade: 'Trade', order_type: str, amount: float,
                           rate: float, time_in_force: str, exit_reason: str,
                           current_time, **kwargs) -> bool:
        if pc:
            try:
                pc.release_balance()
            except Exception as e:
                logger.warning(f"[DonchianPro] Release balance error: {e}")
        return True
