# source: https://raw.githubusercontent.com/qninhdt/lmao/16af76aa39958ea8653d4be95d6363d53c09af1e/user_data/strategies/AlexBattleTankKillerV48.py
import logging
import numpy as np
import pandas as pd
import warnings
from datetime import datetime, timedelta
from typing import Optional, Dict, List, Tuple

import talib.abstract as ta
import pandas_ta as pta  # pandas_ta is imported but not explicitly used in the provided code.
# If it's for future use or part of an older version, that's okay.
# Otherwise, it can be removed if not needed.
from scipy.signal import argrelextrema

import freqtrade.vendor.qtpylib.indicators as qtpylib
from freqtrade.strategy import IStrategy, DecimalParameter, IntParameter, BooleanParameter
from freqtrade.persistence import Trade

warnings.simplefilter(action="ignore", category=pd.errors.PerformanceWarning)
logger = logging.getLogger(__name__)

# Define Murrey Math level names for consistency
MML_LEVEL_NAMES = [
    "[-3/8]P", "[-2/8]P", "[-1/8]P", "[0/8]P", "[1/8]P",
    "[2/8]P", "[3/8]P", "[4/8]P", "[5/8]P", "[6/8]P",
    "[7/8]P", "[8/8]P", "[+1/8]P", "[+2/8]P", "[+3/8]P"
]
def calculate_minima_maxima(df, window):
    if df is None or df.empty:
        return np.zeros(0), np.zeros(0)

    minima = np.zeros(len(df))
    maxima = np.zeros(len(df))

    for i in range(window, len(df)):
        window_data = df['ha_close'].iloc[i - window:i + 1]
        if df['ha_close'].iloc[i] == window_data.min() and (window_data == df['ha_close'].iloc[i]).sum() == 1:
            minima[i] = -window
        if df['ha_close'].iloc[i] == window_data.max() and (window_data == df['ha_close'].iloc[i]).sum() == 1:
            maxima[i] = window

    return minima, maxima


