# source: https://raw.githubusercontent.com/smpsandro1239/Freqtrade/b00d7010e72c9883db45ecc684e4b31fbd206c0b/user_data/strategies/SampleStrategyB.py
import numpy as np
import pandas as pd
from pandas import DataFrame
import talib.abstract as ta
from freqtrade.strategy import IStrategy, DecimalParameter, IntParameter
from datetime import datetime
from typing import Optional
import logging

# Configurar logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

class Github_smpsandro1239_Freqtrade__SampleStrategyB__20250816_051022(IStrategy):
    """
    Estratégia B - Baseada em RSI, MACD e Bollinger Bands
    Estratégia conservadora focada em reversões de tendência
    """
    INTERFACE_VERSION = 3

    # Configurações básicas
    timeframe = '15m'  # Timeframe diferente da Strategy A
    stoploss = -0.08   # Stop loss mais conservador
    minimal_roi = {
        "0": 0.06,     # ROI inicial maior (mais conservador)
        "10": 0.04,
        "20": 0.02,
        "40": 0.01,
        "60": 0.005
    }

    # Sem trailing stop (diferente da A)
    trailing_stop = False

    # Parâmetros otimizáveis
    rsi_period = IntParameter(10, 20, default=14, space='buy')
    rsi_oversold = IntParameter(25, 35, default=30, space='buy')
    rsi_overbought = IntParameter(65, 80, default=70, space='sell')
    
    bb_period = IntParameter(15, 25, default=20, space='buy')
    bb_std = DecimalParameter(1.8, 2.2, default=2.0, space='buy')

    def __init__(self, config: dict) -> None:
        super().__init__(config)
        self.logger = logging.getLogger(__name__)

    @property
    def protections(self):
        """Proteções básicas para Strategy B"""
        return [
            {
                "method": "CooldownPeriod",
                "stop_duration_candles": 3
            },
            {
                "method": "StoplossGuard",
                "lookback_period_candles": 24,
                "trade_limit": 2,
                "stop_duration_candles": 12,
                "only_per_pair": False
            }
        ]

    def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        """
        Indicadores para Strategy B - RSI, MACD, Bollinger Bands
        Foco em reversões e momentum
        """
        
        # RSI
        dataframe['rsi'] = ta.RSI(dataframe['close'], timeperiod=self.rsi_period.value)
        
        # MACD
        macd = ta.MACD(dataframe['close'])
        dataframe['macd'] = macd['macd']
        dataframe['macdsignal'] = macd['macdsignal']
        dataframe['macdhist'] = macd['macdhist']
        
        # Bollinger Bands
        bollinger = ta.BBANDS(dataframe['close'], 
                             timeperiod=self.bb_period.value, 
                             nbdevup=self.bb_std.value, 
                             nbdevdn=self.bb_std.value)
        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'])
        
        # Médias móveis simples
        dataframe['sma_20'] = ta.SMA(dataframe['close'], timeperiod=20)
        dataframe['sma_50'] = ta.SMA(dataframe['close'], timeperiod=50)
        
        # Volume
        dataframe['volume_sma'] = ta.SMA(dataframe['volume'], timeperiod=20)
        
        # ATR para volatilidade
        dataframe['atr'] = ta.ATR(dataframe['high'], dataframe['low'], dataframe['close'], timeperiod=14)
        
        # Stochastic
        stoch = ta.STOCH(dataframe['high'], dataframe['low'], dataframe['close'])
        dataframe['stoch_k'] = stoch['slowk']
        dataframe['stoch_d'] = stoch['slowd']
        
        return dataframe

    def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        """
        Sinais de entrada para Strategy B
        Baseado em RSI oversold + MACD bullish + preço próximo à BB inferior
        """
        
        # Condições de entrada (mais conservadoras)
        rsi_condition = (
            dataframe['rsi'] < self.rsi_oversold.value
        )
        
        macd_condition = (
            (dataframe['macd'] > dataframe['macdsignal']) &
            (dataframe['macdhist'] > 0)
        )
        
        bb_condition = (
            (dataframe['close'] <= dataframe['bb_lower'] * 1.02) |  # Próximo à banda inferior
            (dataframe['bb_percent'] < 0.2)  # Na parte inferior das bandas
        )
        
        volume_condition = (
            dataframe['volume'] > dataframe['volume_sma'] * 0.8
        )
        
        trend_condition = (
            dataframe['close'] > dataframe['sma_50']  # Tendência de alta geral
        )
        
        stoch_condition = (
            (dataframe['stoch_k'] < 30) &  # Stochastic oversold
            (dataframe['stoch_k'] > dataframe['stoch_d'])  # K cruzando D para cima
        )
        
        # Combinar condições
        dataframe.loc[
            (
                rsi_condition &
                (macd_condition | bb_condition) &  # MACD OU BB (mais flexível)
                volume_condition &
                (trend_condition | stoch_condition)  # Tendência OU Stochastic
            ),
            'enter_long'
        ] = 1
        
        # Tag para identificação
        dataframe.loc[dataframe['enter_long'] == 1, 'enter_tag'] = (
            f"RSI:{dataframe['rsi']:.1f} | "
            f"MACD:{dataframe['macd']:.4f} | "
            f"BB%:{dataframe['bb_percent']:.2f} | "
            f"Stoch:{dataframe['stoch_k']:.1f}"
        )
        
        return dataframe

    def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        """
        Sinais de saída para Strategy B
        Baseado em RSI overbought + MACD bearish + preço próximo à BB superior
        """
        
        # Condições de saída
        rsi_condition = (
            dataframe['rsi'] > self.rsi_overbought.value
        )
        
        macd_condition = (
            (dataframe['macd'] < dataframe['macdsignal']) &
            (dataframe['macdhist'] < 0)
        )
        
        bb_condition = (
            (dataframe['close'] >= dataframe['bb_upper'] * 0.98) |  # Próximo à banda superior
            (dataframe['bb_percent'] > 0.8)  # Na parte superior das bandas
        )
        
        volume_condition = (
            dataframe['volume'] > dataframe['volume_sma'] * 1.2  # Volume alto para confirmação
        )
        
        stoch_condition = (
            (dataframe['stoch_k'] > 70) &  # Stochastic overbought
            (dataframe['stoch_k'] < dataframe['stoch_d'])  # K cruzando D para baixo
        )
        
        # Combinar condições de saída
        dataframe.loc[
            (
                rsi_condition &
                (macd_condition | bb_condition) &  # MACD OU BB
                (volume_condition | stoch_condition)  # Volume OU Stochastic
            ),
            'exit_long'
        ] = 1
        
        # Tag para identificação
        dataframe.loc[dataframe['exit_long'] == 1, 'exit_tag'] = (
            f"RSI:{dataframe['rsi']:.1f} | "
            f"MACD:{dataframe['macd']:.4f} | "
            f"BB%:{dataframe['bb_percent']:.2f} | "
            f"Stoch:{dataframe['stoch_k']:.1f}"
        )
        
        return dataframe

    def custom_stoploss(self, pair: str, trade: 'Trade', current_time: datetime,
                        current_rate: float, current_profit: float, **kwargs) -> float:
        """
        Stop loss dinâmico para Strategy B
        Mais conservador que a Strategy A
        """
        
        # Trailing stop baseado no lucro
        if current_profit > 0.08:  # 8% de lucro
            return 0.04  # Proteger 4%
        elif current_profit > 0.06:  # 6% de lucro
            return 0.03  # Proteger 3%
        elif current_profit > 0.04:  # 4% de lucro
            return 0.02  # Proteger 2%
        elif current_profit > 0.02:  # 2% de lucro
            return 0.01  # Proteger 1%
        
        # Stop loss baseado no tempo (mais conservador)
        trade_duration = (current_time - trade.open_date_utc).total_seconds()
        
        if trade_duration < 1800:  # Primeiros 30 minutos
            return self.stoploss  # Stop loss original
        elif trade_duration < 3600:  # Primeira hora
            return self.stoploss * 0.8  # Reduzir stop loss
        else:  # Após 1 hora
            return self.stoploss * 0.9  # Stop loss mais solto
        
        return self.stoploss