# source: https://raw.githubusercontent.com/RoboticAutomations/freqtradestrategies/52c4bd40414645f079a980cf4486dc7af4061efb/AIRA%20-%20open%20code%20v2/strategies/bollinger/bbw1h.py
# directory_url: https://github.com/RoboticAutomations/freqtradestrategies/blob/main/AIRA - open code v2/strategies/bollinger/
# User: RoboticAutomations
# Repository: freqtradestrategies
# --------------------import numpy as np
import talib.abstract as ta
from freqtrade.strategy import IStrategy, merge_informative_pair
from pandas import DataFrame
from datetime import datetime
import freqtrade.vendor.qtpylib.indicators as qtpylib
from freqtrade.persistence import Trade

class Github_RoboticAutomations_freqtradestrategies__bbw1h__20260915_233254(IStrategy):
    """
    Github_RoboticAutomations_freqtradestrategies__bbw1h__20260915_233254 V2 (Revisi Safety)
    
    Logika Perbaikan:
    1. TREND FILTER: Hanya Long di atas EMA 200, Hanya Short di bawah EMA 200.
    2. HARD STOP: Stoploss fix -5% (tanpa dynamic SL yang berisiko).
    3. TIME LIMIT: Keluar otomatis jika 2 jam tidak profit (ROI).
    4. VOLATILITY FILTER: Tidak masuk saat BB Width > 10% (Market Menggila).
    """

    INTERFACE_VERSION = 3
    timeframe = '5m'
    
    # Izinkan Shorting
    can_short = True 

    # 1. STOPLOSS FIX (SAFETY NET)
    # Kita kunci di 5%. Jika market crash, langsung keluar.
    stoploss = -0.03 
    
    # 2. ROI (TIME LIMIT)
    # - Menit 0-60: Target profit tinggi (via custom_exit)
    # - Menit 60: Profit 1% ambil saja.
    # - Menit 120 (2 Jam): Jika masih impas (0%), keluar paksa (Dead Trade).
    minimal_roi = {
        "0": 1.0,   
        "60": 0.01, 
        "120": 0    
    }

    # Buffer Jarak Aman TP (0.5% sebelum dinding)
    EXIT_BUFFER = 0.005
    
    order_types = {
        'entry': 'limit',
        'exit': 'limit',
        'stoploss': 'market',
        'stoploss_on_exchange': False
    }

    def informative_pairs(self):
        pairs = self.dp.current_whitelist()
        return [(pair, '1h') for pair in pairs]

    def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        # ==========================================
        # 1. INDIKATOR 1H (INFORMATIVE)
        # ==========================================
        inf_tf = '1h'
        informative = self.dp.get_pair_dataframe(pair=metadata['pair'], timeframe=inf_tf)
        
        # Bollinger Bands 1H
        bollinger = qtpylib.bollinger_bands(qtpylib.typical_price(informative), window=20, stds=2)
        informative['bb_upper'] = bollinger['upper']
        informative['bb_lower'] = bollinger['lower']
        informative['bb_mid']   = bollinger['mid']
        
        # Hitung Lebar BB & Zona 30%
        bb_width_val = informative['bb_upper'] - informative['bb_lower']
        informative['zone_short_limit'] = informative['bb_upper'] - (bb_width_val * 0.30)
        informative['zone_long_limit'] = informative['bb_lower'] + (bb_width_val * 0.30)

