# source: https://raw.githubusercontent.com/remiotore/freqtrade/44beaeb6a420cd8e9f2e4ea93e11d6cfa192ee03/strategies/GGShotStrategy.py
# pragma pylint: disable=missing-docstring, invalid-name, pointless-string-statement
# flake8: noqa: F401
# isort: skip_file
# --- Do not remove these libs ---
import numpy as np
import pandas as pd
from pandas import DataFrame
from datetime import datetime
from typing import Optional, Union

from freqtrade.strategy import (BooleanParameter, CategoricalParameter, DecimalParameter,
                                IStrategy, IntParameter)

# --------------------------------
# Add your lib to import here
import talib.abstract as ta
import freqtrade.vendor.qtpylib.indicators as qtpylib


class Github_remiotore_freqtrade__GGShotStrategy__20260111_210550(IStrategy):
    """
    GG-SHOT AI Strategy for Freqtrade
    Pine Script'ten çevrilmiş Freqtrade versiyonu
    
    Author: GG-SHOT AI Team
    github: https://github.com/freqtrade/freqtrade-strategies
    
    How to use it?
    > freqtrade trade --strategy Github_remiotore_freqtrade__GGShotStrategy__20260111_210550
    """

    INTERFACE_VERSION = 3

    # Strategy interface version - allow new iterations of the strategy interface.
    # Check the documentation or the Sample strategy to get the latest version.

    # Optimal timeframe for the strategy.
    timeframe = '5m'

    # Can this strategy go short?
    can_short: bool = True

    # Minimal ROI designed for the strategy.
    # This attribute will be overridden if the config file contains "minimal_roi".
    minimal_roi = {
        "0": 0.04,      # TP4: 4%
        "60": 0.025,    # TP3: 2.5% 
        "120": 0.011,   # TP2: 1.1%
        "180": 0.005,   # TP1: 0.5%
    }

    # Optimal stoploss designed for the strategy.
    # This attribute will be overridden if the config file contains "stoploss".
    stoploss = -0.015  # SL: -1.5%

    # Trailing stoploss
    trailing_stop = False
    trailing_stop_positive = None
    trailing_stop_positive_offset = 0.0
    trailing_only_offset_is_reached = False

    # Exit signal configuration
    use_exit_signal = True
    exit_profit_only = False
    exit_profit_offset = 0.0
    ignore_roi_if_entry_signal = False

    # Number of candles the strategy requires before producing valid signals
    startup_candle_count: int = 300

    # Strategy parameters - Pine Script benzeri parametreler
    # RSI Parameters
    rsi_period = IntParameter(5, 21, default=7, space="buy")
    rsi_top_limit = IntParameter(40, 60, default=45, space="buy")
    rsi_bot_limit = IntParameter(5, 20, default=10, space="buy")
    
    # Bollinger Bands Parameters
    bb_period = IntParameter(50, 200, default=100, space="buy")
    bb_std = DecimalParameter(1.0, 3.0, default=1.5, space="buy")
    
    # ATR Parameters
    atr_period = IntParameter(10, 30, default=14, space="buy")
    atr_ma_period = IntParameter(3, 10, default=5, space="buy")
    
    # Fibonacci Parameters
    sensitivity = IntParameter(100, 300, default=200, space="buy")
    
    # TP/SL Parameters
    tp1_percent = DecimalParameter(0.3, 1.0, default=0.5, space="sell")
    tp2_percent = DecimalParameter(0.8, 2.0, default=1.1, space="sell")
    tp3_percent = DecimalParameter(1.5, 4.0, default=2.5, space="sell")
    sl_percent = DecimalParameter(1.0, 3.0, default=1.5, space="sell")

    # Protection parameters
    @property
    def protections(self):
        return [
            {
                "method": "CooldownPeriod",
                "stop_duration_candles": 2
            },
            {
                "method": "MaxDrawdown",
                "lookback_period_candles": 48,
                "trade_limit": 20,
                "stop_duration_candles": 4,
                "max_allowed_drawdown": 0.2
            },
            {
                "method": "StoplossGuard",
                "lookback_period_candles": 24,
                "trade_limit": 4,
                "stop_duration_candles": 2,
                "only_per_pair": False
            },
            {
                "method": "LowProfitPairs",
                "lookback_period_candles": 480,
                "trade_limit": 2,
                "stop_duration_candles": 60,
                "required_profit": 0.02
            }
        ]

    def informative_pairs(self):
        """
        Define additional, informative pair/interval combinations to be cached from the exchange.
        These pairs will not be traded, but they are cached to have informative data available.
        """
        return []

    def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        """
        Pine Script'teki indikatörlerin Freqtrade versiyonu
        Look-ahead bias olmadan hesaplama
        """
        
        # Veri kontrolü
        if dataframe.empty:
            return dataframe
            
        try:
            # RSI Calculation (Pine Script: ta.rsi(close, 7))
            dataframe['rsi'] = ta.RSI(dataframe, timeperiod=self.rsi_period.value)
            
            # Bollinger Bands (Pine Script BB sinyali için) - qtpylib kullan
            bollinger = qtpylib.bollinger_bands(
                qtpylib.typical_price(dataframe), 
                window=self.bb_period.value, 
                stds=self.bb_std.value
            )
            dataframe['bb_lower'] = bollinger['lower']
            dataframe['bb_middle'] = bollinger['mid']
            dataframe['bb_upper'] = bollinger['upper']
            
            # ATR for filtering (Pine Script benzeri)
            dataframe['atr'] = ta.ATR(dataframe, timeperiod=self.atr_period.value)
            dataframe['atr_ma'] = ta.EMA(dataframe['atr'], timeperiod=self.atr_ma_period.value)
            
            # Fibonacci Levels (Pine Script'teki sensitivity ile)
            # Rolling highest/lowest - bias-free
            dataframe['high_line'] = dataframe['high'].rolling(
                window=self.sensitivity.value, min_periods=1
            ).max()
            dataframe['low_line'] = dataframe['low'].rolling(
                window=self.sensitivity.value, min_periods=1
            ).min()
            
            # Channel range calculation
            dataframe['channel_range'] = dataframe['high_line'] - dataframe['low_line']
            
            # Fibonacci levels (Pine Script benzeri)
            dataframe['fib_236'] = dataframe['high_line'] - (dataframe['channel_range'] * 0.236)
            dataframe['fib_5'] = dataframe['high_line'] - (dataframe['channel_range'] * 0.5)  # Trend line
            dataframe['fib_786'] = dataframe['high_line'] - (dataframe['channel_range'] * 0.786)
            dataframe['fib_456'] = dataframe['high_line'] - (dataframe['channel_range'] * (-0.456))  # SL line
            
            # Pine Script'teki imba_trend_line
            dataframe['imba_trend_line'] = dataframe['fib_5']
            
            # OC2 calculation (Pine Script: oc2 = (close+open)/2)
            dataframe['oc2'] = (dataframe['close'] + dataframe['open']) / 2
            
            # Güvenli BB Signal calculation
            dataframe['bb_signal'] = 0
            
            # Veri türlerini kontrol et ve güvenli karşılaştırma yap
            try:
                # Long BB condition
                long_bb_mask = (
                    (dataframe['close'] > dataframe['bb_upper']) & 
                    (dataframe['oc2'] > dataframe['bb_upper']) &
                    ~dataframe['close'].isna() &
                    ~dataframe['bb_upper'].isna() &
                    ~dataframe['oc2'].isna()
                )
                dataframe.loc[long_bb_mask, 'bb_signal'] = 1
                
                # Short BB condition
                short_bb_mask = (
                    (dataframe['close'] < dataframe['bb_lower']) & 
                    (dataframe['oc2'] < dataframe['bb_lower']) &
                    ~dataframe['close'].isna() &
                    ~dataframe['bb_lower'].isna() &
                    ~dataframe['oc2'].isna()
                )
                dataframe.loc[short_bb_mask, 'bb_signal'] = -1
                
            except Exception as e:
                print(f"BB Signal hesaplama hatası: {e}")
                dataframe['bb_signal'] = 0
            
            # Trend Filter (Pine Script: ATR ve RSI bazlı)
            dataframe['atr_condition'] = (
                (~dataframe['atr'].isna()) & 
                (~dataframe['atr_ma'].isna()) &
                (dataframe['atr'] >= dataframe['atr_ma'])
            )
            dataframe['rsi_condition'] = (
                (~dataframe['rsi'].isna()) &
                ((dataframe['rsi'] > self.rsi_top_limit.value) | 
                 (dataframe['rsi'] < self.rsi_bot_limit.value))
            )
            dataframe['trend_filter'] = dataframe['atr_condition'] | dataframe['rsi_condition']
            
            # Pine Script can_long ve can_short mantığı
            dataframe['can_long'] = (
                (~dataframe['close'].isna()) &
                (~dataframe['imba_trend_line'].isna()) &
                (~dataframe['fib_236'].isna()) &
                (dataframe['close'] >= dataframe['imba_trend_line']) &
                (dataframe['close'] >= dataframe['fib_236']) &
                dataframe['trend_filter']
            )
            
            dataframe['can_short'] = (
                (~dataframe['close'].isna()) &
                (~dataframe['imba_trend_line'].isna()) &
                (~dataframe['fib_786'].isna()) &
                (dataframe['close'] <= dataframe['imba_trend_line']) &
                (dataframe['close'] <= dataframe['fib_786']) &
                dataframe['trend_filter']
            )
            
            # Final signals (Pine Script mantığı)
            dataframe['long_signal'] = (
                (dataframe['bb_signal'] == 1) &
                dataframe['can_long'] &
                (~dataframe['volume'].isna()) &
                (dataframe['volume'] > 0)
            )
            
            dataframe['short_signal'] = (
                (dataframe['bb_signal'] == -1) &
                dataframe['can_short'] &
                (~dataframe['volume'].isna()) &
                (dataframe['volume'] > 0)
            )
            
        except Exception as e:
            print(f"İndikatör hesaplama hatası: {e}")
            # Hata durumunda güvenli default değerler
            for col in ['rsi', 'bb_upper', 'bb_lower', 'bb_middle', 'atr', 'atr_ma', 
                       'long_signal', 'short_signal', 'bb_signal']:
                if col not in dataframe.columns:
                    if col.endswith('_signal'):
                        dataframe[col] = False
                    else:
                        dataframe[col] = np.nan
        
        return dataframe

    def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        """
        Pine Script'teki buy/sell mantığı
        Based on TA indicators, populates the entry signal for the given dataframe
        """
        
        # Long Entry Conditions (Pine Script: buy signal)
        dataframe.loc[
            (
                dataframe['long_signal'] &
                (dataframe['volume'] > 0)  # Volume check
            ),
            ['enter_long', 'enter_tag']
        ] = (1, 'gg_long')

        # Short Entry Conditions (Pine Script: sell signal)  
        dataframe.loc[
            (
                dataframe['short_signal'] &
                (dataframe['volume'] > 0)  # Volume check
            ),
            ['enter_short', 'enter_tag']
        ] = (1, 'gg_short')

        return dataframe

    def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        """
        Pine Script'teki exit mantığı
        Based on TA indicators, populates the exit signal for the given dataframe
        """
        
        # Long Exit Conditions (ters sinyal gelirse çık)
        dataframe.loc[
            (
                dataframe['short_signal'] &
                (dataframe['volume'] > 0)
            ),
            ['exit_long', 'exit_tag']
        ] = (1, 'gg_long_exit')

        # Short Exit Conditions (ters sinyal gelirse çık)
        dataframe.loc[
            (
                dataframe['long_signal'] &
                (dataframe['volume'] > 0)
            ),
            ['exit_short', 'exit_tag']
        ] = (1, 'gg_short_exit')

        return dataframe

    def custom_stoploss(self, pair: str, trade: 'Trade', current_time: datetime,
                       current_rate: float, current_profit: float, **kwargs) -> float:
        """
        Pine Script'teki dynamic SL mantığı
        Custom stoploss logic, returning the new distance relative to current_rate (as ratio).
        """
        
        # SL percentage from parameters
        sl_ratio = -self.sl_percent.value / 100
        
        return sl_ratio

    def custom_exit(self, pair: str, trade: 'Trade', current_time: datetime, current_rate: float,
                   current_profit: float, **kwargs):
        """
        Pine Script'teki multi-TP mantığı
        Custom exit signal logic
        """
        
        # Multi-TP system (Pine Script benzeri)
        if current_profit >= (self.tp1_percent.value / 100):
            return 'tp1_hit'
        elif current_profit >= (self.tp2_percent.value / 100):
            return 'tp2_hit'  
        elif current_profit >= (self.tp3_percent.value / 100):
            return 'tp3_hit'
            
        return None

    def leverage(self, pair: str, current_time: datetime, current_rate: float,
                proposed_leverage: float, max_leverage: float, entry_tag: Optional[str], 
                side: str, **kwargs) -> float:
        """
        Leverage kontrolü
        """
        return min(proposed_leverage, 10.0)  # Max 10x leverage

    def confirm_trade_entry(self, pair: str, order_type: str, amount: float, rate: float,
                           time_in_force: str, current_time: datetime, entry_tag: Optional[str], 
                           side: str, **kwargs) -> bool:
        """
        Pine Script benzeri trade confirmation
        """
        
        # Volume check
        if 'volume' in kwargs and kwargs['volume'] <= 0:
            return False
            
        return True

    def adjust_trade_position(self, trade: 'Trade', current_time: datetime,
                             current_rate: float, current_profit: float,
                             min_stake: Optional[float], max_stake: float, **kwargs):
        """
        Pine Script'teki multi-TP exit mantığı
        Position adjustment for multi-TP system
        """
        
        # Multi-TP system
        if current_profit >= (self.tp1_percent.value / 100):
            # TP1 hit - reduce position by 30%
            if not hasattr(trade, 'tp1_hit'):
                trade.tp1_hit = True
                return -(trade.amount * 0.3)
                
        elif current_profit >= (self.tp2_percent.value / 100):
            # TP2 hit - reduce position by another 30%
            if not hasattr(trade, 'tp2_hit'):
                trade.tp2_hit = True
                return -(trade.amount * 0.3)
                
        elif current_profit >= (self.tp3_percent.value / 100):
            # TP3 hit - reduce position by 50% of remaining
            if not hasattr(trade, 'tp3_hit'):
                trade.tp3_hit = True
                return -(trade.amount * 0.5)
        
        return None

    def bot_start(self, **kwargs) -> None:
        """
        Bot başlatıldığında çalışır
        """
        print("🤖 GG-SHOT AI Strategy Started!")
        print("📊 Pine Script mantığı ile Freqtrade'de çalışıyor")
        print("⚙️ Parameters:")
        print(f"   RSI Period: {self.rsi_period.value}")
        print(f"   BB Period: {self.bb_period.value}")
        print(f"   Sensitivity: {self.sensitivity.value}")
        print(f"   TP1/TP2/TP3: {self.tp1_percent.value}%/{self.tp2_percent.value}%/{self.tp3_percent.value}%")
        print(f"   SL: {self.sl_percent.value}%")

    def bot_loop_start(self, **kwargs) -> None:
        """
        Her bot loop'unda çalışır
        """
        pass

    def check_entry_timeout(self, pair: str, trade: 'Trade', order: dict,
                           current_time: datetime, **kwargs) -> bool:
        """
        Entry order timeout check
        """
        return False

    def check_exit_timeout(self, pair: str, trade: 'Trade', order: dict,
                          current_time: datetime, **kwargs) -> bool:
        """
        Exit order timeout check
        """
        return False