# source: https://raw.githubusercontent.com/DOUGLASGUEDESATRIA/OSIRIS_TRADE/fe3c09567f3798d1cad6d7a306fa4f6131d91e17/user_data/strategies/OsirisGoldV10.py
"""
OSIRIS RESEARCH STRATEGY v1.0 - Deep Proprietary Analysis
============================================================
50+ indicadores | Volume Flow | Microestrutura | Smart Money | Entropia
Sistema de Score por Confluência | Otimização para i9 + 128GB + RTX 4090

INDICADORES PROPRIETÁRIOS:
1. Volume Delta & CVD (Order Flow proxy sem order book)
2. Pressão Compradora/Vendedora por candle (OHLC decomposition)
3. Absorção & Exaustão Institucional (microestrutura)
4. Fair Value Gaps + Liquidity Sweeps (Smart Money Concepts)
5. Kaufman Efficiency Ratio (proxy Hurst Exponent)
6. Volatility Entropy (detecção de regime)
7. Squeeze Momentum (BB inside Keltner = explosão iminente)
8. Volume-Price Divergence (acumulação/distribuição oculta)
9. Composite Momentum (multi-period ROC ponderado)
10. EMA Ribbon Score (alinhamento multi-EMA quantificado)

SISTEMA DE ENTRADA/SAÍDA:
- Score por Confluência: cada indicador contribui pontos
- Threshold otimizável via Hyperopt
- Sem lógica AND rígida — sistema flexível e adaptativo
- Hyperopt encontra os pesos ideais automaticamente

100% proprietário. Desenvolvido exclusivamente para OSIRIS.
"""

import logging
import numpy as np
from pandas import DataFrame
from typing import Optional

from freqtrade.strategy import IStrategy, merge_informative_pair
from freqtrade.strategy import CategoricalParameter, DecimalParameter, IntParameter
from freqtrade.persistence import Trade
import talib.abstract as ta

logger = logging.getLogger(__name__)


