# source: https://raw.githubusercontent.com/Eksz3lsi0r/DOLLAMNY/f6178a8e9385159556a6443262fc338e43d6f88c/example_strategy_with_daily_drawdown_guard.py
"""
Example Strategy with Daily Drawdown Guard Protection

This strategy demonstrates how to use the DailyDrawdownGuard protection
to pause new entries when daily loss exceeds 20% and resume on the following day.
"""

from datetime import datetime
import logging
from pandas import DataFrame

from freqtrade.strategy import IStrategy, merge_informative_pair
from freqtrade.strategy.parameters import IntParameter


logger = logging.getLogger(__name__)


class Github_Eksz3lsi0r_DOLLAMNY__example_strategy_with_daily_drawdown_guard__20250916_061219(IStrategy):
    """
    Example strategy demonstrating DailyDrawdownGuard protection.

    This strategy will:
    1. Use a simple RSI-based entry/exit logic
    2. Implement DailyDrawdownGuard to pause new entries when daily loss > 20%
    3. Resume trading at midnight (00:00) the following day
    """

    # Strategy interface version
    INTERFACE_VERSION = 3

    # Optimal timeframe for the strategy
    timeframe = "5m"

    # Can this strategy go short?
    can_short: bool = False

    # Minimal ROI designed for the strategy
    minimal_roi = {"60": 0.01, "30": 0.02, "0": 0.04}

    # Optimal stoploss designed for the strategy
    stoploss = -0.10

    # Trailing stoploss
    trailing_stop = False
    trailing_stop_positive = 0.01
    trailing_stop_positive_offset = 0.02
    trailing_only_offset_is_reached = False

    # Run "populate_indicators" only for new candle
    process_only_new_candles = False

    # These values can be overridden in the config
    use_exit_signal = True
    exit_profit_only = False
    ignore_roi_if_entry_signal = False

    # Number of candles the strategy requires before producing valid signals
    startup_candle_count: int = 30

    # Optional order type mapping
    order_types = {"entry": "limit", "exit": "limit", "stoploss": "market", "stoploss_on_exchange": False}

    # Optional order time in force
    order_time_in_force = {"entry": "gtc", "exit": "gtc"}

    # Strategy parameters
    rsi_buy = IntParameter(20, 40, default=30, space="buy")
    rsi_sell = IntParameter(60, 80, default=70, space="sell")
    rsi_period = IntParameter(10, 20, default=14, space="buy")

    def informative_pairs(self):
        """
        Define additional, informative pair/interval combinations to be cached from the exchange.
        These pairs will automatically be available for use in the `populate_indicators` method.
        """
        return []

    def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        """
        Adds several different TA indicators to the given DataFrame

        Performance Note: For the best performance be frugal on the number of indicators
        you are using. Let uncomment only the indicator you are using in your strategies
        or your hyperopt configuration, otherwise you will pay for unused indicators.
        :param dataframe: Dataframe with data from the exchange
        :param metadata: Additional information, like the currently traded pair
        :return: a Dataframe with all mandatory indicators for the strategies
        """

        # RSI
        dataframe["rsi"] = self.rsi(dataframe, timeperiod=self.rsi_period.value)

        # Bollinger Bands
        bollinger = self.qtpylib.bollinger_bands(dataframe["close"], window=20, stds=2)
        dataframe["bb_lowerband"] = bollinger["lower"]
        dataframe["bb_middleband"] = bollinger["mid"]
        dataframe["bb_upperband"] = bollinger["upper"]
        dataframe["bb_percent"] = (dataframe["close"] - dataframe["bb_lowerband"]) / (
            dataframe["bb_upperband"] - dataframe["bb_lowerband"]
        )
        dataframe["bb_width"] = (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) / dataframe["bb_middleband"]

        # MACD
        macd = self.populate_macd(dataframe)
        dataframe = merge_informative_pair(dataframe, macd, self.timeframe, self.timeframe, ffill=True)

        # EMA - Exponential Moving Average
        dataframe["ema_12"] = self.ema(dataframe, timeperiod=12)
        dataframe["ema_26"] = self.ema(dataframe, timeperiod=26)

        # SMA - Simple Moving Average
        dataframe["sma_5"] = self.sma(dataframe, timeperiod=5)
        dataframe["sma_10"] = self.sma(dataframe, timeperiod=10)

        return dataframe

    def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        """
        Based on TA indicators, populates the entry signal for the given dataframe
        :param dataframe: DataFrame populated with indicators
        :param metadata: Additional information, like the currently traded pair
        :return: DataFrame with entry columns populated
        """
        dataframe.loc[
            (
                # RSI conditions
                (dataframe["rsi"] < self.rsi_buy.value)
                &
                # Price conditions
                (dataframe["close"] < dataframe["bb_lowerband"])
                & (dataframe["close"] > dataframe["ema_12"])
                & (dataframe["close"] > dataframe["sma_5"])
                &
                # Volume conditions
                (dataframe["volume"] > 0)
            ),
            "enter_long",
        ] = 1

        return dataframe

    def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        """
        Based on TA indicators, populates the exit signal for the given dataframe
        :param dataframe: DataFrame populated with indicators
        :param metadata: Additional information, like the currently traded pair
        :return: DataFrame with exit columns populated
        """
        dataframe.loc[
            (
                # RSI conditions
                (dataframe["rsi"] > self.rsi_sell.value)
                &
                # Price conditions
                (dataframe["close"] > dataframe["bb_upperband"])
                & (dataframe["close"] < dataframe["ema_26"])
                & (dataframe["close"] < dataframe["sma_10"])
            ),
            "exit_long",
        ] = 1

        return dataframe

    @property
    def protections(self):
        """
        Define protection settings for the strategy.

        The DailyDrawdownGuard will:
        - Monitor daily PnL
        - Pause new entries when daily loss exceeds 20%
        - Resume trading at 00:00 the following day
        """
        return [
            {
                "method": "DailyDrawdownGuard",
                "max_daily_loss": 0.20,  # 20% daily loss threshold
                "unlock_at": "00:00",  # Resume trading at midnight
            },
            # Additional protections can be added here
            {
                "method": "StoplossGuard",
                "lookback_period_candles": 24,
                "trade_limit": 4,
                "stop_duration_candles": 2,
                "only_per_pair": False,
            },
        ]

    def custom_exit(
        self, pair: str, trade, current_time: datetime, current_rate: float, current_profit: float, **kwargs
    ) -> str | bool | None:
        """
        Custom exit logic can be added here.
        This method is called for every open trade.
        """
        return None

    def confirm_trade_entry(
        self,
        pair: str,
        order_type: str,
        amount: float,
        rate: float,
        time_in_force: str,
        current_time: datetime,
        entry_tag: str | None,
        side: str,
        **kwargs,
    ) -> bool:
        """
        Called right before placing a entry order.
        Timing for this function is critical, so avoid doing heavy computations here.

        For full documentation please go to https://www.freqtrade.io/en/latest/strategy-advanced/

        When not implemented by a strategy, returns True (always allowing).

        :param pair: Pair that's about to be bought/shorted.
        :param order_type: Order type (as configured in order_types). Usually limit or market.
        :param amount: Amount in stake currency to buy/sell.
        :param rate: Rate in stake currency.
        :param time_in_force: Time in force. Defaults to GTC (Good-til-cancelled).
        :param current_time: datetime object, containing the current datetime
        :param entry_tag: Optional entry_tag (buy_tag) if provided with buy order.
        :param side: 'long' or 'short' - indicating the direction of the proposed trade
        :return: True when the entry should be allowed, False to abort
        """
        return True

    def confirm_trade_exit(
        self,
        pair: str,
        trade,
        order_type: str,
        amount: float,
        rate: float,
        time_in_force: str,
        exit_reason: str,
        current_time: datetime,
        **kwargs,
    ) -> bool:
        """
        Called right before placing a exit order.
        Timing for this function is critical, so avoid doing heavy computations here.

        For full documentation please go to https://www.freqtrade.io/en/latest/strategy-advanced/

        When not implemented by a strategy, returns True (always allowing).

        :param pair: Pair that's about to be sold/covered.
        :param trade: trade object.
        :param order_type: Order type (as configured in order_types). Usually limit or market.
        :param amount: Amount in stake currency to sell/cover.
        :param rate: Rate in stake currency.
        :param time_in_force: Time in force. Defaults to GTC (Good-til-cancelled).
        :param exit_reason: Exit reason.
            Can be any of ['roi', 'stop_loss', 'stoploss_on_exchange', 'trailing_stop_loss',
                           'exit_signal', 'force_exit', 'emergency_exit']
        :param current_time: datetime object, containing the current datetime
        :return: True when the exit should be allowed, False to abort
        """
        return True
