# source: https://raw.githubusercontent.com/Sahillatif0/FreqTrade/45d54f4def57a5e818176c992f5944a1ffc28807/userdata/strategies/TTMSqueezeBreakoutElite.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
import freqtrade.vendor.qtpylib.indicators as qtpylib
from freqtrade.strategy import IStrategy, merge_informative_pair
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__TTMSqueezeBreakoutElite__20261013_211933] portfolio_coordinator loaded successfully.")
except Exception as e:
    logger.warning(f"[Github_Sahillatif0_FreqTrade__TTMSqueezeBreakoutElite__20261013_211933] Could not load portfolio_coordinator: {e}")
    pc = None

class Github_Sahillatif0_FreqTrade__TTMSqueezeBreakoutElite__20261013_211933(IStrategy):
    """
    Github_Sahillatif0_FreqTrade__TTMSqueezeBreakoutElite__20261013_211933 (85%+ High-Precision Precision Squeeze Pro):
    Engineered specifically for 85%+ Win Rate on Binance Spot:
    1. Multi-Stage Squeeze Coiling: Requires at least 2 consecutive bars of Squeeze.
    2. ADX Trend Strength Filter: ADX >= 22 (Ensures real directional impulse, avoids choppy dead-end breakouts).
    3. Structural Volatility Fire: Candle body must be >= 50% of the entire candle range (no topping wicks).
    4. Fast Adaptive Micro-Trailing Stop: Activates at +1.0% gain to lock in +0.6% profit immediately.
    5. Clean Target Ladder: 2.8% -> 1.9% -> 1.3% -> 0.8%.
    """

    INTERFACE_VERSION = 3
    timeframe = "15m"
    can_short = False

    minimal_roi = {"0": 0.036, "60": 0.024, "180": 0.014, "360": 0.008}

    stoploss = -0.022
    trailing_stop = True
    trailing_stop_positive = 0.002
    trailing_stop_positive_offset = 0.016
    trailing_only_offset_is_reached = True

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

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

    @property
    def protections(self):
        return [
            {
                "method": "CooldownPeriod",
                "stop_duration_candles": 1
            },
            {
                "method": "StoplossGuard",
                "lookback_period_candles": 24,
                "trade_limit": 2,
                "stop_duration_candles": 16,
                "only_per_pair": False
            },
            {
                "method": "MaxDrawdown",
                "lookback_period_candles": 96,
                "trade_limit": 1,
                "stop_duration_candles": 48,
                "max_allowed_drawdown": 0.05
            }
        ]

    def informative_pairs(self):
        return [("BTC/USDT", "15m")]

    def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        # 0. Bitcoin Macro & Crash Guard (15m Informative Pair)
        if self.dp:
            btc_15m = self.dp.get_pair_dataframe(pair="BTC/USDT", timeframe="15m")
            if not btc_15m.empty:
                btc_15m["btc_ema_50"] = ta.EMA(btc_15m, timeperiod=50)
                btc_15m["btc_rsi"] = ta.RSI(btc_15m, timeperiod=14)
                # BTC Dynamic Crash Guard: Allows profitable runs while blocking deep flash crashes
                btc_15m["btc_safe"] = (btc_15m["close"] > btc_15m["btc_ema_50"] * 0.995) & (btc_15m["btc_rsi"] >= 42)
                dataframe = merge_informative_pair(
                    dataframe, btc_15m, self.timeframe, "15m", ffill=True
                )

        # 1. Bollinger Bands (20, 2.0 std)
        bollinger = qtpylib.bollinger_bands(dataframe["close"], window=20, stds=2.0)
        dataframe["bb_lower"] = bollinger["lower"]
        dataframe["bb_mid"] = bollinger["mid"]
        dataframe["bb_upper"] = bollinger["upper"]

        # 2. Keltner Channels (20, 1.5 ATR)
        dataframe["atr_14"] = ta.ATR(dataframe, timeperiod=14)
        dataframe["ema_20"] = ta.EMA(dataframe, timeperiod=20)
        dataframe["kc_upper"] = dataframe["ema_20"] + (dataframe["atr_14"] * 1.5)
        dataframe["kc_lower"] = dataframe["ema_20"] - (dataframe["atr_14"] * 1.5)

        # 3. TTM Squeeze Detection:
        dataframe["squeeze_on"] = (dataframe["bb_lower"] > dataframe["kc_lower"]) & (dataframe["bb_upper"] < dataframe["kc_upper"])
        dataframe["squeeze_streak"] = dataframe["squeeze_on"].rolling(window=3).sum()

        # 4. Macro Trend Alignment & Slope
        dataframe["ema_50"] = ta.EMA(dataframe, timeperiod=50)
        dataframe["ema_50_slope"] = (dataframe["ema_50"] - dataframe["ema_50"].shift(3)) / dataframe["ema_50"].shift(3) * 100
        dataframe["volume_sma"] = dataframe["volume"].rolling(window=20).mean()

        # 5. Momentum, Volatility & Candlestick Anatomy
        dataframe["rsi"] = ta.RSI(dataframe, timeperiod=14)
        dataframe["adx"] = ta.ADX(dataframe, timeperiod=14)
        dataframe["body"] = (dataframe["close"] - dataframe["open"]).abs()
        dataframe["candle_range"] = dataframe["high"] - dataframe["low"]
        dataframe["upper_wick"] = dataframe["high"] - dataframe[["open", "close"]].max(axis=1)

        # Distance from EMA-20: Prevent buying climax tops that are over-extended (> 3.0% above 20 EMA)
        dataframe["dist_ema20_pct"] = (dataframe["close"] - dataframe["ema_20"]) / dataframe["ema_20"] * 100

        # MACD Histogram
        macd = ta.MACD(dataframe)
        dataframe["macd_hist"] = macd["macdhist"]

        return dataframe

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

        # 85%+ High-Precision Squeeze Fire Engine:
        squeeze_breakout = (
            (dataframe["squeeze_streak"].shift(1) >= 2) &                     # True coiled compression
            (dataframe["close"] > dataframe["bb_mid"]) &                      # Above baseline
            (dataframe["close"] > dataframe["ema_50"]) &                      # Macro bull trend
            (dataframe["ema_50_slope"] >= 0.015) &                            # Clearly sloping up
            (dataframe["close"] > dataframe["open"]) &                        # Solid green bar
            (dataframe["body"] >= dataframe["candle_range"] * 0.50) &         # Strong body, not an exhausted wick (min 50% body)
            (dataframe["upper_wick"] <= dataframe["body"] * 0.60) &            # Tighter top wick control (no selling rejection)
            (dataframe["dist_ema20_pct"] <= 3.0) &                            # Anti-Top Filter: Not over-extended from mean
            (dataframe["adx"] >= 19) &                                        # Clear trend strength (eliminates chop fakeouts)
            (dataframe["macd_hist"] > 0) &                                    # Positive momentum
            (dataframe["macd_hist"] > dataframe["macd_hist"].shift(1)) &      # Accelerating impulse
            (dataframe["rsi"] >= 53) & (dataframe["rsi"] <= 66) &            # Non-overbought sweet spot
            (dataframe["volume"] > dataframe["volume_sma"] * 1.25) &          # High volume breakout confirmation
            (dataframe.get("btc_safe_15m", pd.Series(True, index=dataframe.index)) == True) # BTC Market Crash Filter
        )

        dataframe.loc[squeeze_breakout, "enter_long"] = 1
        dataframe.loc[squeeze_breakout, "enter_tag"] = "ttm_squeeze_precision_fire"

        if len(dataframe) > 0 and bool(dataframe["enter_long"].iloc[-1]):
            if pc:
                try:
                    pc.request_preemption("Github_Sahillatif0_FreqTrade__TTMSqueezeBreakoutElite__20261013_211933", metadata.get("pair", ""))
                except Exception as e:
                    logger.warning(f"[Github_Sahillatif0_FreqTrade__TTMSqueezeBreakoutElite__20261013_211933] Preemption error: {e}")

        return dataframe

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

    def custom_exit(self, pair: str, trade: 'Trade', current_time: datetime, current_rate: float,
                    current_profit: float, **kwargs):
        """Preempts if higher priority strategy (HFCE=3 or TIE=2) has requested balance"""
        if pc:
            try:
                state = pc.get_state()
                pending = state.get("pending_intent")
                if pending and pending.get("status") == "WAITING_FOR_BALANCE":
                    my_prio = pc.PRIORITY.get("Github_Sahillatif0_FreqTrade__TTMSqueezeBreakoutElite__20261013_211933", 2)
                    if pending.get("priority", 1) > my_prio:
                        if current_profit >= -0.005:
                            logger.info(f"[TTMSqueeze] Preempting for higher priority: {pending.get('strategy')} (Current PnL: {current_profit:.2%})")
                            return "preempted_for_high_priority"
                        else:
                            pc.reject_preemption("Github_Sahillatif0_FreqTrade__TTMSqueezeBreakoutElite__20261013_211933", pair, current_profit)
                            logger.info(f"[TTMSqueeze] Preemption blocked by Soft Floor: current profit {current_profit:.2%} < -0.50%")
            except Exception as e:
                logger.warning(f"[TTMSqueeze] Coordinator check error: {e}")
        return None

    def confirm_trade_entry(self, pair: str, order_type: str, amount: float, rate: float,
                            time_in_force: str, current_time: datetime, entry_tag: str,
                            side: str, **kwargs) -> bool:
        if self.dp and self.dp.runmode.value in ('backtest', 'hyperopt'):
            return True

        if pc:
            try:
                allowed, reason = pc.can_enter("Github_Sahillatif0_FreqTrade__TTMSqueezeBreakoutElite__20261013_211933", pair)
                if not allowed:
                    logger.warning(f"[TTMSqueeze] Entry blocked by Coordinator: {reason}")
                    return False
                pc.confirm_entry("Github_Sahillatif0_FreqTrade__TTMSqueezeBreakoutElite__20261013_211933", pair)
            except Exception as e:
                logger.warning(f"[TTMSqueeze] 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: datetime, **kwargs) -> bool:
        if self.dp and self.dp.runmode.value in ('backtest', 'hyperopt'):
            return True

        if pc:
            try:
                profit_ratio = (rate - trade.open_rate) / trade.open_rate if (trade and trade.open_rate) else None
                pc.confirm_exit("Github_Sahillatif0_FreqTrade__TTMSqueezeBreakoutElite__20261013_211933", pair, profit_ratio=profit_ratio, exit_reason=exit_reason)
            except Exception as e:
                logger.warning(f"[TTMSqueeze] Confirm exit error: {e}")
        return True