class Github_DOUGLASGUEDESATRIA_OSIRIS_TRADE__OsirisGoldV10__20260329_060843(IStrategy):
    """
    OSIRIS Research - Estratégia proprietária com análise profunda de 1 ano.
    
    Combina 50+ indicadores (convencionais + inovadores) num sistema de
    Score por Confluência. Cada condição adiciona pontos. Entrada/saída
    ocorre quando o score total atinge o threshold otimizado.
    
    Projetada para hardware potente: i9 + 128GB RAM + RTX 4090.
    Usa todos os cores via Hyperopt (-j -1).
    """

    timeframe = "5m"

    # ROI - será otimizado pelo Hyperopt
    minimal_roi = {
        "0": 0.05,
        "20": 0.03,
        "60": 0.02,
        "120": 0.01,
    }

    stoploss = -0.025
    trailing_stop = True
    trailing_stop_positive = 0.01
    trailing_stop_positive_offset = 0.02
    trailing_only_offset_is_reached = True

    startup_candle_count = 200
    process_only_new_candles = True

    # ===================================================================
    # HYPEROPT PARAMETERS - BUY (16 parâmetros)
    # ===================================================================

    # Score threshold — quantos pontos de confluência mínimos para entrar
    buy_score_min = IntParameter(4, 17, default=10, space="buy", optimize=True)

    # --- Volume Flow ---
    buy_pressure_ratio = DecimalParameter(
        0.50, 0.70, default=0.55, decimals=2, space="buy", optimize=True
    )
    buy_rvol_min = DecimalParameter(
        0.8, 3.0, default=1.2, decimals=1, space="buy", optimize=True
    )
    buy_cvd_periods = IntParameter(3, 10, default=5, space="buy", optimize=True)
    buy_mfi_min = IntParameter(20, 50, default=30, space="buy", optimize=True)

    # --- Momentum ---
    buy_rsi_min = IntParameter(25, 45, default=30, space="buy", optimize=True)
    buy_rsi_max = IntParameter(55, 75, default=65, space="buy", optimize=True)
    buy_stochrsi_max = IntParameter(20, 50, default=30, space="buy", optimize=True)
    buy_cci_min = IntParameter(-200, -30, default=-100, space="buy", optimize=True)

    # --- Trend ---
    buy_adx_min = IntParameter(15, 40, default=20, space="buy", optimize=True)
    buy_ema_ribbon_min = IntParameter(1, 4, default=2, space="buy", optimize=True)

    # --- Innovation ---
    buy_efficiency_min = DecimalParameter(
        0.05, 0.40, default=0.15, decimals=2, space="buy", optimize=True
    )
    buy_volatility_ratio_max = DecimalParameter(
        0.8, 2.0, default=1.3, decimals=1, space="buy", optimize=True
    )

    # --- Multi-TF ---
    buy_mtf_rsi_15m_min = IntParameter(30, 55, default=40, space="buy", optimize=True)
    buy_mtf_aligned = CategoricalParameter(
        [True, False], default=True, space="buy", optimize=True
    )

    # ===================================================================
    # HYPEROPT PARAMETERS - SELL (10 parâmetros)
    # ===================================================================

    sell_score_min = IntParameter(3, 16, default=8, space="sell", optimize=True)
    sell_rsi_max = IntParameter(60, 90, default=75, space="sell", optimize=True)
    sell_pressure_ratio_max = DecimalParameter(
        0.25, 0.50, default=0.45, decimals=2, space="sell", optimize=True
    )
    sell_stochrsi_min = IntParameter(55, 95, default=80, space="sell", optimize=True)
    sell_cci_max = IntParameter(50, 250, default=100, space="sell", optimize=True)
    sell_adx_weakening = CategoricalParameter(
        [True, False], default=True, space="sell", optimize=True
    )
    sell_efficiency_min = DecimalParameter(
        0.05, 0.40, default=0.15, decimals=2, space="sell", optimize=True
    )
    sell_exhaustion_rvol = DecimalParameter(
        1.5, 4.0, default=2.0, decimals=1, space="sell", optimize=True
    )
    sell_cmf_threshold = DecimalParameter(
        -0.20, 0.0, default=-0.05, decimals=2, space="sell", optimize=True
    )
    sell_macd_hist_negative = CategoricalParameter(
        [True, False], default=True, space="sell", optimize=True
    )

    def informative_pairs(self):
        pairs = self.dp.current_whitelist()
        informative = []
        for pair in pairs:
            informative.append((pair, "15m"))
            informative.append((pair, "1h"))
        return informative

    # ===================================================================
    # PROPRIETARY: VOLUME FLOW ANALYSIS
    # ===================================================================

    def _calc_volume_flow(self, df: DataFrame) -> DataFrame:
        """
        Análise de Fluxo de Volume (Order Flow Proxy).
        Decompõe cada candle em pressão compradora/vendedora usando OHLC.
        
        Conceito: Se close > open, o candle é "comprador". A proporção
        (close - low) / (high - low) estima quanto do volume foi de compra.
        Isso é uma aproximação do order book sem precisar de dados L2.
        """
        # Amplitude do candle
        hl_range = (df["high"] - df["low"]).replace(0, np.nan)

        # Volume Delta Approximation
        df["buy_volume"] = df["volume"] * (df["close"] - df["low"]) / hl_range
        df["sell_volume"] = df["volume"] * (df["high"] - df["close"]) / hl_range
        df["buy_volume"] = df["buy_volume"].fillna(df["volume"] * 0.5)
        df["sell_volume"] = df["sell_volume"].fillna(df["volume"] * 0.5)

        # Volume Delta (+ = mais compra, - = mais venda)
        df["volume_delta"] = df["buy_volume"] - df["sell_volume"]

        # Cumulative Volume Delta (CVD) — fluxo acumulado
        df["cvd"] = df["volume_delta"].cumsum()
        df["cvd_ema"] = ta.EMA(df["cvd"], timeperiod=20)
        df["cvd_rising"] = (df["cvd"] > df["cvd"].shift(1)).astype(int)

        # Pressure Ratio (0-1, >0.5 = compradores dominam)
        total_vol = (df["buy_volume"] + df["sell_volume"]).replace(0, 1)
        df["pressure_ratio"] = (df["buy_volume"] / total_vol).fillna(0.5)
        df["pressure_ratio_sma"] = df["pressure_ratio"].rolling(10).mean()

        # Relative Volume (RVOL) — volume vs média
        df["volume_sma_20"] = df["volume"].rolling(20).mean()
        df["rvol"] = (df["volume"] / df["volume_sma_20"].replace(0, 1)).fillna(1)

        # On-Balance Volume (OBV)
        df["obv"] = (np.sign(df["close"].diff()) * df["volume"]).fillna(0).cumsum()
        df["obv_ema"] = ta.EMA(df["obv"], timeperiod=20)

        # Money Flow Index (MFI) — RSI ponderado por volume
        df["mfi"] = ta.MFI(df, timeperiod=14)

        # Chaikin Money Flow (CMF)
        mf_mult = ((df["close"] - df["low"]) - (df["high"] - df["close"])) / hl_range
        mf_mult = mf_mult.fillna(0)
        mf_volume = mf_mult * df["volume"]
        vol_sum = df["volume"].rolling(20).sum().replace(0, 1)
        df["cmf"] = mf_volume.rolling(20).sum() / vol_sum

        # Volume Price Trend (VPT) — preço + volume combinados
        df["vpt"] = (df["close"].pct_change() * df["volume"]).fillna(0).cumsum()
        df["vpt_ema"] = ta.EMA(df["vpt"], timeperiod=20)

        # Force Index = variação de preço × volume
        df["force_index"] = df["close"].diff() * df["volume"]
        df["force_index_ema"] = ta.EMA(df["force_index"], timeperiod=13)

        # Rolling VWAP (50 períodos)
        tp = (df["high"] + df["low"] + df["close"]) / 3
        vwap_vol = df["volume"].rolling(50).sum().replace(0, 1)
        df["vwap"] = (tp * df["volume"]).rolling(50).sum() / vwap_vol
        df["vwap_distance"] = (
            (df["close"] - df["vwap"]) / df["vwap"].replace(0, 1) * 100
        )

        return df

    # ===================================================================
    # PROPRIETARY: MICROSTRUCTURE ANALYSIS
    # ===================================================================

    def _calc_microstructure(self, df: DataFrame) -> DataFrame:
        """
        Análise de Microestrutura — detecta ações institucionais.
        
        Absorção: Volume alto + corpo pequeno = institucionais absorvendo
        Exaustão: Volume extremo + corpo grande = possível esgotamento
        Smart Money: FVG, Liquidity Sweeps, Displacement
        """
        hl_range = (df["high"] - df["low"]).replace(0, np.nan).fillna(0.0001)
        body = (df["close"] - df["open"]).abs()

        # Body Ratio (0=doji, 1=marubozu)
        df["body_ratio"] = body / hl_range

        # Wick Ratios
        upper_wick = df["high"] - df[["open", "close"]].max(axis=1)
        lower_wick = df[["open", "close"]].min(axis=1) - df["low"]
        df["upper_wick_ratio"] = upper_wick / hl_range
        df["lower_wick_ratio"] = lower_wick / hl_range

        # === PADRÕES INSTITUCIONAIS ===

        # Absorção: corpo pequeno + volume alto = absorção silenciosa
        df["is_absorption"] = (
            (df["body_ratio"] < 0.30) & (df["rvol"] > 1.5)
        ).astype(int)

        # Exaustão: corpo grande + volume extremo = esgotamento
        df["is_exhaustion_bull"] = (
            (df["body_ratio"] > 0.70)
            & (df["rvol"] > 2.0)
            & (df["close"] > df["open"])
        ).astype(int)
        df["is_exhaustion_bear"] = (
            (df["body_ratio"] > 0.70)
            & (df["rvol"] > 2.0)
            & (df["close"] < df["open"])
        ).astype(int)

        # Pin Bar Bullish: wick inferior longo, corpo no topo
        df["is_pin_bar_bull"] = (
            (df["lower_wick_ratio"] > 0.60)
            & (df["body_ratio"] < 0.25)
            & (df["upper_wick_ratio"] < 0.15)
        ).astype(int)

        # Pin Bar Bearish
        df["is_pin_bar_bear"] = (
            (df["upper_wick_ratio"] > 0.60)
            & (df["body_ratio"] < 0.25)
            & (df["lower_wick_ratio"] < 0.15)
        ).astype(int)

        # Displacement Bullish: corpo grande, pouca wick, volume (institucional)
        df["is_displacement_bull"] = (
            (df["body_ratio"] > 0.80)
            & (df["close"] > df["open"])
            & (df["rvol"] > 1.2)
        ).astype(int)

        df["is_displacement_bear"] = (
            (df["body_ratio"] > 0.80)
            & (df["close"] < df["open"])
            & (df["rvol"] > 1.2)
        ).astype(int)

        # === SMART MONEY CONCEPTS ===

        # Fair Value Gap Bullish: gap entre candle[i].low e candle[i-2].high
        df["fvg_bull"] = (df["low"] > df["high"].shift(2)).astype(int)
        df["fvg_bear"] = (df["high"] < df["low"].shift(2)).astype(int)

        # Liquidity Sweep: preço ultrapassa extremo e fecha de volta
        recent_high = df["high"].rolling(20).max().shift(1)
        recent_low = df["low"].rolling(20).min().shift(1)
        df["liquidity_sweep_high"] = (
            (df["high"] > recent_high) & (df["close"] < recent_high)
        ).astype(int)
        df["liquidity_sweep_low"] = (
            (df["low"] < recent_low) & (df["close"] > recent_low)
        ).astype(int)

        # Volume Climax com reversão confirmada (sem look-ahead)
        df["volume_climax_reversal_bear"] = (
            (df["rvol"].shift(1) > 3.0)
            & (df["close"].shift(1) > df["open"].shift(1))
            & (df["close"] < df["open"])
        ).astype(int)

        df["volume_climax_reversal_bull"] = (
            (df["rvol"].shift(1) > 3.0)
            & (df["close"].shift(1) < df["open"].shift(1))
            & (df["close"] > df["open"])
        ).astype(int)

        return df

    # ===================================================================
    # PROPRIETARY: INNOVATION INDICATORS
    # ===================================================================

    def _calc_innovation(self, df: DataFrame) -> DataFrame:
        """
        Indicadores inovadores baseados em Teoria da Informação e Física.
        
        - Kaufman Efficiency Ratio: proxy do Hurst Exponent
        - Volatility Ratio: proxy de Entropia (ordem vs caos)
        - Oscillation Index: mede ruído do preço
        - Trend Acceleration: derivada segunda do preço
        - Volume-Price Divergence: acumulação/distribuição oculta
        - Composite Momentum: ROC multi-período ponderado
        - Squeeze Momentum: BB dentro de Keltner = explosão iminente
        """
        # === KAUFMAN EFFICIENCY RATIO ===
        # ER → 1 = trending (Hurst > 0.5)
        # ER → 0 = random walk (Hurst ≤ 0.5)
        period = 20
        direction = (df["close"] - df["close"].shift(period)).abs()
        volatility = df["close"].diff().abs().rolling(period).sum()
        df["efficiency_ratio"] = (direction / volatility.replace(0, 1)).fillna(0)

        # === VOLATILITY RATIO (Proxy de Entropia) ===
        df["atr"] = ta.ATR(df, timeperiod=14)
        atr_long = ta.ATR(df, timeperiod=50)
        df["volatility_ratio"] = (df["atr"] / atr_long.replace(0, 1)).fillna(1)

        # === PRICE OSCILLATION INDEX ===
        # Baixo = preço em linha reta (trend), Alto = preço oscilando (range)
        net_move_10 = (df["close"] - df["close"].shift(10)).abs()
        total_move_10 = df["close"].diff().abs().rolling(10).sum()
        df["oscillation_index"] = 1 - (net_move_10 / total_move_10.replace(0, 1))

        # === TREND ACCELERATION ===
        # Segunda derivada do preço (momentum do momentum)
        price_velocity = df["close"].diff(5)
        df["trend_acceleration"] = price_velocity.diff(5)
        df["trend_accel_norm"] = (
            df["trend_acceleration"] / df["atr"].replace(0, 1)
        ).fillna(0)

        # === VOLUME-PRICE DIVERGENCE ===
        # Preço sobe + volume cai = divergência bearish (distribuição)
        # Preço cai + volume sobe = possível acumulação
        price_chg = df["close"].pct_change(10).fillna(0)
        vol_chg = df["volume"].pct_change(10).fillna(0)
        df["vol_price_divergence"] = price_chg - vol_chg

        # === COMPOSITE MOMENTUM (Multi-Period ROC) ===
        roc_3 = ta.ROC(df, timeperiod=3).fillna(0)
        roc_5 = ta.ROC(df, timeperiod=5).fillna(0)
        roc_8 = ta.ROC(df, timeperiod=8).fillna(0)
        roc_13 = ta.ROC(df, timeperiod=13).fillna(0)
        roc_21 = ta.ROC(df, timeperiod=21).fillna(0)
        df["composite_momentum"] = (
            roc_3 * 5 + roc_5 * 4 + roc_8 * 3 + roc_13 * 2 + roc_21 * 1
        ) / 15

        # === SQUEEZE MOMENTUM (TTM Squeeze) ===
        # Bollinger Bands dentro de Keltner Channels = compressão de volatilidade
        # Quando a squeeze libera = movimento explosivo
        kelt_ema_20 = ta.EMA(df, timeperiod=20)
        kelt_atr_10 = ta.ATR(df, timeperiod=10)
        kelt_upper = kelt_ema_20 + kelt_atr_10 * 1.5
        kelt_lower = kelt_ema_20 - kelt_atr_10 * 1.5

        bb = ta.BBANDS(df, timeperiod=20, nbdevup=2.0, nbdevdn=2.0)
        bb_upper = bb["upperband"]
        bb_lower = bb["lowerband"]
        bb_middle = bb["middleband"]

        df["bb_upper"] = bb_upper
        df["bb_middle"] = bb_middle
        df["bb_lower"] = bb_lower
        df["bb_width"] = (bb_upper - bb_lower) / bb_middle.replace(0, 1)
        df["bb_percent"] = (
            (df["close"] - bb_lower) / (bb_upper - bb_lower).replace(0, 1)
        )

        df["keltner_upper"] = kelt_upper
        df["keltner_lower"] = kelt_lower

        # Squeeze ativo = BB está dentro de Keltner
        df["squeeze_on"] = (
            (bb_lower > kelt_lower) & (bb_upper < kelt_upper)
        ).astype(int)

        # Squeeze release bullish: squeeze acabou + momentum positivo
        df["squeeze_release_bull"] = (
            (df["squeeze_on"].shift(1) == 1)
            & (df["squeeze_on"] == 0)
            & (df["close"] > df["close"].shift(1))
        ).astype(int)

        df["squeeze_release_bear"] = (
            (df["squeeze_on"].shift(1) == 1)
            & (df["squeeze_on"] == 0)
            & (df["close"] < df["close"].shift(1))
        ).astype(int)

        return df

    # ===================================================================
    # INDICATOR COMPUTATION (runs once, cached)
    # ===================================================================

    def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        """
        Calcula 50+ indicadores: convencionais + proprietários + inovadores.
        Tudo vetorizado para máxima performance no i9.
        """

        # ============================================================
        # CONVENTIONAL INDICATORS (Enhanced)
        # ============================================================

        # RSI (dual period)
        dataframe["rsi"] = ta.RSI(dataframe, timeperiod=14)
        dataframe["rsi_fast"] = ta.RSI(dataframe, timeperiod=7)

        # MACD
        macd = ta.MACD(dataframe, fastperiod=12, slowperiod=26, signalperiod=9)
        dataframe["macd"] = macd["macd"]
        dataframe["macd_signal"] = macd["macdsignal"]
        dataframe["macd_hist"] = macd["macdhist"]

        # EMAs (ribbon)
        dataframe["ema_8"] = ta.EMA(dataframe, timeperiod=8)
        dataframe["ema_13"] = ta.EMA(dataframe, timeperiod=13)
        dataframe["ema_21"] = ta.EMA(dataframe, timeperiod=21)
        dataframe["ema_50"] = ta.EMA(dataframe, timeperiod=50)
        dataframe["ema_200"] = ta.EMA(dataframe, timeperiod=200)

        # EMA Ribbon Score (0-4: quantas EMAs alinhadas bullish)
        dataframe["ema_ribbon"] = (
            (dataframe["ema_8"] > dataframe["ema_13"]).astype(int)
            + (dataframe["ema_13"] > dataframe["ema_21"]).astype(int)
            + (dataframe["ema_21"] > dataframe["ema_50"]).astype(int)
            + (dataframe["ema_50"] > dataframe["ema_200"]).astype(int)
        )

        # ADX + Directional Movement
        dataframe["adx"] = ta.ADX(dataframe, timeperiod=14)
        dataframe["plus_di"] = ta.PLUS_DI(dataframe, timeperiod=14)
        dataframe["minus_di"] = ta.MINUS_DI(dataframe, timeperiod=14)

        # Stochastic RSI
        stoch = ta.STOCHRSI(dataframe, timeperiod=14, fastk_period=3, fastd_period=3)
        dataframe["stochrsi_k"] = stoch["fastk"]
        dataframe["stochrsi_d"] = stoch["fastd"]

        # CCI
        dataframe["cci"] = ta.CCI(dataframe, timeperiod=14)

        # Williams %R
        dataframe["willr"] = ta.WILLR(dataframe, timeperiod=14)

        # Rate of Change
        dataframe["roc"] = ta.ROC(dataframe, timeperiod=10)

        # ATR
        dataframe["atr"] = ta.ATR(dataframe, timeperiod=14)
        dataframe["atr_pct"] = dataframe["atr"] / dataframe["close"] * 100

        # Ichimoku componentes
        ichi_tenkan = (
            dataframe["high"].rolling(9).max() + dataframe["low"].rolling(9).min()
        ) / 2
        ichi_kijun = (
            dataframe["high"].rolling(26).max() + dataframe["low"].rolling(26).min()
        ) / 2
        dataframe["ichi_tenkan"] = ichi_tenkan
        dataframe["ichi_kijun"] = ichi_kijun
        dataframe["ichi_above"] = (
            (dataframe["close"] > ichi_tenkan).astype(int)
            + (dataframe["close"] > ichi_kijun).astype(int)
        )

        # Parabolic SAR
        dataframe["sar"] = ta.SAR(dataframe, acceleration=0.02, maximum=0.2)
        dataframe["sar_bullish"] = (dataframe["close"] > dataframe["sar"]).astype(int)

        # ============================================================
        # PROPRIETARY: VOLUME FLOW
        # ============================================================
        dataframe = self._calc_volume_flow(dataframe)

        # ============================================================
        # PROPRIETARY: MICROSTRUCTURE
        # ============================================================
        dataframe = self._calc_microstructure(dataframe)

        # ============================================================
        # PROPRIETARY: INNOVATION
        # ============================================================
        dataframe = self._calc_innovation(dataframe)

        # ============================================================
        # MULTI-TIMEFRAME (15m + 1h)
        # ============================================================
        if self.dp:
            # --- 15 MINUTES ---
            inf_15m = self.dp.get_pair_dataframe(
                pair=metadata["pair"], timeframe="15m"
            )
            if not inf_15m.empty:
                inf_15m["rsi"] = ta.RSI(inf_15m, timeperiod=14)
                macd_15m = ta.MACD(inf_15m)
                inf_15m["macd_hist"] = macd_15m["macdhist"]
                inf_15m["ema_50"] = ta.EMA(inf_15m, timeperiod=50)
                inf_15m["ema_200"] = ta.EMA(inf_15m, timeperiod=200)
                inf_15m["adx"] = ta.ADX(inf_15m, timeperiod=14)

                # Volume flow 15m
                hl_15m = (inf_15m["high"] - inf_15m["low"]).replace(0, np.nan)
                bv_15m = inf_15m["volume"] * (inf_15m["close"] - inf_15m["low"]) / hl_15m
                sv_15m = inf_15m["volume"] * (inf_15m["high"] - inf_15m["close"]) / hl_15m
                bv_15m = bv_15m.fillna(inf_15m["volume"] * 0.5)
                sv_15m = sv_15m.fillna(inf_15m["volume"] * 0.5)
                inf_15m["pressure"] = (bv_15m / (bv_15m + sv_15m).replace(0, 1)).fillna(
                    0.5
                )

                dataframe = merge_informative_pair(
                    dataframe, inf_15m, self.timeframe, "15m", ffill=True
                )

            # --- 1 HOUR ---
            inf_1h = self.dp.get_pair_dataframe(
                pair=metadata["pair"], timeframe="1h"
            )
            if not inf_1h.empty:
                inf_1h["rsi"] = ta.RSI(inf_1h, timeperiod=14)
                inf_1h["ema_50"] = ta.EMA(inf_1h, timeperiod=50)
                inf_1h["ema_200"] = ta.EMA(inf_1h, timeperiod=200)
                inf_1h["adx"] = ta.ADX(inf_1h, timeperiod=14)

                dataframe = merge_informative_pair(
                    dataframe, inf_1h, self.timeframe, "1h", ffill=True
                )

        return dataframe

    # ===================================================================
    # CONFLUENCE SCORE SYSTEM - ENTRY
    # ===================================================================

    def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        """
        Sistema de Score por Confluência — Entrada.
        
        Cada indicador que confirma a oportunidade adiciona 1 ponto.
        Entrada quando score total >= threshold (otimizado por Hyperopt).
        
        Pontuação máxima: 18 pontos
        - Volume Flow:     4 pontos
        - Momentum:        4 pontos
        - Trend:           3 pontos
        - Microstructure:  3 pontos
        - Innovation:      2 pontos
        - Multi-TF:        2 pontos
        """
        score = np.zeros(len(dataframe))

        # ── VOLUME FLOW (max 4 pontos) ──────────────────────

        # 1. Pressão compradora dominante
        score += (
            dataframe["pressure_ratio"] > self.buy_pressure_ratio.value
        ).astype(int)

        # 2. Volume acima da média (RVOL)
        score += (dataframe["rvol"] > self.buy_rvol_min.value).astype(int)

        # 3. CVD subindo por N períodos consecutivos
        cvd_rising_count = (
            dataframe["cvd_rising"].rolling(self.buy_cvd_periods.value).sum()
        )
        score += (cvd_rising_count >= self.buy_cvd_periods.value).astype(int)

        # 4. MFI indicando entrada de dinheiro Smart Money
        score += (dataframe["mfi"] > self.buy_mfi_min.value).astype(int)

        # ── MOMENTUM (max 4 pontos) ─────────────────────────

        # 5. RSI na zona de compra (não sobrevendido nem sobrecomprado)
        score += (
            (dataframe["rsi"] > self.buy_rsi_min.value)
            & (dataframe["rsi"] < self.buy_rsi_max.value)
        ).astype(int)

        # 6. MACD Histogram positivo OU subindo
        score += (
            (dataframe["macd_hist"] > 0)
            | (dataframe["macd_hist"] > dataframe["macd_hist"].shift(1))
        ).astype(int)

        # 7. StochRSI em zona favorável (saindo de sobrevenda)
        score += (
            dataframe["stochrsi_k"] < self.buy_stochrsi_max.value
        ).astype(int)

        # 8. CCI saindo de oversold (momentum reversal)
        score += (
            (dataframe["cci"] > self.buy_cci_min.value)
            & (dataframe["cci"].shift(1) <= self.buy_cci_min.value)
        ).astype(int)

        # ── TREND (max 3 pontos) ────────────────────────────

        # 9. ADX mostrando tendência ativa
        score += (dataframe["adx"] > self.buy_adx_min.value).astype(int)

        # 10. EMA Ribbon alinhado (N de 4 EMAs em ordem bullish)
        score += (
            dataframe["ema_ribbon"] >= self.buy_ema_ribbon_min.value
        ).astype(int)

        # 11. Preço acima da VWAP (institucional favorável)
        score += (dataframe["close"] > dataframe["vwap"]).astype(int)

        # ── MICROSTRUCTURE (max 3 pontos) ───────────────────

        # 12. Absorção detectada nas últimas 3 candles
        score += (
            dataframe["is_absorption"].rolling(3).sum() > 0
        ).astype(int)

        # 13. Pin bar bullish OU liquidity sweep baixo
        score += (
            (dataframe["is_pin_bar_bull"] == 1)
            | (dataframe["liquidity_sweep_low"] == 1)
        ).astype(int)

        # 14. Squeeze release bullish (explosão de volatilidade)
        score += (dataframe["squeeze_release_bull"] == 1).astype(int)

        # ── INNOVATION (max 2 pontos) ───────────────────────

        # 15. Efficiency Ratio indica tendência (não aleatório)
        score += (
            dataframe["efficiency_ratio"] > self.buy_efficiency_min.value
        ).astype(int)

        # 16. Volatilidade controlada (não excessiva)
        score += (
            dataframe["volatility_ratio"] < self.buy_volatility_ratio_max.value
        ).astype(int)

        # ── MULTI-TIMEFRAME (max 2 pontos) ──────────────────

        # 17. RSI 15m favorável
        rsi_15m_col = "rsi_15m"
        if rsi_15m_col in dataframe.columns:
            score += (
                dataframe[rsi_15m_col] > self.buy_mtf_rsi_15m_min.value
            ).astype(int)

        # 18. Tendência 15m + 1h alinhadas (preço acima de EMA50)
        if self.buy_mtf_aligned.value:
            ema50_15m = "ema_50_15m"
            ema50_1h = "ema_50_1h"
            if ema50_15m in dataframe.columns and ema50_1h in dataframe.columns:
                score += (
                    (dataframe["close"] > dataframe[ema50_15m])
                    & (dataframe["close"] > dataframe[ema50_1h])
                ).astype(int)

        # === ENTRY SIGNAL ===
        dataframe.loc[
            (score >= self.buy_score_min.value) & (dataframe["volume"] > 0),
            "enter_long",
        ] = 1

        return dataframe

    # ===================================================================
    # CONFLUENCE SCORE SYSTEM - EXIT
    # ===================================================================

    def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        """
        Sistema de Score por Confluência — Saída.
        
        Pontuação máxima: 12 pontos
        - Volume Flow:     3 pontos
        - Momentum:        3 pontos
        - Trend Weakening: 2 pontos
        - Microstructure:  2 pontos
        - Innovation:      2 pontos
        """
        score = np.zeros(len(dataframe))

        # ── VOLUME FLOW (max 3 pontos) ──────────────────────

        # 1. Pressão vendedora dominante
        score += (
            dataframe["pressure_ratio"] < self.sell_pressure_ratio_max.value
        ).astype(int)

        # 2. CVD caindo abaixo da EMA (fluxo virando)
        score += (dataframe["cvd"] < dataframe["cvd_ema"]).astype(int)

        # 3. CMF negativo (dinheiro saindo)
        score += (
            dataframe["cmf"] < self.sell_cmf_threshold.value
        ).astype(int)

        # ── MOMENTUM (max 3 pontos) ─────────────────────────

        # 4. RSI sobrecomprado
        score += (dataframe["rsi"] > self.sell_rsi_max.value).astype(int)

        # 5. StochRSI sobrecomprado
        score += (
            dataframe["stochrsi_k"] > self.sell_stochrsi_min.value
        ).astype(int)

        # 6. CCI extremo (overbought)
        score += (dataframe["cci"] > self.sell_cci_max.value).astype(int)

        # ── TREND WEAKENING (max 2 pontos) ──────────────────

        # 7. MACD histogram virando negativo e caindo
        if self.sell_macd_hist_negative.value:
            score += (
                (dataframe["macd_hist"] < 0)
                & (dataframe["macd_hist"] < dataframe["macd_hist"].shift(1))
            ).astype(int)

        # 8. ADX caindo (tendência enfraquecendo)
        if self.sell_adx_weakening.value:
            score += (
                dataframe["adx"] < dataframe["adx"].shift(1)
            ).astype(int)

        # ── MICROSTRUCTURE (max 2 pontos) ───────────────────

        # 9. Exaustão bullish detectada (topo potencial)
        score += (
            (dataframe["is_exhaustion_bull"] == 1)
            & (dataframe["rvol"] > self.sell_exhaustion_rvol.value)
        ).astype(int)

        # 10. Displacement bearish OU liquidity sweep alto
        score += (
            (dataframe["is_displacement_bear"] == 1)
            | (dataframe["liquidity_sweep_high"] == 1)
        ).astype(int)

        # ── INNOVATION (max 2 pontos) ───────────────────────

        # 11. Eficiência baixa (mercado ficando aleatório)
        score += (
            dataframe["efficiency_ratio"] < self.sell_efficiency_min.value
        ).astype(int)

        # 12. Volume climax bearish (reversão após climax)
        score += (
            dataframe["volume_climax_reversal_bear"] == 1
        ).astype(int)

        # === EXIT SIGNAL ===
        dataframe.loc[
            (score >= self.sell_score_min.value) & (dataframe["volume"] > 0),
            "exit_long",
        ] = 1

        return dataframe

    # ===================================================================
    # DYNAMIC STOPLOSS (ATR-based)
    # ===================================================================

    def custom_stoploss(
        self,
        pair: str,
        trade: Trade,
        current_time,
        current_rate,
        current_profit,
        after_fill,
        **kwargs,
    ) -> Optional[float]:
        """Stop loss dinâmico baseado em ATR — adapta à volatilidade atual."""
        dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe)
        if dataframe is not None and len(dataframe) > 0:
            last = dataframe.iloc[-1]
            atr_pct = last.get("atr_pct", 0)
            if atr_pct > 0 and trade.open_rate > 0:
                # Stop = 2.5x ATR abaixo da entrada
                sl_pct = -(atr_pct / 100) * 2.5
                return max(sl_pct, -0.05)  # Cap at -5%
        return None
