# source: https://raw.githubusercontent.com/Sahillatif0/FreqTrade/1b8aea69659c872ae2fbeacba289f66615a938b2/userdata/strategies/TrendIgnitionSimplePullback.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
from datetime import datetime
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("[Github_Sahillatif0_FreqTrade__TrendIgnitionSimplePullback__20261010_112950] portfolio_coordinator loaded successfully.")
except Exception as e:
    logger.warning(f"[Github_Sahillatif0_FreqTrade__TrendIgnitionSimplePullback__20261010_112950] Could not load portfolio_coordinator: {e}")
    pc = None

class Github_Sahillatif0_FreqTrade__TrendIgnitionSimplePullback__20261010_112950(IStrategy):
    """
    Github_Sahillatif0_FreqTrade__TrendIgnitionSimplePullback__20261010_112950 (36.2% Tested Engine):
    - Exact logic from backtest-result-2026-09-26_10-42-49 (SL -2.4%).
    - EMA 9 touch + green candle rebound confirmation (close > open).
    - ROI targets: 3.5% -> 2.4% -> 1.8% -> 1.2% -> 0.8%.
    - Wide trailing stop to allow full runner expansion.
    """

    INTERFACE_VERSION = 3
    timeframe = "15m"
    can_short = False
 
    minimal_roi = {
        "0": 0.038,
        "15": 0.030,
        "45": 0.021,
        "90": 0.017,
        "180": 0.012,
        "360": 0.005
    }

    stoploss = -0.024


    trailing_stop = True
    trailing_stop_positive = 0.011
    trailing_stop_positive_offset = 0.022
    trailing_only_offset_is_reached = True

    process_only_new_candles = True
    use_exit_signal = False
    exit_profit_only = False
    ignore_roi_if_entry_signal = False

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

    @property
    def protections(self):
        return [
            # Solution 2: If a pair hits 1 Stoploss, pause that specific pair for 8 candles (2 hours)
            # Baqi pairs freely trade karenge. Prevents consecutive bleed on AAVE/TIA.
            {
                "method": "StoplossGuard",
                "lookback_period_candles": 12,
                "trade_limit": 1,
                "stop_duration_candles": 8,
                "only_per_pair": True
            },
            {
                "method": "CooldownPeriod",
                "stop_duration_candles": 2
            }
        ]

    def informative_pairs(self):
        return [("BTC/USDT", "1h")]

    def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        # Informative BTC/USDT 1h Trend Gate
        if self.dp:
            btc_1h = self.dp.get_pair_dataframe("BTC/USDT", "1h")
            if not btc_1h.empty:
                btc_1h["btc_ema_20"] = ta.EMA(btc_1h, timeperiod=20)
                from freqtrade.strategy import merge_informative_pair
                dataframe = merge_informative_pair(
                    dataframe, btc_1h, self.timeframe, "1h", ffill=True
                )

        # Moving Averages
        dataframe["ema_9"] = ta.EMA(dataframe, timeperiod=9)
        dataframe["ema_21"] = ta.EMA(dataframe, timeperiod=21)
        dataframe["ema_50"] = ta.EMA(dataframe, timeperiod=50)
        dataframe["ema_200"] = ta.EMA(dataframe, timeperiod=200)

        # Volume & Slopes
        dataframe["volume_sma"] = dataframe["volume"].rolling(window=20).mean()
        dataframe["ema_50_slope"] = (dataframe["ema_50"] - dataframe["ema_50"].shift(4)) / dataframe["ema_50"].shift(4) * 100

        # Momentum & Volatility
        dataframe["rsi"] = ta.RSI(dataframe, timeperiod=14)
        dataframe["adx"] = ta.ADX(dataframe, timeperiod=14)
        dataframe["atr"] = ta.ATR(dataframe, timeperiod=14)

        return dataframe

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

        # BTC 1h Bull Market Gate (Trade only when macro market is healthy)
        btc_gate = True
        if "btc_ema_20_1h" in dataframe.columns and "close_1h" in dataframe.columns:
            btc_gate = (dataframe["close_1h"] > dataframe["btc_ema_20_1h"])

        # Exact Pullback Dip Entry:
        pullback = (
            btc_gate &
            (dataframe["ema_9"] > dataframe["ema_21"]) &
            (dataframe["close"] > dataframe["ema_50"]) &
            (dataframe["ema_50_slope"] >= 0.015) &
            (dataframe["low"] <= dataframe["ema_9"] * 1.002) &
            (dataframe["close"] >= dataframe["ema_9"] * 0.998) &
            (dataframe["close"] > dataframe["open"]) &
            (dataframe["rsi"] >= 46) & (dataframe["rsi"] <= 63) &
            (dataframe["volume"] > dataframe["volume_sma"] * 0.75)
        )

        dataframe.loc[pullback, "enter_long"] = 1
        dataframe.loc[pullback, "enter_tag"] = "pullback_dip_entry"

        if len(dataframe) > 0 and bool(dataframe["enter_long"].iloc[-1]):
            if pc:
                try:
                    pc.request_preemption("Github_Sahillatif0_FreqTrade__TrendIgnitionSimplePullback__20261010_112950", metadata.get("pair", ""))
                except Exception as e:
                    logger.warning(f"[Github_Sahillatif0_FreqTrade__TrendIgnitionSimplePullback__20261010_112950] 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"] = ""
        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__TrendIgnitionSimplePullback__20261010_112950", pair)
            except Exception as e:
                logger.warning(f"[Github_Sahillatif0_FreqTrade__TrendIgnitionSimplePullback__20261010_112950] 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.confirm_exit("Github_Sahillatif0_FreqTrade__TrendIgnitionSimplePullback__20261010_112950", pair)
            except Exception as e:
                logger.warning(f"[Github_Sahillatif0_FreqTrade__TrendIgnitionSimplePullback__20261010_112950] Confirm exit error: {e}")
        return True