# ... (Kode Bollinger Bands 1H sebelumnya biarkan saja) ...

        # ==========================================
        # B. ADX 4h (Resampling dari Data 1H)
        # ==========================================
        
        # 1. Buat salinan data 1h dan set index ke tanggal (wajib untuk resampling)
        resampled_data = informative.set_index('date')
        
        # 2. Ubah (Resample) data 1h menjadi candle 4h
        # Kita perlu Open, High, Low, Close yang valid untuk perhitungan ADX
        inform_4h = resampled_data.resample('4h').agg({
            'open': 'first',
            'high': 'max',  # High 4h adalah High tertinggi dari 6 candle 1h
            'low': 'min',   # Low 4h adalah Low terendah dari 6 candle 1h
            'close': 'last',
            'volume': 'sum'
        })
        
        # 3. Hitung ADX menggunakan data 4h yang baru dibuat
        inform_4h['adx_4h'] = ta.ADX(inform_4h, timeperiod=14)
        
        # 4. Bersihkan kolom tidak perlu (sisakan hanya adx_4h) agar ringan saat merge
        inform_4h = inform_4h[['adx_4h']]
        
        # 5. Gabungkan kembali ke data informative asli (1h)
        # Kita merge berdasarkan index waktu (date)
        informative = informative.merge(inform_4h, left_on='date', right_index=True, how='left')
        
        # 6. Isi nilai kosong (Forward Fill)
        # Karena data 4h cuma muncul setiap 6 baris sekali di data 1h, baris kosongnya harus diisi
        # dengan nilai terakhir yang tersedia.
        informative['adx_4h'] = informative['adx_4h'].ffill()
        
        # 7. LOGIKA ADX FALLING (Menggunakan data 4h)
        # Shift(1) di sini artinya kita membandingkan dengan ADX 4h periode sebelumnya
        # (Karena sudah di-ffill, shift(1) di candle 1h akan mengecek perubahan nilai 4h terakhir)
        informative['adx_falling'] = np.where(informative['adx_4h'] < informative['adx_4h'].shift(1), 1, 0)

        # ... (Lanjut ke merge_informative_pair seperti biasa) ...
        # Merge ke 5m
        dataframe = merge_informative_pair(dataframe, informative, self.timeframe, inf_tf, ffill=True)

        # ==========================================
        # 2. INDIKATOR 5M (MAIN)
        # ==========================================
        
        # MFI 5m (Trigger)
        dataframe['mfi'] = ta.MFI(dataframe)
        
        # EMA 200 (Trend Filter Wajib)
        dataframe['ema_200'] = ta.EMA(dataframe, timeperiod=200)
        
        # BB Width 5m (Volatility Filter)
        # Kita pakai data 1h yang sudah di-merge ('bb_upper_1h') untuk cek volatilitas global
        dataframe['bb_width_1h'] = (dataframe['bb_upper_1h'] - dataframe['bb_lower_1h']) / dataframe['bb_mid_1h']

        return dataframe

    def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        
        # V3: LOGIKA LEBIH LONGGAR (Supaya ada trade)
        
        dataframe.loc[
            (
                # 1. Trend & Volatility (DILONGGARKAN)
                (dataframe['adx_falling_1h'] == 1) &       # Market tenang/Sideways (Tetap dipakai)
                (dataframe['bb_width_1h'] < 0.20) &        # Diubah: Izinkan volatilitas sampai 20%
                
                # HAPUS EMA 200: Biarkan bot trading di kondisi sideways murni
                # (dataframe['close'] > dataframe['ema_200']) & 

                # 2. Location & Trigger (DILONGGARKAN)
                (dataframe['close'] < dataframe['zone_long_limit_1h']) & # Harga di Zona Bawah 30%
                (dataframe['mfi'] < 25)                                  # Diubah: MFI < 25 (lebih mudah masuk)
            ),
            'enter_long'] = 1

        dataframe.loc[
            (
                # 1. Trend & Volatility
                (dataframe['adx_falling_1h'] == 1) &
                (dataframe['bb_width_1h'] < 0.20) &
                
                # HAPUS EMA 200
                # (dataframe['close'] < dataframe['ema_200']) & 
                
                # 2. Location & Trigger
                (dataframe['close'] > dataframe['zone_short_limit_1h']) & # Harga di Zona Atas 30%
                (dataframe['mfi'] > 75)                                   # Diubah: MFI > 75 (lebih mudah masuk)
            ),
            'enter_short'] = 1

        return dataframe

    def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        return dataframe

    # SAYA HAPUS custom_stoploss AGAR MENGGUNAKAN HARD STOP -5% DI ATAS
    # Ini untuk mencegah SL 'lari' saat crash.

    def custom_exit(self, pair: str, trade: Trade, current_time: datetime, current_rate: float,
                    current_profit: float, **kwargs):
        
        dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe)
        last_candle = dataframe.iloc[-1]
        
        tag = "front_run_wall"

        # ---------------------------------------
        # TP POSISI LONG (Target: BB Upper 1H)
        # ---------------------------------------
        if trade.is_short is False:
            # Target Dinamis: Keluar sedikit di bawah Resistance
            target_price = last_candle['bb_upper_1h'] * (1 - self.EXIT_BUFFER)

            if current_rate >= target_price:
                return tag

        # ---------------------------------------
        # TP POSISI SHORT (Target: BB Lower 1H)
        # ---------------------------------------
        else:
            # Target Dinamis: Keluar sedikit di atas Support
            target_price = last_candle['bb_lower_1h'] * (1 + self.EXIT_BUFFER)

            if current_rate <= target_price:
                return tag

        return None