# source: https://raw.githubusercontent.com/zrowan1/CryptoBuddy/95337605e36ea45da681ce5626a68713a85f7eb4/user_data/strategies/MomentumRSI.py
from freqtrade.strategy import IStrategy
from pandas import DataFrame
import talib.abstract as ta
import numpy as np


class Github_zrowan1_CryptoBuddy__MomentumRSI__20260406_183845(IStrategy):
    """
    Github_zrowan1_CryptoBuddy__MomentumRSI__20260406_183845 V9 (Final Edition) — Bybit Spot USDT

    Orthogonale indicator-matrix:
      1. StochRSI  (momentum timing)
      2. Bollinger Bands (volatiliteits-uitrekking)
      3. ADX (directionele trendsterkte)

    Trendfilters:
      - 1h EMA 200 (lokaal regime)
      - BTC/USDT EMA 200 "Remora" macro-filter (informative pair)

    Risicobeheer:
      - Dynamische ATR-stoploss (Chandelier Exit) via custom_stoploss
      - MaxDrawdown + StoplossGuard protections
    """

    INTERFACE_VERSION = 3

    # ROI tabel
    minimal_roi = {
        "0": 0.15,
        "120": 0.08,
        "240": 0.04,
        "480": 0.02,
    }

    # Vangnet stoploss — custom_stoploss overschrijft dit dynamisch
    stoploss = -0.10

    # Oude trailing stop verwijderd; Chandelier Exit via custom_stoploss
    trailing_stop = False

    # Activeer dynamische ATR-stoploss
    use_custom_stoploss = True

    timeframe = "1h"
    startup_candle_count = 200

    # ATR multiplier voor Chandelier Exit
    atr_multiplier = 3.0

    # --- Informative Pairs: BTC macro-filter ("Remora") ---

    def informative_pairs(self):
        return [("BTC/USDT", self.timeframe)]

    # --- Protections: Cash-Out logica ---

    @property
    def protections(self):
        return [
            {
                "method": "MaxDrawdown",
                "lookback_period_candles": 48,
                "trade_back_period_candles": 48,
                "max_allowed_drawdown": 0.15,
                "stop_duration_candles": 24,
            },
            {
                "method": "StoplossGuard",
                "lookback_period_candles": 24,
                "trade_limit": 3,
                "stop_duration_candles": 12,
                "only_per_pair": False,
            },
        ]

    # --- Indicators ---

    def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        # StochRSI (momentum timing)
        rsi = ta.RSI(dataframe, timeperiod=14)
        stochrsi_k, stochrsi_d = ta.STOCH(
            {"high": rsi, "low": rsi, "close": rsi, "open": rsi},
            fastk_period=14,
            slowk_period=3,
            slowk_matype=0,
            slowd_period=3,
            slowd_matype=0,
        )
        dataframe["stochrsi_k"] = stochrsi_k / 100.0  # Normaliseer naar 0-1
        dataframe["stochrsi_d"] = stochrsi_d / 100.0

        # Bollinger Bands (volatiliteits-uitrekking)
        bb_result = ta.BBANDS(
            dataframe, timeperiod=20, nbdevup=2.0, nbdevdn=2.0, matype=0
        )
        dataframe["bb_upper"] = bb_result["upperband"]
        dataframe["bb_middle"] = bb_result["middleband"]
        dataframe["bb_lower"] = bb_result["lowerband"]

        # ADX (directionele trendsterkte)
        dataframe["adx"] = ta.ADX(dataframe, timeperiod=14)

        # EMA 200 (1h trendfilter)
        dataframe["ema200"] = ta.EMA(dataframe, timeperiod=200)

        # ATR 14 (voor custom_stoploss — pre-computed)
        dataframe["atr"] = ta.ATR(dataframe, timeperiod=14)

        # BTC Remora filter via informative pair merge
        # Freqtrade merget BTC/USDT data automatisch met prefix "BTC/USDT_"
        # maar we moeten de kolommen zelf berekenen als ze niet bestaan
        if "BTC/USDT_close" not in dataframe.columns:
            # Wanneer het paar BTC/USDT zelf is, gebruik eigen data
            if metadata.get("pair") == "BTC/USDT":
                dataframe["btc_close"] = dataframe["close"]
                dataframe["btc_ema200"] = dataframe["ema200"]
            else:
                # Informative pair data ophalen via dataprovider
                btc_dataframe = self.dp.get_pair_dataframe(
                    pair="BTC/USDT", timeframe=self.timeframe
                )
                if len(btc_dataframe) > 0:
                    btc_dataframe["btc_ema200"] = ta.EMA(btc_dataframe, timeperiod=200)
                    # Merge op date kolom
                    btc_dataframe = btc_dataframe[["date", "close", "btc_ema200"]].rename(
                        columns={"close": "btc_close"}
                    )
                    dataframe = dataframe.merge(btc_dataframe, on="date", how="left")
                else:
                    dataframe["btc_close"] = np.nan
                    dataframe["btc_ema200"] = np.nan
        else:
            # Freqtrade auto-merge format
            dataframe["btc_close"] = dataframe["BTC/USDT_close"]
            dataframe["btc_ema200"] = ta.EMA(
                dataframe["BTC/USDT_close"], timeperiod=200
            )

        return dataframe

    # --- Entry ---

    def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        conditions = (
            # 1. Bollinger Band: prijs onder lower band (vorige gesloten kaars)
            (dataframe["close"].shift(1) < dataframe["bb_lower"].shift(1))
            # 2. StochRSI: oversold zone + kruist opwaarts
            & (dataframe["stochrsi_k"].shift(1) < 0.20)
            & (dataframe["stochrsi_k"] > dataframe["stochrsi_d"])
            # 3. ADX: sterke trend
            & (dataframe["adx"].shift(1) > 25)
            # 4. EMA 200: macro-uptrend op 1h
            & (dataframe["close"].shift(1) > dataframe["ema200"].shift(1))
            # 5. Remora: BTC in uptrend
            & (dataframe["btc_close"].shift(1) > dataframe["btc_ema200"].shift(1))
            # Basis volume check
            & (dataframe["volume"] > 0)
        )

        dataframe.loc[conditions, "enter_long"] = 1
        return dataframe

    # --- Exit ---

    def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        dataframe.loc[
            (
                (dataframe["stochrsi_k"] > 0.80)
                & (dataframe["stochrsi_k"] < dataframe["stochrsi_d"])
            ),
            "exit_long",
        ] = 1
        return dataframe

    # --- Custom Stake Amount: dynamisch risicopercentage op basis van equity ---

    def custom_stake_amount(self, pair, current_time, current_rate,
                            proposed_stake, min_stake, max_stake,
                            leverage, entry_tag, side, **kwargs):
        equity = self.wallets.get_total_stake_amount()

        if equity < 500:
            risk_pct = 0.22
        elif equity < 750:
            risk_pct = 0.18
        elif equity < 1000:
            risk_pct = 0.15
        elif equity < 1500:
            risk_pct = 0.12
        else:
            risk_pct = 0.10

        stake = equity * risk_pct
        min_viable = max(min_stake or 1, 5.0)  # Bybit minimum + buffer
        return max(min(stake, max_stake), min_viable)

    # --- Custom Stoploss: Chandelier Exit (ATR-based trailing) ---

    def custom_stoploss(
        self, pair, trade, current_time, current_rate, current_profit, **kwargs
    ):
        dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe)
        if len(dataframe) < 1:
            return self.stoploss

        atr = dataframe["atr"].iat[-1]
        if atr == 0 or np.isnan(atr):
            return self.stoploss

        # Hoogste close since trade entry (echte Chandelier Exit)
        candles_since_entry = dataframe.loc[dataframe["date"] >= trade.open_date_utc]
        if len(candles_since_entry) > 0:
            highest_close = candles_since_entry["close"].max()
        else:
            highest_close = current_rate

        # Stop level = hoogste_close - (ATR * multiplier)
        chandelier_stop = highest_close - (atr * self.atr_multiplier)

        # Omzetten naar relatieve stoploss t.o.v. huidige koers (negatief getal)
        stoploss = (chandelier_stop / current_rate) - 1.0

        # Nooit verder openen dan de harde stoploss
        return max(stoploss, self.stoploss)
