# source: https://raw.githubusercontent.com/remiotore/freqtrade/44beaeb6a420cd8e9f2e4ea93e11d6cfa192ee03/strategies/ZaratustraV27.py
# pragma pylint: disable=missing-docstring, invalid-name, pointless-string-statement
# flake8: noqa: F401
# isort: skip_file
# --- Do not remove these imports ---
from datetime import datetime
from pandas import DataFrame
from freqtrade.strategy import IStrategy, Trade, informative, IntParameter, CategoricalParameter
import talib.abstract as ta
from technical import qtpylib


class Github_remiotore_freqtrade__ZaratustraV27__20260111_210550(IStrategy):
    INTERFACE_VERSION = 3
    timeframe = "15m"
    can_short = True
    use_exit_signal = False
    exit_profit_only = True
    position_adjustment_enable = True

    # ROI table:
    minimal_roi = {}

    # Base Stoploss:
    stoploss = -0.99

    # Trailing Stop Settings:
    trailing_stop = True
    trailing_stop_positive = 0.01
    trailing_stop_positive_offset = 0.03
    trailing_only_offset_is_reached = True

    # Max Open Trades
    max_open_trades = 1

    # Optional Risk/Trade Management parameters
    max_dca_orders = 3
    max_exit_position_adjustment = 2

    def leverage(self, pair: str, current_time: "datetime", current_rate: float, proposed_leverage: float, max_leverage: float, side: str, **kwargs) -> float:
        return 10

    def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        # Bollinger Bands for trend context
        bollinger = qtpylib.bollinger_bands(qtpylib.typical_price(dataframe), window=20, stds=2)
        dataframe[['BBL', 'BBM', 'BBU']] = bollinger[['lower', 'mid', 'upper']]

        # RSI: Momentum and overbought/oversold indicator
        dataframe['RSI'] = ta.RSI(dataframe)

        # ADX and DI components: measure trend strength and direction
        dataframe['ADX'] = ta.ADX(dataframe)
        dataframe['PDI'] = ta.PLUS_DI(dataframe)
        dataframe['MDI'] = ta.MINUS_DI(dataframe)

        # TSF: Time Series Forecast as a trend direction indicator
        dataframe['TSF'] = ta.TSF(dataframe)

        # ATR: Average True Range to capture volatility (useful for adaptive stoploss)
        dataframe['ATR'] = ta.ATR(dataframe, timeperiod=14)

        return dataframe

    def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        # Bullish entry: Price above the mid Bollinger band and ADX confirms trend strength
        dataframe.loc[
            (
                (dataframe['close'] > dataframe['BBM']) &
                (dataframe['ADX'] > 20) &
                (dataframe['PDI'] > dataframe['MDI'])
            ),
            ['enter_long', 'enter_tag']
        ] = (1, 'Bullish trend')

        # Bearish entry: Price below the mid Bollinger band and ADX confirms trend strength
        dataframe.loc[
            (
                (dataframe['close'] < dataframe['BBM']) &
                (dataframe['ADX'] > 20) &
                (dataframe['MDI'] > dataframe['PDI'])
            ),
            ['enter_short', 'enter_tag']
        ] = (1, 'Bearish trend')

        return dataframe

    def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        # Custom exit logic is handled via custom_exit function.
        return dataframe

    def adjust_trade_position(self, trade: Trade, current_time: datetime, current_rate: float,
                              current_profit: float, min_stake: float, max_stake: float, **kwargs) -> float:
        """
        Dynamically adjust trade position:
          - Scale into losing positions if RSI is favorable and TSF confirms trend.
          - Scale out of winning positions when RSI indicates exhaustion.
        """
        # Get the most recent candle data for the pair
        dataframe, _ = self.dp.get_analyzed_dataframe(trade.pair, self.timeframe)
        last_candle = dataframe.iloc[-1]

        rsi = last_candle['RSI']
        tsf = last_candle['TSF']
        adx = last_candle['ADX']

        filled_entries = trade.select_filled_orders('enter_short' if trade.is_short else 'enter_long')
        filled_exits = trade.select_filled_orders('exit_short' if trade.is_short else 'exit_long')

        count_entries = len(filled_entries)
        count_exits = len(filled_exits)

        # ------ Scale Into the Position (DCA) ------
        # Conditions: trade is losing, market oversold for longs or overbought for shorts,
        # the price respects the TSF line, and the overall trend is strong (ADX > 20)
        if current_profit < 0 and count_entries < self.max_dca_orders and adx > 20:
            if not trade.is_short and rsi < 40 and current_rate > tsf:
                stake_amount = min(trade.stake_amount * 1.5, max_stake)
                return stake_amount
            elif trade.is_short and rsi > 60 and current_rate < tsf:
                stake_amount = min(trade.stake_amount * 1.5, max_stake)
                return stake_amount

        # ------ Scale Out of the Position ------
        # Conditions: trade is profitable; RSI indicates potential overextension;
        # and only scale out a limited number of times.
        if current_profit > 0.05 and count_exits < self.max_exit_position_adjustment:
            if not trade.is_short and rsi > 70:
                return -(trade.amount * 0.25)  # exit 25% of the position
            elif trade.is_short and rsi < 30:
                return -(trade.amount * 0.25)

        return None
