# source: https://raw.githubusercontent.com/tuncaykurt/agentlar-workspace/6d5f1208928f25c619186faf80a14e28e14fa919/projeler/Freqtrade_Core/user_data/strategies/SmartScannerStrategy.py
"""
Github_tuncaykurt_agentlar_workspace__SmartScannerStrategy__20260603_145734 — Kripto Bot Platform'un Smart Scanner stratejisinin
Freqtrade Hyperopt uyumlu versiyonu.

Bu strateji, Smart Scanner'ın kullandığı tüm indikatörleri (RSI, ADX, MACD,
BB, ATR, Supertrend, Volume, EMA200) Freqtrade formatında hesaplar ve
Hyperopt ile optimize edilebilir parametreler tanımlar.

Hyperopt çalıştırma:
    freqtrade hyperopt --strategy Github_tuncaykurt_agentlar_workspace__SmartScannerStrategy__20260603_145734 --hyperopt-loss SharpeHyperOptLoss \
        --spaces buy sell --epochs 500 -j 4

Backtest çalıştırma:
    freqtrade backtesting --strategy Github_tuncaykurt_agentlar_workspace__SmartScannerStrategy__20260603_145734 --timerange 20250101-20250601
"""
from freqtrade.strategy import IStrategy, IntParameter, DecimalParameter, BooleanParameter
from pandas import DataFrame
import talib.abstract as ta
import freqtrade.vendor.qtpylib.indicators as qtpylib
import numpy as np


