# source: https://raw.githubusercontent.com/vigoferrel/qbtc-unified/ef7ffc8f45cc7ab041884cd0c08a2043acc1f0cb/freqtrade_qbtc/strategies/QBTCQuantumStrategyV4_Permissive.py
# pragma pylint: disable=missing-docstring, invalid-name, pointless-string-statement
# flake8: noqa: F401
# isort: skip_file

"""
🌌 QBTC QUANTUM STRATEGY V4.0 PERMISSIVE - Para Backtesting 🌌
Versión permisiva de la estrategia cuántica para generar más trades en backtest

Desarrollado por: vigoferrel@gmail.com
Framework: QBTC-UNIFIED Quantum Trading System V4.0
Licencia: Propietaria - vigoferrel

==============================================================================
🚀 CONFIGURACIÓN PERMISIVA PARA BACKTESTING:
- Thresholds cuánticos más bajos (0.50-0.65 en lugar de 0.70-0.85)
- RSI más permisivo (entrada: 15-45, salida: 65-85) 
- Quantum score threshold reducido (0.55-0.75)
- Coherence threshold más bajo (0.45-0.70)
- Volumen multiplier reducido (1.2-2.5)
- Parámetros optimizables para hyperopt
==============================================================================
"""

import numpy as np
import pandas as pd
from datetime import datetime, timezone, timedelta
from pandas import DataFrame
from typing import Optional, Union, Dict, List, Tuple, Any
import requests
import json
import math
import logging
from functools import lru_cache
import warnings
warnings.filterwarnings('ignore')

# Imports para ML y análisis avanzado
try:
    from sklearn.ensemble import RandomForestRegressor
    from sklearn.preprocessing import StandardScaler
    from sklearn.model_selection import train_test_split
    SKLEARN_AVAILABLE = True
except ImportError:
    SKLEARN_AVAILABLE = False

# Freqtrade imports
from freqtrade.strategy import (
    IStrategy,
    Trade,
    Order,
    PairLocks,
    informative,
    BooleanParameter,
    CategoricalParameter,
    DecimalParameter,
    IntParameter,
    RealParameter,
    timeframe_to_minutes,
    merge_informative_pair,
    stoploss_from_absolute,
    stoploss_from_open,
)

import talib.abstract as ta
from technical import qtpylib