class Github_qninhdt_lmao__AlexBattleTankKillerV48__20250920_110742(IStrategy):
    """
    Enhanced strategy on the 15-minute timeframe with Market Correlation Filters.

    Key improvements:
      - Adjustet Entrys
      - Added Option to use Stoploss on Exchange for Trades
      - Fixed Stop Loss and Added Long and Short Exits
      - Dynamic stoploss based on ATR.
      - Dynamic leverage calculation.
      - Murrey Math level calculation (rolling window for performance).
      - Enhanced DCA (Average Price) logic.
      - Translated to English and code structured for clarity.
      - Parameterization of internal constants for optimization.
      - Changed Exit Signals for Opposite.
      - Change SL to -0.07
      - Changed stake amout for renentry
      - FIXED: Prevents opening opposite position when trade is active
      - FIXED: Trailing stop properly disabled
      - NEW: Market correlation filters for better entry timing
    """

    # General strategy parameters
    timeframe = "1h"
    startup_candle_count: int = 200
    stoploss = -0.15  # Fixed 8% stop loss
    use_custom_stoploss = False  # Disable custom logic
    stoploss_on_exchange = True  # Let exchange handle it
    stoploss_on_exchange_interval = 60  # Check every 60 seconds
    trailing_stop = True
    trailing_stop_positive = 0.015  # Start trailing at 1.5%
    trailing_stop_positive_offset = 0.03   # Begin after 3% profit
    trailing_only_offset_is_reached = True
    position_adjustment_enable = True
    can_short = True
    use_exit_signal = True
    ignore_roi_if_entry_signal = False
    max_stake_per_trade = 10.0
    max_portfolio_percentage_per_trade = 0.05
    max_entry_position_adjustment = 1
    process_only_new_candles = True
    max_dca_orders = 1
    max_total_stake_per_pair = 10
    max_single_dca_amount = 4
    use_custom_exits_advanced = True
    use_emergency_exits = True
    
    enable_position_flip = True
    disable_volatility_filter = BooleanParameter(default=True, space="buy", optimize=False)
    # 🎯 REENTRY PARAMETERS
    enable_reentry = BooleanParameter(default=False, space='buy')
    reentry_cooldown_minutes = IntParameter(15, 120, default=45, space='buy')     # 15min-2h (1-8 candles)
    max_reentries_per_direction = IntParameter(1, 3, default=2, space='buy')      # Reduced from 7→2
    enable_quick_reentry_on_roi = BooleanParameter(default=True, space='buy')
    quick_reentry_cooldown = IntParameter(15, 45, default=30, space='buy')        # 15-45min (1-3 candles)
    reentry_signal_strength_threshold = DecimalParameter(0.3, 0.8, default=0.4, space='buy')

    signal_quality_enabled = BooleanParameter(default=True, space='buy', optimize=True)
    min_signal_strength = DecimalParameter(0.3, 0.8, default=0.5, space='buy', optimize=True)

    # 🎯 NEW SIGNAL FLIP PARAMETERS
    signal_flip_enabled = BooleanParameter(default=True, space="sell", optimize=True, load=True)  # NEW
    signal_flip_min_profit = DecimalParameter(-0.05, 0.02, default=-0.02, decimals=3, space="sell", optimize=True, load=True)  # NEW
    signal_flip_max_loss = DecimalParameter(-0.10, -0.03, default=-0.06, decimals=2, space="sell", optimize=True, load=True)  # NEW
    
    # Volume confirmation (referenced but not defined!)
    volume_confirmation_enabled = BooleanParameter(default=True, space='buy', optimize=True)
    require_volume_confirmation = BooleanParameter(default=False, space='buy', optimize=True)
    # Signal confirmation parameters
    signal_confirmation_candles = IntParameter(1, 5, default=2, space="buy", optimize=True, load=True)
    signal_cooldown_period = IntParameter(3, 15, default=5, space="buy", optimize=True, load=True)
    min_signal_strength = DecimalParameter(0.7, 0.9, default=0.8, decimals=2, space="buy", optimize=True, load=True)

    # Trend stability parameters  
    trend_stability_period = IntParameter(5, 20, default=10, space="buy", optimize=True, load=True)
    min_trend_strength = DecimalParameter(0.01, 0.05, default=0.02, decimals=3, space="buy", optimize=True, load=True)

    # Signal filtering parameters
    signal_quality_enabled = BooleanParameter(default=True, space='buy')
    min_signal_strength = DecimalParameter(0.3, 0.8, default=0.5, space='buy')
    
    flip_profit_threshold = DecimalParameter(low=0.01, high=0.10, default=0.03, decimals=3, space="buy", optimize=True, load=True)
    # 🚀 FOLLOW THE PRICE PARAMETERS (NEU)
    follow_price_enabled = BooleanParameter(default=False, space="sell", optimize=True, load=True)
    follow_price_activation_profit = DecimalParameter(0.01, 0.05, default=0.02, decimals=3, space="sell", optimize=True, load=True)  # CHANGED: Was 0.01, now 0.02
    follow_price_pullback_pct = DecimalParameter(0.005, 0.03, default=0.015, decimals=3, space="sell", optimize=True, load=True)  # CHANGED: Range tightened
    follow_price_min_profit = DecimalParameter(0.005, 0.04, default=0.01, decimals=3, space="sell", optimize=True, load=True)
    
    # 🎯 MML-BASIERTE FOLLOW PRICE (NEU)
    follow_price_use_mml = BooleanParameter(default=True, space="sell", optimize=True, load=True)
    follow_price_mml_buffer = DecimalParameter(0.001, 0.01, default=0.003, decimals=4, space="sell", optimize=True, load=True)
    
    # 📊 MARKET CONDITION ADJUSTMENTS (NEU)
    follow_price_market_adjustment = BooleanParameter(default=True, space="sell", optimize=True, load=True)
    follow_price_volatile_multiplier = DecimalParameter(0.5, 1.0, default=0.7, decimals=2, space="sell", optimize=True, load=True)
    follow_price_bearish_multiplier = DecimalParameter(0.6, 1.0, default=0.8, decimals=2, space="sell", optimize=True, load=True)
    
    # 🔧 ATR STOPLOSS PARAMETERS (Anpassbar machen)
    atr_stoploss_multiplier = DecimalParameter(2.5, 3.5, default=2.8, space="sell")
    atr_stoploss_minimum = DecimalParameter(-0.30, -0.15, default=-0.25, space="sell")
    atr_stoploss_maximum = DecimalParameter(-0.12, -0.06, default=-0.08, space="sell")
    early_trade_multiplier = DecimalParameter(1.2, 1.6, default=1.4, space="sell")
    early_trade_hours = DecimalParameter(1.0, 3.0, default=2.0, space="sell")
    # DCA parameters
    initial_safety_order_trigger = DecimalParameter(
        low=-0.02, high=-0.01, default=-0.018, decimals=3, space="buy", optimize=True, load=True
    )
    max_safety_orders = IntParameter(1, 3, default=2, space="buy", optimize=True, load=True)
    safety_order_step_scale = DecimalParameter(
        low=1.05, high=1.5, default=1.25, decimals=2, space="buy", optimize=True, load=True
    )
    safety_order_volume_scale = DecimalParameter(
        low=1.1, high=2.0, default=1.4, decimals=1, space="buy", optimize=True, load=True
    )
    h2 = IntParameter(20, 60, default=40, space="buy", optimize=True, load=True)
    h1 = IntParameter(10, 40, default=20, space="buy", optimize=True, load=True)
    h0 = IntParameter(5, 20, default=10, space="buy", optimize=True, load=True)
    cp = IntParameter(5, 20, default=10, space="buy", optimize=True, load=True)

    # Entry parameters
    increment_for_unique_price = DecimalParameter(
        low=1.0005, high=1.002, default=1.001, decimals=4, space="buy", optimize=True, load=True
    )
    last_entry_price: Optional[float] = None

    # Protection parameters
    cooldown_lookback = IntParameter(2, 48, default=1, space="protection", optimize=True)
    stop_duration = IntParameter(12, 200, default=4, space="protection", optimize=True)
    use_stop_protection = BooleanParameter(default=True, space="protection", optimize=True)

    # Murrey Math level parameters
    mml_const1 = DecimalParameter(1.0, 1.1, default=1.0699, decimals=4, space="buy", optimize=True, load=True)
    mml_const2 = DecimalParameter(0.99, 1.0, default=0.99875, decimals=5, space="buy", optimize=True, load=True)
    indicator_mml_window = IntParameter(32, 128, default=64, space="buy", optimize=True, load=True)

    # Dynamic Stoploss parameters
    use_dynamic_stoploss = BooleanParameter(default=True, space='sell', optimize=True)
    atr_multiplier = DecimalParameter(1.5, 3.0, default=2.0, space='sell', optimize=True)
    max_stoploss = DecimalParameter(-0.25, -0.10, default=-0.15, space='sell', optimize=True)
    min_stoploss = DecimalParameter(-0.10, -0.05, default=-0.08, space='sell', optimize=True)
    stoploss_atr_multiplier = DecimalParameter(1.0, 3.0, default=1.5, decimals=1, space="sell", optimize=True,
                                               load=True)
    stoploss_max_reasonable = DecimalParameter(-0.30, -0.10, default=-0.20, decimals=2, space="sell", optimize=True,
                                               load=True)
    # === Hyperopt Parameters ===
    dominance_threshold = IntParameter(1, 10, default=3, space="buy", optimize=True)
    tightness_factor = DecimalParameter(0.5, 2.0, default=1.0, space="buy", optimize=True)
    long_rsi_threshold = IntParameter(50, 65, default=55, space="buy", optimize=True)
    short_rsi_threshold = IntParameter(30, 45, default=40, space="sell", optimize=True)

    # Dynamic Leverage parameters
    leverage_window_size = IntParameter(20, 100, default=50, space="buy", optimize=True, load=True)
    leverage_base = DecimalParameter(5.0, 20.0, default=7.0, decimals=1, space="buy", optimize=True, load=True)
    leverage_rsi_low = DecimalParameter(20.0, 40.0, default=30.0, decimals=1, space="buy", optimize=True, load=True)
    leverage_rsi_high = DecimalParameter(60.0, 80.0, default=70.0, decimals=1, space="buy", optimize=True, load=True)
    leverage_long_increase_factor = DecimalParameter(1.1, 2.0, default=1.5, decimals=1, space="buy", optimize=True,
                                                     load=True)
    leverage_long_decrease_factor = DecimalParameter(0.3, 0.9, default=0.5, decimals=1, space="buy", optimize=True,
                                                     load=True)
    leverage_volatility_decrease_factor = DecimalParameter(0.5, 0.95, default=0.8, decimals=2, space="buy",
                                                           optimize=True, load=True)
    leverage_atr_threshold_pct = DecimalParameter(0.01, 0.05, default=0.03, decimals=3, space="buy", optimize=True,
                                                  load=True)

    # Indicator parameters
    indicator_extrema_order = IntParameter(3, 10, default=5, space="buy", optimize=True, load=True)
    indicator_rolling_window_threshold = IntParameter(5, 20, default=10, space="buy", optimize=True, load=True)
    indicator_rolling_check_window = IntParameter(2, 10, default=4, space="buy", optimize=True, load=True)

    # === Market Correlation Parameters ===
    # Bitcoin correlation parameters
    enable_btc_correlation = BooleanParameter(default=True, space="buy", optimize=False)
    btc_correlation_enabled = BooleanParameter(default=False, space="buy", optimize=False)
    btc_correlation_threshold = DecimalParameter(0.3, 0.8, default=0.5, decimals=2, space="buy", optimize=True)
    btc_trend_filter_enabled = BooleanParameter(default=False, space="buy", optimize=True)
    
    # Market Breadth
    # Add these to your strategy class:
    market_breadth_enabled = BooleanParameter(default=True, space="buy", optimize=True)
    market_breadth_threshold = DecimalParameter(0.3, 0.6, default=0.45, space="buy", optimize=True)
    market_breadth_bull_threshold = DecimalParameter(0.35, 0.65, default=0.50, space="buy", optimize=True, load=True)
    market_breadth_bear_threshold = DecimalParameter(0.35, 0.65, default=0.50, space="buy", optimize=True, load=True)
    market_breadth_sensitivity = DecimalParameter(0.05, 0.20, default=0.10, space="buy", optimize=True, load=True)
    breadth_weighting_enabled = BooleanParameter(default=True, space="buy", optimize=True, load=True)
    volume_confirmation_enabled = BooleanParameter(default=True, space="buy", optimize=True, load=True)
    
    # Total market cap parameters
    total_mcap_filter_enabled = BooleanParameter(default=True, space="buy", optimize=True)
    total_mcap_ma_period = IntParameter(20, 100, default=50, space="buy", optimize=True)
    
    # Market regime parameters
    regime_filter_enabled = BooleanParameter(default=True, space="buy", optimize=True)
    regime_lookback_period = IntParameter(24, 168, default=72, space="buy", optimize=True)  # hours
    
    # Fear & Greed parameters
    fear_greed_enabled = BooleanParameter(default=False, space="buy", optimize=True)  # Optional
    fear_greed_extreme_threshold = IntParameter(20, 30, default=25, space="buy", optimize=True)
    fear_greed_greed_threshold = IntParameter(70, 80, default=75, space="buy", optimize=True)
    # Momentum
    avoid_strong_trends = BooleanParameter(default=True, space="buy", optimize=True)
    trend_strength_threshold = DecimalParameter(0.01, 0.05, default=0.02, space="buy", optimize=True)
    momentum_confirmation_candles = IntParameter(1, 5, default=2, space="buy", optimize=True)
    # ROI table (minutes to decimal)
    minimal_roi = {
        "0": 0.17,     # 4.5% immediate (vs your 7%)
        "64": 0.113,      # 4% after 30 min
        "170": 0.051,     # 3.5% after 1h
        "304": 0      # 3% after 2h
    }
    plot_config = {
        "main_plot": {
            # Price action and key levels
            "actual_stoploss": {"color": "white", "type": "line"},
            "follow_price_level": {"color": "green", "type": "line"},
            
            # Key MML levels on main chart for better visibility
            "[2/8]P": {"color": "#ff6b6b", "type": "line"},  # 25% - Support/Resistance
            "[4/8]P": {"color": "#4ecdc4", "type": "line"},  # 50% - Key level
            "[6/8]P": {"color": "#45b7d1", "type": "line"},  # 75% - Support/Resistance
            
            # Entry signals on main chart
            "enter_long": {"color": "lime", "type": "scatter"},
            "enter_short": {"color": "red", "type": "scatter"},
        },
        "subplots": {
            # 📊 EXIT SIGNALS ANALYSIS (NEW - Most Important)
            "exit_signals": {
                "exit_long": {"color": "#ff4757", "type": "scatter"},          # Red exit longs
                "exit_short": {"color": "#2ed573", "type": "scatter"},         # Green exit shorts
                "Signal_Flip_Short": {"color": "#ff9ff3", "type": "scatter"}, # Pink signal flips
                "Signal_Flip_Long": {"color": "#54a0ff", "type": "scatter"},   # Blue signal flips
                "Momentum_Loss": {"color": "#ff6b35", "type": "scatter"},      # Orange momentum loss
                "Support_Break": {"color": "#ff3838", "type": "scatter"},      # Dark red breaks
                "Resistance_Break": {"color": "#2ecc71", "type": "scatter"},   # Green breaks
            },
            
            # 🎯 TRADE PERFORMANCE INDICATORS (NEW)
            "trade_metrics": {
                "price_change_5": {"color": "#74b9ff", "type": "line"},       # 5-period price change
                "rsi_change": {"color": "#fd79a8", "type": "line"},           # RSI momentum
                "volume_surge": {"color": "#fdcb6e", "type": "line"},         # Volume spikes
            },
            
            # 📈 RSI WITH EXIT ZONES (ENHANCED)
            "rsi_analysis": {
                "rsi": {"color": "#6c5ce7", "type": "line"},
                "rsi_overbought_70": {"color": "#fd79a8", "type": "line"},    # 70 level
                "rsi_oversold_30": {"color": "#00b894", "type": "line"},      # 30 level
            },
            
            # 🏔️ EXTREMA ANALYSIS (KEEP - Important for entries)
            "extrema_analysis": {
                "s_extrema": {"color": "#f53580", "type": "line"},
                "minima_sort_threshold": {"color": "#4ae747", "type": "line"},
                "maxima_sort_threshold": {"color": "#5b5e4b", "type": "line"},
            },
            
            # 📍 MIN/MAX VISUALIZATION (KEEP - Entry confirmation)
            "min_max_viz": {
                "maxima": {"color": "#a29db9", "type": "scatter"},            # Changed to scatter
                "minima": {"color": "#aac7fc", "type": "scatter"},            # Changed to scatter
                "maxima_check": {"color": "#e17055", "type": "line"},
                "minima_check": {"color": "#74b9ff", "type": "line"},
            },
            
            # 🎯 ADDITIONAL MML LEVELS (ENHANCED)
            "murrey_math_levels": {
                "[1/8]P": {"color": "#d63031", "type": "line"},               # 12.5% - Extreme oversold
                "[3/8]P": {"color": "#fd79a8", "type": "line"},               # 37.5% - Support
                "[4/8]P": {"color": "#0984e3", "type": "line"},               # 50% - Key level (duplicate for subplot)
                "[5/8]P": {"color": "#00b894", "type": "line"},               # 62.5% - Resistance  
                "[7/8]P": {"color": "#e84393", "type": "line"},               # 87.5% - Extreme overbought
                "[8/8]P": {"color": "#d63031", "type": "line"},               # 100% - Top
                "[0/8]P": {"color": "#d63031", "type": "line"},               # 0% - Bottom
            },
            
            # 🌐 MARKET CORRELATION (KEEP - Market context)
            "market_correlation": {
                "btc_correlation": {"color": "#0984e3", "type": "line"},
                "market_breadth": {"color": "#00b894", "type": "line"},
                "market_score": {"color": "#6c5ce7", "type": "line"},
                "market_direction": {"color": "#fd79a8", "type": "line"},     # NEW
            },
            
            # 📊 MARKET REGIME (ENHANCED)
            "market_regime": {
                "market_volatility": {"color": "#e17055", "type": "line"},
                "mcap_trend": {"color": "#fdcb6e", "type": "line"},
                "market_adx": {"color": "#74b9ff", "type": "line"},           # NEW - Trend strength
            },
            
            # 📉 VOLUME ANALYSIS (NEW - Important for exit confirmation)
            "volume_analysis": {
                "volume": {"color": "#636e72", "type": "bar"},
                "volume_ratio": {"color": "#00cec9", "type": "line"},         # Volume vs average
                "avg_volume": {"color": "#fd79a8", "type": "line"},           # 20-period average
            },
            
            # 🎯 TREND ANALYSIS (NEW - For exit timing)
            "trend_analysis": {
                "trend_consistency": {"color": "#a29bfe", "type": "line"},    # Multi-timeframe trend
                "trend_strength": {"color": "#fd79a8", "type": "line"},       # Trend momentum
                "strong_uptrend": {"color": "#00b894", "type": "line"},       # Boolean uptrend
                "strong_downtrend": {"color": "#d63031", "type": "line"},     # Boolean downtrend
            },
            
            # 🔥 SIGNAL QUALITY (NEW - Signal strength analysis)
            "signal_quality": {
                "combined_signal_strength": {"color": "#fd79a8", "type": "line"},  # Overall signal strength
                "mml_signal_strength": {"color": "#74b9ff", "type": "line"},       # MML-based strength
                "rsi_signal_strength": {"color": "#00b894", "type": "line"},       # RSI-based strength
                "volume_strength": {"color": "#fdcb6e", "type": "line"},           # Volume confirmation
            }
        },
    }
    def get_or_init_reentry_data(self, pair: str):
        """
        📝 Initialize re-entry data for pair if not exists
        (Use this approach if __init__ causes issues)
        """
        
        # Initialize recent_exits if not exists
        if not hasattr(self, 'recent_exits'):
            self.recent_exits = {}
        
        if pair not in self.recent_exits:
            self.recent_exits[pair] = []
        
        # Initialize reentry_count if not exists
        if not hasattr(self, 'reentry_count'):
            self.reentry_count = {}
            
        if pair not in self.reentry_count:
            self.reentry_count[pair] = {'long': 0, 'short': 0}
        
        # Initialize signal_strength_cache if not exists
        if not hasattr(self, 'signal_strength_cache'):
            self.signal_strength_cache = {}


    def calculate_signal_strength(self, dataframe: pd.DataFrame) -> pd.DataFrame:
        """
        🔧 FIXED: Enhanced signal strength calculation that actually works
        """
        
        # Initialize all strength columns
        dataframe['rsi_signal_strength'] = 0.0
        dataframe['mml_signal_strength'] = 0.0
        dataframe['mml_bounce_strength'] = 0.0
        dataframe['volume_strength'] = 0.0
        dataframe['extrema_strength'] = 0.0
        
        # RSI signal strength
        rsi = dataframe['rsi'].fillna(50)
        
        # Long RSI strength
        dataframe.loc[rsi < 25, 'rsi_signal_strength'] = 1.0  # Very oversold
        dataframe.loc[rsi.between(25, 35), 'rsi_signal_strength'] = 0.8  # Oversold
        dataframe.loc[rsi.between(35, 40), 'rsi_signal_strength'] = 0.6  # Mildly oversold
        
        # Short RSI strength  
        dataframe.loc[rsi > 75, 'rsi_signal_strength'] = 1.0  # Very overbought
        dataframe.loc[rsi.between(65, 75), 'rsi_signal_strength'] = 0.8  # Overbought
        dataframe.loc[rsi.between(60, 65), 'rsi_signal_strength'] = 0.6  # Mildly overbought
        
        # MML signal strength (safe column access)
        try:
            if '[1/8]P' in dataframe.columns and '[7/8]P' in dataframe.columns:
                # Extreme MML signals
                dataframe.loc[dataframe['close'] < dataframe['[1/8]P'], 'mml_signal_strength'] = 1.0
                dataframe.loc[dataframe['close'] > dataframe['[7/8]P'], 'mml_signal_strength'] = 1.0
            
            if '[2/8]P' in dataframe.columns and '[6/8]P' in dataframe.columns:
                # Medium MML signals
                dataframe.loc[dataframe['close'] < dataframe['[2/8]P'], 'mml_signal_strength'] = 0.8
                dataframe.loc[dataframe['close'] > dataframe['[6/8]P'], 'mml_signal_strength'] = 0.8
                
                # MML bounces
                bounce_conditions = (
                    ((dataframe['low'] <= dataframe['[2/8]P']) & (dataframe['close'] > dataframe['[2/8]P'])) |
                    ((dataframe['high'] >= dataframe['[6/8]P']) & (dataframe['close'] < dataframe['[6/8]P']))
                )
                dataframe.loc[bounce_conditions, 'mml_bounce_strength'] = 0.9
                
        except Exception as e:
            logger.warning(f"MML signal strength calculation error: {e}")
        
        # Volume strength (safe calculation)
        try:
            avg_volume = dataframe['volume'].rolling(20).mean()
            
            dataframe.loc[dataframe['volume'] > avg_volume * 1.5, 'volume_strength'] = 1.0
            dataframe.loc[dataframe['volume'] > avg_volume * 1.2, 'volume_strength'] = 0.8
            dataframe.loc[dataframe['volume'] > avg_volume, 'volume_strength'] = 0.6
            dataframe.loc[dataframe['volume'] <= avg_volume, 'volume_strength'] = 0.3
            
        except Exception as e:
            logger.warning(f"Volume strength calculation error: {e}")
            dataframe['volume_strength'] = 0.5
        
        # Extrema strength (safe access)
        try:
            if 'minima' in dataframe.columns:
                dataframe.loc[dataframe['minima'] == 1, 'extrema_strength'] = 0.8
            if 'maxima' in dataframe.columns:
                dataframe.loc[dataframe['maxima'] == 1, 'extrema_strength'] = 0.8
        except Exception as e:
            logger.warning(f"Extrema strength calculation error: {e}")
        
        # Combined signal strength (weighted average)
        dataframe['combined_signal_strength'] = (
            dataframe['rsi_signal_strength'] * 0.3 +
            dataframe['mml_signal_strength'] * 0.25 +
            dataframe['mml_bounce_strength'] * 0.2 +
            dataframe['volume_strength'] * 0.15 +
            dataframe['extrema_strength'] * 0.1
        ).clip(0, 1)
        
        return dataframe
    
    def check_signal_confirmation(self, dataframe: pd.DataFrame, signal_type: str) -> pd.Series:
        """
        Check if signal persists for required confirmation period
        """
        confirmation_period = self.signal_confirmation_candles.value
        
        if signal_type == 'long':
            # Check if bullish conditions persist
            bullish_conditions = (
                (dataframe['rsi'] < 45) |  # Oversold or neutral
                (dataframe['close'] > dataframe['[4/8]P']) |  # Above 50% MML
                (dataframe['minima'] == 1) |  # Local bottom
                (dataframe['close'] > dataframe['close'].shift(1))  # Price rising
            )
            
            # Signal confirmed if conditions met for X candles
            confirmed = bullish_conditions.rolling(confirmation_period).sum() >= (confirmation_period * 0.7)
            
        elif signal_type == 'short':
            # Check if bearish conditions persist
            bearish_conditions = (
                (dataframe['rsi'] > 55) |  # Overbought or neutral
                (dataframe['close'] < dataframe['[4/8]P']) |  # Below 50% MML
                (dataframe['maxima'] == 1) |  # Local top
                (dataframe['close'] < dataframe['close'].shift(1))  # Price falling
            )
            
            # Signal confirmed if conditions met for X candles
            confirmed = bearish_conditions.rolling(confirmation_period).sum() >= (confirmation_period * 0.7)
        else:
            confirmed = pd.Series([False] * len(dataframe))
        
        return confirmed
    
    def check_signal_cooldown(self, dataframe: pd.DataFrame) -> pd.Series:
        """
        Prevent signals too close together
        """
        cooldown_period = self.signal_cooldown_period.value
        
        # Track recent signals
        dataframe['recent_long_signal'] = dataframe['enter_long'].rolling(cooldown_period).sum()
        dataframe['recent_short_signal'] = dataframe['enter_short'].rolling(cooldown_period).sum()
        
        # Only allow new signals if no recent signals
        can_signal = (
            (dataframe['recent_long_signal'] == 0) & 
            (dataframe['recent_short_signal'] == 0)
        )
        
        return can_signal

    def detect_market_state(self, df: pd.DataFrame) -> pd.DataFrame:
        """
        Detect current market state with pair-specific calculations
        """
        df = df.copy()
        
        df['price_volatility'] = df['atr'] / df['close']
        df['volatility_percentile'] = df['price_volatility'].rolling(self.regime_lookback_period.value).rank(pct=True)
        df['volume_ratio'] = df['volume'] / df['volume'].rolling(20).mean()
        df['volume_increasing'] = df['volume'] > df['volume'].shift(1)
        
        df['momentum_3'] = df['close'].pct_change(3)
        df['momentum_5'] = df['close'].pct_change(5)
        df['momentum_10'] = df['close'].pct_change(10)
        
        df['consistent_uptrend'] = (
            (df['momentum_3'] > 0) &
            (df['momentum_5'] > 0) &
            (df['momentum_10'] > 0)
        )
        df['consistent_downtrend'] = (
            (df['momentum_3'] < 0) &
            (df['momentum_5'] < 0) &
            (df['momentum_10'] < 0)
        )
        
        df['market_state'] = 'neutral'
        df.loc[
            (df['consistent_uptrend'] & (df['adx'] > 25) & (df['volume_ratio'] > 1.2)),
            'market_state'
        ] = 'bull_run'
        df.loc[
            (df['consistent_downtrend'] & (df['adx'] > 25) & (df['volume_ratio'] > 1.2)),
            'market_state'
        ] = 'bear_market'
        df.loc[
            (df['adx'] < 20) & (df['volatility_percentile'] < 0.5),
            'market_state'
        ] = 'ranging'
        df.loc[
            (df['volatility_percentile'] > 0.8),
            'market_state'
        ] = 'high_volatility'
        
        df['potential_reversal'] = (
            ((df['high'] >= df['high'].rolling(10).max()) &
             (df['momentum_3'] < df['momentum_3'].shift(3))) |
            ((df['low'] <= df['low'].rolling(10).min()) &
             (df['momentum_3'] > df['momentum_3'].shift(3)))
        )
        
        df['potential_breakout'] = (
            (df['close'] > df['high'].rolling(20).max().shift(1)) |
            (df['close'] < df['low'].rolling(20).min().shift(1))
        ) & (df['volume_ratio'] > 1.5)
        
        df['market_regime'] = df['market_state']
        
        logger.info(f"🏛️ Market regime: {df['market_regime'].iloc[-1]}, "
                    f"Volatility: {df['price_volatility'].iloc[-1]:.4f}, "
                    f"ADX: {df['adx'].iloc[-1]:.1f}, "
                    f"Trend Strength: {df['momentum_10'].iloc[-1]:.2%}")
        return df
    def get_timing_filters(self, dataframe: pd.DataFrame, direction: str) -> pd.Series:
        """
        Get timing-based filters to avoid bad entry timing
        """
        
        if direction == 'long':
            good_timing = (
                # Avoid entering during strong downtrends
                (dataframe['market_state'] != 'strong_downtrend') &
                
                # Prefer breakouts or oversold conditions
                (
                    dataframe['potential_breakout'] |
                    (dataframe['rsi'] < 40) |
                    (dataframe['market_state'] == 'strong_uptrend')
                ) &
                
                # Avoid high volatility unless extremely oversold
                (
                    (dataframe['market_state'] != 'high_volatility') |
                    (dataframe['rsi'] < 25)
                ) &
                
                # Volume confirmation if required
                (
                    (~self.require_volume_confirmation.value) |
                    (dataframe['volume_ratio'] > 1.1)
                )
            )
            
        else:  # short
            good_timing = (
                # Avoid entering during strong uptrends
                (dataframe['market_state'] != 'strong_uptrend') &
                
                # Prefer breakdowns or overbought conditions
                (
                    dataframe['potential_breakout'] |
                    (dataframe['rsi'] > 60) |
                    (dataframe['market_state'] == 'strong_downtrend')
                ) &
                
                # Avoid high volatility unless extremely overbought
                (
                    (dataframe['market_state'] != 'high_volatility') |
                    (dataframe['rsi'] > 75)
                ) &
                
                # Volume confirmation if required
                (
                    (~self.require_volume_confirmation.value) |
                    (dataframe['volume_ratio'] > 1.1)
                )
            )
        
        return good_timing
    # Helper method to check if we have an active position in the opposite direction
    def has_active_trade(self, pair: str, side: str) -> bool:
        """
        Check if there's an active trade in the specified direction
        """
        try:
            trades = Trade.get_open_trades()
            for trade in trades:
                if trade.pair == pair:
                    if side == "long" and not trade.is_short:
                        return True
                    elif side == "short" and trade.is_short:
                        return True
        except Exception as e:
            logger.warning(f"Error checking active trades for {pair}: {e}")
        return False

    @staticmethod
    def _calculate_mml_core(mn: float, finalH: float, mx: float, finalL: float,
                            mml_c1: float, mml_c2: float) -> Dict[str, float]:
        dmml_calc = ((finalH - finalL) / 8.0) * mml_c1
        if dmml_calc == 0 or np.isinf(dmml_calc) or np.isnan(dmml_calc) or finalH == finalL:
            return {key: finalL for key in MML_LEVEL_NAMES}
        mml_val = (mx * mml_c2) + (dmml_calc * 3)
        if np.isinf(mml_val) or np.isnan(mml_val):
            return {key: finalL for key in MML_LEVEL_NAMES}
        ml = [mml_val - (dmml_calc * i) for i in range(16)]
        return {
            "[-3/8]P": ml[14], "[-2/8]P": ml[13], "[-1/8]P": ml[12],
            "[0/8]P": ml[11], "[1/8]P": ml[10], "[2/8]P": ml[9],
            "[3/8]P": ml[8], "[4/8]P": ml[7], "[5/8]P": ml[6],
            "[6/8]P": ml[5], "[7/8]P": ml[4], "[8/8]P": ml[3],
            "[+1/8]P": ml[2], "[+2/8]P": ml[1], "[+3/8]P": ml[0],
        }

    def calculate_rolling_murrey_math_levels_optimized(self, df: pd.DataFrame, window_size: int) -> Dict[str, pd.Series]:
        """
        OPTIMIZED: Calculate Murrey Math Levels using a rolling window with caching and interpolation.
        Only recalculates every 5 candles for performance, interpolating intermediate values.
        Compatible with Pandas 2.0+ and handles edge cases robustly.
        """
        # Initialize cache if not present
        if not hasattr(self, 'mml_cache'):
            self.mml_cache = {}
        pair = getattr(self, 'current_pair', 'default_pair')  # Fallback for pair context
        if pair not in self.mml_cache or len(self.mml_cache[pair]) != len(df):
            murrey_levels_data = {key: [np.nan] * len(df) for key in MML_LEVEL_NAMES}
            
            # Use rolling max/min with minimum periods to avoid NaN issues
            rolling_high = df["high"].rolling(window=window_size, min_periods=1).max()
            rolling_low = df["low"].rolling(window=window_size, min_periods=1).min()
            mml_c1 = self.mml_const1.value
            mml_c2 = self.mml_const2.value
            
            # Optimize by calculating only every 5th candle
            calculation_step = 5
            
            for i in range(0, len(df), calculation_step):
                if i < window_size - 1:
                    continue
                    
                mn_period = rolling_low.iloc[i]
                mx_period = rolling_high.iloc[i]
                current_close = df["close"].iloc[i]
                
                # Handle edge cases where data might be invalid
                if pd.isna(mn_period) or pd.isna(mx_period) or mn_period == mx_period or mn_period == 0:
                    for key in MML_LEVEL_NAMES:
                        murrey_levels_data[key][i] = current_close
                    continue
                    
                dmml_calc = ((mx_period - mn_period) / 8.0) * mml_c1
                if dmml_calc == 0 or np.isinf(dmml_calc) or np.isnan(dmml_calc):
                    for key in MML_LEVEL_NAMES:
                        murrey_levels_data[key][i] = current_close
                    continue
                    
                mml_val = (mx_period * mml_c2) + (dmml_calc * 3)
                if np.isinf(mml_val) or np.isnan(mml_val):
                    for key in MML_LEVEL_NAMES:
                        murrey_levels_data[key][i] = current_close
                    continue
                    
                ml = [mml_val - (dmml_calc * i) for i in range(16)]
                levels = {
                    "[-3/8]P": ml[14], "[-2/8]P": ml[13], "[-1/8]P": ml[12],
                    "[0/8]P": ml[11], "[1/8]P": ml[10], "[2/8]P": ml[9],
                    "[3/8]P": ml[8], "[4/8]P": ml[7], "[5/8]P": ml[6],
                    "[6/8]P": ml[5], "[7/8]P": ml[4], "[8/8]P": ml[3],
                    "[+1/8]P": ml[2], "[+2/8]P": ml[1], "[+3/8]P": ml[0],
                }
                
                for key in MML_LEVEL_NAMES:
                    murrey_levels_data[key][i] = levels.get(key, current_close)
            
            # Interpolate and fill missing values efficiently
            for key in MML_LEVEL_NAMES:
                series = pd.Series(murrey_levels_data[key], index=df.index)
                series = series.interpolate(method='linear', limit_direction='both').ffill().bfill()
                murrey_levels_data[key] = series.tolist()
            
            self.mml_cache[pair] = {key: pd.Series(data, index=df.index) for key, data in murrey_levels_data.items()}
        
        return self.mml_cache[pair]

    def calculate_market_correlation_simple(self, dataframe: pd.DataFrame, metadata: dict) -> pd.DataFrame:
        """
        Simplified correlation calculation that avoids index issues
        """
        pair = metadata['pair']
        base_currency = pair.split('/')[0]
        
        # Skip if this IS BTC
        if base_currency == 'BTC':
            dataframe['btc_correlation'] = 1.0
            dataframe['btc_trend'] = 1
            dataframe['btc_close'] = dataframe['close']
            dataframe['btc_sma20'] = dataframe['close'].rolling(20).mean()
            dataframe['btc_sma50'] = dataframe['close'].rolling(50).mean()
            dataframe['returns'] = dataframe['close'].pct_change()
            return dataframe
        
        # Try to get BTC data
        btc_pairs = ["BTC/USDT:USDT", "BTC/USDT", "BTC/USD"]
        btc_dataframe = None
        
        for btc_pair in btc_pairs:
            try:
                btc_dataframe, _ = self.dp.get_analyzed_dataframe(btc_pair, self.timeframe)
                if btc_dataframe is not None and not btc_dataframe.empty and len(btc_dataframe) >= 50:
                    logger.info(f"Using {btc_pair} for simplified BTC correlation with {pair}")
                    break
            except:
                continue
        
        # If no BTC data, use neutral values
        if btc_dataframe is None or btc_dataframe.empty:
            dataframe['btc_correlation'] = 0.5
            dataframe['btc_trend'] = 0
            dataframe['btc_close'] = dataframe['close']
            dataframe['btc_sma20'] = dataframe['close']
            dataframe['btc_sma50'] = dataframe['close']
            dataframe['returns'] = dataframe['close'].pct_change()
            logger.warning(f"No BTC data for {pair}, using neutral correlation")
            return dataframe
        
        # Simple correlation using only latest values
        try:
            # Use shorter period to avoid length issues
            correlation_period = min(20, len(dataframe) // 5, len(btc_dataframe) // 5)
            correlation_period = max(5, correlation_period)
            
            # Calculate returns
            pair_returns = dataframe['close'].pct_change().dropna()
            btc_returns = btc_dataframe['close'].pct_change().dropna()
            
            # Use only last N periods for correlation
            if len(pair_returns) >= correlation_period and len(btc_returns) >= correlation_period:
                pair_recent = pair_returns.tail(correlation_period)
                btc_recent = btc_returns.tail(correlation_period)
                
                # Align lengths
                min_len = min(len(pair_recent), len(btc_recent))
                correlation = pair_recent.tail(min_len).corr(btc_recent.tail(min_len))
                
                if pd.isna(correlation):
                    correlation = 0.5
            else:
                correlation = 0.5
            
            # BTC trend (simple)
            btc_close = btc_dataframe['close'].iloc[-1]
            btc_sma20 = btc_dataframe['close'].rolling(20).mean().iloc[-1]
            btc_sma50 = btc_dataframe['close'].rolling(50).mean().iloc[-1]
            
            if pd.isna(btc_sma20) or pd.isna(btc_sma50):
                btc_trend = 0
            elif btc_close > btc_sma20 > btc_sma50:
                btc_trend = 1
            elif btc_close < btc_sma20 < btc_sma50:
                btc_trend = -1
            else:
                btc_trend = 0
            
            # Set constant values for entire dataframe
            dataframe['btc_correlation'] = correlation
            dataframe['btc_trend'] = btc_trend
            dataframe['btc_close'] = btc_close
            dataframe['btc_sma20'] = btc_sma20
            dataframe['btc_sma50'] = btc_sma50
            
            logger.debug(f"{pair} Simple correlation: {correlation:.3f}, BTC trend: {btc_trend}")
            
        except Exception as e:
            logger.warning(f"Simple correlation failed for {pair}: {e}")
            dataframe['btc_correlation'] = 0.5
            dataframe['btc_trend'] = 0
            dataframe['btc_close'] = dataframe['close']
            dataframe['btc_sma20'] = dataframe['close']
            dataframe['btc_sma50'] = dataframe['close']
        
        # Always calculate returns
        dataframe['returns'] = dataframe['close'].pct_change()
        
        return dataframe

    def informative_pairs(self):
            """
            Define additional, informative pair/interval combinations to download
            """
            pairs = self.dp.current_whitelist()
            informative_pairs = []
            
            # Add major crypto pairs for market breadth calculation
            # These are the pairs used in calculate_market_breadth_weighted
            major_pairs = [
                'BTC/USDT:USDT',
                'ETH/USDT:USDT', 
                'BNB/USDT:USDT',
                'SOL/USDT:USDT',
                'XRP/USDT:USDT',
                'ADA/USDT:USDT',
                'AVAX/USDT:USDT'
            ]
            
            # Add major pairs for multiple timeframes (needed for market breadth)
            for pair in major_pairs:
                for tf in ['15m', '1h', '4h']:
                    informative_pairs.append((pair, tf))
            
            # Add informative timeframes for current pairs
            for tf in ['15m', '1h', '4h']:
                informative_pairs.extend([(pair, tf) for pair in pairs])
            
            # Remove duplicates while preserving order
            seen = set()
            unique_pairs = []
            for pair in informative_pairs:
                if pair not in seen:
                    seen.add(pair)
                    unique_pairs.append(pair)
            
            return unique_pairs

    def _extract_dataframe_safely(self, result, pair_name):
        """
        Safely extract DataFrame from get_analyzed_dataframe result
        """
        try:
            # Handle tuple return (dataframe, metadata)
            if isinstance(result, tuple) and len(result) > 0:
                potential_df = result[0]
                if isinstance(potential_df, pd.DataFrame):
                    return potential_df
                elif isinstance(potential_df, list) and len(potential_df) > 0:
                    # If first element of tuple is a list, try to get DataFrame from it
                    if isinstance(potential_df[0], pd.DataFrame):
                        return potential_df[0]
            
            # Handle direct DataFrame return
            elif isinstance(result, pd.DataFrame):
                return result
            
            # Handle list return
            elif isinstance(result, list) and len(result) > 0:
                if isinstance(result[0], pd.DataFrame):
                    return result[0]
                elif isinstance(result[0], tuple) and len(result[0]) > 0:
                    # List of tuples case
                    if isinstance(result[0][0], pd.DataFrame):
                        return result[0][0]
            
            logger.debug(f"   ❌ {pair_name}: Cannot extract DataFrame from result (type: {type(result)})")
            return None
            
        except Exception as e:
            logger.debug(f"   ❌ {pair_name}: Error extracting DataFrame: {str(e)}")
            return None

    def calculate_market_breadth_weighted(self, dataframe: pd.DataFrame, metadata: dict) -> pd.DataFrame:
        """
        🔧 FIXED: Proper logging levels for market breadth calculation
        """
        current_pair = metadata['pair']
        is_futures = ':' in current_pair
        
        if is_futures:
            settlement = current_pair.split(':')[1]
            primary_pairs = [
                (f"BTC/USDT:{settlement}", 0.40),
                (f"ETH/USDT:{settlement}", 0.25),
                (f"BNB/USDT:{settlement}", 0.10),
                (f"SOL/USDT:{settlement}", 0.08),
                (f"XRP/USDT:{settlement}", 0.07),
                (f"ADA/USDT:{settlement}", 0.05),
                (f"AVAX/USDT:{settlement}", 0.02),
            ]
            
            fallback_pairs = [
                ("BTC/USDT", 0.40),
                ("ETH/USDT", 0.25),
                ("BNB/USDT", 0.10),
                ("SOL/USDT", 0.08),
                ("XRP/USDT", 0.07),
                ("ADA/USDT", 0.05),
                ("AVAX/USDT", 0.02),
            ]
        else:
            primary_pairs = [
                ("BTC/USDT", 0.40),
                ("ETH/USDT", 0.25), 
                ("BNB/USDT", 0.10),
                ("SOL/USDT", 0.08),
                ("XRP/USDT", 0.07),
                ("ADA/USDT", 0.05),
                ("AVAX/USDT", 0.02),
            ]
            fallback_pairs = []
        
        bullish_weight = 0.0
        bearish_weight = 0.0
        neutral_weight = 0.0
        total_weight = 0.0
        pairs_checked = 0
        
        logger.debug(f"🔍 DEBUG BREADTH for {metadata['pair']}:")  # Changed from ERROR to DEBUG
        
        # Try primary pairs with proper logging levels
        for check_pair, weight in primary_pairs:
            try:
                pair_data = self.dp.get_pair_dataframe(check_pair, self.timeframe)
                
                if pair_data is None or pair_data.empty:
                    logger.debug(f"   ❌ {check_pair}: No data")  # Changed from ERROR to DEBUG
                    continue
                    
                if len(pair_data) < 20:
                    logger.debug(f"   ❌ {check_pair}: Only {len(pair_data)} candles")  # Changed from ERROR to DEBUG
                    continue
                    
                current_close = pair_data['close'].iloc[-1]
                sma20 = pair_data['close'].rolling(20).mean().iloc[-1]
                
                if pd.isna(current_close) or pd.isna(sma20) or sma20 <= 0:
                    logger.debug(f"   ❌ {check_pair}: Invalid data (close: {current_close}, sma: {sma20})")  # Changed from ERROR to DEBUG
                    continue
                
                # Enhanced threshold logic
                threshold = 1.002  # 0.2% buffer
                price_vs_sma = current_close / sma20
                
                logger.debug(f"   📊 {check_pair}: Close=${current_close:.3f}, SMA20=${sma20:.3f}, Ratio={price_vs_sma:.6f}")  # Changed from ERROR to DEBUG
                
                if current_close > sma20 * threshold:
                    bullish_weight += weight
                    trend_status = "BULLISH"
                elif current_close < sma20 / threshold:
                    bearish_weight += weight
                    trend_status = "BEARISH"
                else:
                    neutral_weight += weight
                    trend_status = "NEUTRAL"
                
                total_weight += weight
                pairs_checked += 1
                
                logger.debug(f"   ✅ {check_pair}: {trend_status} (weight: {weight:.2f})")  # Changed from ERROR to DEBUG
                
            except Exception as e:
                logger.warning(f"   💥 {check_pair} ERROR: {str(e)}")  # Changed from ERROR to WARNING
                continue
        
        # Enhanced calculation logic with proper logging
        logger.debug(f"📊 BREADTH CALCULATION:")  # Changed from ERROR to DEBUG
        logger.debug(f"   Bullish weight: {bullish_weight:.3f}")  # Changed from ERROR to DEBUG
        logger.debug(f"   Bearish weight: {bearish_weight:.3f}")  # Changed from ERROR to DEBUG
        logger.debug(f"   Neutral weight: {neutral_weight:.3f}")  # Changed from ERROR to DEBUG
        logger.debug(f"   Total weight: {total_weight:.3f}")  # Changed from ERROR to DEBUG
        logger.debug(f"   Pairs checked: {pairs_checked}")  # Changed from ERROR to DEBUG
        
        if pairs_checked > 0 and total_weight > 0:
            # Include neutral in calculation
            directional_weight = bullish_weight + bearish_weight
            
            if directional_weight > 0:
                market_breadth = bullish_weight / directional_weight
            else:
                # All neutral = 50%
                market_breadth = 0.5
                logger.debug(f"   🔧 All pairs neutral, setting breadth to 50%")  # Changed from ERROR to DEBUG
            
            # Market direction
            if bullish_weight > bearish_weight:
                market_direction = 1
            elif bearish_weight > bullish_weight:
                market_direction = -1
            else:
                market_direction = 0
                
            logger.info(f"📊 {metadata['pair']} Market Breadth: {market_breadth:.1%} ({pairs_checked} pairs)")  # Changed from ERROR to INFO
            
        else:
            logger.info(f"🔄 {metadata['pair']} No breadth data - using fallback calculation")  # Changed from ERROR to INFO
            
            # Fallback: Use current pair trend
            try:
                current_close = dataframe['close'].iloc[-1]
                sma20 = dataframe['close'].rolling(20).mean().iloc[-1]
                
                if not pd.isna(sma20) and sma20 > 0:
                    pair_ratio = current_close / sma20
                    logger.debug(f"   📈 Current pair trend: {pair_ratio:.6f}")  # Changed from ERROR to DEBUG
                    
                    if pair_ratio > 1.01:  # 1% above SMA
                        market_breadth = 0.65
                        market_direction = 1
                        logger.debug(f"   📈 Pair bullish -> 65% breadth")  # Changed from ERROR to DEBUG
                    elif pair_ratio < 0.99:  # 1% below SMA  
                        market_breadth = 0.35
                        market_direction = -1
                        logger.debug(f"   📉 Pair bearish -> 35% breadth")  # Changed from ERROR to DEBUG
                    else:
                        market_breadth = 0.5
                        market_direction = 0
                        logger.debug(f"   📊 Pair neutral -> 50% breadth")  # Changed from ERROR to DEBUG
                else:
                    market_breadth = 0.5
                    market_direction = 0
                    logger.debug(f"   🔧 Invalid pair data -> 50% breadth")  # Changed from ERROR to DEBUG
                    
                pairs_checked = 1
                
            except Exception as e:
                logger.warning(f"   💥 Fallback failed: {e}")  # Changed from ERROR to WARNING
                market_breadth = 0.5
                market_direction = 0
                pairs_checked = 0
        
        # Prevent 0% breadth with proper logging
        if market_breadth == 0.0:
            logger.info(f"🔧 {metadata['pair']} Adjusting 0% breadth to 35% (extreme bearish)")  # Changed from ERROR to INFO
            market_breadth = 0.35  # Assume bearish but not extreme
            market_direction = -1
        
        logger.info(f"🎯 {metadata['pair']} Final Breadth: {market_breadth:.1%} (Direction: {market_direction})")  # Changed from ERROR to INFO, improved formatting
        
        # Set dataframe values
        dataframe['market_breadth'] = market_breadth
        dataframe['market_direction'] = market_direction
        dataframe['bullish_weight'] = bullish_weight
        dataframe['bearish_weight'] = bearish_weight
        dataframe['total_weight'] = total_weight
        dataframe['breadth_pairs_checked'] = pairs_checked
        
        return dataframe


    def calculate_market_regime_aligned(self, dataframe: pd.DataFrame, metadata: dict) -> pd.DataFrame:
        """
        🔍 Determine the current market regime based on volatility, trend strength, and ADX.
        Uses BTC data as a proxy and aligns with the strategy's timeframe for consistency.
        Handles NumPy array issues by ensuring Pandas Series compatibility.
        """
        lookback = self.regime_lookback_period.value
        current_pair = metadata['pair']
        
        # Detect mode and set BTC pairs to try
        if ':' in current_pair:
            settlement = current_pair.split(':')[1]
            btc_pairs_to_try = [f'BTC/USDT:{settlement}', 'BTC/USDT']
        else:
            btc_pairs_to_try = ['BTC/USDT', 'BTC/USD']
        
        btc_data = None
        btc_pair_used = None
        
        # Attempt to fetch BTC data with appropriate logging
        for btc_pair in btc_pairs_to_try:
            try:
                logger.debug(f"🔍 {current_pair} Trying BTC pair: {btc_pair}")
                btc_data = self.dp.get_pair_dataframe(btc_pair, self.timeframe)
                if btc_data is not None and not btc_data.empty and len(btc_data) >= lookback:
                    btc_pair_used = btc_pair
                    logger.debug(f"✅ {current_pair} Successfully fetched {len(btc_data)} candles from {btc_pair}")
                    break
                else:
                    available_candles = len(btc_data) if btc_data is not None and not btc_data.empty else 0
                    logger.debug(f"⚠️ {current_pair} {btc_pair} insufficient: {available_candles}/{lookback} candles")
            except Exception as e:
                logger.debug(f"💥 {current_pair} Error accessing {btc_pair}: {str(e)}")
                continue
        
        # Fallback to neutral regime if no BTC data
        if btc_data is None or btc_data.empty or len(btc_data) < lookback:
            logger.info(f"🔄 {current_pair} No BTC data available, using neutral regime")
            dataframe['market_regime'] = 'neutral'
            dataframe['market_volatility'] = 0.02
            dataframe['market_adx'] = 25.0
            dataframe['btc_trend_strength'] = 0.0
            return dataframe
        
        try:
            # Calculate market indicators with Pandas Series compatibility
            logger.debug(f"📊 {current_pair} Calculating regime using {btc_pair_used} ({len(btc_data)} candles)")
            
            # Daily volatility based on timeframe (assuming 30m)
            timeframe_minutes = 30
            btc_returns = btc_data['close'].pct_change().to_frame('returns')
            market_volatility = btc_returns['returns'].rolling(window=lookback).std() * np.sqrt(1440 / timeframe_minutes)
            
            # Trend strength using ADX and price movement
            adx_values = ta.ADX(btc_data['high'], btc_data['low'], btc_data['close'], timeperiod=14)
            market_adx = pd.Series(adx_values, index=btc_data.index) if isinstance(adx_values, np.ndarray) else adx_values
            btc_mean = btc_data['close'].rolling(window=lookback).mean()
            btc_std = btc_data['close'].rolling(window=lookback).std()
            btc_trend_strength = ((btc_data['close'].iloc[-1] - btc_mean.iloc[-1]) / btc_std.iloc[-1] 
                                if btc_std.iloc[-1] != 0 else 0.0)
            
            # Ensure we have the latest values
            last_volatility = market_volatility.iloc[-1] if not market_volatility.empty else 0.02
            last_adx = market_adx.iloc[-1] if not market_adx.empty else 25.0
            last_trend_strength = btc_trend_strength if not pd.isna(btc_trend_strength) else 0.0
            
            if last_volatility > 0.05 and last_trend_strength > 1.5 and last_adx > 35:
                regime = 'bull_run'
            elif last_volatility > 0.05 and last_trend_strength < -1.5 and last_adx > 35:
                regime = 'bear_market'
            elif last_volatility < 0.008 and last_adx < 15:
                regime = 'ranging'
            elif last_volatility > 0.08:  # Only extreme volatility
                regime = 'high_volatility'
            else:
                regime = 'neutral'  # Default to neutral instead of high_volatility
            
            # Apply to dataframe
            dataframe['market_regime'] = regime
            dataframe['market_volatility'] = last_volatility
            dataframe['market_adx'] = last_adx
            dataframe['btc_trend_strength'] = last_trend_strength
            
            logger.info(f"🏛️ {current_pair} Market regime: {regime}, Volatility: {last_volatility:.4f}, "
                       f"ADX: {last_adx:.1f}, Trend Strength: {last_trend_strength:.2f}")
            return dataframe
            
        except Exception as e:
            logger.warning(f"💥 {current_pair} Regime calculation failed: {str(e)}, using neutral regime")
            dataframe['market_regime'] = 'neutral'
            dataframe['market_volatility'] = 0.02
            dataframe['market_adx'] = 25.0
            dataframe['btc_trend_strength'] = 0.0
            return dataframe
    def apply_market_regime_filters(self, df: pd.DataFrame, any_long_signal: pd.Series, 
                                   any_short_signal: pd.Series, metadata: dict) -> tuple:
        """
        🧠 INTELLIGENT: Adjust signal sensitivity based on market conditions
        
        This method modifies your entry signals based on the current market regime
        to improve timing and reduce stop losses.
        
        Returns: (enhanced_long_signal, enhanced_short_signal)
        """
        
        try:
            # Get current market regime (use last available value)
            current_regime = df['market_regime'].iloc[-1] if 'market_regime' in df.columns else 'neutral'
            btc_strength = df['btc_strength'].iloc[-1] if 'btc_strength' in df.columns else 0
            fear_greed = df['market_fear_greed'].iloc[-1] if 'market_fear_greed' in df.columns else 50
            
            # ===========================================
            # REGIME-SPECIFIC SIGNAL ADJUSTMENTS
            # ===========================================
            
            if current_regime == 'bull_run':
                # 🚀 BULL RUN MODE: Favor longs, restrict shorts
                long_boost = df['rsi'] < 60  # More aggressive long entries
                short_restrict = df['rsi'] > 80  # Only extreme short entries
                
                enhanced_long_signal = any_long_signal & long_boost
                enhanced_short_signal = any_short_signal & short_restrict
                
                logger.info(f"{metadata['pair']} 🚀 BULL RUN MODE: Boosting longs, restricting shorts")
                
            elif current_regime == 'bear_market':
                # 🐻 BEAR MARKET MODE: Favor shorts, restrict longs
                short_boost = df['rsi'] > 40  # More aggressive short entries  
                long_restrict = df['rsi'] < 20  # Only extreme long entries
                
                enhanced_long_signal = any_long_signal & long_restrict
                enhanced_short_signal = any_short_signal & short_boost
                
                logger.info(f"{metadata['pair']} 🐻 BEAR MARKET MODE: Boosting shorts, restricting longs")
                
            elif current_regime == 'high_volatility':
                # ⚡ HIGH VOLATILITY MODE: Only trade extremes
                extreme_oversold = df['rsi'] < 25
                extreme_overbought = df['rsi'] > 75
                
                enhanced_long_signal = any_long_signal & extreme_oversold
                enhanced_short_signal = any_short_signal & extreme_overbought
                
                logger.info(f"{metadata['pair']} ⚡ HIGH VOLATILITY MODE: Only extreme entries")
                
            elif current_regime == 'sideways':
                # 📊 SIDEWAYS MODE: Range trading, both directions OK
                range_long = (df['rsi'] < 35) & (df['close'] <= df.get('[3/8]P', df['close']))
                range_short = (df['rsi'] > 65) & (df['close'] >= df.get('[5/8]P', df['close']))
                
                enhanced_long_signal = any_long_signal & range_long
                enhanced_short_signal = any_short_signal & range_short
                
                logger.info(f"{metadata['pair']} 📊 SIDEWAYS MODE: Range trading activated")
                
            elif current_regime == 'transitional':
                # 🔄 TRANSITIONAL MODE: Be more selective
                quality_long = (df['rsi'] < 40) & (df['volume'] > df['volume'].rolling(20).mean() * 1.2)
                quality_short = (df['rsi'] > 60) & (df['volume'] > df['volume'].rolling(20).mean() * 1.2)
                
                enhanced_long_signal = any_long_signal & quality_long
                enhanced_short_signal = any_short_signal & quality_short
                
                logger.info(f"{metadata['pair']} 🔄 TRANSITIONAL MODE: Quality-focused entries")
                
            else:  # neutral
                # 😐 NEUTRAL MODE: Standard filtering
                enhanced_long_signal = any_long_signal
                enhanced_short_signal = any_short_signal
                
            # ===========================================
            # FEAR/GREED ADJUSTMENTS
            # ===========================================
            
            if fear_greed > 75:  # Extreme greed
                # Be more careful with longs, favor shorts
                enhanced_long_signal = enhanced_long_signal & (df['rsi'] < 30)
                logger.info(f"{metadata['pair']} 🤑 EXTREME GREED: Restricting longs")
                
            elif fear_greed < 25:  # Extreme fear
                # Be more careful with shorts, favor longs
                enhanced_short_signal = enhanced_short_signal & (df['rsi'] > 70)
                logger.info(f"{metadata['pair']} 😨 EXTREME FEAR: Restricting shorts")
            
            return enhanced_long_signal, enhanced_short_signal
            
        except Exception as e:
            logger.error(f"{metadata['pair']} ❌ Market regime filter error: {e}")
            # Return original signals on error
            return any_long_signal, any_short_signal
    def calculate_total_market_cap_trend(self, dataframe: pd.DataFrame, metadata: dict) -> pd.DataFrame:
        """
        Calculate total crypto market cap trend
        FIXED: Handles futures format
        """
        # Detect futures mode
        is_futures = ':' in metadata['pair']
        
        if is_futures:
            settlement = metadata['pair'].split(':')[1]
            # Futures weighted pairs
            major_coins = {
                f"BTC/USDT:{settlement}": 0.4,
                f"ETH/USDT:{settlement}": 0.2,
                f"BNB/USDT:{settlement}": 0.1,
                f"SOL/USDT:{settlement}": 0.05,
                f"ADA/USDT:{settlement}": 0.05,
            }
        else:
            # Spot pairs
            major_coins = {
                "BTC/USDT": 0.4,
                "ETH/USDT": 0.2,
                "BNB/USDT": 0.1,
                "SOL/USDT": 0.05,
                "ADA/USDT": 0.05,
            }
        
        weighted_trend = 0
        total_weight = 0
        
        for coin_pair, weight in major_coins.items():
            try:
                coin_data, _ = self.dp.get_analyzed_dataframe(coin_pair, self.timeframe)
                if coin_data.empty or len(coin_data) < self.total_mcap_ma_period.value:
                    # Try spot equivalent if futures not found
                    if is_futures and ':' in coin_pair:
                        spot_pair = coin_pair.split(':')[0]
                        coin_data, _ = self.dp.get_analyzed_dataframe(spot_pair, self.timeframe)
                        if coin_data.empty:
                            continue
                    else:
                        continue
                
                # Calculate trend using MA
                ma_period = self.total_mcap_ma_period.value
                current_price = coin_data['close'].iloc[-1]
                ma_value = coin_data['close'].rolling(ma_period).mean().iloc[-1]
                
                # Trend strength
                trend_strength = (current_price - ma_value) / ma_value
                weighted_trend += trend_strength * weight
                total_weight += weight
                
            except Exception as e:
                logger.debug(f"Could not process {coin_pair} for mcap trend: {e}")
                continue
        
        if total_weight > 0:
            mcap_trend = weighted_trend / total_weight
        else:
            mcap_trend = 0
        
        # Classify trend
        if mcap_trend > 0.05:
            mcap_status = 'bullish'
        elif mcap_trend < -0.05:
            mcap_status = 'bearish'
        else:
            mcap_status = 'neutral'
        
        dataframe['mcap_trend'] = mcap_trend
        dataframe['mcap_status'] = mcap_status
        
        return dataframe
    
    def apply_correlation_filters(self, dataframe: pd.DataFrame, direction: str = 'long') -> pd.Series:
        """
        Apply market correlation filters using ENHANCED correlation data
        Returns a boolean Series indicating whether market conditions are favorable
        """
        conditions = pd.Series(True, index=dataframe.index)
        
        # ===========================================
        # ENHANCED BTC CORRELATION FILTER
        # ===========================================
        if self.enable_btc_correlation.value and 'btc_correlation_ok' in dataframe.columns:
            # Simple approach: Only trade when BTC conditions are favorable
            conditions &= dataframe['btc_correlation_ok']
            
            # Optional: More restrictive based on BTC trend score
            if 'btc_trend_score' in dataframe.columns:
                if direction == 'long':
                    # For longs: Allow when BTC trend score >= 2 (at least neutral)
                    # OR when extremely oversold (RSI < 25)
                    btc_long_ok = (
                        (dataframe['btc_trend_score'] >= 2) |
                        (dataframe.get('rsi', 50) < 25)  # Emergency oversold exception
                    )
                    conditions &= btc_long_ok
                    
                else:  # short
                    # For shorts: Allow when BTC trend score <= 3 (not too bullish)
                    # OR when extremely overbought (RSI > 75)
                    btc_short_ok = (
                        (dataframe['btc_trend_score'] <= 3) |
                        (dataframe.get('rsi', 50) > 75)  # Emergency overbought exception
                    )
                    conditions &= btc_short_ok
        
        # ===========================================
        # MARKET BREADTH FILTER (Keep as is)
        # ===========================================
        if self.market_breadth_enabled.value and 'market_breadth' in dataframe.columns:
            if direction == 'long':
                # Long only when majority of market is bullish
                conditions &= (dataframe['market_breadth'] > self.market_breadth_threshold.value)
            else:  # short
                # Short only when majority of market is bearish
                conditions &= (dataframe['market_breadth'] < (1 - self.market_breadth_threshold.value))
        
        # ===========================================
        # MARKET CAP TREND FILTER (Keep as is)
        # ===========================================
        if self.total_mcap_filter_enabled.value and 'mcap_status' in dataframe.columns:
            if direction == 'long':
                conditions &= (dataframe['mcap_status'] != 'bearish')
            else:  # short
                conditions &= (dataframe['mcap_status'] != 'bullish')
        
        # ===========================================
        # MARKET REGIME FILTER (Keep as is)
        # ===========================================
        if self.regime_filter_enabled.value and 'market_regime' in dataframe.columns:
            # Avoid trading in high volatility regimes
            conditions &= (dataframe['market_regime'] != 'high_volatility')
        
        return conditions
    def calculate_enhanced_trend_filters(self, dataframe: pd.DataFrame) -> pd.DataFrame:
            """
            Enhanced trend detection to avoid choppy market entries
            """
            
            # Multi-timeframe trend alignment
            dataframe['ema_5'] = ta.EMA(dataframe['close'], timeperiod=5)
            dataframe['ema_13'] = ta.EMA(dataframe['close'], timeperiod=13)
            dataframe['ema_21'] = ta.EMA(dataframe['close'], timeperiod=21)
            dataframe['ema_50'] = ta.EMA(dataframe['close'], timeperiod=50)
            
            # Trend alignment score
            dataframe['trend_alignment_bull'] = (
                (dataframe['close'] > dataframe['ema_5']) &
                (dataframe['ema_5'] > dataframe['ema_13']) &
                (dataframe['ema_13'] > dataframe['ema_21']) &
                (dataframe['ema_21'] > dataframe['ema_50'])
            ).astype(int)
            
            dataframe['trend_alignment_bear'] = (
                (dataframe['close'] < dataframe['ema_5']) &
                (dataframe['ema_5'] < dataframe['ema_13']) &
                (dataframe['ema_13'] < dataframe['ema_21']) &
                (dataframe['ema_21'] < dataframe['ema_50'])
            ).astype(int)
            
            # ADX for trend strength
            dataframe['adx'] = ta.ADX(dataframe, timeperiod=14)
            dataframe['strong_trend'] = dataframe['adx'] > 25
            dataframe['very_strong_trend'] = dataframe['adx'] > 40
            
            # Momentum indicators
            dataframe['macd'], dataframe['macd_signal'], dataframe['macd_hist'] = ta.MACD(
                dataframe['close'], fastperiod=12, slowperiod=26, signalperiod=9
            )
            
            # MACD momentum alignment
            dataframe['macd_bull'] = (
                (dataframe['macd'] > dataframe['macd_signal']) &
                (dataframe['macd_hist'] > dataframe['macd_hist'].shift(1))
            )
            
            dataframe['macd_bear'] = (
                (dataframe['macd'] < dataframe['macd_signal']) &
                (dataframe['macd_hist'] < dataframe['macd_hist'].shift(1))
            )
            
            # Price action patterns
            dataframe['higher_highs'] = (
                (dataframe['high'] > dataframe['high'].shift(1)) &
                (dataframe['high'].shift(1) > dataframe['high'].shift(2))
            )
            
            dataframe['lower_lows'] = (
                (dataframe['low'] < dataframe['low'].shift(1)) &
                (dataframe['low'].shift(1) < dataframe['low'].shift(2))
            )
            
            # Higher lows (bullish structure)
            dataframe['higher_lows'] = (
                (dataframe['low'] > dataframe['low'].shift(1)) &
                (dataframe['low'].shift(1) > dataframe['low'].shift(2))
            )
            
            # Lower highs (bearish structure)
            dataframe['lower_highs'] = (
                (dataframe['high'] < dataframe['high'].shift(1)) &
                (dataframe['high'].shift(1) < dataframe['high'].shift(2))
            )
            
            # Volatility filter using Bollinger Bands
            dataframe['bb_upper'], dataframe['bb_middle'], dataframe['bb_lower'] = ta.BBANDS(
                dataframe['close'], timeperiod=20, nbdevup=2.0, nbdevdn=2.0
            )
            
            # BB squeeze detection (low volatility)
            dataframe['bb_squeeze'] = (
                (dataframe['bb_upper'] - dataframe['bb_lower']) / dataframe['bb_middle'] < 0.1
            )
            
            # BB expansion (high volatility breakout)
            dataframe['bb_expansion'] = (
                (dataframe['bb_upper'] - dataframe['bb_lower']) / dataframe['bb_middle'] > 0.2
            )
            
            # Market regime classification
            dataframe['trending_up'] = (
                dataframe['trend_alignment_bull'] &
                dataframe['strong_trend'] &
                dataframe['macd_bull'] &
                (~dataframe['bb_squeeze'])
            )
            
            dataframe['trending_down'] = (
                dataframe['trend_alignment_bear'] &
                dataframe['strong_trend'] &
                dataframe['macd_bear'] &
                (~dataframe['bb_squeeze'])
            )
            
            dataframe['choppy_market'] = (
                (dataframe['adx'] < 20) |
                dataframe['bb_squeeze'] |
                (
                    (~dataframe['trend_alignment_bull']) & 
                    (~dataframe['trend_alignment_bear'])
                )
            )
            
            # Market structure breaks
            dataframe['structure_break_bull'] = (
                (dataframe['close'] > dataframe['high'].rolling(20).max().shift(1)) &
                dataframe['trending_up'] &
                (dataframe['volume'] > dataframe['volume'].rolling(20).mean() * 1.2)
            )
            
            dataframe['structure_break_bear'] = (
                (dataframe['close'] < dataframe['low'].rolling(20).min().shift(1)) &
                dataframe['trending_down'] &
                (dataframe['volume'] > dataframe['volume'].rolling(20).mean() * 1.2)
            )
            
            # Trend momentum strength
            dataframe['trend_momentum'] = dataframe['close'].pct_change(10)
            dataframe['trend_momentum_strength'] = abs(dataframe['trend_momentum'])
            
            # Consistent trend detection (multiple timeframes)
            dataframe['short_term_trend'] = np.where(
                dataframe['close'] > dataframe['ema_5'], 1,
                np.where(dataframe['close'] < dataframe['ema_5'], -1, 0)
            )
            
            dataframe['medium_term_trend'] = np.where(
                dataframe['close'] > dataframe['ema_21'], 1,
                np.where(dataframe['close'] < dataframe['ema_21'], -1, 0)
            )
            
            dataframe['long_term_trend'] = np.where(
                dataframe['close'] > dataframe['ema_50'], 1,
                np.where(dataframe['close'] < dataframe['ema_50'], -1, 0)
            )
            
            # Trend consistency score
            dataframe['trend_consistency'] = (
                dataframe['short_term_trend'] + 
                dataframe['medium_term_trend'] + 
                dataframe['long_term_trend']
            ) / 3
            
            # Strong consistent trends
            dataframe['strong_consistent_uptrend'] = dataframe['trend_consistency'] > 0.6
            dataframe['strong_consistent_downtrend'] = dataframe['trend_consistency'] < -0.6
            
            # Trend change detection
            dataframe['trend_change_up'] = (
                (dataframe['trend_consistency'] > 0) &
                (dataframe['trend_consistency'].shift(1) <= 0)
            )
            
            dataframe['trend_change_down'] = (
                (dataframe['trend_consistency'] < 0) &
                (dataframe['trend_consistency'].shift(1) >= 0)
            )
            
            # Support and resistance levels based on EMAs
            dataframe['dynamic_resistance'] = np.maximum.reduce([
                dataframe['ema_5'], dataframe['ema_13'], 
                dataframe['ema_21'], dataframe['ema_50']
            ])
            
            dataframe['dynamic_support'] = np.minimum.reduce([
                dataframe['ema_5'], dataframe['ema_13'], 
                dataframe['ema_21'], dataframe['ema_50']
            ])
            
            # Price position relative to dynamic levels
            dataframe['above_all_emas'] = (
                (dataframe['close'] > dataframe['ema_5']) &
                (dataframe['close'] > dataframe['ema_13']) &
                (dataframe['close'] > dataframe['ema_21']) &
                (dataframe['close'] > dataframe['ema_50'])
            )
            
            dataframe['below_all_emas'] = (
                (dataframe['close'] < dataframe['ema_5']) &
                (dataframe['close'] < dataframe['ema_13']) &
                (dataframe['close'] < dataframe['ema_21']) &
                (dataframe['close'] < dataframe['ema_50'])
            )
            
            # Momentum divergence detection
            # Price vs RSI divergence
            price_peaks = dataframe['high'].rolling(10).max()
            price_troughs = dataframe['low'].rolling(10).min()
            
            dataframe['bullish_divergence'] = (
                (dataframe['low'] == price_troughs) &
                (dataframe['low'] < dataframe['low'].shift(10)) &
                (dataframe['rsi'] > dataframe['rsi'].shift(10))
            )
            
            dataframe['bearish_divergence'] = (
                (dataframe['high'] == price_peaks) &
                (dataframe['high'] > dataframe['high'].shift(10)) &
                (dataframe['rsi'] < dataframe['rsi'].shift(10))
            )
            
            return dataframe
    def calculate_trend_strength(self, df: pd.DataFrame) -> pd.DataFrame:
        """
        Calculate trend strength to avoid entering against strong trends
        """
        # Linear regression slope
        def calc_slope(series, period=10):
            """Calculate linear regression slope"""
            if len(series) < period:
                return 0
            x = np.arange(period)
            y = series.iloc[-period:].values
            if np.isnan(y).any():
                return 0
            slope = np.polyfit(x, y, 1)[0]
            return slope
        
        # Calculate trend strength using multiple timeframes
        df['slope_5'] = df['close'].rolling(5).apply(lambda x: calc_slope(x, 5), raw=False)
        df['slope_10'] = df['close'].rolling(10).apply(lambda x: calc_slope(x, 10), raw=False)
        df['slope_20'] = df['close'].rolling(20).apply(lambda x: calc_slope(x, 20), raw=False)
        
        # Normalize slopes by price
        df['trend_strength_5'] = df['slope_5'] / df['close'] * 100
        df['trend_strength_10'] = df['slope_10'] / df['close'] * 100
        df['trend_strength_20'] = df['slope_20'] / df['close'] * 100
        
        # Combined trend strength
        df['trend_strength'] = (df['trend_strength_5'] + df['trend_strength_10'] + df['trend_strength_20']) / 3
        
        # Trend classification
        strong_up_threshold = self.trend_strength_threshold.value
        strong_down_threshold = -self.trend_strength_threshold.value
        
        df['strong_uptrend'] = df['trend_strength'] > strong_up_threshold
        df['strong_downtrend'] = df['trend_strength'] < strong_down_threshold
        df['ranging'] = (df['trend_strength'].abs() < strong_up_threshold * 0.5)
        
        return df
    @property
    def protections(self):
        prot = [{"method": "CooldownPeriod", "stop_duration_candles": self.cooldown_lookback.value}]
        if self.use_stop_protection.value:
            prot.append({
                "method": "StoplossGuard",
                "lookback_period_candles": 72,
                "trade_limit": 2,
                "stop_duration_candles": self.stop_duration.value,
                "only_per_pair": False,
            })
        return prot


    def custom_stake_amount(self, pair: str, current_time: datetime, current_rate: float,
                            proposed_stake: float, min_stake: Optional[float], max_stake: float,
                            leverage: float, entry_tag: Optional[str], side: str, **kwargs) -> float:
        
        # ===========================================
        # STAKE LIMITS DEFINIEREN
        # ===========================================
        
        # Maximaler Stake pro Trade (in USDT) - kannst du anpassen
        MAX_STAKE_PER_TRADE = self.max_stake_per_trade
        
        # Maximaler Stake basierend auf Portfolio
        try:
            total_portfolio = self.wallets.get_total_stake_amount()
            MAX_STAKE_PERCENTAGE = self.max_portfolio_percentage_per_trade
            max_stake_from_portfolio = total_portfolio * MAX_STAKE_PERCENTAGE
        except:
            # Fallback wenn wallets nicht verfügbar
            max_stake_from_portfolio = MAX_STAKE_PER_TRADE
            total_portfolio = 1000.0  # Dummy value
        
        # Market condition check für volatility-based stake reduction (DEIN CODE)
        dataframe, _ = self.dp.get_analyzed_dataframe(pair=pair, timeframe=self.timeframe)
        if not dataframe.empty:
            last_candle = dataframe.iloc[-1]
            current_volatility = last_candle.get("volatility", 0.02)
            
            # Reduziere Stake in hochvolatilen Märkten
            if current_volatility > 0.05:  # 5% ATR/Price ratio
                volatility_reduction = min(0.5, current_volatility * 10)  # Max 50% reduction
                proposed_stake *= (1 - volatility_reduction)
                logger.info(f"{pair} Stake reduced by {volatility_reduction:.1%} due to high volatility ({current_volatility:.2%})")
        
        # DCA Multiplier Berechnung (DEIN CODE)
        calculated_max_dca_multiplier = 1.0
        if self.position_adjustment_enable:
            num_safety_orders = int(self.max_safety_orders.value)
            volume_scale = self.safety_order_volume_scale.value
            if num_safety_orders > 0 and volume_scale > 0:
                current_order_relative_size = 1.0
                for _ in range(num_safety_orders):
                    current_order_relative_size *= volume_scale
                    calculated_max_dca_multiplier += current_order_relative_size
            else:
                logger.warning(f"{pair}: Could not calculate max_dca_multiplier due to "
                               f"invalid max_safety_orders ({num_safety_orders}) or "
                               f"safety_order_volume_scale ({volume_scale}). Defaulting to 1.0.")
        else:
            logger.debug(f"{pair}: Position adjustment not enabled. max_dca_multiplier is 1.0.")

        if calculated_max_dca_multiplier > 0:
            stake_amount = proposed_stake / calculated_max_dca_multiplier
            
            # ===========================================
            # NEUE STAKE LIMITS ANWENDEN
            # ===========================================
            
            # Verschiedene Limits prüfen
            final_stake = min(
                stake_amount,
                MAX_STAKE_PER_TRADE,
                max_stake_from_portfolio,
                max_stake  # Freqtrade's max_stake
            )
            
            # Bestimme welches Limit gegriffen hat
            limit_reason = "calculated"
            if final_stake == MAX_STAKE_PER_TRADE:
                limit_reason = "max_per_trade"
            elif final_stake == max_stake_from_portfolio:
                limit_reason = "portfolio_percentage"
            elif final_stake == max_stake:
                limit_reason = "freqtrade_max"
            
            logger.info(f"{pair} Initial stake calculated: {final_stake:.8f} (Proposed: {proposed_stake:.8f}, "
                        f"Calculated Max DCA Multiplier: {calculated_max_dca_multiplier:.2f}, "
                        f"Limited by: {limit_reason}, Portfolio %: {(final_stake/total_portfolio)*100:.1f}%)")
            
            # Min stake prüfen (DEIN CODE)
            if min_stake is not None and final_stake < min_stake:
                logger.info(f"{pair} Initial stake {final_stake:.8f} was below min_stake {min_stake:.8f}. "
                            f"Adjusting to min_stake. Consider tuning your DCA parameters or proposed stake.")
                final_stake = min_stake
            
            return final_stake
        else:
            # Fallback (DEIN CODE)
            logger.warning(
                f"{pair} Calculated max_dca_multiplier is {calculated_max_dca_multiplier:.2f}, which is invalid. "
                f"Using proposed_stake: {proposed_stake:.8f}")
            return proposed_stake

    def custom_entry_price(self, pair: str, trade: Optional[Trade], current_time: datetime,
                           proposed_rate: float, entry_tag: Optional[str], side: str, **kwargs) -> float:
        """
        🎯 IMPROVED: Wait for better entry prices to reduce immediate stop loss risk
        """
        dataframe, _ = self.dp.get_analyzed_dataframe(pair=pair, timeframe=self.timeframe)
        if dataframe.empty:
            return proposed_rate
        
        last_candle = dataframe.iloc[-1]
        
        # Calculate volatility-adjusted entry
        atr = last_candle.get('atr', 0)
        if atr == 0:
            atr = (last_candle['high'] - last_candle['low'])
        
        # Get MML levels for better entry timing
        mml_25 = last_candle.get('[2/8]P', last_candle['close'])
        mml_50 = last_candle.get('[4/8]P', last_candle['close'])
        mml_75 = last_candle.get('[6/8]P', last_candle['close'])
        
        if side == "long":
            # For longs: Try to enter closer to support or on pullbacks
            if entry_tag in ["MML_50_Reclaim", "MML_Support_Bounce"]:
                # Enter slightly above support with buffer
                support_buffer = atr * 0.2  # 20% of ATR buffer
                optimal_entry = max(mml_25, mml_50) + support_buffer
                entry_price = min(proposed_rate, optimal_entry)
            elif entry_tag == "MML_Bullish_Breakout":
                # For breakouts: Enter on slight pullback, not at peak
                pullback_entry = proposed_rate - (atr * 0.1)  # 10% ATR pullback
                entry_price = max(pullback_entry, proposed_rate * 0.999)  # Max 0.1% below
            else:
                # Conservative entry for other signals
                entry_price = proposed_rate - (atr * 0.05)  # Small discount
                
        elif side == "short":
            # For shorts: Try to enter closer to resistance
            if entry_tag in ["MML_50_Breakdown", "MML_Resistance_Reject"]:
                # Enter slightly below resistance with buffer
                resistance_buffer = atr * 0.2
                optimal_entry = min(mml_75, mml_50) - resistance_buffer
                entry_price = max(proposed_rate, optimal_entry)
            elif entry_tag == "MML_Bearish_Breakdown":
                # For breakdowns: Enter on slight bounce, not at bottom
                bounce_entry = proposed_rate + (atr * 0.1)
                entry_price = min(bounce_entry, proposed_rate * 1.001)
            else:
                entry_price = proposed_rate + (atr * 0.05)
        else:
            entry_price = proposed_rate
        
        # Ensure entry price is reasonable
        max_deviation = proposed_rate * 0.005  # Max 0.5% from proposed
        if side == "long":
            entry_price = max(entry_price, proposed_rate - max_deviation)
        else:
            entry_price = min(entry_price, proposed_rate + max_deviation)
        
        logger.info(f"{pair} Optimized entry: {entry_price:.6f} (was {proposed_rate:.6f}, tag: {entry_tag})")
        return entry_price

    # ═══════════════════════════════════════════════════════════════
    # 🛑 CRITICAL FIX: TIME-BASED STOP LOSS LOOSENING
    # ═══════════════════════════════════════════════════════════════

    def custom_stoploss(self, pair: str, trade: Trade, current_time: datetime,
                        current_rate: float, current_profit: float, **kwargs) -> float:
        """
        🔧 REVOLUTIONARY: Time-based stop loss loosening + MML support
        
        Based on your backtest results:
        - ROI hits at ~1.5 hours with 4% profit
        - Stop loss hits at ~1.25 hours with -6.5% loss
        - Need to give trades more time to develop before stopping out
        """
        dataframe, _ = self.dp.get_analyzed_dataframe(pair=pair, timeframe=self.timeframe)
        if dataframe.empty or 'atr' not in dataframe.columns or len(dataframe) < 10:
            logger.warning(f"{pair} Insufficient data for dynamic stop loss. Using conservative default: -0.03")
            return -0.03  # Conservative default
        
        last_candle = dataframe.iloc[-1]
        last_atr = last_candle.get("atr", 0)
        
        # Handle missing or invalid ATR
        if pd.isna(last_atr) or last_atr == 0:
            valid_atr = dataframe["atr"].dropna()
            if not valid_atr.empty:
                last_atr = valid_atr.rolling(window=5).mean().iloc[-1]
                logger.info(f"{pair} Using smoothed ATR: {last_atr:.8f}")
            else:
                logger.warning(f"{pair} No valid ATR found. Using fallback stop loss -0.03")
                return -0.03

        if last_atr == 0 or current_rate == 0:
            logger.warning(f"{pair} ATR or rate is 0. Using fallback stop loss -0.03")
            return -0.03
        
        # ⏰ TIME IN TRADE CALCULATION
        time_in_trade = (current_time - trade.open_date_utc).total_seconds() / 3600  # Hours
        
        # Get MML levels for dynamic support/resistance
        if trade.is_short:
            # For shorts: Use MML resistance as dynamic stop
            resistance_level = last_candle.get('[6/8]P', current_rate * 1.02)
            if pd.isna(resistance_level):
                resistance_level = current_rate * 1.02
            mml_stop_distance = abs((resistance_level - current_rate) / current_rate)
        else:
            # For longs: Use MML support as dynamic stop  
            support_level = last_candle.get('[2/8]P', current_rate * 0.98)
            if pd.isna(support_level):
                support_level = current_rate * 0.98
            mml_stop_distance = abs((current_rate - support_level) / current_rate)
        
        # Base ATR stop loss calculation
        base_atr_stop = (last_atr / current_rate) * 2.0  # Start with 2x ATR
        
        # 🕐 CRITICAL: TIME-BASED STOP LOSS LOOSENING
        # Based on your data: Need more room in first 1.5 hours
        if time_in_trade < 0.25:  # First 15 minutes - tightest (but not too tight)
            time_multiplier = 1.2  # Give some room immediately
        elif time_in_trade < 0.5:  # 15-30 minutes - slight loosening
            time_multiplier = 1.5
        elif time_in_trade < 1.0:  # 30-60 minutes - more room
            time_multiplier = 2.0
        elif time_in_trade < 1.5:  # 1-1.5 hours - CRITICAL PERIOD
            time_multiplier = 2.5  # Much more room where stop losses currently hit
        elif time_in_trade < 2.0:  # 1.5-2 hours - ROI development time
            time_multiplier = 3.0  # Maximum room for ROI to develop
        elif time_in_trade < 4.0:  # 2-4 hours - long-term holds
            time_multiplier = 2.8  # Slight tightening but still wide
        else:  # 4+ hours - prevent runaway losses
            time_multiplier = 2.5
        
        # 📈 PROFIT-BASED ADJUSTMENTS (override time loosening when profitable)
        if current_profit > 0.08:  # 8%+ profit - lock in gains aggressively
            profit_stop = max(-0.015, -(base_atr_stop * 0.3))  # Very tight
        elif current_profit > 0.06:  # 6%+ profit - good protection
            profit_stop = max(-0.02, -(base_atr_stop * 0.5))
        elif current_profit > 0.04:  # 4%+ profit - moderate protection
            profit_stop = max(-0.025, -(base_atr_stop * 0.7))
        elif current_profit > 0.02:  # 2%+ profit - light protection
            profit_stop = max(-0.03, -(base_atr_stop * 1.0))
        elif current_profit > 0:  # Any profit - don't tighten
            profit_stop = -(base_atr_stop * time_multiplier)
        else:
            # Apply time-based loosening for losing trades
            profit_stop = -(base_atr_stop * time_multiplier)
        
        # 🎯 MML-BASED STOP LOSS ADJUSTMENT
        # Use MML levels as natural stop areas, but don't make stops too wide
        mml_adjusted_stop = min(
            abs(profit_stop),  # Don't make tighter than profit-based
            max(mml_stop_distance * 1.3, 0.02),  # At least 2%, max 30% past MML
            0.10  # Never wider than 10%
        )
        
        # 🛡️ SAFETY BOUNDARIES
        # Ensure stop loss isn't too tight or too wide
        if current_profit <= 0:
            # For losing trades: Ensure minimum room based on time
            if time_in_trade < 1.5:  # Critical period - be generous
                min_stop = -0.08  # Minimum 8% room
            else:
                min_stop = -0.06  # Standard minimum
            
            final_stop = -max(mml_adjusted_stop, abs(min_stop))
        else:
            # For profitable trades: Use calculated stop
            final_stop = -mml_adjusted_stop
        
        # Final safety check: Never tighter than -1.5% unless big profit
        if final_stop > -0.015 and current_profit < 0.06:
            final_stop = -0.015
        
        # Never wider than -12%
        final_stop = max(final_stop, -0.12)
        
        # 📊 DETAILED LOGGING
        logger.info(f"{pair} 🛑 DYNAMIC STOP LOSS:")
        logger.info(f"   ⏰ Time in trade: {time_in_trade:.2f}h")
        logger.info(f"   📈 Current profit: {current_profit:.2%}")
        logger.info(f"   🔢 Time multiplier: {time_multiplier}x")
        logger.info(f"   💰 ATR base: {base_atr_stop:.4f}")
        logger.info(f"   🎯 MML distance: {mml_stop_distance:.4f}")
        logger.info(f"   🛡️ Final stop: {final_stop:.4f}")
        
        return final_stop
        
    def minimal_roi_market_adjusted(self, pair: str, trade: Trade, current_time: datetime, 
                                   current_rate: float, current_profit: float) -> Dict[int, float]:
        """
        Dynamically adjust ROI based on market conditions
        """
        dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe)
        if dataframe.empty:
            return self.minimal_roi
        
        last_candle = dataframe.iloc[-1]
        market_score = last_candle.get('market_score', 0.5)
        market_regime = last_candle.get('market_regime', 'normal')
        
        # Copy original ROI
        adjusted_roi = self.minimal_roi.copy()
        
        # In high volatility or bear market, take profits earlier
        if market_regime == 'high_volatility' or market_score < 0.3:
            # Reduce all ROI targets by 20%
            adjusted_roi = {k: v * 0.8 for k, v in adjusted_roi.items()}
            logger.info(f"{pair} ROI adjusted down due to market conditions")
        
        # In strong bull market, let winners run
        elif market_score > 0.7 and market_regime == 'strong_trend':
            # Increase ROI targets by 20%
            adjusted_roi = {k: v * 1.2 for k, v in adjusted_roi.items()}
            logger.info(f"{pair} ROI adjusted up due to bullish market")
        
        return adjusted_roi
    
    def adjust_trade_position(self, trade: Trade, current_time: datetime, current_rate: float,
                              current_profit: float, min_stake: Optional[float], max_stake: float,
                              current_entry_rate: float, current_exit_rate: float, current_entry_profit: float,
                              current_exit_profit: float, **kwargs) -> Optional[float]:
        
        # Get market conditions
        dataframe, _ = self.dp.get_analyzed_dataframe(trade.pair, self.timeframe)
        if not dataframe.empty:
            last_candle = dataframe.iloc[-1]
            btc_trend = last_candle.get('btc_trend', 0)
            market_score = last_candle.get('market_score', 0.5)
            market_breadth = last_candle.get('market_breadth', 0.5)
            rsi = last_candle.get('rsi', 50)
        else:
            market_score = 0.5
            market_breadth = 0.5
            rsi = 50
        
        count_of_entries = trade.nr_of_successful_entries
        count_of_exits = trade.nr_of_successful_exits
        
        # === ENHANCED PROFIT TAKING BASED ON MARKET CONDITIONS ===
        
        # More aggressive profit taking in overbought market
        if market_score > 0.75 and current_profit > 0.15 and count_of_exits == 0:
            logger.info(f"{trade.pair} Taking profit early due to market greed: {market_score:.2f}")
            amount_to_sell = (trade.amount * current_rate) * 0.33  # Sell 33%
            return -amount_to_sell
        
        # Original profit taking logic (keep as is)
        if current_profit > 0.25 and count_of_exits == 0:
            logger.info(f"{trade.pair} Taking partial profit (25%) at {current_profit:.2%}")
            amount_to_sell = (trade.amount * current_rate) * 0.25
            return -amount_to_sell

        # === BEAR MARKET PROFIT TAKING ===
        # Nimm Gewinne schneller mit im Bärenmarkt
        if not trade.is_short and btc_trend < 0:  # Long im Downtrend
            if current_profit > 0.10 and count_of_exits == 0:  # Statt 0.25
                logger.info(f"{trade.pair} Bear market quick profit taking at {current_profit:.2%}")
                amount_to_sell = (trade.amount * current_rate) * 0.5  # 50% verkaufen
                return -amount_to_sell

        if current_profit > 0.40 and count_of_exits == 1:
            logger.info(f"{trade.pair} Taking additional profit (33%) at {current_profit:.2%}")
            amount_to_sell = (trade.amount * current_rate) * (1 / 3)
            return -amount_to_sell
        
        # === 🔧 ENHANCED DCA LOGIC WITH STRICT CONTROLS ===
        if not self.position_adjustment_enable:
            return None
        
        # 🛑 USE STRATEGY VARIABLES FOR DCA LIMITS
        max_dca_for_pair = self.max_dca_orders
        max_total_stake = self.max_total_stake_per_pair
        max_single_dca = self.max_single_dca_amount
        
        # 🛑 CHECK: Already too many DCA orders?
        if count_of_entries > max_dca_for_pair:
            logger.info(f"{trade.pair} 🛑 MAX DCA REACHED: {count_of_entries}/{max_dca_for_pair}")
            return None
        
        # 🛑 CHECK: Total stake amount already too high?
        if trade.stake_amount >= max_total_stake:
            logger.info(f"{trade.pair} 🛑 MAX STAKE REACHED: {trade.stake_amount:.2f}/{max_total_stake} USDT")
            return None
        
        # Block DCA in crashing market
        if market_breadth < 0.25 and trade.is_short == False:
            logger.info(f"{trade.pair} Blocking DCA due to bearish market breadth: {market_breadth:.2%}")
            return None
        
        # More conservative DCA triggers in volatile markets
        if dataframe.iloc[-1].get('market_regime') == 'high_volatility':
            dca_multiplier = 1.5  # Require 50% deeper drawdown
        else:
            dca_multiplier = 1.0
        
        # Apply multiplier to original DCA logic
        trigger = self.initial_safety_order_trigger.value * dca_multiplier
        
        if (current_profit > trigger / 2.0 and count_of_entries == 1) or \
           (current_profit > trigger and count_of_entries == 2) or \
           (current_profit > trigger * 1.5 and count_of_entries == 3):
            logger.info(f"{trade.pair} DCA condition not met. Current profit {current_profit:.2%} above threshold")
            return None
        
        # 🛑 CHECK: Original max safety orders (falls du das noch nutzt)
        if hasattr(self, 'max_safety_orders') and count_of_entries >= self.max_safety_orders.value + 1:
            logger.info(f"{trade.pair} 🛑 Original max_safety_orders reached: {count_of_entries}")
            return None

        try:
            filled_entry_orders = trade.select_filled_orders(trade.entry_side)
            if not filled_entry_orders:
                logger.error(f"{trade.pair} No filled entry orders found for DCA calculation")
                return None
            
            last_order_cost = filled_entry_orders[-1].cost
            
            # 🔧 USE STRATEGY VARIABLE FOR DCA SIZING
            base_dca_amount = max_single_dca  # Use strategy variable
            
            # Progressive DCA sizing (each DCA gets smaller!)
            dca_multipliers = [1.0, 0.8, 0.6]  # 1st: 5 USDT, 2nd: 4 USDT, 3rd: 3 USDT
            
            if count_of_entries <= len(dca_multipliers):
                current_multiplier = dca_multipliers[count_of_entries - 1]
            else:
                current_multiplier = 0.5  # Fallback für unerwartete Orders
            
            # Calculate DCA amount
            dca_stake_amount = base_dca_amount * current_multiplier
            
            # 🛑 HARD CAP: Never exceed remaining budget
            remaining_budget = max_total_stake - trade.stake_amount
            if dca_stake_amount > remaining_budget:
                if remaining_budget > 1:  # Only proceed if at least 1 USDT remaining
                    dca_stake_amount = remaining_budget
                    logger.info(f"{trade.pair} 🔧 DCA capped to remaining budget: {dca_stake_amount:.2f} USDT")
                else:
                    logger.info(f"{trade.pair} 🛑 Insufficient remaining budget: {remaining_budget:.2f} USDT")
                    return None
            
            # Standard min/max stake checks
            if min_stake is not None and dca_stake_amount < min_stake:
                logger.warning(f"{trade.pair} DCA below min_stake. Adjusting to {min_stake:.2f} USDT")
                dca_stake_amount = min_stake
            
            if max_stake is not None and (trade.stake_amount + dca_stake_amount) > max_stake:
                available_for_dca = max_stake - trade.stake_amount
                if available_for_dca > (min_stake or 0):
                    dca_stake_amount = available_for_dca
                    logger.warning(f"{trade.pair} DCA reduced due to max_stake: {dca_stake_amount:.2f} USDT")
                else:
                    logger.warning(f"{trade.pair} Cannot DCA due to max_stake limit")
                    return None
            
            # 🔧 FINAL SAFETY CHECK
            new_total_stake = trade.stake_amount + dca_stake_amount
            if new_total_stake > max_total_stake:
                logger.error(f"{trade.pair} 🚨 SAFETY VIOLATION: Would exceed max total stake!")
                return None
            
            logger.info(f"{trade.pair} ✅ DCA #{count_of_entries}: +{dca_stake_amount:.2f} USDT "
                       f"(Total: {new_total_stake:.2f}/{max_total_stake} USDT)")
            
            return dca_stake_amount
            
        except IndexError:
            logger.error(f"Error calculating DCA stake for {trade.pair}: IndexError accessing last_order")
            return None
        except Exception as e:
            logger.error(f"Error calculating DCA stake for {trade.pair}: {e}")
            return None

    def leverage(self, pair: str, current_time: datetime, current_rate: float, proposed_leverage: float,
                 max_leverage: float, side: str, **kwargs) -> float:
        window_size = self.leverage_window_size.value
        dataframe, _ = self.dp.get_analyzed_dataframe(pair=pair, timeframe=self.timeframe)
        if len(dataframe) < window_size:
            logger.warning(
                f"{pair} Not enough data ({len(dataframe)} candles) to calculate dynamic leverage (requires {window_size}). Using proposed: {proposed_leverage}")
            return proposed_leverage
        close_prices_series = dataframe["close"].tail(window_size)
        high_prices_series = dataframe["high"].tail(window_size)
        low_prices_series = dataframe["low"].tail(window_size)
        base_leverage = self.leverage_base.value
        rsi_array = ta.RSI(close_prices_series, timeperiod=14)
        atr_array = ta.ATR(high_prices_series, low_prices_series, close_prices_series, timeperiod=14)
        sma_array = ta.SMA(close_prices_series, timeperiod=20)
        macd_output = ta.MACD(close_prices_series, fastperiod=12, slowperiod=26, signalperiod=9)

        current_rsi = rsi_array[-1] if rsi_array.size > 0 and not np.isnan(rsi_array[-1]) else 50.0
        current_atr = atr_array[-1] if atr_array.size > 0 and not np.isnan(atr_array[-1]) else 0.0
        current_sma = sma_array[-1] if sma_array.size > 0 and not np.isnan(sma_array[-1]) else current_rate
        current_macd_hist = 0.0

        if isinstance(macd_output, pd.DataFrame):
            if not macd_output.empty and 'macdhist' in macd_output.columns:
                valid_macdhist_series = macd_output['macdhist'].dropna()
                if not valid_macdhist_series.empty:
                    current_macd_hist = valid_macdhist_series.iloc[-1]

        # Apply rules based on indicators
        if side == "long":
            if current_rsi < self.leverage_rsi_low.value:
                base_leverage *= self.leverage_long_increase_factor.value
            elif current_rsi > self.leverage_rsi_high.value:
                base_leverage *= self.leverage_long_decrease_factor.value

            if current_atr > 0 and current_rate > 0:
                if (current_atr / current_rate) > self.leverage_atr_threshold_pct.value:
                    base_leverage *= self.leverage_volatility_decrease_factor.value

            if current_macd_hist > 0:
                base_leverage *= self.leverage_long_increase_factor.value

            if current_sma > 0 and current_rate < current_sma:
                base_leverage *= self.leverage_long_decrease_factor.value

        adjusted_leverage = round(max(1.0, min(base_leverage, max_leverage)), 2)
        logger.info(
            f"{pair} Dynamic Leverage: {adjusted_leverage:.2f} (Base: {base_leverage:.2f}, RSI: {current_rsi:.2f}, "
            f"ATR: {current_atr:.4f}, MACD Hist: {current_macd_hist:.4f}, SMA: {current_sma:.4f})")
        return adjusted_leverage

    def populate_indicators(self, dataframe: pd.DataFrame, metadata: dict) -> pd.DataFrame:
        """
        🔧 FIXED: Enhanced indicator calculations with proper syntax
        """
        dataframe['actual_stoploss'] = np.nan
        dataframe['follow_price_level'] = np.nan
        
        # ===========================================
        # YOUR EXISTING INDICATORS (KEEP ALL)
        # ===========================================
        dataframe["atr"] = ta.ATR(dataframe["high"], dataframe["low"], dataframe["close"], timeperiod=7)
        if dataframe["atr"].isna().all():
            logger.warning(f"No valid ATR calculated. Filling with 0 temporarily.")
            dataframe["atr"] = 0.0
        else:
            # Fill NaN with rolling mean of last 5 valid values
            dataframe["atr"] = dataframe["atr"].fillna(dataframe["atr"].rolling(window=5, min_periods=1).mean())
        logger.info(f"ATR values: {', '.join(dataframe['atr'].tail(5).map('{:.6f}'.format))}")
        dataframe["ema20"] = ta.EMA(dataframe["close"], timeperiod=20)
        dataframe["ema50"] = ta.EMA(dataframe["close"], timeperiod=50)
        dataframe["rsi"] = ta.RSI(dataframe["close"], timeperiod=7)
        dataframe["rsi_overbought"] = (dataframe["rsi"] > 70).astype(int)
        dataframe["rsi_oversold"] = (dataframe["rsi"] < 30).astype(int)

        dataframe["plus_di"] = ta.PLUS_DI(dataframe)
        dataframe["minus_di"] = ta.MINUS_DI(dataframe)
        dataframe["DI_values"] = dataframe["plus_di"] - dataframe["minus_di"]
        dataframe["DI_cutoff"] = 0
        
        # Extrema detection (your existing code)
        extrema_order = self.indicator_extrema_order.value
        dataframe["maxima"] = (
            dataframe["close"] == dataframe["close"].shift(1).rolling(window=extrema_order).max()
        ).astype(int)
        dataframe["minima"] = (
            dataframe["close"] == dataframe["close"].shift(1).rolling(window=extrema_order).min()
        ).astype(int)

        dataframe["s_extrema"] = 0
        dataframe.loc[dataframe["minima"] == 1, "s_extrema"] = -1
        dataframe.loc[dataframe["maxima"] == 1, "s_extrema"] = 1
        
        # Heikin-Ashi and rolling extrema
        logger.debug(f"{metadata['pair']} 🛠️ Calculating Heikin-Ashi and extrema")
        dataframe["ha_close"] = (dataframe["open"] + dataframe["high"] + dataframe["low"] + dataframe["close"]) / 4
        if dataframe["ha_close"].isna().all():
            logger.error(f"{metadata['pair']} 🚨 All ha_close values are NaN!")
        dataframe["minh2"], dataframe["maxh2"] = calculate_minima_maxima(dataframe, self.h2.value)
        dataframe["minh1"], dataframe["maxh1"] = calculate_minima_maxima(dataframe, self.h1.value)
        dataframe["minh0"], dataframe["maxh0"] = calculate_minima_maxima(dataframe, self.h0.value)
        dataframe["mincp"], dataframe["maxcp"] = calculate_minima_maxima(dataframe, self.cp.value)
        logger.debug(f"{metadata['pair']} ✅ Extrema calculated: minh2={dataframe['minh2'].sum()}, maxh2={dataframe['maxh2'].sum()}")
        # Murrey Math levels (your existing code)
        mml_window = self.indicator_mml_window.value
        murrey_levels = self.calculate_rolling_murrey_math_levels_optimized(dataframe, window_size=mml_window)
        for level_name in MML_LEVEL_NAMES:
            if level_name in murrey_levels:
                dataframe[level_name] = murrey_levels[level_name]
            else:
                dataframe[level_name] = dataframe["close"]
        
        # MML oscillator (your existing code)
        mml_4_8 = dataframe.get("[4/8]P")
        mml_plus_3_8 = dataframe.get("[+3/8]P")
        mml_minus_3_8 = dataframe.get("[-3/8]P")
        
        if mml_4_8 is not None and mml_plus_3_8 is not None and mml_minus_3_8 is not None:
            osc_denominator = (mml_plus_3_8 - mml_minus_3_8).replace(0, np.nan)
            dataframe["mmlextreme_oscillator"] = 100 * ((dataframe["close"] - mml_4_8) / osc_denominator)
        else:
            dataframe["mmlextreme_oscillator"] = np.nan
        
        # DI Catch and checks (your existing code)
        dataframe["DI_catch"] = np.where(dataframe["DI_values"] > dataframe["DI_cutoff"], 0, 1)
        rolling_window_threshold = self.indicator_rolling_window_threshold.value
        dataframe["minima_sort_threshold"] = dataframe["close"].rolling(
            window=rolling_window_threshold, min_periods=1
        ).min()
        dataframe["maxima_sort_threshold"] = dataframe["close"].rolling(
            window=rolling_window_threshold, min_periods=1
        ).max()
        
        rolling_check_window = self.indicator_rolling_check_window.value
        dataframe["minima_check"] = (
            dataframe["minima"].rolling(window=rolling_check_window, min_periods=1).sum() == 0
        ).astype(int)
        dataframe["maxima_check"] = (
            dataframe["maxima"].rolling(window=rolling_check_window, min_periods=1).sum() == 0
        ).astype(int)
        
        # Your existing volatility calculations
        dataframe["volatility_range"] = dataframe["high"] - dataframe["low"]
        dataframe["avg_volatility"] = dataframe["volatility_range"].rolling(window=10).mean()
        dataframe["avg_volume"] = dataframe["volume"].rolling(window=20).mean()
        
        # ===========================================
        # 🚀 ENHANCED MARKET CORRELATION (PROPER ORDER)
        # ===========================================
        
        # Enhanced BTC correlation FIRST
        if self.enable_btc_correlation.value:
            logger.info(f"🔍 {metadata['pair']} Calculating enhanced BTC correlation...")
            dataframe = self.enhanced_btc_correlation_filter(dataframe, metadata)
        
        # Legacy BTC correlation (keep for compatibility)
        if self.btc_correlation_enabled.value or self.btc_trend_filter_enabled.value:
            logger.info(f"📈 {metadata['pair']} Calculating legacy BTC correlation...")
            dataframe = self.calculate_market_correlation_simple(dataframe, metadata)
        
        # ===========================================
        # 🔧 FIXED: Market breadth calculation
        # ===========================================
        
        if self.market_breadth_enabled.value:
            logger.info(f"📊 {metadata['pair']} Calculating weighted market breadth...")
            try:
                # Use the fixed market breadth method
                dataframe = self.calculate_market_breadth_weighted(dataframe, metadata)
                
                # Debug breadth calculation
                if not dataframe.empty and 'market_breadth' in dataframe.columns and 'breadth_pairs_checked' in dataframe.columns:
                    breadth = dataframe['market_breadth'].iloc[-1]
                    pairs_checked = dataframe['breadth_pairs_checked'].iloc[-1]
                    logger.info(f"🎯 {metadata['pair']} Breadth result: {breadth:.2%} ({pairs_checked} pairs)")
                else:
                    logger.warning(f"⚠️ {metadata['pair']} Market breadth debugging skipped: invalid DataFrame or missing columns")
            except Exception as e:
                logger.warning(f"⚠️ {metadata['pair']} Market breadth calculation failed: {str(e)}")
                # Fallback to simple values
                dataframe['market_breadth'] = 0.5
                dataframe['market_score'] = 0.0
                dataframe['breadth_pairs_checked'] = 0
        
        # ===========================================
        # 🔧 FIXED: Market regime calculation
        # ===========================================
        
        if self.regime_filter_enabled.value:
            logger.info(f"🏛️ {metadata['pair']} Calculating market regime...")
            
            try:
                # Check parameter availability
                if hasattr(self, 'regime_lookback_period'):
                    lookback = self.regime_lookback_period.value
                    logger.info(f"📊 {metadata['pair']} Using lookback period: {lookback}")
                else:
                    logger.warning(f"⚠️ {metadata['pair']} Missing regime_lookback_period parameter, using default")
                    lookback = 48
                
                # Call the regime calculation
                dataframe = self.calculate_market_regime_aligned(dataframe, metadata)
                
                # Debug regime calculation
                if len(dataframe) > 0:
                    regime = dataframe['market_regime'].iloc[-1]
                    volatility = dataframe.get('market_volatility', [0.02]).iloc[-1]
                    adx = dataframe.get('market_adx', [25]).iloc[-1]
                    logger.info(f"🎯 {metadata['pair']} Regime result: {regime} (vol: {volatility:.3f}, adx: {adx:.1f})")
                    
            except Exception as e:
                logger.warning(f"⚠️ {metadata['pair']} Market regime calculation failed: {e}")
                # Fallback values
                dataframe['market_regime'] = 'neutral'
                dataframe['market_volatility'] = 0.02
                dataframe['market_adx'] = 25
        
        # Market cap trend (keep existing)
        if self.total_mcap_filter_enabled.value:
            logger.info(f"💰 {metadata['pair']} Calculating market cap trend...")
            try:
                dataframe = self.calculate_total_market_cap_trend(dataframe, metadata)
            except Exception as e:
                logger.warning(f"⚠️ {metadata['pair']} Market cap trend calculation failed: {e}")
                dataframe['mcap_trend'] = 0
        
        # ===========================================
        # 🆕 ENHANCED SIGNAL FILTERING INDICATORS
        # ===========================================
        
        # Enhanced trend detection
        try:
            dataframe = self.calculate_enhanced_trend_filters(dataframe)
            logger.debug(f"✅ {metadata['pair']} Enhanced trend filters calculated")
        except Exception as e:
            logger.warning(f"⚠️ {metadata['pair']} Enhanced trend filters failed: {e}")
            # Add basic trend fallbacks
            dataframe['trend_alignment_bull'] = 0.5
            dataframe['trend_alignment_bear'] = 0.5
        
        # Market state detection  
        try:
            dataframe = self.detect_market_state(dataframe)
            logger.debug(f"✅ {metadata['pair']} Market state detected")
        except Exception as e:
            logger.warning(f"⚠️ {metadata['pair']} Market state detection failed: {e}")
            # Add basic state fallbacks
            dataframe['market_state'] = 'normal'
        
        # Signal strength calculation
        try:
            dataframe = self.calculate_signal_strength(dataframe)
            logger.debug(f"✅ {metadata['pair']} Signal strength calculated")
        except Exception as e:
            logger.warning(f"⚠️ {metadata['pair']} Signal strength calculation failed: {e}")
            # Add basic strength fallbacks
            dataframe['signal_strength'] = 0.5
        
        # ===========================================
        # 🔧 FIXED: ENHANCED MARKET SCORE CALCULATION
        # ===========================================
        
        # Base market score
        dataframe['market_score'] = 0.5
        
        # BTC correlation component
        if 'btc_correlation' in dataframe.columns:
            dataframe['market_score'] += dataframe['btc_correlation'] * 0.15
        
        # 🔧 FIXED: Removed the "6" that was causing the syntax error
        if 'btc_correlation_ok' in dataframe.columns:
            # Enhanced BTC correlation gets higher weight
            dataframe['market_score'] += dataframe['btc_correlation_ok'].astype(float) * 0.20
        
        # Market breadth component (higher weight due to weighting)
        if 'market_breadth' in dataframe.columns:
            breadth_contribution = (dataframe['market_breadth'] - 0.5) * 0.25
            dataframe['market_score'] += breadth_contribution
        
        # Market cap trend component  
        if 'mcap_trend' in dataframe.columns:
            dataframe['market_score'] += dataframe['mcap_trend'] * 0.10
        
        # Market regime component (aligned names)
        if 'market_regime' in dataframe.columns:
            regime_scores = {
                'bull_run': 0.85,        # Strong bullish
                'bear_market': 0.15,     # Strong bearish  
                'sideways': 0.50,        # Neutral ranging
                'high_volatility': 0.30, # Caution - volatile
                'transitional': 0.50,    # Neutral transition
                'neutral': 0.50          # Default neutral
            }
            
            dataframe['regime_score'] = dataframe['market_regime'].map(
                lambda x: regime_scores.get(x, 0.5)
            )
            dataframe['market_score'] += (dataframe['regime_score'] - 0.5) * 0.15
        
        # Enhanced trend alignment (if available)
        if 'trend_alignment_bull' in dataframe.columns:
            dataframe['market_score'] += dataframe['trend_alignment_bull'] * 0.10
            dataframe['market_score'] -= dataframe['trend_alignment_bear'] * 0.10
        
        # MACD momentum (if available)
        if 'macd_bull' in dataframe.columns:
            dataframe['market_score'] += dataframe['macd_bull'].astype(int) * 0.05
            dataframe['market_score'] -= dataframe['macd_bear'].astype(int) * 0.05
        
        # Volume confirmation component
        if 'volume_confirmation' in dataframe.columns:
            volume_contribution = (dataframe['volume_confirmation'] - 0.5) * 0.10
            dataframe['market_score'] += volume_contribution
        
        # Ensure market_score stays within bounds
        dataframe['market_score'] = dataframe['market_score'].clip(0, 1)
        
        # ===========================================
        # 🔧 SIGNAL QUALITY INDICATORS 
        # ===========================================
        
        # Signal persistence
        dataframe['signal_persistence'] = 0
        for window in [3, 5, 10]:
            dataframe[f'bullish_persistence_{window}'] = (
                (dataframe['rsi'] < 50).rolling(window).sum() / window
            )
            dataframe[f'bearish_persistence_{window}'] = (
                (dataframe['rsi'] > 50).rolling(window).sum() / window
            )
        
        # Volume momentum
        dataframe['volume_momentum'] = (
            dataframe['volume'].rolling(3).mean() / 
            dataframe['volume'].rolling(20).mean()
        ).fillna(1.0)
        
        # Price momentum strength
        dataframe['price_momentum_strength'] = abs(
            dataframe['close'].pct_change(5)
        ).rolling(10).mean()
        
        # ===========================================
        # 🔧 BREADTH-SPECIFIC INDICATORS
        # ===========================================
        
        # Breadth momentum (rate of change)
        if 'market_breadth' in dataframe.columns:
            dataframe['breadth_momentum'] = dataframe['market_breadth'].diff()
            dataframe['breadth_trend'] = dataframe['market_breadth'].rolling(5).mean()
            
            # Breadth divergence detection
            price_direction = (dataframe['close'] > dataframe['close'].shift(5)).astype(int)
            breadth_direction = (dataframe['market_breadth'] > 0.5).astype(int)
            dataframe['breadth_price_divergence'] = abs(price_direction - breadth_direction)
        
        # Market regime strength
        if 'market_adx' in dataframe.columns:
            dataframe['regime_strength'] = dataframe['market_adx'] / 50  # Normalize ADX
            dataframe['strong_regime'] = dataframe['market_adx'] > 30
        
        # ===========================================
        # 📊 FINAL LOGGING & VALIDATION
        # ===========================================
        
        try:
            # Log final market conditions
            if len(dataframe) > 0:
                last_row = dataframe.iloc[-1]
                
                market_breadth = last_row.get('market_breadth', 0.5)
                market_regime = last_row.get('market_regime', 'unknown')
                market_score = last_row.get('market_score', 0.5)
                btc_ok = last_row.get('btc_correlation_ok', True)
                
                logger.info(f"📊 FINAL MARKET CONDITIONS for {metadata['pair']}:")
                logger.info(f"   🌐 Breadth: {market_breadth:.2%}")
                logger.info(f"   🏛️ Regime: {market_regime}")
                logger.info(f"   📈 Score: {market_score:.2%}")
                logger.info(f"   ₿ BTC OK: {btc_ok}")
                
                # 🔍 REGIME DEBUGGING
                if market_regime == 'unknown':
                    logger.warning(f"⚠️ {metadata['pair']} Market regime is unknown - check calculation method")
                    
            else:
                logger.error(f"🚨 {metadata['pair']} Empty dataframe after indicators!")
                
        except Exception as e:
            logger.error(f"💥 {metadata['pair']} Final logging error: {e}")
        
        return dataframe

    def enhanced_btc_correlation_filter(self, dataframe: pd.DataFrame, metadata: dict) -> pd.DataFrame:
        """
        🛡️ ROBUST: BTC correlation with comprehensive error handling and logging
        """
        
        pair = metadata['pair']
        
        # Skip BTC correlation for BTC pairs
        if 'BTC' in pair:
            logger.info(f"{pair} 🟡 Skipping BTC correlation for BTC pair")
            dataframe['btc_correlation_ok'] = True
            return dataframe
        
        try:
            # ===========================================
            # BTC DATA RETRIEVAL WITH VALIDATION
            # ===========================================
            
            current_pair = metadata['pair']
            if ':' in current_pair:
                # Futures mode - extract settlement currency
                settlement = current_pair.split(':')[1]
                btc_pair = f'BTC/USDT:{settlement}'
            else:
                # Spot mode
                btc_pair = 'BTC/USDT'

            logger.info(f"{current_pair} Using BTC pair: {btc_pair}")

            # Now use the detected format
            btc_15m = self.dp.get_pair_dataframe(btc_pair, '15m')
            btc_1h = self.dp.get_pair_dataframe(btc_pair, '1h') 
            btc_4h = self.dp.get_pair_dataframe(btc_pair, '4h')
            
            # Comprehensive validation
            btc_data_available = False
            btc_timeframes_ok = []
            
            for tf_name, btc_df in [('15m', btc_15m), ('1h', btc_1h), ('4h', btc_4h)]:
                if not btc_df.empty and len(btc_df) > 30:
                    btc_timeframes_ok.append(tf_name)
                    btc_data_available = True
                    logger.info(f"{pair} ✅ BTC {tf_name} data: {len(btc_df)} candles")
                else:
                    logger.warning(f"{pair} ❌ BTC {tf_name} data insufficient: {len(btc_df) if not btc_df.empty else 0} candles")
            
            if not btc_data_available:
                logger.error(f"{pair} 🚨 NO BTC DATA AVAILABLE - DISABLING BTC CORRELATION")
                logger.error(f"{pair} 📊 This means backtest results may not match live trading!")
                
                # Fallback: Allow all trades (backtest won't match live)
                dataframe['btc_correlation_ok'] = True
                dataframe['btc_status'] = 'NO_DATA'
                return dataframe
            
            # Use best available timeframe (prefer 1h, fallback to others)
            if '1h' in btc_timeframes_ok:
                btc_main = btc_1h
                main_tf = '1h'
            elif '4h' in btc_timeframes_ok:
                btc_main = btc_4h  
                main_tf = '4h'
            elif '15m' in btc_timeframes_ok:
                btc_main = btc_15m
                main_tf = '15m'
            else:
                raise Exception("No usable BTC timeframe data")
            
            logger.info(f"{pair} 🎯 Using BTC {main_tf} data for correlation ({len(btc_main)} candles)")
            
            # ===========================================
            # BTC TREND ANALYSIS
            # ===========================================
            
            # Calculate BTC indicators
            btc_main['sma_24'] = btc_main['close'].rolling(24).mean()
            btc_main['sma_50'] = btc_main['close'].rolling(50).mean()
            btc_main['rsi'] = ta.RSI(btc_main['close'], timeperiod=14)
            
            # BTC trend determination
            btc_last = btc_main.iloc[-1]
            btc_prev = btc_main.iloc[-2] if len(btc_main) > 1 else btc_last
            
            # Multiple trend indicators
            btc_above_sma24 = btc_last['close'] > btc_last['sma_24']
            btc_above_sma50 = btc_last['close'] > btc_last['sma_50']
            btc_sma_bullish = btc_last['sma_24'] > btc_last['sma_50']
            btc_price_momentum = (btc_last['close'] - btc_prev['close']) / btc_prev['close']
            
            # BTC trend classification
            btc_bullish_signals = sum([
                btc_above_sma24,
                btc_above_sma50, 
                btc_sma_bullish,
                btc_price_momentum > 0.001,  # 0.1% positive momentum
                30 < btc_last['rsi'] < 70    # Healthy RSI range
            ])
            
            btc_trend_strong = btc_bullish_signals >= 4
            btc_trend_weak = btc_bullish_signals <= 1
            btc_trend_neutral = not btc_trend_strong and not btc_trend_weak
            
            # ===========================================
            # CORRELATION FILTER LOGIC
            # ===========================================
            
            # Conservative approach: Allow trades when BTC is not strongly bearish
            btc_correlation_ok = not btc_trend_weak
            
            # Enhanced logging
            logger.info(f"{pair} 🔍 BTC CORRELATION ANALYSIS:")
            logger.info(f"   📊 BTC Price: ${btc_last['close']:.0f}")
            logger.info(f"   📈 Above SMA24: {btc_above_sma24}")
            logger.info(f"   📈 Above SMA50: {btc_above_sma50}")
            logger.info(f"   🎯 Bullish Signals: {btc_bullish_signals}/5")
            logger.info(f"   🚦 BTC Trend: {'STRONG' if btc_trend_strong else 'WEAK' if btc_trend_weak else 'NEUTRAL'}")
            logger.info(f"   ✅ Correlation OK: {btc_correlation_ok}")
            
            # Apply to dataframe
            dataframe['btc_correlation_ok'] = btc_correlation_ok
            dataframe['btc_trend_score'] = btc_bullish_signals
            dataframe['btc_status'] = f"OK_{main_tf}"
            
            return dataframe
            
        except Exception as e:
            logger.error(f"{pair} 💥 BTC CORRELATION ERROR: {str(e)}")
            logger.error(f"{pair} 🚨 FALLING BACK TO NO BTC FILTER")
            
            # Emergency fallback
            dataframe['btc_correlation_ok'] = True
            dataframe['btc_status'] = 'ERROR'
            return dataframe
    def safe_time_diff(self, time1, time2):
        """Safe time difference calculation"""
        try:
            # Handle various timestamp formats
            if isinstance(time1, (int, float)):
                time1 = pd.Timestamp.fromtimestamp(time1)
            if isinstance(time2, (int, float)):
                time2 = pd.Timestamp.fromtimestamp(time2)
                
            return (pd.Timestamp(time1) - pd.Timestamp(time2)).total_seconds() / 60
        except Exception as e:
            logger.warning(f"Time diff error: {e}")
            return 0

    def populate_entry_trend(self, df: pd.DataFrame, metadata: dict) -> pd.DataFrame:
        """
        🔄 Optimized Implementation: Simplified long and short signals with adaptive filters
        🔧 Incorporates community suggestions with relaxed constraints for longs and mirrored for shorts
        Enhanced with stricter trend confirmation, ADX, volume rising, and volatility filters
        """
        pair = metadata['pair']
        
        # Safe initialization for re-entry system
        self.get_or_init_reentry_data(pair)
        
        # ===========================================
        # INITIALIZE COLUMNS
        # ===========================================
        
        df["enter_long"] = 0
        df["enter_short"] = 0  
        df["enter_tag"] = ""
        df["exit_long"] = 0    
        df["exit_short"] = 0
        
        # ===========================================
        # PRELIMINARY CALCULATIONS (Enhanced)
        # ===========================================
        
        # Add ADX for trend strength (new: compute here if not in populate_indicators)
        if 'adx' not in df.columns:
            df['adx'] = ta.ADX(df['high'], df['low'], df['close'], timeperiod=14)
            df['plus_di'] = ta.PLUS_DI(df['high'], df['low'], df['close'], timeperiod=14)
            df['minus_di'] = ta.MINUS_DI(df['high'], df['low'], df['close'], timeperiod=14)
        
        # Basic volatility filter (new: skip high-vol entries unless extreme signal)
        volatility_high = df['atr'] / df['close'] > 0.03  # >3% ATR/close
        df['volume_rising'] = (df['volume'] > df['volume'].shift(1)) & \
                              (df['volume'] > df['volume'].rolling(5).mean() * 1.2)  # New: stricter volume
        
        # Existing market condition calculations
        df['green_candle'] = (df['close'] > df['open']).astype(int)
        df['red_candle'] = (df['close'] < df['open']).astype(int)
        df['consecutive_green'] = df['green_candle'].rolling(3).sum()
        df['consecutive_red'] = df['red_candle'].rolling(3).sum()
        
        df['trend_consistency'] = (df['close'] / df['close'].shift(10) - 1).fillna(0)
        df['market_score'] = df.get('market_score', 0.5)
        
        # Conditions for long signals
        df['below_midpoint'] = df['close'] < df['[4/8]P']
        lookback_candles = getattr(self, 'lookback_candles', 15)
        min_drop_pct = getattr(self, 'min_drop_pct', 0.015)
        df['highest_close'] = df['close'].rolling(lookback_candles).max()
        df['pct_drop_from_high'] = (df['highest_close'] - df['close']) / df['highest_close']
        df['significant_pullback'] = df['pct_drop_from_high'] > min_drop_pct
        
        momentum_lookback = getattr(self, 'momentum_lookback', 3)
        max_drop_pct = getattr(self, 'max_drop_pct', 0.03)
        df['recent_drop'] = (df['close'].shift(1) - df['close'].rolling(momentum_lookback).min()) / df['close'].shift(1)
        df['momentum_safe'] = df['recent_drop'] <= max_drop_pct
        
        # Conditions for short signals
        df['above_midpoint'] = df['close'] > df['[4/8]P']
        min_rise_pct = getattr(self, 'min_rise_pct', 0.015)
        df['lowest_close'] = df['close'].rolling(lookback_candles).min()
        df['pct_rise_from_low'] = (df['close'] - df['lowest_close']) / df['lowest_close']
        df['significant_rise'] = df['pct_rise_from_low'] > min_rise_pct
        
        max_rise_pct = getattr(self, 'max_rise_pct', 0.03)
        df['recent_rise'] = (df['close'].rolling(momentum_lookback).max() - df['close'].shift(1)) / df['close'].shift(1)
        df['momentum_safe_short'] = df['recent_rise'] <= max_rise_pct
        
        # MML analysis (existing)
        bullish_mml = (
            (df["close"] > df["[6/8]P"]) |
            ((df["close"] > df["[4/8]P"]) & (df["close"].shift(5) < df["[4/8]P"].shift(5)))
        )
        bearish_mml = (
            (df["close"] < df["[2/8]P"]) |
            ((df["close"] < df["[4/8]P"]) & (df["close"].shift(5) > df["[4/8]P"].shift(5)))
        )
        range_bound = (
            (df["close"] >= df["[2/8]P"]) & 
            (df["close"] <= df["[6/8]P"]) &
            (~bullish_mml) & (~bearish_mml)
        )
        mml_support_bounce = (
            ((df["low"] <= df["[2/8]P"]) & (df["close"] > df["[2/8]P"])) |
            ((df["low"] <= df["[4/8]P"]) & (df["close"] > df["[4/8]P"]))
        )
        mml_resistance_reject = (
            ((df["high"] >= df["[6/8]P"]) & (df["close"] < df["[6/8]P"])) |
            ((df["high"] >= df["[8/8]P"]) & (df["close"] < df["[8/8]P"]))
        )
        
        # ===========================================
        # LONG SIGNALS (Optimized with enhancements: volume_rising, volatility checks, ADX for trend_cont)
        # ===========================================
        
        long_signal_mml_breakout = (
            (df["close"] > df["[6/8]P"]) &
            (df["close"].shift(1) <= df["[6/8]P"].shift(1)) &
            (df["volume"] > df["volume"].rolling(20).mean() * 1.1) &  # Fixed typo, integrated volume_rising loosely
            (df["rsi"] > 50) &
            (df["close"] > df["ema20"]) &
            (df["below_midpoint"] | (df["rsi"].shift(1) < df["rsi"])) &
            (df['volume_rising']) &  # New: stricter volume rising
            (~volatility_high | (df["rsi"] < 30))  # New: volatility filter, allow if oversold
        )
        
        long_signal_support_bounce = (
            mml_support_bounce &
            (df["rsi"] < 45) &
            (df["close"] > df["close"].shift(1)) &
            (df["volume"] > df["volume"].rolling(10).mean() * 1.1) &
            (df["minima"] == 1) &
            (df["significant_pullback"]) &
            (df["momentum_safe"]) &
            (df['volume_rising']) &  # New: volume surge
            (~volatility_high | (df["rsi"] < 25))  # New: volatility filter
        )
        
        long_signal_50_reclaim = (
            (df["close"] > df["[4/8]P"]) &
            (df["close"].shift(1) <= df["[4/8]P"].shift(1)) &
            (df["rsi"] > 50) &
            (df["volume"] > df["volume"].rolling(15).mean() * 1.1) &
            (df["close"] > df["ema20"]) &
            (df["market_score"] > 0.2) &
            (df['adx'] > 20) &  # New: some trend strength
            (df['volume_rising'])  # New: volume confirmation
        )
        
        long_signal_range_long = (
            range_bound &
            (df["low"] <= df["[2/8]P"]) &
            (df["close"] > df["[2/8]P"]) &
            (df["rsi"] < 50) &
            (df["close"] > df["close"].shift(1)) &
            (df["minima"] == 1) &
            (df["volume"] > df["volume"].rolling(10).mean() * 1.1) &
            (df["significant_pullback"]) &
            (df['volume_rising']) &  # New: volume surge
            (~volatility_high)  # New: skip in high vol
        )
        
        long_signal_trend_continuation = (
            bullish_mml &
            (df["close"] > df["[5/8]P"]) &
            (df["rsi"].between(40, 65)) &
            (df["close"] > df["ema20"]) &
            (df["volume"] > df["volume"].rolling(15).mean() * 1.1) &
            (df['adx'] > 25) &  # New: strong trend required
            (df['plus_di'] > df['minus_di']) &  # New: bullish direction
            (df["close"] > df["[4/8]P"]) &  # New: above MML midpoint (redundant but emphasized)
            (df['volume_rising']) &  # New: volume confirmation
            (~volatility_high)  # New: skip in high vol
        )
        
        long_signal_extreme_oversold = (
            (df["low"] <= df["[1/8]P"]) &
            (df["close"] > df["[1/8]P"]) &
            (df["rsi"] < 35) &
            (df["volume"] > df["volume"].rolling(20).mean() * 1.2) &
            (df["close"] > df["close"].shift(1)) &  # New: starting to bounce
            (df["minima"] == 1) &
            (df["significant_pullback"]) &
            (df["momentum_safe"]) &
            (df['volume_rising']) &  # New: volume surge
            (~volatility_high | (df["rsi"] < 20))  # New: allow in high vol only if very oversold
        )
        
        any_long_signal = (
            long_signal_mml_breakout |
            long_signal_support_bounce |
            long_signal_50_reclaim |
            long_signal_range_long |
            long_signal_trend_continuation |
            long_signal_extreme_oversold
        )
        
        # ===========================================
        # SHORT SIGNALS (Optimized with symmetric enhancements)
        # ===========================================
        
        if self.can_short:
            short_signal_bearish_breakdown = (
                (df["close"] < df["[2/8]P"]) &
                (df["close"].shift(1) >= df["[2/8]P"].shift(1)) &
                (df["volume"] > df["volume"].rolling(20).mean() * 1.1) &  # Relaxed volume
                (df["rsi"] < 50) &  # Relaxed RSI
                (df["close"] < df["ema20"]) &
                (df["above_midpoint"] | (df["rsi"].shift(1) > df["rsi"])) &  # Avoid bottoms
                (df['volume_rising']) &  # New: stricter volume rising
                (~volatility_high | (df["rsi"] > 70))  # New: volatility filter, allow if overbought
            )
            
            short_signal_resistance_reject = (
                mml_resistance_reject &
                (df["rsi"] > 50) &  # Relaxed from rsi_overbought
                (df["close"] < df["close"].shift(1)) &
                (df["volume"] > df["volume"].rolling(10).mean() * 1.1) &
                (df["maxima"] == 1) &
                (df["significant_rise"]) &  # Pullback (rise) filter
                (df["momentum_safe_short"]) &  # Momentum filter
                (df['volume_rising']) &  # New: volume surge
                (~volatility_high | (df["rsi"] > 75))  # New: volatility filter
            )
            
            short_signal_50_breakdown = (
                (df["close"] < df["[4/8]P"]) &
                (df["close"].shift(1) >= df["[4/8]P"].shift(1)) &
                (df["rsi"] < 50) &  # Relaxed RSI
                (df["volume"] > df["volume"].rolling(15).mean() * 1.1) &
                (df["close"] < df["ema20"]) &
                (df["market_score"] < 0.8) &  # No pullback/momentum filter
                (df['adx'] > 20) &  # New: some trend strength
                (df['volume_rising'])  # New: volume confirmation
            )
            
            short_signal_range_short = (
                range_bound &
                (df["high"] >= df["[6/8]P"]) &
                (df["close"] < df["[6/8]P"]) &
                (df["rsi"] > 50) &  # Relaxed RSI
                (df["close"] < df["close"].shift(1)) &
                (df["maxima"] == 1) &
                (df["volume"] > df["volume"].rolling(10).mean() * 1.1) &
                (df["significant_rise"]) &  # Pullback filter
                (df['volume_rising']) &  # New: volume surge
                (~volatility_high)  # New: skip in high vol
            )
            
            short_signal_trend_continuation = (
                bearish_mml &
                (df["close"] < df["[3/8]P"]) &
                (df["rsi"].between(35, 60)) &  # Wider RSI range
                (df["close"] < df["ema20"]) &
                (df["volume"] > df["volume"].rolling(15).mean() * 1.1) &  # No pullback/momentum filter
                (df['adx'] > 25) &  # New: strong trend required
                (df['minus_di'] > df['plus_di']) &  # New: bearish direction
                (df["close"] < df["[4/8]P"]) &  # New: below MML midpoint
                (df['volume_rising']) &  # New: volume confirmation
                (~volatility_high)  # New: skip in high vol
            )
            
            short_signal_extreme_overbought = (
                (df["high"] >= df["[7/8]P"]) &
                (df["close"] < df["[7/8]P"]) &
                (df["rsi"] > 65) &
                (df["volume"] > df["volume"].rolling(20).mean() * 1.2) &
                (df["close"] < df["close"].shift(1)) &  # New: starting to reject
                (df["maxima"] == 1) &
                (df["significant_rise"]) &
                (df["momentum_safe_short"]) &  # Both filters
                (df['volume_rising']) &  # New: volume surge
                (~volatility_high | (df["rsi"] > 80))  # New: allow in high vol only if very overbought
            )
            
            any_short_signal = (
                short_signal_bearish_breakdown |
                short_signal_resistance_reject |
                short_signal_50_breakdown |
                short_signal_range_short |
                short_signal_trend_continuation |
                short_signal_extreme_overbought
            )
        else:
            any_short_signal = pd.Series([False] * len(df), index=df.index)
        
        # Apply market regime filter
        if 'market_regime' in df.columns:
            regime = df['market_regime'].iloc[-1]
            if regime == 'bear_market':
                any_long_signal = any_long_signal & (df['rsi'] < 35)
                any_short_signal = any_short_signal & (df['rsi'] > 45)  # Looser for shorts
            elif regime == 'bull_run':
                any_long_signal = any_long_signal & (df['rsi'] > 40)
                any_short_signal = any_short_signal & (df['rsi'] > 65)  # Stricter for shorts
            logger.info(f"{pair} 🏛️ Market regime: {regime}")
        
        # ===========================================
        # RE-ENTRY AND MARKET FILTERS (Unchanged)
        # ===========================================
        
        if self.enable_reentry.value:
            try:
                current_time = df.index[-1] if not df.empty else datetime.now()
                recent_exits = self.get_recent_exits(pair, current_time)
                
                logger.info(f"{pair} 🔄 Re-entry check: {len(recent_exits)} recent exits")
                
                reentry_long_signals = pd.Series([False] * len(df), index=df.index)
                reentry_short_signals = pd.Series([False] * len(df), index=df.index)
                
                market_score = df['market_score'].fillna(0.5)
                market_breadth = df.get('market_breadth', pd.Series([0.5] * len(df), index=df.index)).fillna(0.5)
                btc_ok = df.get('btc_correlation_ok', pd.Series([True] * len(df), index=df.index)).fillna(True)
                
                for exit_time, direction, reason, profit in recent_exits:
                    try:
                        time_since_exit = self.safe_time_diff(current_time, exit_time)
                        if pair in self.reentry_count:
                            current_count = self.reentry_count[pair].get(direction, 0)
                            max_reentries = int(self.max_reentries_per_direction.value)
                            if current_count >= max_reentries:
                                logger.info(f"{pair} 🛑 Max {direction} re-entries: {current_count}")
                                continue
                    except Exception as e:
                        logger.warning(f"{pair} Re-entry processing error: {e}, skipping this exit")
                        continue
                    
                    if direction == 'long':
                        if (reason == 'roi' and 
                            profit > 0.015 and 
                            self.quick_reentry_cooldown.value <= time_since_exit <= 30):
                            roi_long_conditions = (
                                (df["rsi"] < 55) &
                                (df["close"] > df["[2/8]P"]) &
                                (df["volume"] > df["volume"].rolling(10).mean()) &
                                (market_score > 0.4) &
                                (market_breadth > 0.3) &
                                btc_ok
                            )
                            reentry_long_signals.iloc[-3:] = reentry_long_signals.iloc[-3:] | roi_long_conditions.iloc[-3:]
                            if roi_long_conditions.iloc[-1]:
                                logger.info(f"{pair} 🎯 LONG ROI re-entry: {profit:.2%} profit")
                        
                        elif (self.reentry_cooldown_minutes.value <= time_since_exit <= 120):
                            original_long_active = (
                                long_signal_mml_breakout |
                                long_signal_support_bounce |
                                long_signal_50_reclaim |
                                long_signal_range_long |
                                long_signal_extreme_oversold
                            )
                            std_long_conditions = (
                                original_long_active &
                                (df["rsi"] < 45) &
                                (df["close"] > df["[1/8]P"]) &
                                (df["volume"] > df["volume"].rolling(10).mean()) &
                                (market_breadth > 0.35) &
                                btc_ok
                            )
                            reentry_long_signals.iloc[-5:] = reentry_long_signals.iloc[-5:] | std_long_conditions.iloc[-5:]
                            if std_long_conditions.iloc[-1]:
                                logger.info(f"{pair} 🔄 LONG standard re-entry ({time_since_exit:.0f}m)")
                    
                    elif direction == 'short' and self.can_short:
                        if (reason == 'roi' and 
                            profit > 0.015 and 
                            self.quick_reentry_cooldown.value <= time_since_exit <= 30):
                            roi_short_conditions = (
                                (df["rsi"] > 45) &
                                (df["close"] < df["[6/8]P"]) &
                                (df["volume"] > df["volume"].rolling(10).mean()) &
                                (market_score < 0.6) &
                                (market_breadth < 0.7) &
                                btc_ok
                            )
                            reentry_short_signals.iloc[-3:] = reentry_short_signals.iloc[-3:] | roi_short_conditions.iloc[-3:]
                            if roi_short_conditions.iloc[-1]:
                                logger.info(f"{pair} 🎯 SHORT ROI re-entry: {profit:.2%} profit")
                        
                        elif (self.reentry_cooldown_minutes.value <= time_since_exit <= 120):
                            original_short_active = (
                                short_signal_bearish_breakdown |
                                short_signal_resistance_reject |
                                short_signal_50_breakdown |
                                short_signal_range_short |
                                short_signal_extreme_overbought
                            )
                            std_short_conditions = (
                                original_short_active &
                                (df["rsi"] > 55) &
                                (df["close"] < df["[7/8]P"]) &
                                (df["volume"] > df["volume"].rolling(10).mean()) &
                                (market_breadth < 0.65) &
                                btc_ok
                            )
                            reentry_short_signals.iloc[-5:] = reentry_short_signals.iloc[-5:] | std_short_conditions.iloc[-5:]
                            if std_short_conditions.iloc[-1]:
                                logger.info(f"{pair} 🔄 SHORT standard re-entry ({time_since_exit:.0f}m)")
                
                any_long_signal = any_long_signal | reentry_long_signals
                any_short_signal = any_short_signal | reentry_short_signals
                
                if pair not in self.reentry_count:
                    self.reentry_count[pair] = {'long': 0, 'short': 0}
                
                reentry_long_count = reentry_long_signals.sum()
                reentry_short_count = reentry_short_signals.sum()
                
                if reentry_long_count > 0:
                    self.reentry_count[pair]['long'] += 1
                    logger.info(f"{pair} 📈 LONG re-entry #{self.reentry_count[pair]['long']} ({reentry_long_count} signals)")
                
                if reentry_short_count > 0:
                    self.reentry_count[pair]['short'] += 1
                    logger.info(f"{pair} 📉 SHORT re-entry #{self.reentry_count[pair]['short']} ({reentry_short_count} signals)")
            
            except Exception as e:
                logger.error(f"{pair} 💥 Re-entry error: {e}")
        
        # Apply market breadth filter
        if 'market_breadth' in df.columns:
            breadth = df['market_breadth'].fillna(0.5)
            breadth_long_ok = breadth > 0.25
            breadth_short_ok = breadth < 0.75
            any_long_signal = any_long_signal & breadth_long_ok
            any_short_signal = any_short_signal & breadth_short_ok
            logger.info(f"{pair} 📊 Market breadth: {breadth.iloc[-1]:.1%}")
        

        # Apply BTC correlation filter
        if 'btc_correlation_ok' in df.columns:
            btc_ok = df['btc_correlation_ok'].fillna(True).astype(bool)
            # any_long_signal = any_long_signal & btc_ok  # Commented out to disable BTC correlation filter
            # any_short_signal = any_short_signal & btc_ok  # Commented out to disable BTC correlation filter
            logger.info(f"{pair} ₿ BTC correlation: {'✅' if btc_ok.iloc[-1] else '❌'}")
        
        # Apply market score filter
        if 'market_score' in df.columns:
            score = df['market_score'].fillna(0.5)
            score_long_ok = score > 0.20
            score_short_ok = score < 0.80
            any_long_signal = any_long_signal & score_long_ok
            any_short_signal = any_short_signal & score_short_ok
            logger.info(f"{pair} 📈 Market score: {score.iloc[-1]:.1%}")
        
        # ===========================================
        # FINAL SIGNAL ASSIGNMENT
        # ===========================================
        
        df.loc[any_long_signal, "enter_long"] = 1
        if self.can_short:
            df.loc[any_short_signal, "enter_short"] = 1
        
        # Long signal tags
        df.loc[any_long_signal & long_signal_mml_breakout, "enter_tag"] = "MML_Breakout_75"
        df.loc[any_long_signal & long_signal_support_bounce & (df["enter_tag"] == ""), "enter_tag"] = "Support_Bounce"
        df.loc[any_long_signal & long_signal_50_reclaim & (df["enter_tag"] == ""), "enter_tag"] = "MML_50_Reclaim"
        df.loc[any_long_signal & long_signal_range_long & (df["enter_tag"] == ""), "enter_tag"] = "Range_Long"
        df.loc[any_long_signal & long_signal_trend_continuation & (df["enter_tag"] == ""), "enter_tag"] = "Trend_Cont"
        df.loc[any_long_signal & long_signal_extreme_oversold & (df["enter_tag"] == ""), "enter_tag"] = "Extreme_Oversold"
        
        # Short signal tags
        if self.can_short:
            df.loc[any_short_signal & short_signal_bearish_breakdown, "enter_tag"] = "Bear_Breakdown_25"
            df.loc[any_short_signal & short_signal_resistance_reject & (df["enter_tag"] == ""), "enter_tag"] = "Resistance_Reject"
            df.loc[any_short_signal & short_signal_50_breakdown & (df["enter_tag"] == ""), "enter_tag"] = "MML_50_Breakdown"
            df.loc[any_short_signal & short_signal_range_short & (df["enter_tag"] == ""), "enter_tag"] = "Range_Short"
            df.loc[any_short_signal & short_signal_trend_continuation & (df["enter_tag"] == ""), "enter_tag"] = "Bear_Trend_Cont"
            df.loc[any_short_signal & short_signal_extreme_overbought & (df["enter_tag"] == ""), "enter_tag"] = "Extreme_Overbought"
        
        # Add re-entry suffix
        if self.enable_reentry.value and hasattr(self, 'reentry_count') and pair in self.reentry_count:
            total_reentries = self.reentry_count[pair]['long'] + self.reentry_count[pair]['short']
            if total_reentries > 0:
                reentry_suffix = f"_RE{total_reentries}"
                mask = df["enter_tag"] != ""
                df.loc[mask, "enter_tag"] = df.loc[mask, "enter_tag"] + reentry_suffix
        
        # Debug logging
        logger.info(f"{pair} 🎯 FINAL SIGNALS: Long={any_long_signal.sum()}, Short={any_short_signal.sum()}")
        
        # ===========================================
        # 🔧 FINAL VALIDATION & STATISTICS
        # ===========================================
        
        # Count different signal types for analysis
        final_long_count = any_long_signal.sum()
        final_short_count = any_short_signal.sum()

        if final_long_count > 0:
            signal_breakdown = {
                'support_bounce': long_signal_support_bounce.sum(),
                '50_reclaim': long_signal_50_reclaim.sum(),
                'range_long': long_signal_range_long.sum(),
                'trend_continuation': long_signal_trend_continuation.sum(),
                'extreme_oversold': long_signal_extreme_oversold.sum(),
                # 'pure_reversal': long_signal_pure_reversal.sum()  # Commented out: undefined
            }
            
            logger.info(f"{pair} 📋 LONG SIGNAL BREAKDOWN:")
            for signal_type, count in signal_breakdown.items():
                if count > 0:
                    logger.info(f"   {signal_type}: {count}")

        if final_short_count > 0 and self.can_short:
            short_breakdown = {
                'bearish_breakdown': short_signal_bearish_breakdown.sum(),
                'resistance_reject': short_signal_resistance_reject.sum(),
                '50_breakdown': short_signal_50_breakdown.sum(),
                'range_short': short_signal_range_short.sum(),
                'trend_continuation': short_signal_trend_continuation.sum(),
                'extreme_overbought': short_signal_extreme_overbought.sum(),
                # 'pure_reversal': short_signal_pure_reversal.sum()  # Commented out: undefined
            }
            
            logger.info(f"{pair} 📋 SHORT SIGNAL BREAKDOWN:")
            for signal_type, count in short_breakdown.items():
                if count > 0:
                    logger.info(f"   {signal_type}: {count}")

        # Final safety check - ensure no NaN values in signals
        df["enter_long"] = df["enter_long"].fillna(0).astype(int)
        df["enter_short"] = df["enter_short"].fillna(0).astype(int)
        df["enter_tag"] = df["enter_tag"].fillna("")
        
        return df


    def calculate_mml_signal_strengths(self, df: pd.DataFrame, bullish_mml, bearish_mml,
                                     mml_support_bounce, mml_resistance_reject, range_bound) -> pd.DataFrame:
        """
        📊 Calculate signal strength for your MML signals
        """
        
        df['long_signal_strength'] = 0.0
        df['short_signal_strength'] = 0.0
        
        # ===========================================
        # LONG SIGNAL STRENGTH COMPONENTS
        # ===========================================
        
        long_strength_components = []
        
        # MML structure strength (40% weight)
        mml_long_strength = (
            bullish_mml.astype(float) * 0.4 +  # Strong bullish structure
            mml_support_bounce.astype(float) * 0.3 +  # Support bounce
            ((df["close"] > df["[4/8]P"]).astype(float) * 0.2) +  # Above midpoint
            ((df["close"] > df["[6/8]P"]).astype(float) * 0.1)    # Above 75%
        )
        long_strength_components.append(mml_long_strength.clip(0, 0.4))
        
        # RSI strength (20% weight)
        rsi_long_strength = (
            ((df["rsi"] < 30).astype(float) * 0.2) +  # Oversold
            ((df["rsi"] < 40).astype(float) * 0.15) +  # Getting oversold
            ((df["rsi"] > df["rsi"].shift(1)).astype(float) * 0.05)  # RSI improving
        ).clip(0, 0.2)
        long_strength_components.append(rsi_long_strength)
        
        # Volume strength (20% weight)
        volume_long_strength = (
            ((df["volume"] > df["volume"].rolling(20).mean() * 1.5).astype(float) * 0.2) +
            ((df["volume"] > df["volume"].rolling(10).mean()).astype(float) * 0.1)
        ).clip(0, 0.2)
        long_strength_components.append(volume_long_strength)
        
        # Momentum strength (20% weight)
        momentum_long_strength = (
            ((df["close"] > df["close"].shift(1)).astype(float) * 0.1) +
            ((df["close"] > df["open"]).astype(float) * 0.1)  # Green candle
        ).clip(0, 0.2)
        long_strength_components.append(momentum_long_strength)
        
        # ===========================================
        # SHORT SIGNAL STRENGTH COMPONENTS
        # ===========================================
        
        short_strength_components = []
        
        # MML structure strength (40% weight)
        mml_short_strength = (
            bearish_mml.astype(float) * 0.4 +  # Strong bearish structure
            mml_resistance_reject.astype(float) * 0.3 +  # Resistance rejection
            ((df["close"] < df["[4/8]P"]).astype(float) * 0.2) +  # Below midpoint
            ((df["close"] < df["[2/8]P"]).astype(float) * 0.1)    # Below 25%
        )
        short_strength_components.append(mml_short_strength.clip(0, 0.4))
        
        # RSI strength (20% weight)
        rsi_short_strength = (
            ((df["rsi"] > 70).astype(float) * 0.2) +  # Overbought
            ((df["rsi"] > 60).astype(float) * 0.15) +  # Getting overbought
            ((df["rsi"] < df["rsi"].shift(1)).astype(float) * 0.05)  # RSI declining
        ).clip(0, 0.2)
        short_strength_components.append(rsi_short_strength)
        
        # Volume strength (20% weight) - same as long
        short_strength_components.append(volume_long_strength)
        
        # Momentum strength (20% weight)
        momentum_short_strength = (
            ((df["close"] < df["close"].shift(1)).astype(float) * 0.1) +
            ((df["close"] < df["open"]).astype(float) * 0.1)  # Red candle
        ).clip(0, 0.2)
        short_strength_components.append(momentum_short_strength)
        
        # ===========================================
        # COMBINE STRENGTH COMPONENTS
        # ===========================================
        
        df['long_signal_strength'] = sum(long_strength_components).clip(0, 1)
        df['short_signal_strength'] = sum(short_strength_components).clip(0, 1)
        
        return df
    
    def apply_mml_reentry_logic(self, df: pd.DataFrame, pair: str, 
                               any_long_signal: pd.Series, any_short_signal: pd.Series) -> tuple:
        current_time = pd.Timestamp.now() if df.empty else pd.Timestamp(df.index[-1])
        if pair not in self.reentry_count:
            self.reentry_count[pair] = {'long': 0, 'short': 0}
        
        recent_exits = self.get_recent_exits(pair, current_time)
        reentry_long_mask = pd.Series([False] * len(df), index=df.index)
        reentry_short_mask = pd.Series([False] * len(df), index=df.index)

        for i in range(len(df)):
            current_candle_time = df.index[i]
            for direction in ['long', 'short']:
                if (self.should_attempt_reentry(pair, direction, current_candle_time, recent_exits) and 
                    self.reentry_count[pair][direction] < self.max_reentries_per_direction.value):
                    favorable = (df.iloc[i]['close'] > df.iloc[i]['[2/8]P'] if direction == 'long' 
                               else df.iloc[i]['close'] < df.iloc[i]['[6/8]P'])
                    strength_threshold = df.iloc[i][f'{direction}_signal_strength'] >= self.reentry_signal_strength_threshold.value
                    
                    if favorable and strength_threshold:
                        conditions = (
                            (df.iloc[i]['rsi'] < 45 if direction == 'long' else df.iloc[i]['rsi'] > 55) &
                            (df.iloc[i]['volume'] > df.iloc[i]['volume_avg_10']) &
                            (df.iloc[i]['close'] > df.iloc[i]['close'].shift(1) if direction == 'long' else 
                             df.iloc[i]['close'] < df.iloc[i]['close'].shift(1) if i > 0 else True)
                        )
                        if conditions:
                            if direction == 'long':
                                reentry_long_mask.iloc[i] = True
                                self.reentry_count[pair]['long'] += 1
                                logger.info(f"{pair} 🔄 STANDARD {direction.upper()} RE-ENTRY #{self.reentry_count[pair]['long']}")
                            else:
                                reentry_short_mask.iloc[i] = True
                                self.reentry_count[pair]['short'] += 1
                                logger.info(f"{pair} 🔄 STANDARD {direction.upper()} RE-ENTRY #{self.reentry_count[pair]['short']}")

        return any_long_signal | reentry_long_mask, any_short_signal | reentry_short_mask
    
    def set_enhanced_entry_tags(self, df: pd.DataFrame, final_long_signal, final_short_signal,
                               long_signal_mml_breakout, long_signal_support_bounce,
                               long_signal_50_reclaim, long_signal_sextrema,
                               long_signal_rolling_MinH2, short_signal_bearish_breakdown,
                               short_signal_confirmed_max_entry, short_signal_rolling_MaxH2_Entry,
                               pair: str) -> pd.DataFrame:
        """
        🏷️ Set enhanced entry tags that include re-entry information
        """
        
        # Your existing tag logic with re-entry enhancement
        df.loc[final_long_signal & long_signal_mml_breakout, "enter_tag"] = "MML_Bullish_Breakout"
        df.loc[final_long_signal & long_signal_support_bounce & (df["enter_tag"] == ""), "enter_tag"] = "MML_Support_Bounce"
        df.loc[final_long_signal & long_signal_50_reclaim & (df["enter_tag"] == ""), "enter_tag"] = "MML_50_Reclaim"
        df.loc[final_long_signal & long_signal_rolling_MinH2 & (df["enter_tag"] == ""), "enter_tag"] = "Rolling_MinH2"
        df.loc[final_long_signal & long_signal_sextrema & (df["enter_tag"] == ""), "enter_tag"] = "Sextrema"
        
        if self.can_short:
            df.loc[final_short_signal & short_signal_bearish_breakdown, "enter_tag"] = "MML_Bearish_Breakdown"
            df.loc[final_short_signal & short_signal_rolling_MaxH2_Entry & (df["enter_tag"] == ""), "enter_tag"] = "Rolling_MaxH2_Short"
            df.loc[final_short_signal & short_signal_confirmed_max_entry & (df["enter_tag"] == ""), "enter_tag"] = "Confirmed_Max_Entry_Short"
        
        # Add re-entry indicators to tags
        if pair in self.reentry_count:
            reentry_suffix = ""
            if self.reentry_count[pair]['long'] > 0 or self.reentry_count[pair]['short'] > 0:
                total_reentries = self.reentry_count[pair]['long'] + self.reentry_count[pair]['short']
                reentry_suffix = f"_RE{total_reentries}"
            
            # Append re-entry info to existing tags
            df.loc[df["enter_tag"] != "", "enter_tag"] = df.loc[df["enter_tag"] != "", "enter_tag"] + reentry_suffix
        
        return df
    
    # ===========================================
    # 🛠️ HELPER FUNCTIONS (same as before)
    # ===========================================
    
    def should_attempt_reentry(self, pair: str, direction: str, current_time: datetime, recent_exits: list) -> bool:
        """Check if we should attempt re-entry"""
        
        try:
            if self.reentry_count[pair][direction] >= self.max_reentries_per_direction.value:
                return False
            
            recent_exit_in_direction = None
            for exit_time, exit_direction, exit_reason, profit in recent_exits:
                if exit_direction == direction:
                    recent_exit_in_direction = (exit_time, exit_reason)
                    break
            
            if not recent_exit_in_direction:
                return False
            
            exit_time, exit_reason = recent_exit_in_direction
            
            if exit_reason == 'roi' and self.enable_quick_reentry_on_roi.value:
                cooldown = float(self.quick_reentry_cooldown.value)  # Ensure float
            else:
                cooldown = float(self.reentry_cooldown_minutes.value)  # Ensure float
            
            # Use your safe_time_diff function instead
            time_since_exit = self.safe_time_diff(current_time, exit_time)
            
            return time_since_exit >= cooldown
            
        except Exception as e:
            logger.warning(f"Re-entry check error for {pair} {direction}: {e}")
            return False  # Don't attempt re-entry if there's an error
    
    def was_recent_roi_exit(self, pair: str, direction: str, current_time: datetime, recent_exits: list) -> bool:
        """Check if there was a recent ROI exit"""
        
        try:
            for exit_time, exit_direction, exit_reason, profit in recent_exits:
                if (exit_direction == direction and 
                    exit_reason == 'roi'):
                    
                    # Use safe_time_diff instead of manual calculation
                    time_since_exit = self.safe_time_diff(current_time, exit_time)
                    
                    if time_since_exit <= float(self.quick_reentry_cooldown.value):
                        return True
            return False
            
        except Exception as e:
            logger.warning(f"Recent ROI check error for {pair} {direction}: {e}")
            return False
    
    def get_recent_exits(self, pair: str, current_time: datetime) -> list:
        """Get recent exits for this pair"""
        
        try:
            if not hasattr(self, 'recent_exits') or pair not in self.recent_exits:
                return []
            
            # Safe cutoff time calculation
            try:
                cutoff_time = pd.Timestamp(current_time) - pd.Timedelta(hours=1)
            except:
                cutoff_time = pd.Timestamp.now() - pd.Timedelta(hours=1)
            
            recent_exits = []
            for exit_time, direction, reason, profit in self.recent_exits[pair]:
                try:
                    # Safe time comparison using safe_time_diff
                    time_since_exit = self.safe_time_diff(current_time, exit_time)
                    
                    # If less than 60 minutes ago, include it
                    if time_since_exit <= 60:  # 60 minutes
                        recent_exits.append((exit_time, direction, reason, profit))
                        
                except Exception as e:
                    logger.warning(f"Exit time comparison error: {e}")
                    continue
            
            return recent_exits
            
        except Exception as e:
            logger.warning(f"Get recent exits error for {pair}: {e}")
            return []
    
    def populate_exit_trend(self, df: pd.DataFrame, metadata: dict) -> pd.DataFrame:
        """
        UNIFIED EXIT SYSTEM - Enhanced with stricter momentum, ADX for trend weakness, and volatility checks
        Blends custom MML exits with simple opposite signals for better reliability
        """
        # ===========================================
        # INITIALIZE EXIT COLUMNS
        # ===========================================
        df["exit_long"] = 0
        df["exit_short"] = 0
        df["exit_tag"] = ""
        
        # ===========================================
        # PRELIMINARY CALCULATIONS (Enhanced)
        # ===========================================
        
        # Ensure ADX is available (from entries or add here)
        if 'adx' not in df.columns:
            df['adx'] = ta.ADX(df['high'], df['low'], df['close'], timeperiod=14)
        
        # Volume fading check (new: for exhaustion exits)
        df['volume_fading'] = (df['volume'] < df['volume'].rolling(20).mean() * 0.8) & \
                              (df['volume'] < df['volume'].shift(1))
        
        # Volatility check (new: for emergency)
        volatility_high = df['atr'] / df['close'] > 0.03
        
        # Existing MML structures (bullish_mml, bearish_mml, at_resistance, at_support) - assume from entries or add if needed
        bullish_mml = (
            (df["close"] > df["[6/8]P"]) |
            ((df["close"] > df["[4/8]P"]) & (df["close"].shift(5) < df["[4/8]P"].shift(5)))
        )
        bearish_mml = (
            (df["close"] < df["[2/8]P"]) |
            ((df["close"] < df["[4/8]P"]) & (df["close"].shift(5) > df["[4/8]P"].shift(5)))
        )
        at_resistance = (
            (df["high"] >= df["[6/8]P"]) |
            (df["high"] >= df["[7/8]P"]) |
            (df["high"] >= df["[8/8]P"])
        )
        at_support = (
            (df["low"] <= df["[2/8]P"]) |
            (df["low"] <= df["[1/8]P"]) |
            (df["low"] <= df["[0/8]P"])
        )
        
        # ===========================================
        # CHOOSE EXIT SYSTEM
        # ===========================================
        if self.use_custom_exits_advanced:
            # Use enhanced custom MML-based exits
            return self._populate_custom_exits_advanced(df, metadata)
        else:
            # Use simple opposite-signal exits (with enhancements)
            return self._populate_simple_exits(df, metadata)
    
    def _populate_custom_exits_advanced(self, df: pd.DataFrame, metadata: dict) -> pd.DataFrame:
        """
        ADVANCED MML-BASED EXIT SYSTEM - Enhanced for better trend handling
        """
        pair = metadata['pair']
        # Ensure ADX is available (from entries or add here)
        if 'adx' not in df.columns:
            df['adx'] = ta.ADX(df['high'], df['low'], df['close'], timeperiod=14)
        
        # Volume fading check (new: for exhaustion exits)
        df['volume_fading'] = (df['volume'] < df['volume'].rolling(20).mean() * 0.8) & \
                              (df['volume'] < df['volume'].shift(1))
        
        # Volatility check (new: for emergency)
        volatility_high = df['atr'] / df['close'] > 0.03

        # Add missing MML structure calculations (moved here to ensure definition)
        bullish_mml = (
            (df["close"] > df["[6/8]P"]) |
            ((df["close"] > df["[4/8]P"]) & (df["close"].shift(5) < df["[4/8]P"].shift(5)))
        )
        bearish_mml = (
            (df["close"] < df["[2/8]P"]) |
            ((df["close"] < df["[4/8]P"]) & (df["close"].shift(5) > df["[4/8]P"].shift(5)))
        )
        at_resistance = (
            (df["high"] >= df["[6/8]P"]) |
            (df["high"] >= df["[7/8]P"]) |
            (df["high"] >= df["[8/8]P"])
        )
        at_support = (
            (df["low"] <= df["[2/8]P"]) |
            (df["low"] <= df["[1/8]P"]) |
            (df["low"] <= df["[0/8]P"])
        )
        
        # 1. Profit-Taking Exits (tighter trailing)
        atr_adjusted = df["atr"].fillna(0).astype(float) * self.atr_multiplier.value  # Use param (default 2.0)
        trailing_stop_level = df["high"] - atr_adjusted
        long_exit_resistance_profit = (
            at_resistance &
            (df["close"] < df["high"]) &
            (df["rsi"] > 60) &
            (df["maxima"] == 1) &
            (df["volume"] > df["volume"].rolling(10).mean()) &
            (df["close"] < trailing_stop_level) &  # Trailing ATR
            (df["market_score"] > 0.3) &
            (df["consecutive_red"] >= 1) &  # Exit if downtrend starts
            (df['adx'] < 25)  # New: only if trend weakening
        )
        
        long_exit_extreme_overbought = (
            (df["close"] > df["[7/8]P"]) &
            (df["rsi"] > 75) &
            (df["close"] < df["close"].shift(1)) &
            (df["maxima"] == 1) &
            (df['volume_fading'])  # New: volume exhaustion
        )
        
        long_exit_volume_exhaustion = (
            at_resistance &
            (df['volume_fading']) &  # New: stricter fading check
            (df["rsi"] > 70) &
            (df["close"] < df["close"].shift(1)) &
            (~volatility_high)  # New: skip in high vol (false exhaustion)
        )
        
        # 2. Structure Breakdown (stricter)
        long_exit_structure_breakdown = (
            (df["close"] < df["[4/8]P"]) &
            (df["close"].shift(1) >= df["[4/8]P"].shift(1)) &
            bullish_mml.shift(1) &
            (df["close"] < df["[4/8]P"] * 0.995) &
            (df["close"] < df["close"].shift(1)) &
            (df["close"] < df["close"].shift(2)) &
            (df["rsi"] < 50) &
            (df["volume"] > df["volume"].rolling(15).mean() * 1.2) &
            (df["close"] < df["open"]) &
            (df["low"] < df["low"].shift(1)) &
            (df["close"] < df["close"].rolling(3).mean()) &
            (df["atr"] > df["atr"].rolling(10).mean()) &
            (df["market_regime"] != 'bull_run') &
            (df['adx'] < 20)  # New: confirm trend loss
        )
        
        # 3. Momentum Divergence (stricter: 3-candle RSI decline)
        long_exit_momentum_divergence = (
            at_resistance &
            (df["rsi"] < df["rsi"].shift(1)) &
            (df["rsi"].shift(1) < df["rsi"].shift(2)) &
            (df["rsi"] < df["rsi"].shift(3)) &  # New: 3-candle decline
            (df["close"] >= df["close"].shift(1)) &
            (df["maxima"] == 1) &
            (df["rsi"] > 60) &
            (df["consecutive_red"].shift(1) >= 1)  # Potential reversal
        )
        
        # 4. Range Exit (add volume)
        long_exit_range = (
            (df["close"] >= df["[2/8]P"]) &
            (df["close"] <= df["[6/8]P"]) &  # In range
            (df["high"] >= df["[6/8]P"]) &  # HIGH touched 75%
            (df["close"] < df["[6/8]P"] * 0.995) &  # Closed below
            (df["rsi"] > 65) &  # Conservative RSI
            (df["maxima"] == 1) &
            (df["volume"] > df["volume"].rolling(10).mean() * 1.2)  # Volume confirmation
        )
        
        # 5. Emergency Exit (vol-aware)
        long_exit_emergency = (
            (df["close"] < df["[0/8]P"]) &  # At 0%
            (df["rsi"] < 25) &
            (df["volume"] > df["volume"].rolling(20).mean() * 2) &
            (df["close"] < df["close"].shift(1)) &
            (df["close"] < df["close"].shift(2)) &
            (df["close"] < df["open"]) &
            volatility_high  # New: only in high vol dumps
        ) if self.use_emergency_exits else pd.Series([False] * len(df), index=df.index)
        
        # Combine all Long Exit signals
        any_long_exit = (
            long_exit_resistance_profit |
            long_exit_extreme_overbought |
            long_exit_volume_exhaustion |
            long_exit_structure_breakdown |
            long_exit_momentum_divergence |
            long_exit_range |
            long_exit_emergency
        )
        
        # ===========================================
        # SHORT EXIT SIGNALS (Symmetric enhancements)
        # ===========================================
        
        if self.can_short:
            atr_adjusted_short = df["atr"].fillna(0).astype(float) * self.atr_multiplier.value
            trailing_stop_level_short = df["low"] + atr_adjusted_short
            short_exit_support_profit = (
                at_support &
                (df["close"] > df["low"]) &
                (df["rsi"] < 40) &
                (df["minima"] == 1) &
                (df["volume"] > df["volume"].rolling(10).mean()) &
                (df["close"] > trailing_stop_level_short) &  # Trailing ATR
                (df["market_score"] < 0.7) &
                (~(df["consecutive_red"] >= 1)) &
                (df['volume_fading'])  # New: volume exhaustion
            )
            
            short_exit_extreme_oversold = (
                (df["close"] < df["[1/8]P"]) &
                (df["rsi"] < 25) &
                (df["close"] > df["close"].shift(1)) &
                (df["minima"] == 1) &
                (df['volume_fading'])  # New
            )
            
            short_exit_volume_exhaustion = (
                at_support &
                (df['volume_fading']) &
                (df["rsi"] < 30) &
                (df["close"] > df["close"].shift(1)) &
                (~volatility_high)
            )
            
            short_exit_structure_breakout = (
                (df["close"] > df["[4/8]P"]) &
                (df["close"].shift(1) <= df["[4/8]P"].shift(1)) &
                bearish_mml.shift(1) &
                (df["close"] > df["[4/8]P"] * 1.005) &
                (df["close"] > df["close"].shift(1)) &
                (df["close"] > df["close"].shift(2)) &
                (df["rsi"] > 50) &
                (df["volume"] > df["volume"].rolling(15).mean() * 1.2) &
                (df["close"] > df["open"]) &
                (df["high"] > df["high"].shift(1)) &
                (df["atr"] > df["atr"].rolling(10).mean()) &
                (df["market_regime"] != 'bear_market') &
                (df['adx'] < 20)  # New: confirm trend loss
            )
            
            short_exit_momentum_divergence = (
                at_support &
                (df["rsi"] > df["rsi"].shift(1)) &
                (df["rsi"].shift(1) > df["rsi"].shift(2)) &
                (df["rsi"] > df["rsi"].shift(3)) &  # New: 3-candle increase
                (df["close"] <= df["close"].shift(1)) &
                (df["minima"] == 1) &
                (df["rsi"] < 40)
            )
            
            short_exit_range = (
                (df["close"] >= df["[2/8]P"]) &
                (df["close"] <= df["[6/8]P"]) &
                (df["low"] <= df["[2/8]P"]) &
                (df["close"] > df["[2/8]P"] * 1.005) &
                (df["rsi"] < 35) &
                (df["minima"] == 1) &
                (df["volume"] > df["volume"].rolling(10).mean() * 1.2)
            )
            
            short_exit_emergency = (
                (df["close"] > df["[8/8]P"]) &
                (df["rsi"] > 85) &
                (df["volume"] > df["volume"].rolling(20).mean() * 2) &
                (df["close"] > df["close"].shift(1)) &
                (df["close"] > df["close"].shift(2)) &
                (df["close"] > df["open"]) &
                volatility_high  # New: only in high vol pumps
            ) if self.use_emergency_exits else pd.Series([False] * len(df), index=df.index)
            
            any_short_exit = (
                short_exit_support_profit |
                short_exit_extreme_oversold |
                short_exit_volume_exhaustion |
                short_exit_structure_breakout |
                short_exit_momentum_divergence |
                short_exit_range |
                short_exit_emergency
            )
        else:
            any_short_exit = pd.Series([False] * len(df), index=df.index)
        
        # ===========================================
        # COORDINATION WITH ENTRY SIGNALS (Blend with simple)
        # ===========================================
        
        # Exit on opposite entry signals (existing)
        if 'enter_long' in df.columns and 'enter_short' in df.columns:
            df.loc[df['enter_short'] == 1, 'exit_long'] = 1
            df.loc[df['enter_long'] == 1, 'exit_short'] = 1
            
            df.loc[df['exit_long'] == 1, 'exit_tag'] = 'Reversal_Short_Entry'
            if self.can_short:
                df.loc[df['exit_short'] == 1, 'exit_tag'] = 'Reversal_Long_Entry'
        else:
            logger.warning("Missing 'enter_long' or 'enter_short' columns; skipping reversal-based exits")
        
        # Set final exits and tags
        df.loc[any_long_exit, "exit_long"] = 1
        if self.can_short:
            df.loc[any_short_exit, "exit_short"] = 1
        
        # Exit tags (updated for new conditions)
        df.loc[any_long_exit & long_exit_emergency, "exit_tag"] = "MML_Emergency_Long_Exit"
        df.loc[any_long_exit & long_exit_structure_breakdown & (df["exit_tag"] == ""), "exit_tag"] = "MML_Structure_Breakdown"
        df.loc[any_long_exit & long_exit_resistance_profit & (df["exit_tag"] == ""), "exit_tag"] = "MML_Resistance_Profit"
        df.loc[any_long_exit & long_exit_extreme_overbought & (df["exit_tag"] == ""), "exit_tag"] = "MML_Extreme_Overbought"
        df.loc[any_long_exit & long_exit_volume_exhaustion & (df["exit_tag"] == ""), "exit_tag"] = "MML_Volume_Exhaustion_Long"
        df.loc[any_long_exit & long_exit_momentum_divergence & (df["exit_tag"] == ""), "exit_tag"] = "MML_Momentum_Divergence_Long"
        df.loc[any_long_exit & long_exit_range & (df["exit_tag"] == ""), "exit_tag"] = "MML_Range_Exit_Long"
        
        if self.can_short:
            df.loc[any_short_exit & short_exit_emergency, "exit_tag"] = "MML_Emergency_Short_Exit"
            df.loc[any_short_exit & short_exit_structure_breakout & (df["exit_tag"] == ""), "exit_tag"] = "MML_Structure_Breakout"
            df.loc[any_short_exit & short_exit_support_profit & (df["exit_tag"] == ""), "exit_tag"] = "MML_Support_Profit"
            df.loc[any_short_exit & short_exit_extreme_oversold & (df["exit_tag"] == ""), "exit_tag"] = "MML_Extreme_Oversold"
            df.loc[any_short_exit & short_exit_volume_exhaustion & (df["exit_tag"] == ""), "exit_tag"] = "MML_Volume_Exhaustion_Short"
            df.loc[any_short_exit & short_exit_momentum_divergence & (df["exit_tag"] == ""), "exit_tag"] = "MML_Momentum_Divergence_Short"
            df.loc[any_short_exit & short_exit_range & (df["exit_tag"] == ""), "exit_tag"] = "MML_Range_Exit_Short"
        
        logger.info(f"{pair} 🚪 FINAL EXITS: Long={any_long_exit.sum()}, Short={any_short_exit.sum()}")
        
        return df

    def _populate_simple_exits(self, df: pd.DataFrame, metadata: dict) -> pd.DataFrame:
        """
        SIMPLE OPPOSITE SIGNAL EXIT SYSTEM - Enhanced with vol/momentum checks
        """
        # Exit LONG on SHORT signal (existing, add vol confirmation)
        long_exit_on_short = (df["enter_short"] == 1) & df['volume_rising']  # New: confirm with volume
        
        # Exit SHORT on LONG signal
        short_exit_on_long = (df["enter_long"] == 1) & df['volume_rising']
        
        # Emergency exits (if enabled, vol-aware)
        if self.use_emergency_exits:
            emergency_long_exit = (
                (df['rsi'] > 85) &
                (df['volume'] > df['volume'].rolling(20).mean() * 3) &
                (df['close'] < df['open']) &
                (df['close'] < df['low'].shift(1)) &
                volatility_high  # New
            ) | (
                (df.get('structure_break_down', 0) == 1) &
                (df['volume'] > df['volume'].rolling(20).mean() * 2.5) &
                (df['atr'] > df['atr'].rolling(20).mean() * 2)
            )
            
            emergency_short_exit = (
                (df['rsi'] < 15) &
                (df['volume'] > df['volume'].rolling(20).mean() * 3) &
                (df['close'] > df['open']) &
                (df['close'] > df['high'].shift(1)) &
                volatility_high  # New
            ) | (
                (df.get('structure_break_up', 0) == 1) &
                (df['volume'] > df['volume'].rolling(20).mean() * 2.5) &
                (df['atr'] > df['atr'].rolling(20).mean() * 2)
            )
        else:
            emergency_long_exit = pd.Series([False] * len(df), index=df.index)
            emergency_short_exit = pd.Series([False] * len(df), index=df.index)
        
        # Combine
        df.loc[long_exit_on_short | emergency_long_exit, 'exit_long'] = 1
        df.loc[short_exit_on_long | emergency_short_exit, 'exit_short'] = 1
        
        # Debugging (existing)
        if metadata['pair'] in ['BTC/USDT:USDT', 'ETH/USDT:USDT']:
            recent_exits = df['exit_long'].tail(5).sum() + df['exit_short'].tail(5).sum()
            if recent_exits > 0:
                exit_tag = df['exit_tag'].iloc[-1]
                logger.info(f"{metadata['pair']} EXIT SIGNAL - Tag: {exit_tag}")
                logger.info(f"  Exit System: {'Custom MML' if self.use_custom_exits_advanced else 'Simple Opposite'}")
                logger.info(f"  RSI: {df['rsi'].iloc[-1]:.1f}")
        
        return df

    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:
        
        current_profit_ratio = trade.calc_profit_ratio(rate)
        time_in_trade = (current_time - trade.open_date_utc).total_seconds() / 3600
        
        logger.warning(f"🚪 {pair} EXIT REQUEST: {exit_reason}")
        logger.warning(f"   💰 Profit: {current_profit_ratio:.4f} ({current_profit_ratio*100:.2f}%)")
        logger.warning(f"   ⏰ Time in trade: {time_in_trade:.1f} hours")
        
        # 🛑 BLOCK FORCE EXITS
        if exit_reason in ["force_exit", "force_sell", "emergency_exit"]:
            logger.warning(f"{pair} 🛑 BLOCKED FORCE EXIT: {exit_reason} (Profit: {current_profit_ratio:.2%})")
            return False
        
        # 🛑 BLOCK PROTECTION EXITS
        if exit_reason in ["timeout", "protection", "cooldown"]:
            logger.warning(f"{pair} 🛑 BLOCKED PROTECTION EXIT: {exit_reason}")
            return False
        
        # ✅ BLOCK TRAILING STOPS (NEW!)
        if exit_reason in ["trailing_stop_loss", "trailing_stop"]:
            logger.warning(f"{pair} ✅ TRAILING STOP: {exit_reason} (Profit: {current_profit_ratio:.2%})")
            return True
        
        # ✅ ALLOW ROI EXITS
        if exit_reason == "roi":
            logger.info(f"{pair} ✅ ROI EXIT: {current_profit_ratio:.2%} after {time_in_trade:.1f}h")
            return True
        
        # ✅ ALLOW OTHER LEGITIMATE EXITS
        if exit_reason in ["stop_loss", "exit_signal", "sell_signal", "custom_exit"]:
            logger.info(f"{pair} ✅ EXIT: {exit_reason} ({current_profit_ratio:.2%})")
            return True
        
        # ✅ DEFAULT ALLOW (for any other exit reasons)
        logger.info(f"{pair} ✅ FINAL CHECK PASSED: Profit {current_profit_ratio:.2%}")
        return True
    # 🔍 ADD THIS METHOD HERE (anywhere in the class)
    
    def should_exit_early_warning(self, pair: str, trade: Trade, current_time: datetime,
                                  current_rate: float, current_profit: float) -> bool:
        """
        ⚠️ EARLY WARNING: Exit before stop loss if conditions deteriorate
        """
        try:
            dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe)
            if dataframe.empty:
                return False
            
            last_candle = dataframe.iloc[-1]
            time_in_trade = (current_time - trade.open_date_utc).total_seconds() / 3600
            
            # Don't exit too early
            if time_in_trade < 0.25:  # Give trade at least 15 minutes
                return False
            
            # Critical deterioration signals
            rsi = last_candle.get('rsi', 50)
            
            if trade.is_short:
                # Short position warning signs
                warning_signals = (
                    (rsi < 30 and current_profit < -0.02) or  # RSI turning bullish while losing
                    (last_candle.get('close', 0) > last_candle.get('[6/8]P', 0)) or  # Breaking above 75%
                    (current_profit < -0.04 and time_in_trade > 2)  # Deep loss after 2h
                )
            else:
                # Long position warning signs  
                warning_signals = (
                    (rsi > 70 and current_profit < -0.02) or  # RSI turning bearish while losing
                    (last_candle.get('close', 0) < last_candle.get('[2/8]P', 0)) or  # Breaking below 25%
                    (current_profit < -0.04 and time_in_trade > 2)  # Deep loss after 2h
                )
            
            if warning_signals:
                logger.warning(f"⚠️ {pair} EARLY WARNING: Deteriorating conditions detected")
                return True
                
            return False
            
        except Exception as e:
            logger.error(f"Early warning error for {pair}: {e}")
            return False
            


    def validate_backtest_conditions(self, dataframe: pd.DataFrame, metadata: dict) -> None:
        """
        📊 VALIDATION: Check if backtest conditions match live trading
        """
        
        pair = metadata['pair']
        
        # Check BTC correlation status
        if 'btc_status' in dataframe.columns:
            btc_status_counts = dataframe['btc_status'].value_counts()
            total_candles = len(dataframe)
            
            logger.warning(f"{pair} 📊 BACKTEST VALIDATION REPORT:")
            logger.warning(f"   Total candles: {total_candles}")
            
            for status, count in btc_status_counts.items():
                percentage = (count / total_candles) * 100
                logger.warning(f"   BTC Status '{status}': {count} candles ({percentage:.1f}%)")
            
            # Alert if significant portion has no BTC data
            no_data_percentage = btc_status_counts.get('NO_DATA', 0) / total_candles * 100
            if no_data_percentage > 10:
                logger.error(f"{pair} 🚨 WARNING: {no_data_percentage:.1f}% of backtest has no BTC data!")
                logger.error(f"{pair} 🚨 BACKTEST RESULTS MAY NOT MATCH LIVE TRADING!")
            
            # Count signal reduction due to BTC filter
            if 'btc_correlation_ok' in df.columns:
                btc_ok = df['btc_correlation_ok'].fillna(True).astype(bool)
                # any_long_signal = any_long_signal & btc_ok  # Commented out to disable BTC correlation filter
                # any_short_signal = any_short_signal & btc_ok  # Commented out to disable BTC correlation filter
                logger.info(f"{pair} ₿ BTC correlation: {'✅' if btc_ok.iloc[-1] else '❌'}")
        
        else:
            logger.error(f"{pair} 🚨 NO BTC CORRELATION DATA IN BACKTEST!")
            logger.error(f"{pair} 🚨 BACKTEST IS DEFINITELY NOT MATCHING LIVE CONDITIONS!")

    def custom_exit(self, pair: str, trade: Trade, current_time: datetime, 
                    current_rate: float, current_profit: float, **kwargs) -> Optional[str]:
        """
        🚪 Smart exit logic to prevent force exits
        """
        
        # Calculate how long trade has been running
        trade_duration_hours = (current_time - trade.open_date_utc).total_seconds() / 3600
        
        # Get entry tag for smart handling
        entry_tag = getattr(trade, 'enter_tag', '') or ''
        
        # 🎯 PREVENT LONG-RUNNING TRADES THAT BECOME FORCE EXITS
        
        # MML_Breakout_75 - these are getting force exited at -33%
        if 'MML_Breakout_75' in entry_tag:
            # Exit after 7 days if still negative to prevent worse force exits
            if trade_duration_hours > (7 * 24) and current_profit < -0.05:  # -5%
                logger.info(f"{pair} 🚪 Smart exit MML_Breakout_75: {current_profit:.2%} after {trade_duration_hours/24:.1f} days")
                return "smart_exit_mml75"
        
        # Bear_Breakdown_25 - these are better but still getting force exited
        elif 'Bear_Breakdown_25' in entry_tag:
            # Exit after 14 days if still negative
            if trade_duration_hours > (14 * 24) and current_profit < -0.02:  # -2%
                logger.info(f"{pair} 🚪 Smart exit Bear_Breakdown_25: {current_profit:.2%} after {trade_duration_hours/24:.1f} days")
                return "smart_exit_bear25"
        
        # General rule for any trade running over 21 days
        if trade_duration_hours > (21 * 24):
            logger.info(f"{pair} 🚪 Smart exit long trade: {current_profit:.2%} after {trade_duration_hours/24:.1f} days")
            return "smart_exit_timeout"
        
        return None

    def bot_loop_start(self, **kwargs) -> None:
        """
        🔍 Log any trades that might be candidates for force exit
        """
        try:
            trades = Trade.get_open_trades()
            current_time = datetime.now()
            
            for trade in trades:
                time_in_trade = (current_time - trade.open_date_utc).total_seconds() / 3600
                current_profit = trade.calc_profit_ratio(trade.close_rate) if trade.close_rate else 0
                
                # Warn about very old trades (might become force exits)
                if time_in_trade > 48:  # 2+ days
                    logger.warning(f"⚠️ OLD TRADE {trade.pair}: {time_in_trade:.1f}h old, "
                                 f"Profit: {current_profit:.2%}")
                    
                # Warn about large losses (might become force exits)  
                if current_profit < -0.10:  # >10% loss
                    logger.warning(f"⚠️ LARGE LOSS {trade.pair}: {current_profit:.2%}, "
                                 f"Time: {time_in_trade:.1f}h")
                    
        except Exception as e:
            # Don't let debugging break the bot
            logger.debug(f"bot_loop_start debug error: {e}")