# source: https://raw.githubusercontent.com/vigoferrel/qbtc-unified/ef7ffc8f45cc7ab041884cd0c08a2043acc1f0cb/freqtrade_qbtc/strategies/QBTCSimpleWins.py
"""
Github_vigoferrel_qbtc_unified__QBTCSimpleWins__20251009_202533 - Estrategia Simple pero Efectiva
===============================================

Filosofía: Trades consistentes con reglas claras y simples.
No más complejidad innecesaria - solo trading sólido.

Creado: 2025-09-09
Autor: QBTC Trading System
"""

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
import logging
from datetime import datetime, time

class Github_vigoferrel_qbtc_unified__QBTCSimpleWins__20251009_202533(IStrategy):
    
    # Metadatos básicos
    INTERFACE_VERSION = 3
    timeframe = '15m'
    can_short = False
    stoploss = -0.03  # Stop loss 3%
    
    # ROI escalonado - más realista
    minimal_roi = {
        "0": 0.04,    # 4% inicial (más realista)
        "60": 0.03,   # 3% después de 1h  
        "240": 0.02,  # 2% después de 4h
        "720": 0.01   # 1% después de 12h
    }
    
    # Trailing stop después de 3% ganancia (más espacio)
    trailing_stop = True
    trailing_stop_positive = 0.03
    trailing_stop_positive_offset = 0.04
    trailing_only_offset_is_reached = True
    
    # Configuración de trades
    max_open_trades = 3
    stake_currency = 'USDT'
    stake_amount = 100
    use_exit_signal = True
    exit_profit_only = False
    ignore_roi_if_entry_signal = False
    
    # Orden types
    order_types = {
        'entry': 'limit',
        'exit': 'limit', 
        'stoploss': 'market',
        'stoploss_on_exchange': False,
    }
    
    order_time_in_force = {
        'entry': 'gtc',
        'exit': 'gtc',
    }
    
    # Parámetros optimizables (valores mejorados)
    rsi_buy_min = IntParameter(50, 60, default=52, space="buy")  # MÁS ALTO - evitar oversold
    rsi_buy_max = IntParameter(65, 75, default=68, space="buy")  # MÁS ALTO - mejor momentum
    rsi_sell = IntParameter(70, 80, default=75, space="sell")
    
    volume_factor = DecimalParameter(1.5, 2.5, default=1.8, space="buy")  # MÁS CONFIRMACIÓN
    atr_threshold = DecimalParameter(0.05, 0.10, default=0.07, space="buy")  # MÁS PERMISIVO
    
    def __init__(self, config: dict) -> None:
        super().__init__(config)
        self.logger = logging.getLogger(__name__)
        
        # Control de trades por día
        self._daily_trades = {}
        
    def informative_pairs(self):
        return []
    
    def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        """Indicadores simples pero efectivos"""
        
        # EMAs para tendencia
        dataframe['ema21'] = ta.EMA(dataframe, timeperiod=21)
        dataframe['ema55'] = ta.EMA(dataframe, timeperiod=55)
        
        # RSI para momentum
        dataframe['rsi'] = ta.RSI(dataframe, timeperiod=14)
        
        # MACD para confirmación
        macd = ta.MACD(dataframe)
        dataframe['macd'] = macd['macd']
        dataframe['macdsignal'] = macd['macdsignal']
        dataframe['macdhist'] = macd['macdhist']
        
        # Volume para validación
        dataframe['volume_sma'] = ta.SMA(dataframe['volume'], timeperiod=20)
        dataframe['volume_ratio'] = dataframe['volume'] / dataframe['volume_sma']
        
        # ATR para volatilidad
        dataframe['atr'] = ta.ATR(dataframe, timeperiod=14)
        dataframe['volatility'] = dataframe['atr'] / dataframe['close']
        
        # Señal de tendencia simple
        dataframe['uptrend'] = (dataframe['ema21'] > dataframe['ema55']).astype('int')
        dataframe['price_above_ema21'] = (dataframe['close'] > dataframe['ema21']).astype('int')
        
        return dataframe
    
    def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        """
        Lógica de entrada SIMPLE:
        1. Tendencia alcista (EMA21 > EMA55)
        2. Precio por encima de EMA21 (momentum)
        3. RSI en zona saludable (no overbought)
        4. Volumen por encima del promedio
        5. MACD positivo
        6. Volatilidad controlada
        """
        
        pair = metadata['pair']
        
        # Control de trades diarios
        current_date = datetime.now().date()
        if current_date not in self._daily_trades:
            self._daily_trades[current_date] = set()
            
        # Solo 1 trade por par por día
        if pair in self._daily_trades[current_date]:
            dataframe.loc[:, 'enter_long'] = 0
            return dataframe
        
        # Filtro de horarios (evitar domingos)
        current_time = datetime.now()
        if current_time.weekday() == 6:  # Domingo
            dataframe.loc[:, 'enter_long'] = 0
            return dataframe
            
        # Condiciones de entrada
        conditions = []
        
        # 1. Tendencia alcista clara
        conditions.append(dataframe['uptrend'] == 1)
        
        # 2. Precio por encima de EMA21 (momentum)
        conditions.append(dataframe['price_above_ema21'] == 1)
        
        # 3. RSI en zona saludable
        conditions.append(
            (dataframe['rsi'] >= self.rsi_buy_min.value) &
            (dataframe['rsi'] <= self.rsi_buy_max.value)
        )
        
        # 4. Volumen confirmatorio
        conditions.append(dataframe['volume_ratio'] >= self.volume_factor.value)
        
        # 5. MACD positivo
        conditions.append(dataframe['macd'] > dataframe['macdsignal'])
        
        # 6. Volatilidad controlada
        conditions.append(dataframe['volatility'] <= self.atr_threshold.value)
        
        # 7. Confirmation candle (precio subió en la vela anterior)
        conditions.append(dataframe['close'] > dataframe['close'].shift(1))
        
        # Combinar todas las condiciones
        if conditions:
            entry_signal = conditions[0]
            for condition in conditions[1:]:
                entry_signal = entry_signal & condition
            
            dataframe.loc[entry_signal, 'enter_long'] = 1
            dataframe.loc[entry_signal, 'enter_tag'] = 'simple_momentum'
            
            # Registrar trade del día
            if entry_signal.any():
                self._daily_trades[current_date].add(pair)
                self.logger.info(f"✅ ENTRADA SIMPLE: {pair} | RSI={dataframe['rsi'].iloc[-1]:.1f} | "
                               f"Volume_Ratio={dataframe['volume_ratio'].iloc[-1]:.2f} | "
                               f"Volatility={dataframe['volatility'].iloc[-1]:.3f}")
        else:
            dataframe.loc[:, 'enter_long'] = 0
            
        return dataframe
    
    def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        """
        Lógica de salida MEJORADA (menos agresiva):
        1. RSI muy overbought Y precio alto (confirmación doble)
        2. Pérdida fuerte de momentum (precio < EMA21 Y RSI < 40)
        3. MACD fuertemente negativo con confirmación
        """
        
        conditions = []
        
        # 1. RSI muy overbought Y precio por encima de EMA21 (venta de ganancia)
        conditions.append(
            (dataframe['rsi'] >= self.rsi_sell.value) &
            (dataframe['close'] > dataframe['ema21'])
        )
        
        # 2. Pérdida fuerte de momentum (precio < EMA21 Y RSI bajo)
        conditions.append(
            (dataframe['close'] < dataframe['ema21']) &
            (dataframe['rsi'] < 40)  # RSI también debe estar bajo
        )
        
        # 3. MACD muy negativo (no solo cruce)
        conditions.append(
            (dataframe['macd'] < dataframe['macdsignal']) &
            (dataframe['macdhist'] < -0.001)  # Histograma negativo confirmatorio
        )
        
        # Cualquiera de las condiciones activa la salida (lógica OR conservada)
        if conditions:
            exit_signal = conditions[0]
            for condition in conditions[1:]:
                exit_signal = exit_signal | condition  # OR logic
            
            dataframe.loc[exit_signal, 'exit_long'] = 1
            dataframe.loc[exit_signal, 'exit_tag'] = 'conservative_exit'
            
            if exit_signal.any():
                pair = metadata['pair']
                self.logger.info(f"🚺 SALIDA CONSERVADORA: {pair} | RSI={dataframe['rsi'].iloc[-1]:.1f} | "
                               f"Price_vs_EMA21={((dataframe['close'].iloc[-1] / dataframe['ema21'].iloc[-1]) - 1) * 100:.2f}% | "
                               f"MACD_Hist={dataframe['macdhist'].iloc[-1]:.4f}")
        else:
            dataframe.loc[:, 'exit_long'] = 0
            
        return dataframe
    
    def custom_stoploss(self, pair: str, trade: 'Trade', current_time: datetime,
                       current_rate: float, current_profit: float, **kwargs) -> float:
        """
        Stop loss adaptativo basado en volatilidad
        """
        
        # Stop loss base
        stoploss_value = self.stoploss
        
        # Si tenemos ganancia > 1%, reducir stop loss
        if current_profit > 0.01:
            stoploss_value = -0.015  # Stop loss más estrecho
            
        # Si tenemos ganancia > 3%, usar trailing stop agresivo  
        if current_profit > 0.03:
            stoploss_value = -0.01
            
        return stoploss_value
    
    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:
        """
        Confirmación final antes de entrada
        """
        
        # Verificaciones adicionales de seguridad
        current_date = current_time.date()
        
        # No más trades si ya operamos este par hoy
        if current_date in self._daily_trades and pair in self._daily_trades[current_date]:
            self.logger.info(f"❌ Trade bloqueado: {pair} ya operado hoy")
            return False
            
        # No operar en fines de semana con baja liquidez
        if current_time.weekday() >= 5:  # Sábado=5, Domingo=6
            self.logger.info(f"❌ Trade bloqueado: {pair} - fin de semana")
            return False
            
        self.logger.info(f"✅ Trade confirmado: {pair} - {entry_tag}")
        return True