class Github_vigoferrel_qbtc_unified__QBTCQuantumStrategyV4_Permissive__20251009_202533(IStrategy):
    """
    🌊 QBTC Quantum Strategy V4.0 PERMISSIVE - Versión para Backtesting
    
    Esta versión incorpora parámetros más permisivos para generar más trades:
    - Thresholds cuánticos reducidos
    - RSI más flexible 
    - Condiciones de entrada/salida relajadas
    - Optimización hyperopt habilitada
    """
    
    # ==================== CONFIGURACIÓN BÁSICA ====================
    
    INTERFACE_VERSION = 3
    can_short: bool = False
    
    # Configuración de timeframes
    timeframe = '15m'  # Timeframe principal optimizado
    startup_candle_count: int = 200  # Reducido para backtest más rápido
    
    # ==================== CONFIGURACIÓN DE RIESGO PERMISIVA ====================
    
    # ROI más agresivo para capturar más trades
    minimal_roi = {
        "0": 0.20,      # 20% ROI inmediato
        "15": 0.15,     # 15% después de 15 min
        "30": 0.10,     # 10% después de 30 min
        "60": 0.06,     # 6% después de 1h
        "120": 0.04,    # 4% después de 2h
        "240": 0.02,    # 2% después de 4h
        "480": 0.01,    # 1% después de 8h
    }
    
    stoploss = -0.08  # Stop loss más ajustado para permitir más trades
    
    # Trailing stop más permisivo
    trailing_stop = True
    trailing_stop_positive = 0.01   # 1%
    trailing_stop_positive_offset = 0.025  # 2.5%
    trailing_only_offset_is_reached = True
    
    # ==================== PARÁMETROS HYPEROPT PERMISIVOS ====================
    
    # Parámetros de entrada (BUY) - MÁS PERMISIVOS
    rsi_buy_min = IntParameter(15, 25, default=20, space="buy", optimize=True)
    rsi_buy_max = IntParameter(35, 50, default=42, space="buy", optimize=True)
    
    macd_buy_threshold = DecimalParameter(-0.001, 0.002, default=0.0, decimals=4, space="buy", optimize=True)
    
    quantum_score_buy = DecimalParameter(0.50, 0.75, default=0.60, decimals=2, space="buy", optimize=True)
    coherence_buy = DecimalParameter(0.45, 0.70, default=0.55, decimals=2, space="buy", optimize=True)
    consciousness_buy = DecimalParameter(0.50, 0.75, default=0.60, decimals=2, space="buy", optimize=True)
    
    volume_buy = DecimalParameter(1.2, 2.5, default=1.5, decimals=1, space="buy", optimize=True)
    momentum_buy = DecimalParameter(0.1, 0.4, default=0.2, decimals=1, space="buy", optimize=True)
    
    # Parámetros de salida (SELL) - MÁS PERMISIVOS  
    rsi_sell_min = IntParameter(65, 75, default=70, space="sell", optimize=True)
    rsi_sell_max = IntParameter(80, 95, default=85, space="sell", optimize=True)
    
    quantum_score_sell = DecimalParameter(0.30, 0.50, default=0.40, decimals=2, space="sell", optimize=True)
    coherence_sell = DecimalParameter(0.25, 0.45, default=0.35, decimals=2, space="sell", optimize=True)
    
    macd_sell_threshold = DecimalParameter(-0.002, 0.001, default=-0.001, decimals=4, space="sell", optimize=True)
    momentum_sell = DecimalParameter(-0.4, -0.1, default=-0.2, decimals=1, space="sell", optimize=True)
    
    # Parámetros técnicos optimizables
    ema_fast = IntParameter(8, 15, default=12, space="buy", optimize=True)
    ema_slow = IntParameter(20, 30, default=26, space="buy", optimize=True)
    
    # Toggles para módulos (desactivados algunos para backtest más rápido)
    enable_ml_prediction = BooleanParameter(default=False, space="buy", optimize=False)
    enable_regime_detection = BooleanParameter(default=True, space="buy", optimize=False)
    enable_fractal_analysis = BooleanParameter(default=False, space="buy", optimize=False)
    enable_liquidity_analysis = BooleanParameter(default=False, space="buy", optimize=False)
    enable_sentiment_analysis = BooleanParameter(default=False, space="buy", optimize=False)
    enable_quantum_scoring = BooleanParameter(default=True, space="buy", optimize=False)
    
    # ==================== CONFIGURACIÓN QBTC SIMPLIFICADA ====================
    
    QBTC_URLS = {
        'kernel': 'http://localhost:14050',
        'metaconsciencia': 'http://localhost:14020',
        'guardian': 'http://localhost:14600', 
        'portfolio': 'http://localhost:14300',
    }
    
    # Constantes cuánticas
    PHI = 1.618033988749
    
    def __init__(self, config: dict) -> None:
        super().__init__(config)
        
        # Logger para debug
        self.logger = logging.getLogger(__name__)
        
        # Cache simplificado
        self._quantum_cache = {}
    
    # ==================== MÉTODOS DE INDICADORES TÉCNICOS ====================
    
    def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        """
        Poblado de indicadores - versión optimizada para backtest
        """
        
        # === INDICADORES TÉCNICOS BÁSICOS ===
        dataframe['rsi'] = ta.RSI(dataframe, timeperiod=14)
        dataframe['mfi'] = ta.MFI(dataframe, timeperiod=14)
        
        # MACD
        macd = ta.MACD(dataframe, fastperiod=self.ema_fast.value, 
                       slowperiod=self.ema_slow.value, signalperiod=9)
        dataframe['macd'] = macd['macd']
        dataframe['macdsignal'] = macd['macdsignal']
        dataframe['macdhist'] = macd['macdhist']
        
        # EMAs
        dataframe['ema8'] = ta.EMA(dataframe, timeperiod=8)
        dataframe['ema21'] = ta.EMA(dataframe, timeperiod=21)
        dataframe['ema55'] = ta.EMA(dataframe, timeperiod=55)
        
        # Stochastic
        stoch = ta.STOCH(dataframe)
        dataframe['stoch_k'] = stoch['slowk']
        dataframe['stoch_d'] = stoch['slowd']
        
        # Williams %R
        dataframe['willr'] = ta.WILLR(dataframe, timeperiod=14)
        
        # CCI
        dataframe['cci'] = ta.CCI(dataframe, timeperiod=20)
        
        # === VOLUMEN ===
        dataframe['volume_sma'] = ta.SMA(dataframe['volume'], timeperiod=20)
        dataframe['volume_ratio'] = dataframe['volume'] / dataframe['volume_sma']
        
        # === INDICADORES CUÁNTICOS SIMPLIFICADOS ===
        if self.enable_quantum_scoring.value:
            dataframe['quantum_coherence'] = self.calculate_simple_coherence(dataframe)
            dataframe['quantum_momentum'] = self.calculate_simple_momentum(dataframe)
            dataframe['quantum_score'] = self.calculate_simple_quantum_score(dataframe)
        else:
            dataframe['quantum_coherence'] = 0.6
            dataframe['quantum_momentum'] = 0.0
            dataframe['quantum_score'] = 0.6
        
        # === SEÑALES DE TENDENCIA ===
        dataframe['trend_signal'] = np.where(
            dataframe['ema8'] > dataframe['ema21'], 1, 
            np.where(dataframe['ema8'] < dataframe['ema21'], -1, 0)
        )
        
        # === MOMENTUM ===
        dataframe['momentum_signal'] = (dataframe['close'] / dataframe['close'].shift(10) - 1) * 100
        
        # === PATRONES DE VELAS BÁSICOS ===
        dataframe['cdl_hammer'] = ta.CDLHAMMER(dataframe) / 100
        dataframe['cdl_shootingstar'] = ta.CDLSHOOTINGSTAR(dataframe) / 100
        dataframe['cdl_doji'] = ta.CDLDOJI(dataframe) / 100
        
        return dataframe
    
    def calculate_simple_coherence(self, dataframe: DataFrame) -> pd.Series:
        """Coherencia cuántica simplificada"""
        try:
            # Coherencia basada en sincronización de EMAs
            ema_sync = 1 - abs(dataframe['ema8'] - dataframe['ema21']) / dataframe['ema21']
            ema_sync = np.clip(ema_sync.fillna(0.5), 0, 1)
            
            # Coherencia RSI-Precio
            rsi_norm = dataframe['rsi'] / 100
            price_momentum = dataframe['close'].pct_change(periods=14)
            rsi_coherence = 1 - abs(rsi_norm - 0.5 - price_momentum * 10)
            rsi_coherence = np.clip(rsi_coherence.fillna(0.5), 0, 1)
            
            coherence = (ema_sync * 0.6 + rsi_coherence * 0.4)
            return coherence.ewm(span=10).mean()
            
        except Exception:
            return pd.Series([0.6] * len(dataframe), index=dataframe.index)
    
    def calculate_simple_momentum(self, dataframe: DataFrame) -> pd.Series:
        """Momentum cuántico simplificado"""
        try:
            # Momentum de precio
            price_mom = (dataframe['close'] / dataframe['ema21'] - 1)
            
            # Momentum RSI
            rsi_mom = (dataframe['rsi'] - 50) / 50
            
            # Momentum MACD
            macd_mom = np.where(dataframe['macd'] > dataframe['macdsignal'], 0.5, -0.5)
            
            momentum = (price_mom * 0.5 + rsi_mom * 0.3 + macd_mom * 0.2)
            return momentum.ewm(span=10).mean()
            
        except Exception:
            return pd.Series([0.0] * len(dataframe), index=dataframe.index)
    
    def calculate_simple_quantum_score(self, dataframe: DataFrame) -> pd.Series:
        """Score cuántico simplificado"""
        try:
            # Score basado en múltiples factores
            rsi_score = np.where(
                (dataframe['rsi'] > 30) & (dataframe['rsi'] < 70), 0.8, 0.4
            )
            
            trend_score = np.where(dataframe['trend_signal'] > 0, 0.7, 0.3)
            
            macd_score = np.where(
                dataframe['macd'] > dataframe['macdsignal'], 0.7, 0.3
            )
            
            volume_score = np.where(dataframe['volume_ratio'] > 1.2, 0.8, 0.5)
            
            momentum_score = np.clip(
                0.5 + dataframe['momentum_signal'] / 20, 0.2, 0.8
            )
            
            quantum_score = (
                rsi_score * 0.20 +
                trend_score * 0.25 +
                macd_score * 0.20 +
                volume_score * 0.15 +
                momentum_score * 0.20
            )
            
            return pd.Series(quantum_score).ewm(span=8).mean()
            
        except Exception:
            return pd.Series([0.6] * len(dataframe), index=dataframe.index)
    
    def get_qbtc_service_data(self, service: str, endpoint: str = '/health') -> dict:
        """Obtiene datos de servicios QBTC con fallback"""
        try:
            if service not in self.QBTC_URLS:
                return {}
                
            url = f"{self.QBTC_URLS[service]}{endpoint}"
            response = requests.get(url, timeout=1)
            
            if response.status_code == 200:
                return response.json()
            else:
                return {}
        except Exception:
            return {}
    
    # ==================== LÓGICA DE ENTRADA PERMISIVA ====================
    
    def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        """
        Lógica de entrada permisiva para generar más trades
        """
        
        # ========== CONDICIONES DE COMPRA PRINCIPALES ==========
        
        conditions_buy = []
        
        # 1. RSI en rango permisivo
        rsi_condition = (
            (dataframe['rsi'] >= self.rsi_buy_min.value) & 
            (dataframe['rsi'] <= self.rsi_buy_max.value)
        )
        conditions_buy.append(rsi_condition)
        
        # 2. MACD alcista
        macd_condition = dataframe['macd'] > dataframe['macdsignal'] + self.macd_buy_threshold.value
        conditions_buy.append(macd_condition)
        
        # 3. Quantum score mínimo
        if self.enable_quantum_scoring.value:
            quantum_condition = dataframe['quantum_score'] >= self.quantum_score_buy.value
            conditions_buy.append(quantum_condition)
        
        # 4. Coherencia mínima
        coherence_condition = dataframe['quantum_coherence'] >= self.coherence_buy.value
        conditions_buy.append(coherence_condition)
        
        # 5. Volumen suficiente
        volume_condition = dataframe['volume_ratio'] >= self.volume_buy.value
        conditions_buy.append(volume_condition)
        
        # 6. Momentum positivo
        momentum_condition = dataframe['momentum_signal'] >= self.momentum_buy.value
        conditions_buy.append(momentum_condition)
        
        # 7. Tendencia alcista
        trend_condition = dataframe['trend_signal'] > 0
        conditions_buy.append(trend_condition)
        
        # 8. No sobrecomprado
        not_overbought = (
            (dataframe['rsi'] < 80) &
            (dataframe['stoch_k'] < 85)
        )
        conditions_buy.append(not_overbought)
        
        # ========== CONDICIONES ESPECIALES ==========
        
        # Verificación básica de servicios QBTC
        qbtc_ok = True
        meta_data = self.get_qbtc_service_data('metaconsciencia')
        if meta_data and 'estado' in meta_data:
            qbtc_ok = meta_data['estado'] == 'VIVO'
        
        # ========== APLICACIÓN FINAL ==========
        
        # Combinar todas las condiciones
        final_buy_condition = conditions_buy[0]
        for condition in conditions_buy[1:]:
            final_buy_condition = final_buy_condition & condition
        
        # Aplicar condición QBTC
        final_buy_condition = final_buy_condition & qbtc_ok
        
        dataframe.loc[final_buy_condition, 'enter_long'] = 1
        
        return dataframe
    
    # ==================== LÓGICA DE SALIDA PERMISIVA ====================
    
    def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        """
        Lógica de salida permisiva para mantener trades rentables más tiempo
        """
        
        # ========== CONDICIONES DE VENTA PRINCIPALES ==========
        
        conditions_sell = []
        
        # 1. RSI sobrecomprado
        rsi_condition = (
            (dataframe['rsi'] >= self.rsi_sell_min.value) & 
            (dataframe['rsi'] <= self.rsi_sell_max.value)
        )
        conditions_sell.append(rsi_condition)
        
        # 2. MACD bajista
        macd_condition = dataframe['macd'] < dataframe['macdsignal'] + self.macd_sell_threshold.value
        conditions_sell.append(macd_condition)
        
        # 3. Quantum score bajo
        if self.enable_quantum_scoring.value:
            quantum_condition = dataframe['quantum_score'] <= self.quantum_score_sell.value
            conditions_sell.append(quantum_condition)
        
        # 4. Pérdida de coherencia
        coherence_condition = dataframe['quantum_coherence'] <= self.coherence_sell.value
        conditions_sell.append(coherence_condition)
        
        # 5. Momentum negativo
        momentum_condition = dataframe['momentum_signal'] <= self.momentum_sell.value
        conditions_sell.append(momentum_condition)
        
        # 6. Ruptura de tendencia
        trend_break = (
            (dataframe['trend_signal'] <= 0) &
            (dataframe['close'] < dataframe['ema21'])
        )
        conditions_sell.append(trend_break)
        
        # 7. Patrones bajistas
        bearish_patterns = (
            (dataframe['cdl_shootingstar'] != 0) |
            (dataframe['cdl_doji'] != 0)
        )
        conditions_sell.append(bearish_patterns)
        
        # ========== SALIDAS DE EMERGENCIA ==========
        
        # Emergency exit basado en servicios QBTC
        emergency_exit = False
        meta_data = self.get_qbtc_service_data('metaconsciencia')
        if meta_data and 'decision' in meta_data:
            decision = meta_data.get('decision', {})
            if isinstance(decision, dict):
                action = decision.get('accion', 'HOLD')
                emergency_exit = action == 'SELL'
        
        # ========== APLICACIÓN FINAL ==========
        
        # Combinar condiciones principales (OR logic para ser más permisivo)
        main_sell_condition = conditions_sell[0]
        for condition in conditions_sell[1:4]:  # Solo primeras 4 condiciones
            main_sell_condition = main_sell_condition | condition
        
        # Condiciones adicionales con AND
        additional_sell = (
            conditions_sell[4] &  # momentum
            conditions_sell[5]    # trend break
        )
        
        # Condición final
        final_sell_condition = (
            (main_sell_condition & additional_sell) |
            emergency_exit |
            conditions_sell[6]  # bearish patterns
        )
        
        dataframe.loc[final_sell_condition, 'exit_long'] = 1
        
        return dataframe
