# source: https://raw.githubusercontent.com/vigoferrel/qbtc-unified/ef7ffc8f45cc7ab041884cd0c08a2043acc1f0cb/freqtrade_qbtc/strategies/QBTCOptimal.py
"""
Github_vigoferrel_qbtc_unified__QBTCOptimal__20251009_202533 - Estrategia Crypto Optimizada para Máximo Rendimiento
==================================================================

Filosofía: Capturar la volatilidad del crypto con múltiples señales,
risk management agresivo y frequency optimizada.

Target: >60% win rate, >5% profit semanal, profit factor >2.0

Creado: 2025-09-10
Autor: QBTC Trading System - Optimal Version
"""

import talib.abstract as ta
import pandas as pd
import numpy as np
from pandas import DataFrame
from freqtrade.strategy.interface import IStrategy
from freqtrade.strategy import DecimalParameter, IntParameter, CategoricalParameter
import logging
from datetime import datetime

class Github_vigoferrel_qbtc_unified__QBTCOptimal__20251009_202533(IStrategy):
    
    # Metadatos optimizados para crypto
    INTERFACE_VERSION = 3
    timeframe = '5m'  # Más oportunidades
    can_short = False
    stoploss = -0.03  # Stop loss más amplio para crypto volatilidad
    
    # ROI optimizado para capturar ganancias rápidamente
    minimal_roi = {
        "0": 0.06,     # 6% inmediato para momentum fuerte
        "10": 0.04,    # 4% después de 10min
        "30": 0.025,   # 2.5% después de 30min
        "90": 0.02,    # 2% después de 1.5h
        "300": 0.01    # 1% después de 5h
    }
    
    # Sin trailing stop inicial - usaremos custom exit logic
    trailing_stop = False
    
    # Configuración agresiva
    max_open_trades = 6  # Más trades simultáneos
    stake_currency = 'USDT'
    stake_amount = 100
    use_exit_signal = True
    exit_profit_only = False
    ignore_roi_if_entry_signal = True  # No salir por ROI si hay señal de continuación
    
    # Orden types optimizados
    order_types = {
        'entry': 'market',  # Market orders para speed
        'exit': 'limit', 
        'stoploss': 'market',
        'stoploss_on_exchange': True,  # Protección en exchange
        'stoploss_on_exchange_interval': 30
    }
    
    order_time_in_force = {
        'entry': 'gtc',
        'exit': 'ioc',  # Immediate or cancel para exits rápidos
    }
    
    # Parámetros optimizables para múltiples estrategias
    # OVERSOLD BOUNCE STRATEGY (ultra permisivo)
    rsi_oversold = IntParameter(25, 45, default=42, space="buy")  # MÁS ALTO
    volume_spike_factor = DecimalParameter(1.2, 2.5, default=1.6, space="buy")  # MÁS BAJO
    
    # BREAKOUT STRATEGY (más sensible)
    breakout_periods = IntParameter(15, 30, default=18, space="buy")
    breakout_volume_factor = DecimalParameter(1.2, 2.5, default=1.8, space="buy")
    
    # TREND FOLLOWING STRATEGY (ultra permisivo)
    trend_strength = DecimalParameter(0.008, 0.03, default=0.015, space="buy")  # MÁS BAJO
    pullback_rsi_max = IntParameter(55, 75, default=68, space="buy")  # MÁS ALTO
    
    # EXIT PARAMETERS
    partial_exit_profit = DecimalParameter(0.03, 0.06, default=0.04, space="sell")
    trail_start = DecimalParameter(0.05, 0.08, default=0.06, space="sell")
    rsi_overbought = IntParameter(75, 90, default=82, space="sell")
    
    def __init__(self, config: dict) -> None:
        super().__init__(config)
        self.logger = logging.getLogger(__name__)
        
        # Tracking para múltiples estrategias
        self._strategy_stats = {
            'oversold_trades': 0,
            'breakout_trades': 0, 
            'trend_trades': 0,
            'partial_exits': 0
        }
        
    def informative_pairs(self):
        return []
    
    def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        """Indicadores optimizados para múltiples estrategias"""
        
        # Indicadores base esenciales
        dataframe['rsi'] = ta.RSI(dataframe, timeperiod=14)
        dataframe['rsi_fast'] = ta.RSI(dataframe, timeperiod=7)  # Para momentum rápido
        
        # EMAs optimizadas
        dataframe['ema8'] = ta.EMA(dataframe, timeperiod=8)
        dataframe['ema21'] = ta.EMA(dataframe, timeperiod=21)
        dataframe['ema50'] = ta.EMA(dataframe, timeperiod=50)
        
        # MACD para trend confirmation
        macd = ta.MACD(dataframe, fastperiod=12, slowperiod=26, signalperiod=9)
        dataframe['macd'] = macd['macd']
        dataframe['macdsignal'] = macd['macdsignal']
        dataframe['macdhist'] = macd['macdhist']
        
        # Volume analysis (períodos más cortos para mayor sensibilidad)
        dataframe['volume_sma'] = ta.SMA(dataframe['volume'], timeperiod=12)
        dataframe['volume_ratio'] = dataframe['volume'] / dataframe['volume_sma']
        
        # Bollinger Bands para volatility
        bollinger = ta.BBANDS(dataframe, timeperiod=20, nbdevup=2.0, nbdevdn=2.0)
        dataframe['bb_lower'] = bollinger['lowerband']
        dataframe['bb_middle'] = bollinger['middleband'] 
        dataframe['bb_upper'] = bollinger['upperband']
        dataframe['bb_percent'] = (dataframe['close'] - dataframe['bb_lower']) / (dataframe['bb_upper'] - dataframe['bb_lower'])
        
        # ATR para volatility adjustment
        dataframe['atr'] = ta.ATR(dataframe, timeperiod=14)
        dataframe['volatility'] = dataframe['atr'] / dataframe['close']
        
        # Price action signals
        dataframe['high_20'] = dataframe['high'].rolling(window=20).max()
        dataframe['low_20'] = dataframe['low'].rolling(window=20).min()
        
        # Trend strength
        dataframe['trend_strength'] = (dataframe['ema8'] - dataframe['ema50']) / dataframe['ema50']
        
        return dataframe
    
    def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        """
        MULTI-STRATEGY APPROACH:
        1. Oversold Bounce
        2. Breakout Momentum  
        3. Trend Following
        """
        
        pair = metadata['pair']
        
        # Initialize entry columns
        dataframe.loc[:, 'enter_long'] = 0
        dataframe.loc[:, 'enter_tag'] = ''
        
        # STRATEGY 1: OVERSOLD BOUNCE (más selectivo para mejor win rate)
        oversold_conditions = (
            (dataframe['rsi'] <= self.rsi_oversold.value) &  # Oversold
            (dataframe['volume_ratio'] >= self.volume_spike_factor.value) &  # Volume spike
            (dataframe['bb_percent'] <= 0.25) &  # MÁS SELECTIVO: 25%
            (dataframe['close'] > dataframe['open']) &  # NUEVO: Confirmación de rebote (vela verde)
            (dataframe['close'] > dataframe['bb_lower'] * 1.005) &  # NUEVO: Precio recuperándose del soporte
            (dataframe['macd'] > dataframe['macdsignal']) &  # NUEVO: MACD debe estar positivo
            (dataframe['rsi'] < 35)  # NUEVO: Más estricto, debe estar realmente oversold
        )
        
        dataframe.loc[oversold_conditions, 'enter_long'] = 1
        dataframe.loc[oversold_conditions, 'enter_tag'] = 'oversold_bounce'
        
        # STRATEGY 2: BREAKOUT MOMENTUM (ajustado para balance)
        breakout_conditions = (
            (dataframe['close'] > dataframe['high_20'].shift(1)) &  # Price breakout
            (dataframe['volume_ratio'] >= self.breakout_volume_factor.value) &  # Volume confirmation
            (dataframe['rsi'] > 45) & (dataframe['rsi'] < 78) &  # MÁS AMPLIO: RSI range
            (dataframe['macd'] > dataframe['macdsignal'].shift(1))  # MACD improving (no necesariamente positivo)
        )
        
        # No sobrescribir oversold signals
        breakout_mask = breakout_conditions & (dataframe['enter_long'] == 0)
        dataframe.loc[breakout_mask, 'enter_long'] = 1
        dataframe.loc[breakout_mask, 'enter_tag'] = 'breakout_momentum'
        
        # STRATEGY 3: TREND FOLLOWING (ultra permisivo para más oportunidades)
        trend_conditions = (
            (dataframe['trend_strength'] >= self.trend_strength.value) &  # Uptrend (más permisivo)
            (dataframe['close'] <= dataframe['ema21'] * 1.01) &  # MÁS PERMISIVO: 1% por encima de EMA21
            (dataframe['close'] > dataframe['ema50']) &  # Above EMA50 (menos restrictivo)
            (dataframe['rsi'] <= self.pullback_rsi_max.value) &  # Not overbought
            (dataframe['volume_ratio'] >= 0.8) &  # MÁS PERMISIVO: volumen bajo acceptable
            (dataframe['ema8'] > dataframe['ema21'])  # Confirmar tendencia alcista
        )
        
        # No sobrescribir otras signals
        trend_mask = trend_conditions & (dataframe['enter_long'] == 0)
        dataframe.loc[trend_mask, 'enter_long'] = 1
        dataframe.loc[trend_mask, 'enter_tag'] = 'trend_pullback'
        
        # Log entry signals
        if oversold_conditions.any():
            self._strategy_stats['oversold_trades'] += 1
            self.logger.info(f"🔥 OVERSOLD BOUNCE: {pair} | RSI={dataframe['rsi'].iloc[-1]:.1f} | Vol={dataframe['volume_ratio'].iloc[-1]:.2f}")
            
        if breakout_mask.any():
            self._strategy_stats['breakout_trades'] += 1  
            self.logger.info(f"🚀 BREAKOUT: {pair} | Price={dataframe['close'].iloc[-1]:.4f} vs High20={dataframe['high_20'].iloc[-1]:.4f}")
            
        if trend_mask.any():
            self._strategy_stats['trend_trades'] += 1
            self.logger.info(f"📈 TREND PULLBACK: {pair} | Trend={dataframe['trend_strength'].iloc[-1]:.3f} | RSI={dataframe['rsi'].iloc[-1]:.1f}")
        
        return dataframe
    
    def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        """
        SMART EXIT SYSTEM:
        1. Overbought + momentum loss
        2. Volume collapse
        3. Technical breakdown
        """
        
        dataframe.loc[:, 'exit_long'] = 0
        dataframe.loc[:, 'exit_tag'] = ''
        
        # EXIT CONDITION 1: Strong overbought with momentum loss
        overbought_exit = (
            (dataframe['rsi'] >= self.rsi_overbought.value) &
            (dataframe['rsi_fast'] < dataframe['rsi_fast'].shift(1)) &  # Momentum divergence
            (dataframe['volume_ratio'] < 1.0)  # Volume drying up
        )
        
        dataframe.loc[overbought_exit, 'exit_long'] = 1
        dataframe.loc[overbought_exit, 'exit_tag'] = 'overbought_exit'
        
        # EXIT CONDITION 2: Technical breakdown (MUY SELECTIVO para evitar salidas prematuras)
        technical_breakdown = (
            (dataframe['close'] < dataframe['ema8']) &  # Precio por debajo de EMA rápida
            (dataframe['ema8'] < dataframe['ema8'].shift(3)) &  # MÁS CONFIRMACIÓN: 3 períodos
            (dataframe['rsi'] < 35) &  # MÁS ESTRICTO: RSI muy bajo
            (dataframe['volume_ratio'] > 1.2) &  # CAMBIO: Volumen ALTO confirma verdadero breakdown
            (dataframe['close'] < dataframe['close'].shift(2))  # NUEVO: Precio declinando consistentemente
        )
        
        breakdown_mask = technical_breakdown & (dataframe['exit_long'] == 0)
        dataframe.loc[breakdown_mask, 'exit_long'] = 1
        dataframe.loc[breakdown_mask, 'exit_tag'] = 'technical_breakdown'
        
        # EXIT CONDITION 3: Volume collapse (no buyers)
        volume_collapse = (
            (dataframe['volume_ratio'] < 0.4) &  # Very low volume
            (dataframe['close'] < dataframe['close'].shift(1)) &  # Price declining
            (dataframe['bb_percent'] > 0.8)  # Near upper BB (distribution)
        )
        
        volume_mask = volume_collapse & (dataframe['exit_long'] == 0)
        dataframe.loc[volume_mask, 'exit_long'] = 1
        dataframe.loc[volume_mask, 'exit_tag'] = 'volume_collapse'
        
        return dataframe
    
    def custom_stoploss(self, pair: str, trade: 'Trade', current_time: datetime,
                       current_rate: float, current_profit: float, **kwargs) -> float:
        """
        DYNAMIC STOPLOSS SYSTEM
        """
        
        # Base stoploss
        stoploss_value = self.stoploss
        
        # Tighten stoploss after partial profit taking
        if current_profit > self.partial_exit_profit.value:
            stoploss_value = -0.01  # Tighter stoploss después de partial exit
            
        # Trailing stop after significant profit
        if current_profit > self.trail_start.value:
            # Trailing stop que se mueve con el profit
            trail_distance = 0.02  # 2% trailing distance
            stoploss_value = current_profit - trail_distance
            
        return stoploss_value
    
    def custom_exit(self, pair: str, trade: 'Trade', current_time: datetime, 
                   current_rate: float, current_profit: float, **kwargs) -> tuple:
        """
        SMART PARTIAL EXITS
        """
        
        # Partial exit at target profit
        if current_profit >= self.partial_exit_profit.value:
            self._strategy_stats['partial_exits'] += 1
            self.logger.info(f"💰 PARTIAL EXIT: {pair} at {current_profit:.2%} profit")
            return 0.5, 'partial_profit'  # Exit 50% of position
            
        return None
    
    def confirm_trade_entry(self, pair: str, order_type: str, amount: float,
                           rate: float, time_in_force: str, current_time: datetime,
                           entry_tag: str, side: str, **kwargs) -> bool:
        """
        FINAL ENTRY VALIDATION
        """
        
        # No weekend trading in crypto (low liquidity)
        if current_time.weekday() >= 5:
            return False
            
        # Log strategy performance
        total_trades = sum(self._strategy_stats.values())
        if total_trades > 0:
            self.logger.info(f"📊 STRATEGY STATS: Oversold={self._strategy_stats['oversold_trades']}, "
                           f"Breakout={self._strategy_stats['breakout_trades']}, "
                           f"Trend={self._strategy_stats['trend_trades']}")
            
        return True