class Github_tuncaykurt_agentlar_workspace__SmartScannerStrategy__20260603_145734(IStrategy):
    """
    Smart Scanner'ın Freqtrade Hyperopt uyumlu versiyonu.
    Tüm parametreler Hyperopt ile optimize edilebilir.
    Sonuçlar Kripto Bot Platform'a aktarılarak AI prompt'larını güçlendirir.
    """
    INTERFACE_VERSION = 3
    timeframe = '5m'

    # ── Temel strateji parametreleri ──
    minimal_roi = {
        "120": 0.005,   # 2 saat sonra %0.5 kâr yeterli
        "60": 0.01,     # 1 saat sonra %1
        "30": 0.02,     # 30dk sonra %2
        "0": 0.04       # Anında %4
    }
    stoploss = -0.05    # -%5 stop loss
    trailing_stop = True
    trailing_stop_positive = 0.003   # %0.3 kârda trailing aktif
    trailing_stop_positive_offset = 0.005  # %0.5 kârdan itibaren
    trailing_only_offset_is_reached = True

    # Fee: MEXC futures = %0.02 giriş + %0.02 çıkış
    order_types = {
        "entry": "market",
        "exit": "market",
        "stoploss": "market",
        "stoploss_on_exchange": False,
    }

    # ══════════════════════════════════════════════════════════════
    #  HYPEROPT OPTİMİZE EDİLEBİLİR PARAMETRELER
    #  Bu parametreler Smart Scanner'a aktarılacak optimal değerler
    # ══════════════════════════════════════════════════════════════

    # ── RSI parametreleri ──
    buy_rsi_lower = IntParameter(15, 35, default=28, space="buy", optimize=True)
    buy_rsi_upper = IntParameter(40, 65, default=50, space="buy", optimize=True)
    sell_rsi = IntParameter(65, 85, default=72, space="sell", optimize=True)

    # ── ADX parametreleri ──
    buy_adx_min = IntParameter(15, 35, default=22, space="buy", optimize=True)

    # ── MACD parametreleri (ArXiv 2024: 17,21,15 kripto-optimize) ──
    macd_fast = IntParameter(8, 21, default=17, space="buy", optimize=True)
    macd_slow = IntParameter(18, 30, default=21, space="buy", optimize=True)
    macd_signal = IntParameter(5, 18, default=15, space="buy", optimize=True)

    # ── Volume parametreleri ──
    buy_volume_min = DecimalParameter(1.0, 3.0, default=1.5, decimals=1, space="buy", optimize=True)

    # ── Bollinger Band parametreleri ──
    bb_period = IntParameter(15, 25, default=20, space="buy", optimize=True)
    buy_bb_enabled = BooleanParameter(default=True, space="buy", optimize=True)

    # ── ATR% filtre ──
    buy_atr_min = DecimalParameter(0.1, 1.0, default=0.3, decimals=2, space="buy", optimize=True)
    buy_atr_max = DecimalParameter(2.0, 5.0, default=4.0, decimals=1, space="buy", optimize=True)

    # ── EMA trend filtre ──
    buy_ema_trend = BooleanParameter(default=True, space="buy", optimize=True)

    # ── Supertrend filtre ──
    buy_supertrend = BooleanParameter(default=True, space="buy", optimize=True)

    # ── Short parametreleri ──
    sell_rsi_short = IntParameter(65, 85, default=75, space="sell", optimize=True)
    sell_adx_min_short = IntParameter(15, 35, default=22, space="sell", optimize=True)

    def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        """Smart Scanner'ın kullandığı tüm indikatörleri hesapla."""

        # ── RSI (14 periyot — standart) ──
        dataframe['rsi'] = ta.RSI(dataframe, timeperiod=14)

        # ── ADX (13 periyot — kripto optimize) ──
        dataframe['adx'] = ta.ADX(dataframe, timeperiod=13)
        dataframe['plus_di'] = ta.PLUS_DI(dataframe, timeperiod=13)
        dataframe['minus_di'] = ta.MINUS_DI(dataframe, timeperiod=13)

        # ── MACD (parametreler Hyperopt ile optimize edilecek) ──
        # Varsayılan: 17,21,15 (kripto-optimize)
        for fast in range(8, 22):
            for slow in range(18, 31):
                if fast >= slow:
                    continue
                for sig in range(5, 19):
                    key = f'macd_{fast}_{slow}_{sig}'
                    if key not in dataframe.columns:
                        macd = ta.MACD(dataframe, fastperiod=fast,
                                       slowperiod=slow, signalperiod=sig)
                        dataframe[f'macd_hist_{fast}_{slow}_{sig}'] = macd['macdhist']

        # Varsayılan MACD (17,21,15) her zaman hesapla
        macd = ta.MACD(dataframe, fastperiod=17, slowperiod=21, signalperiod=15)
        dataframe['macd'] = macd['macd']
        dataframe['macd_signal'] = macd['macdsignal']
        dataframe['macd_hist'] = macd['macdhist']

        # ── Bollinger Bands (20, 2 std) ──
        for period in range(15, 26):
            bollinger = qtpylib.bollinger_bands(
                qtpylib.typical_price(dataframe), window=period, stds=2
            )
            dataframe[f'bb_lower_{period}'] = bollinger['lower']
            dataframe[f'bb_upper_{period}'] = bollinger['upper']
            dataframe[f'bb_mid_{period}'] = bollinger['mid']
            dataframe[f'bb_width_{period}'] = (
                (bollinger['upper'] - bollinger['lower']) / bollinger['mid']
            )

        # ── ATR (14 periyot) ──
        dataframe['atr'] = ta.ATR(dataframe, timeperiod=14)
        dataframe['atr_pct'] = (dataframe['atr'] / dataframe['close']) * 100

        # ── Volume Ratio ──
        dataframe['vol_avg'] = dataframe['volume'].rolling(20).mean()
        dataframe['vol_ratio'] = dataframe['volume'] / dataframe['vol_avg']

        # ── EMA'lar ──
        dataframe['ema9'] = ta.EMA(dataframe, timeperiod=9)
        dataframe['ema21'] = ta.EMA(dataframe, timeperiod=21)
        dataframe['ema55'] = ta.EMA(dataframe, timeperiod=55)
        dataframe['ema200'] = ta.EMA(dataframe, timeperiod=200)

        # EMA200 mesafesi
        dataframe['ema200_dist'] = (
            (dataframe['close'] - dataframe['ema200']) / dataframe['ema200'] * 100
        )

        # ── Supertrend (10, 3.0) ──
        dataframe = self._calc_supertrend(dataframe, period=10, multiplier=3.0)

        # ── Stochastic ──
        stoch = ta.STOCHF(dataframe, fastk_period=14, fastd_period=3)
        dataframe['stoch_k'] = stoch['fastk']
        dataframe['stoch_d'] = stoch['fastd']

        # ── MFI ──
        dataframe['mfi'] = ta.MFI(dataframe, timeperiod=14)

        # ── OBV ──
        dataframe['obv'] = ta.OBV(dataframe)

        # ── Rejim tespiti ──
        dataframe['regime'] = 'MEAN_REVERTING'
        dataframe.loc[
            (dataframe['atr_pct'] > 3.0) | (dataframe['vol_ratio'] > 3.0),
            'regime'
        ] = 'HIGH_VOLATILITY'
        dataframe.loc[
            (dataframe['adx'] > 22) & (dataframe['regime'] != 'HIGH_VOLATILITY'),
            'regime'
        ] = 'TRENDING'
        dataframe.loc[
            (dataframe['adx'] < 18) & (dataframe[f'bb_width_20'] < 0.02) &
            (dataframe['regime'] == 'MEAN_REVERTING'),
            'regime'
        ] = 'LOW_VOLATILITY'

        return dataframe

    def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        """Giriş sinyalleri — Smart Scanner mantığı."""

        # Dinamik MACD histogram key
        macd_key = f'macd_hist_{self.macd_fast.value}_{self.macd_slow.value}_{self.macd_signal.value}'
        if macd_key not in dataframe.columns:
            macd_key = 'macd_hist'  # fallback

        bb_lower_key = f'bb_lower_{self.bb_period.value}'
        bb_upper_key = f'bb_upper_{self.bb_period.value}'

        # ── LONG giriş koşulları ──
        conditions_long = [
            # RSI aşırı satım bölgesinde
            (dataframe['rsi'] < self.buy_rsi_lower.value),
            # ADX minimum trend gücü
            (dataframe['adx'] > self.buy_adx_min.value),
            # MACD momentum (histogram pozitife dönüyor veya pozitif)
            (dataframe[macd_key] > dataframe[macd_key].shift(1)),
            # Volume konfirmasyonu
            (dataframe['vol_ratio'] > self.buy_volume_min.value),
            # ATR% filtre (çok düşük veya çok yüksek volatilite dışla)
            (dataframe['atr_pct'] > self.buy_atr_min.value),
            (dataframe['atr_pct'] < self.buy_atr_max.value),
            # Volume sıfır olmamalı
            (dataframe['volume'] > 0),
        ]

        # Opsiyonel: BB alt banda yakınlık
        if self.buy_bb_enabled.value:
            conditions_long.append(
                dataframe['close'] < dataframe[bb_lower_key] * 1.01
            )

        # Opsiyonel: EMA trend filtresi
        if self.buy_ema_trend.value:
            conditions_long.append(
                dataframe['ema9'] > dataframe['ema21']
            )

        # Opsiyonel: Supertrend filtresi
        if self.buy_supertrend.value:
            conditions_long.append(
                dataframe['supertrend_dir'] == 1
            )

        # Koşulları uygula
        if conditions_long:
            combined = conditions_long[0]
            for c in conditions_long[1:]:
                combined = combined & c
            dataframe.loc[combined, 'enter_long'] = 1

        # ── SHORT giriş koşulları ──
        conditions_short = [
            (dataframe['rsi'] > self.sell_rsi_short.value),
            (dataframe['adx'] > self.sell_adx_min_short.value),
            (dataframe[macd_key] < dataframe[macd_key].shift(1)),
            (dataframe['vol_ratio'] > self.buy_volume_min.value),
            (dataframe['atr_pct'] > self.buy_atr_min.value),
            (dataframe['atr_pct'] < self.buy_atr_max.value),
            (dataframe['volume'] > 0),
        ]

        if self.buy_bb_enabled.value:
            conditions_short.append(
                dataframe['close'] > dataframe[bb_upper_key] * 0.99
            )

        if self.buy_ema_trend.value:
            conditions_short.append(
                dataframe['ema9'] < dataframe['ema21']
            )

        if self.buy_supertrend.value:
            conditions_short.append(
                dataframe['supertrend_dir'] == -1
            )

        if conditions_short:
            combined = conditions_short[0]
            for c in conditions_short[1:]:
                combined = combined & c
            dataframe.loc[combined, 'enter_short'] = 1

        return dataframe

    def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        """Çıkış sinyalleri."""

        # Long çıkış: RSI overbought veya supertrend flip
        dataframe.loc[
            (
                (dataframe['rsi'] > self.sell_rsi.value) |
                (dataframe['supertrend_dir'] == -1)
            ) & (dataframe['volume'] > 0),
            'exit_long'
        ] = 1

        # Short çıkış: RSI oversold veya supertrend flip
        dataframe.loc[
            (
                (dataframe['rsi'] < self.buy_rsi_lower.value) |
                (dataframe['supertrend_dir'] == 1)
            ) & (dataframe['volume'] > 0),
            'exit_short'
        ] = 1

        return dataframe

    @staticmethod
    def _calc_supertrend(df: DataFrame, period: int = 10, multiplier: float = 3.0) -> DataFrame:
        """Supertrend hesapla (TA-Lib'de yok, manuel)."""
        hl2 = (df['high'] + df['low']) / 2
        atr = ta.ATR(df, timeperiod=period)

        upper_band = hl2 + multiplier * atr
        lower_band = hl2 - multiplier * atr

        supertrend = np.zeros(len(df))
        direction = np.ones(len(df))

        for i in range(1, len(df)):
            if np.isnan(upper_band.iloc[i]):
                continue

            if direction[i-1] == 1:
                lower_band.iloc[i] = max(lower_band.iloc[i], lower_band.iloc[i-1])
            else:
                upper_band.iloc[i] = min(upper_band.iloc[i], upper_band.iloc[i-1])

            if direction[i-1] == 1:
                if df['close'].iloc[i] < lower_band.iloc[i]:
                    direction[i] = -1
                    supertrend[i] = upper_band.iloc[i]
                else:
                    direction[i] = 1
                    supertrend[i] = lower_band.iloc[i]
            else:
                if df['close'].iloc[i] > upper_band.iloc[i]:
                    direction[i] = 1
                    supertrend[i] = lower_band.iloc[i]
                else:
                    direction[i] = -1
                    supertrend[i] = upper_band.iloc[i]

        df['supertrend'] = supertrend
        df['supertrend_dir'] = direction.astype(int)
        return df
