# source: https://raw.githubusercontent.com/RoboticAutomations/freqtradestrategies/52c4bd40414645f079a980cf4486dc7af4061efb/AIRA%20-%20open%20code%20v2/strategies/rsi-momentum/RSI_Futures__3.py
# directory_url: https://github.com/RoboticAutomations/freqtradestrategies/blob/main/AIRA - open code v2/strategies/rsi-momentum/
# User: RoboticAutomations
# Repository: freqtradestrategies
# --------------------from datetime import datetime
from typing import Optional

import talib.abstract as ta
from pandas import DataFrame

from freqtrade.strategy import IStrategy
from freqtrade.persistence import Trade


class Github_RoboticAutomations_freqtradestrategies__RSI_Futures__3__20260915_233254(IStrategy):

    INTERFACE_VERSION = 3

    _MAX_PROFIT_KEY = "max_profit"

    can_short = True

    timeframe = "4h"

    process_only_new_candles = True

    startup_candle_count = 100

    stoploss = -0.50

    use_exit_signal = True

    position_adjustment_enable = True

    max_entry_position_adjustment = 2

    minimal_roi = {
        "0": 100
    }

    order_types = {
        "entry": "market",
        "exit": "market",
        "stoploss": "market",
        "stoploss_on_exchange": False,
    }

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

        dataframe["ema50"] = ta.EMA(dataframe, timeperiod=50)
        dataframe["ema200"] = ta.EMA(dataframe, timeperiod=200)

        dataframe["adx"] = ta.ADX(dataframe, timeperiod=14)
        dataframe["atr"] = ta.ATR(dataframe, timeperiod=14)
        dataframe["atr_pct"] = dataframe["atr"] / dataframe["close"]

        dataframe["rsi"] = ta.RSI(dataframe, timeperiod=14)

        dataframe["donchian_upper"] = (
            dataframe["high"].rolling(20).max()
        )
        dataframe["donchian_lower"] = (
            dataframe["low"].rolling(20).min()
        )

        dataframe["exit_lower"] = (
            dataframe["low"].rolling(10).min()
        )
        dataframe["exit_upper"] = (
            dataframe["high"].rolling(10).max()
        )

        dataframe["volume_ma"] = (
            dataframe["volume"].rolling(20).mean()
        )
        dataframe["volume_ratio"] = (
            dataframe["volume"] / dataframe["volume_ma"].replace(0, 1)
        )

        dataframe["ema50_slope"] = (
            dataframe["ema50"] - dataframe["ema50"].shift(3)
        ) / dataframe["ema50"].shift(3)

        return dataframe

    def populate_entry_trend(self, dataframe, metadata):

        long_conditions = (
            (dataframe["ema50"] > dataframe["ema200"])
            &
            (dataframe["adx"] > 25)
            &
            (dataframe["close"] > dataframe["donchian_upper"].shift(1))
            &
            (dataframe["volume_ratio"] > 1.0)
        )

        dataframe.loc[
            long_conditions,
            ["enter_long", "enter_tag"]
        ] = (1, "donchian_long")

        short_conditions = (
            (dataframe["ema50"] < dataframe["ema200"])
            &
            (dataframe["ema50_slope"] < 0)
            &
            (dataframe["adx"] > 25)
            &
            (dataframe["close"] < dataframe["donchian_lower"].shift(1))
            &
            (dataframe["volume_ratio"] > 1.0)
            &
            (dataframe["rsi"] < 50)
        )

        dataframe.loc[
            short_conditions,
            ["enter_short", "enter_tag"]
        ] = (1, "donchian_short")

        return dataframe

    def populate_exit_trend(self, dataframe, metadata):

        dataframe.loc[
            (dataframe["close"] < dataframe["exit_lower"].shift(1)),
            "exit_long"
        ] = 1

        dataframe.loc[
            (dataframe["close"] > dataframe["exit_upper"].shift(1)),
            "exit_short"
        ] = 1

        return dataframe

    def leverage(self, pair, current_time, current_rate, proposed_leverage, max_leverage, entry_tag, side, **kwargs):
        if side == "long":
            return min(5.0, max_leverage)
        else:
            return min(1.0, max_leverage)

    def custom_stake_amount(self, current_time, current_rate, proposed_stake, min_stake, max_stake, leverage, entry_tag, side, **kwargs):
        pair = kwargs.get("pair", "")
        dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe)
        if dataframe is None or dataframe.empty:
            return max_stake * 0.05 if side == "long" else max_stake * 0.02
        last = dataframe.iloc[-1]
        atr_pct = float(last.get("atr_pct", 0.02) or 0.02)

        if side == "long":
            pct = 0.05
        else:
            pct = 0.02

        if atr_pct > 0.04:
            pct *= 0.75

        stake = max_stake * max(0.02, min(pct, 0.05))
        min_allowed = max(min_stake or 0, max_stake * 0.02)
        return max(min_allowed, min(stake, max_stake * 0.05))

    def adjust_trade_position(
        self,
        trade: Trade,
        current_time: datetime,
        current_rate: float,
        current_profit: float,
        min_stake: Optional[float],
        max_stake: float,
        current_entry_rate: float,
        current_exit_rate: float,
        current_entry_profit: float,
        current_exit_profit: float,
        **kwargs
    ) -> Optional[float]:
        """
        Profit-based pyramiding: only add when already in profit.
        Long only (shorts have minimal stake, not worth pyramiding).
        """
        entries = trade.nr_of_successful_entries

        # Only pyramid longs
        if trade.trade_direction != "long":
            return None

        # Pyramid 1: at +10% profit, add 30% more
        if entries == 1 and current_profit > 0.10:
            add_pct = 0.30
            add_stake = trade.stake_amount * add_pct
            # Ensure min_stake requirement
            if min_stake and add_stake < min_stake:
                return None
            # Cap additional stake at 3% of wallet (per-entry 2-5% requirement)
            max_additional = max_stake * 0.03
            add_stake = min(add_stake, max_additional)
            return (add_stake, "pyramid_10pct")

        # Pyramid 2: at +20% profit, add 25% more
        if entries == 2 and current_profit > 0.20:
            add_pct = 0.25
            add_stake = trade.stake_amount * add_pct
            if min_stake and add_stake < min_stake:
                return None
            max_additional = max_stake * 0.03
            add_stake = min(add_stake, max_additional)
            return (add_stake, "pyramid_20pct")

        return None

    def custom_exit(self, pair, trade, current_time, current_rate, current_profit, **kwargs):
        max_profit = trade.get_custom_data(
            key=self._MAX_PROFIT_KEY, default=current_profit
        )
        if current_profit > max_profit:
            max_profit = current_profit
            trade.set_custom_data(key=self._MAX_PROFIT_KEY, value=max_profit)

        if max_profit > 0.08:
            if (max_profit - current_profit) > 0.03:
                return "profit_pullback_3pct"
        if max_profit > 0.15:
            if (max_profit - current_profit) > 0.05:
                return "profit_pullback_5pct"
        if max_profit > 0.25:
            if (max_profit - current_profit) > 0.08:
                return "profit_pullback_8pct"
        return None
