# source: https://raw.githubusercontent.com/Mo-101/IdimIkang-main/bf15b84d576b9dfb674d371a005479eabe4a69a2/observer_bundle/ft_strategies/IdimSqueeze.py
"""
Github_Mo_101_IdimIkang_main__IdimSqueeze__20260409_132915.py
==============
Baseline strategy: mirrors Idim's native squeeze gate in FreqTrade DSL.

Purpose: validate the ft_bridge adapter end-to-end.
Not intended as live alpha — it duplicates native Idim gates by design.
Real alpha strategies (OI, funding divergence, CVD) built separately.

Signal logic:
  LONG  — ADX >= 20, not overextended (< 1.5x ATR), recent squeeze fire,
           EMA20 > EMA50, price > EMA20, volume ratio >= 1.1
  SHORT — same gates, opposite direction

Author: The Flame Architect | MoStar Industries
"""

import pandas as pd
from .IStrategy_minimal import IStrategy


class Github_Mo_101_IdimIkang_main__IdimSqueeze__20260409_132915(IStrategy):

    INTERFACE_VERSION = 3
    timeframe = "15m"

    def populate_indicators(self, dataframe: pd.DataFrame, metadata: dict) -> pd.DataFrame:
        """
        Squeeze indicators — only calculated if Idim hasn't already added them.
        Idim's add_indicators() runs first; this is a safety fallback only.
        """
        if "squeeze_on" not in dataframe.columns:
            sma20 = dataframe["close"].rolling(20).mean()
            std20 = dataframe["close"].rolling(20).std()
            bb_upper = sma20 + 2 * std20
            bb_lower = sma20 - 2 * std20
            kc_upper = dataframe["ema20"] + 1.5 * dataframe["atr14"]
            kc_lower = dataframe["ema20"] - 1.5 * dataframe["atr14"]

            dataframe["squeeze_on"] = (
                (bb_upper < kc_upper) & (bb_lower > kc_lower)
            )
            dataframe["squeeze_fired"] = (
                dataframe["squeeze_on"].shift(1).fillna(False) & ~dataframe["squeeze_on"]
            ).infer_objects(copy=False)
            dataframe["recent_squeeze_fire"] = (
                dataframe["squeeze_fired"].rolling(window=3).max().fillna(0)
                .astype(bool).infer_objects(copy=False)
            )

        if "volume_ratio" not in dataframe.columns:
            vol_sma = dataframe["volume"].rolling(20).mean()
            dataframe["volume_ratio"] = dataframe["volume"] / vol_sma.replace(0, float("nan"))

        return dataframe

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

        adx_ok              = dataframe["adx14"] >= 20
        stretch             = (dataframe["close"] - dataframe["ema20"]) / dataframe["atr14"].replace(0, float("nan"))
        not_ext_long        = stretch <= 1.5
        not_ext_short       = stretch >= -1.5
        squeeze_triggered   = dataframe["recent_squeeze_fire"].astype(bool)
        trend_up            = dataframe["ema20"] > dataframe["ema50"]
        trend_down          = dataframe["ema20"] < dataframe["ema50"]
        vol_ok              = dataframe["volume_ratio"] >= 1.1
        price_above_ema20   = dataframe["close"] > dataframe["ema20"]
        price_below_ema20   = dataframe["close"] < dataframe["ema20"]

        dataframe.loc[
            adx_ok & not_ext_long  & squeeze_triggered & trend_up   & vol_ok & price_above_ema20,
            "enter_long"
        ] = 1

        dataframe.loc[
            adx_ok & not_ext_short & squeeze_triggered & trend_down & vol_ok & price_below_ema20,
            "enter_short"
        ] = 1

        return dataframe

    def populate_exit_trend(self, dataframe: pd.DataFrame, metadata: dict) -> pd.DataFrame:
        """Idim executor owns exits. Columns required by interface only."""
        dataframe["exit_long"] = 0
        dataframe["exit_short"] = 0
        return dataframe