# source: https://raw.githubusercontent.com/DOUGLASGUEDESATRIA/OSIRIS_TRADE/fe3c09567f3798d1cad6d7a306fa4f6131d91e17/user_data/strategies/OsirisDayTradeV2.py
"""
OSIRIS DAY TRADE v2 — Multi-Timeframe Trend Pullback
================================================================
DAY TRADE puro em BTC/USDT — LONG e SHORT.

MANIFESTO INVIOLÁVEL:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
  Ativo:        BTC/USDT APENAS
  Tipo:         DAY TRADE (max 3h por operação)
  Meta:         10 operações/dia
  Direção:      LONG e SHORT
  Timeframes:   5m (execução) + 15m (sinal) + 1h (tendência)
  Risco/Alvo:   1.5:1 mínimo, média 1.8:1
  Max Loss/dia: -5R (hard stop)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

FILOSOFIA:
Não tenta adivinhar topos e fundos.
SEGUE A TENDÊNCIA e entra no PULLBACK.
1H diz PRA ONDE. 15m diz QUANDO. 5m diz O PREÇO EXATO.

ENTRADAS (4 tipos, cada um ~2-3/dia = 10 total):
  A) EMA Pullback:   Preço retorna à EMA21, candle de reversão
  B) BB Bounce:      Preço toca BB oposta ao trend, reverte
  C) Momentum Cross: EMA9 cruza EMA21 na direção do 1H
  D) RSI Divergence: RSI volta de zona extrema na direção do trend

STOPS:
  SL = abaixo/acima do swing recente (5 bars) + 0.1×ATR buffer
  Mínimo 0.3%, máximo 1.5%
  Trail: breakeven em 1R, lock 0.5R em 1.5R

100% proprietário. OSIRIS v2 — Trend Following, Not Guessing.
"""

import logging
import numpy as np
import pandas as pd
from pandas import DataFrame
from typing import Optional
from datetime import datetime, timedelta

from freqtrade.strategy import IStrategy, merge_informative_pair
from freqtrade.strategy import DecimalParameter, IntParameter
from freqtrade.persistence import Trade
import talib.abstract as ta

try:
    from freqtrade.strategy import stoploss_from_open
except ImportError:
    def stoploss_from_open(open_relative_stop, current_profit, is_short=False):
        if current_profit == 0:
            return 1
        if is_short:
            return -1 + ((1 - open_relative_stop) / (1 - current_profit))
        return 1 - ((1 + open_relative_stop) / (1 + current_profit))

logger = logging.getLogger(__name__)


class Github_DOUGLASGUEDESATRIA_OSIRIS_TRADE__OsirisDayTradeV2__20260329_060843(IStrategy):
    """
    OSIRIS DAY TRADE v2 — Multi-TF Trend Following com Pullback
    5m main | 15m signal | 1h trend | LONG + SHORT | 10 trades/dia
    """

    INTERFACE_VERSION = 3
    can_short = True
    timeframe = "5m"

    # ROI: wide — custom_exit handles the real exits
    minimal_roi = {"0": 0.10, "60": 0.03, "120": 0.01, "180": 0.003}

    # Safety net stoploss
    stoploss = -0.02

    # Custom stoploss handles R-based trailing
    trailing_stop = False
    use_custom_stoploss = True

    startup_candle_count = 200
    process_only_new_candles = True

    # Runtime state
    _daily_trades = {}
    _daily_losses_r = {}
    _consecutive_losses = 0
    _last_loss_time = None

    # ═══════════════════════════════════════════════════════════════════
    # HYPEROPT PARAMETERS
    # ═══════════════════════════════════════════════════════════════════

    # Trend alignment strictness
    buy_trend_strict = IntParameter(0, 1, default=0, space="buy", optimize=True)

    # Pullback depth (how close to EMA for pullback entry)
    buy_pullback_atr_mult = DecimalParameter(0.2, 1.0, default=0.5, decimals=1, space="buy", optimize=True)

    # RSI thresholds
    buy_rsi_ob = IntParameter(65, 80, default=70, space="buy", optimize=True)
    buy_rsi_os = IntParameter(20, 35, default=30, space="buy", optimize=True)

    # Volume filter
    buy_vol_min = DecimalParameter(0.5, 1.5, default=0.7, decimals=1, space="buy", optimize=True)

    # R:R target
    buy_rr_target = DecimalParameter(1.2, 2.5, default=1.5, decimals=1, space="buy", optimize=True)

    # Max trades per day
    buy_max_daily = IntParameter(8, 15, default=10, space="buy", optimize=True)

    # Cooldown (candles between entries)
    buy_cooldown = IntParameter(2, 8, default=3, space="buy", optimize=True)

    # Max hold (candles of 5m = max 36 = 3 hours)
    buy_max_hold = IntParameter(18, 48, default=36, space="buy", optimize=True)

    # Stop buffer
    buy_sl_buffer_atr = DecimalParameter(0.05, 0.3, default=0.1, decimals=2, space="buy", optimize=True)

    # Exit
    sell_rsi_exit_long = IntParameter(72, 85, default=78, space="sell", optimize=True)
    sell_rsi_exit_short = IntParameter(15, 28, default=22, space="sell", optimize=True)

    # ═══════════════════════════════════════════════════════════════════
    # INFORMATIVE PAIRS
    # ═══════════════════════════════════════════════════════════════════

    def informative_pairs(self):
        return [
            ("BTC/USDT:USDT", "15m"),
            ("BTC/USDT:USDT", "1h"),
        ]

    # ═══════════════════════════════════════════════════════════════════
    # INDICATORS — 5m (main)
    # ═══════════════════════════════════════════════════════════════════

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

        # === EMAs ===
        dataframe["ema9"] = ta.EMA(dataframe, timeperiod=9)
        dataframe["ema21"] = ta.EMA(dataframe, timeperiod=21)
        dataframe["ema50"] = ta.EMA(dataframe, timeperiod=50)

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

        # === MACD ===
        macd = ta.MACD(dataframe, fastperiod=12, slowperiod=26, signalperiod=9)
        dataframe["macd"] = macd["macd"]
        dataframe["macd_signal"] = macd["macdsignal"]
        dataframe["macd_hist"] = macd["macdhist"]

        # === Bollinger Bands ===
        bb = ta.BBANDS(dataframe, timeperiod=20, nbdevup=2.0, nbdevdn=2.0)
        dataframe["bb_upper"] = bb["upperband"]
        dataframe["bb_mid"] = bb["middleband"]
        dataframe["bb_lower"] = bb["lowerband"]

        # === ATR ===
        dataframe["atr"] = ta.ATR(dataframe, timeperiod=14)

        # === ADX ===
        dataframe["adx"] = ta.ADX(dataframe, timeperiod=14)
        dataframe["plus_di"] = ta.PLUS_DI(dataframe, timeperiod=14)
        dataframe["minus_di"] = ta.MINUS_DI(dataframe, timeperiod=14)

        # === Volume ===
        dataframe["vol_sma"] = ta.SMA(dataframe["volume"], timeperiod=20)
        dataframe["vol_ratio"] = dataframe["volume"] / dataframe["vol_sma"].replace(0, 1)

        # === Swing levels (5 bar) ===
        dataframe["swing_high_5"] = dataframe["high"].rolling(5, center=False).max().shift(1)
        dataframe["swing_low_5"] = dataframe["low"].rolling(5, center=False).min().shift(1)
        dataframe["swing_high_10"] = dataframe["high"].rolling(10, center=False).max().shift(1)
        dataframe["swing_low_10"] = dataframe["low"].rolling(10, center=False).min().shift(1)

        # === EMA previous values (for cross detection) ===
        dataframe["ema9_prev"] = dataframe["ema9"].shift(1)
        dataframe["ema21_prev"] = dataframe["ema21"].shift(1)
        dataframe["rsi_prev"] = dataframe["rsi"].shift(1)
        dataframe["close_prev"] = dataframe["close"].shift(1)

        # === Candle patterns ===
        dataframe["is_bullish"] = (dataframe["close"] > dataframe["open"]).astype(int)
        dataframe["is_bearish"] = (dataframe["close"] < dataframe["open"]).astype(int)
        dataframe["body"] = abs(dataframe["close"] - dataframe["open"])
        dataframe["upper_wick"] = dataframe["high"] - dataframe[["close", "open"]].max(axis=1)
        dataframe["lower_wick"] = dataframe[["close", "open"]].min(axis=1) - dataframe["low"]
        dataframe["candle_range"] = dataframe["high"] - dataframe["low"]

        # === Distance to EMAs (in ATR units) ===
        dataframe["dist_ema21"] = (dataframe["close"] - dataframe["ema21"]) / dataframe["atr"].replace(0, 1)
        dataframe["dist_ema50"] = (dataframe["close"] - dataframe["ema50"]) / dataframe["atr"].replace(0, 1)

        # === Multi-TF: 15m ===
        if self.dp:
            pair = metadata["pair"]

            inf_15m = self.dp.get_pair_dataframe(pair=pair, timeframe="15m")
            if not inf_15m.empty:
                inf_15m["ema9"] = ta.EMA(inf_15m, timeperiod=9)
                inf_15m["ema21"] = ta.EMA(inf_15m, timeperiod=21)
                inf_15m["ema50"] = ta.EMA(inf_15m, timeperiod=50)
                inf_15m["rsi"] = ta.RSI(inf_15m, timeperiod=14)
                inf_15m["atr"] = ta.ATR(inf_15m, timeperiod=14)
                macd15 = ta.MACD(inf_15m, fastperiod=12, slowperiod=26, signalperiod=9)
                inf_15m["macd_hist"] = macd15["macdhist"]
                bb15 = ta.BBANDS(inf_15m, timeperiod=20, nbdevup=2.0, nbdevdn=2.0)
                inf_15m["bb_upper"] = bb15["upperband"]
                inf_15m["bb_lower"] = bb15["lowerband"]
                inf_15m["adx"] = ta.ADX(inf_15m, timeperiod=14)

                dataframe = merge_informative_pair(
                    dataframe, inf_15m, self.timeframe, "15m", ffill=True
                )

            # === Multi-TF: 1h ===
            inf_1h = self.dp.get_pair_dataframe(pair=pair, timeframe="1h")
            if not inf_1h.empty:
                inf_1h["ema9"] = ta.EMA(inf_1h, timeperiod=9)
                inf_1h["ema21"] = ta.EMA(inf_1h, timeperiod=21)
                inf_1h["ema50"] = ta.EMA(inf_1h, timeperiod=50)
                inf_1h["rsi"] = ta.RSI(inf_1h, timeperiod=14)
                inf_1h["adx"] = ta.ADX(inf_1h, timeperiod=14)
                macd1h = ta.MACD(inf_1h, fastperiod=12, slowperiod=26, signalperiod=9)
                inf_1h["macd_hist"] = macd1h["macdhist"]

                dataframe = merge_informative_pair(
                    dataframe, inf_1h, self.timeframe, "1h", ffill=True
                )

        return dataframe

    # ═══════════════════════════════════════════════════════════════════
    # ENTRY TREND
    # ═══════════════════════════════════════════════════════════════════

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

        pb_mult = self.buy_pullback_atr_mult.value
        vol_min = self.buy_vol_min.value
        rsi_ob = self.buy_rsi_ob.value
        rsi_os = self.buy_rsi_os.value

        # ───────────────────────────────────────────
        # 1H TREND BIAS
        # ───────────────────────────────────────────
        # Check if 1h columns exist
        has_1h = "ema9_1h" in dataframe.columns
        has_15m = "ema9_15m" in dataframe.columns

        if has_1h:
            trend_up_1h = dataframe["ema9_1h"] > dataframe["ema21_1h"]
            trend_dn_1h = dataframe["ema9_1h"] < dataframe["ema21_1h"]
            above_ema50_1h = dataframe["close"] > dataframe["ema50_1h"]
            below_ema50_1h = dataframe["close"] < dataframe["ema50_1h"]
        else:
            trend_up_1h = pd.Series(True, index=dataframe.index)
            trend_dn_1h = pd.Series(True, index=dataframe.index)
            above_ema50_1h = pd.Series(True, index=dataframe.index)
            below_ema50_1h = pd.Series(True, index=dataframe.index)

        # Strict mode: require close above/below 1h EMA50 too
        if self.buy_trend_strict.value == 1 and has_1h:
            bias_long = trend_up_1h & above_ema50_1h
            bias_short = trend_dn_1h & below_ema50_1h
        else:
            bias_long = trend_up_1h
            bias_short = trend_dn_1h

        # ───────────────────────────────────────────
        # 15M MOMENTUM ALIGNMENT
        # ───────────────────────────────────────────
        if has_15m:
            mom_long_15m = dataframe["ema9_15m"] > dataframe["ema21_15m"]
            mom_short_15m = dataframe["ema9_15m"] < dataframe["ema21_15m"]
        else:
            mom_long_15m = pd.Series(True, index=dataframe.index)
            mom_short_15m = pd.Series(True, index=dataframe.index)

        # ───────────────────────────────────────────
        # 5M COMMON FILTERS
        # ───────────────────────────────────────────
        vol_ok = dataframe["vol_ratio"] > vol_min
        has_volume = dataframe["volume"] > 0
        atr_valid = dataframe["atr"] > 0.5

        # Risk: stop distance must be between 0.3% and 1.5%
        stop_long = dataframe["swing_low_5"]
        stop_short = dataframe["swing_high_5"]
        risk_long_pct = (dataframe["close"] - stop_long) / dataframe["close"]
        risk_short_pct = (stop_short - dataframe["close"]) / dataframe["close"]
        risk_ok_long = (risk_long_pct > 0.003) & (risk_long_pct < 0.015)
        risk_ok_short = (risk_short_pct > 0.003) & (risk_short_pct < 0.015)

        # ───────────────────────────────────────────
        # ENTRY TYPE A: EMA PULLBACK
        # Price touched EMA21 zone and bounced
        # ───────────────────────────────────────────
        near_ema21_long = (
            (dataframe["dist_ema21"] > -pb_mult) &
            (dataframe["dist_ema21"] < pb_mult * 0.5) &
            (dataframe["is_bullish"] == 1)
        )
        near_ema21_short = (
            (dataframe["dist_ema21"] < pb_mult) &
            (dataframe["dist_ema21"] > -pb_mult * 0.5) &
            (dataframe["is_bearish"] == 1)
        )

        # EMA9 still above EMA21 (trend intact)
        ema_trend_up = dataframe["ema9"] > dataframe["ema21"]
        ema_trend_dn = dataframe["ema9"] < dataframe["ema21"]

        entry_a_long = near_ema21_long & ema_trend_up
        entry_a_short = near_ema21_short & ema_trend_dn

        # ───────────────────────────────────────────
        # ENTRY TYPE B: BB BOUNCE
        # Price touched opposite BB and reverses
        # ───────────────────────────────────────────
        touch_bb_lower = dataframe["low"] <= dataframe["bb_lower"] * 1.001
        touch_bb_upper = dataframe["high"] >= dataframe["bb_upper"] * 0.999

        entry_b_long = touch_bb_lower & (dataframe["is_bullish"] == 1) & (dataframe["rsi"] < 40)
        entry_b_short = touch_bb_upper & (dataframe["is_bearish"] == 1) & (dataframe["rsi"] > 60)

        # ───────────────────────────────────────────
        # ENTRY TYPE C: EMA CROSS (momentum start)
        # EMA9 crosses EMA21 in direction of 1h trend
        # ───────────────────────────────────────────
        ema_cross_up = (dataframe["ema9"] > dataframe["ema21"]) & (dataframe["ema9_prev"] <= dataframe["ema21_prev"])
        ema_cross_dn = (dataframe["ema9"] < dataframe["ema21"]) & (dataframe["ema9_prev"] >= dataframe["ema21_prev"])

        entry_c_long = ema_cross_up & (dataframe["adx"] > 18)
        entry_c_short = ema_cross_dn & (dataframe["adx"] > 18)

        # ───────────────────────────────────────────
        # ENTRY TYPE D: RSI REVERSAL FROM EXTREME
        # RSI crosses back from OS/OB zone in trend direction
        # ───────────────────────────────────────────
        rsi_bounce_long = (dataframe["rsi"] > rsi_os) & (dataframe["rsi_prev"] <= rsi_os)
        rsi_bounce_short = (dataframe["rsi"] < rsi_ob) & (dataframe["rsi_prev"] >= rsi_ob)

        entry_d_long = rsi_bounce_long & (dataframe["close"] > dataframe["ema50"])
        entry_d_short = rsi_bounce_short & (dataframe["close"] < dataframe["ema50"])

        # ───────────────────────────────────────────
        # COMBINE: bias + momentum + entry + filters
        # ───────────────────────────────────────────
        any_long_entry = entry_a_long | entry_b_long | entry_c_long | entry_d_long
        any_short_entry = entry_a_short | entry_b_short | entry_c_short | entry_d_short

        go_long = (
            bias_long &
            mom_long_15m &
            any_long_entry &
            vol_ok &
            has_volume &
            atr_valid &
            risk_ok_long
        )

        go_short = (
            bias_short &
            mom_short_15m &
            any_short_entry &
            vol_ok &
            has_volume &
            atr_valid &
            risk_ok_short
        )

        # Tag entries for analysis
        dataframe.loc[go_long, "enter_long"] = 1
        dataframe.loc[go_short, "enter_short"] = 1

        # Entry tags
        conditions_long = [
            (go_long & entry_a_long, "ema_pullback_long"),
            (go_long & entry_b_long, "bb_bounce_long"),
            (go_long & entry_c_long, "ema_cross_long"),
            (go_long & entry_d_long, "rsi_reversal_long"),
        ]
        conditions_short = [
            (go_short & entry_a_short, "ema_pullback_short"),
            (go_short & entry_b_short, "bb_bounce_short"),
            (go_short & entry_c_short, "ema_cross_short"),
            (go_short & entry_d_short, "rsi_reversal_short"),
        ]

        for cond, tag in conditions_long + conditions_short:
            dataframe.loc[cond, "enter_tag"] = tag

        return dataframe

    # ═══════════════════════════════════════════════════════════════════
    # EXIT TREND
    # ═══════════════════════════════════════════════════════════════════

    def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        # Long exit: RSI extreme overbought
        dataframe.loc[
            dataframe["rsi"] > self.sell_rsi_exit_long.value,
            "exit_long",
        ] = 1

        # Short exit: RSI extreme oversold
        dataframe.loc[
            dataframe["rsi"] < self.sell_rsi_exit_short.value,
            "exit_short",
        ] = 1

        return dataframe

    # ═══════════════════════════════════════════════════════════════════
    # CUSTOM STOPLOSS — Structural + R-Multiple Trailing
    # ═══════════════════════════════════════════════════════════════════

    def custom_stoploss(
        self,
        pair: str,
        trade: Trade,
        current_time,
        current_rate: float,
        current_profit: float,
        **kwargs,
    ) -> float:
        dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe)
        if len(dataframe) == 0:
            return -0.015

        last = dataframe.iloc[-1]
        atr = last.get("atr", 0)
        if atr == 0 or trade.open_rate == 0:
            return -0.015

        is_short = trade.is_short if hasattr(trade, "is_short") else False

        # Calculate initial risk from structure
        if is_short:
            swing_ref = last.get("swing_high_5", 0)
            if swing_ref > 0:
                buf = self.buy_sl_buffer_atr.value * atr
                stop_price = swing_ref + buf
                stop_dist_pct = (stop_price - trade.open_rate) / trade.open_rate
            else:
                stop_dist_pct = 1.0 * atr / trade.open_rate
        else:
            swing_ref = last.get("swing_low_5", 0)
            if swing_ref > 0:
                buf = self.buy_sl_buffer_atr.value * atr
                stop_price = swing_ref - buf
                stop_dist_pct = (trade.open_rate - stop_price) / trade.open_rate
            else:
                stop_dist_pct = 1.0 * atr / trade.open_rate

        # Clamp stop distance
        stop_dist_pct = max(0.003, min(stop_dist_pct, 0.015))

        # R-multiple trailing
        if current_profit > 0 and stop_dist_pct > 0:
            r_mult = current_profit / stop_dist_pct

            if r_mult >= 2.0:
                # Near/past target: lock 1R profit
                return stoploss_from_open(
                    1.0 * stop_dist_pct, current_profit, is_short=is_short
                )
            elif r_mult >= 1.5:
                # Lock 0.5R
                return stoploss_from_open(
                    0.5 * stop_dist_pct, current_profit, is_short=is_short
                )
            elif r_mult >= 1.0:
                # Breakeven
                return stoploss_from_open(
                    0.001, current_profit, is_short=is_short
                )

        return -stop_dist_pct

    # ═══════════════════════════════════════════════════════════════════
    # CUSTOM EXIT — R:R Target + Time + Momentum Fade
    # ═══════════════════════════════════════════════════════════════════

    def custom_exit(
        self,
        pair: str,
        trade: Trade,
        current_time,
        current_rate: float,
        current_profit: float,
        **kwargs,
    ) -> Optional[str]:
        dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe)
        if len(dataframe) == 0:
            return None

        last = dataframe.iloc[-1]
        atr = last.get("atr", 0)
        if atr == 0 or trade.open_rate == 0:
            return None

        is_short = trade.is_short if hasattr(trade, "is_short") else False

        # Estimate risk distance
        if is_short:
            swing = last.get("swing_high_5", 0)
            risk = (swing - trade.open_rate) if swing > 0 else 1.0 * atr
        else:
            swing = last.get("swing_low_5", 0)
            risk = (trade.open_rate - swing) if swing > 0 else 1.0 * atr

        risk_pct = max(risk / trade.open_rate, 0.003)
        target_pct = risk_pct * self.buy_rr_target.value

        # ── R:R TARGET HIT ──
        if current_profit >= target_pct:
            return "v2_tp_rr"

        # ── MOMENTUM FADE → early exit at 60% of target ──
        if current_profit >= target_pct * 0.6:
            rsi = last.get("rsi", 50)
            macd_h = last.get("macd_hist", 0)
            if is_short:
                # Short: momentum fades when RSI very low or MACD turns positive
                if rsi < 25 or macd_h > 0:
                    return "v2_tp_fade"
            else:
                # Long: momentum fades when RSI very high or MACD turns negative
                if rsi > 75 or macd_h < 0:
                    return "v2_tp_fade"

        # ── TIME EXIT ──
        minutes = (current_time - trade.open_date_utc).total_seconds() / 60
        max_minutes = self.buy_max_hold.value * 5

        # Small profit over time → take it
        if minutes > max_minutes * 0.7 and current_profit > 0.002:
            return "v2_time_profit"

        # Hard time limit
        if minutes > max_minutes:
            return "v2_time_force"

        return None

    # ═══════════════════════════════════════════════════════════════════
    # CONFIRM ENTRY — Daily limits + cooldown + loss management
    # ═══════════════════════════════════════════════════════════════════

    def confirm_trade_entry(
        self,
        pair: str,
        order_type: str,
        amount: float,
        rate: float,
        time_in_force: str,
        current_time: datetime,
        entry_tag: Optional[str],
        side: str,
        **kwargs,
    ) -> bool:
        today = current_time.strftime("%Y-%m-%d")

        # Reset daily counters
        if today not in self._daily_trades:
            self._daily_trades = {today: 0}
            self._daily_losses_r = {today: 0.0}

        # Daily trade limit
        if self._daily_trades.get(today, 0) >= self.buy_max_daily.value:
            return False

        # Daily loss limit: stop after -5R
        if self._daily_losses_r.get(today, 0) <= -5.0:
            return False

        # 3 consecutive losses → 30 min pause
        if self._consecutive_losses >= 3 and self._last_loss_time:
            pause_until = self._last_loss_time + timedelta(minutes=30)
            if current_time < pause_until:
                return False
            self._consecutive_losses = 0

        self._daily_trades[today] = self._daily_trades.get(today, 0) + 1
        return True

    def confirm_trade_exit(
        self,
        pair: str,
        trade: Trade,
        order_type: str,
        amount: float,
        rate: float,
        time_in_force: str,
        exit_reason: str,
        current_time: datetime,
        **kwargs,
    ) -> bool:
        profit = trade.calc_profit_ratio(rate)
        today = current_time.strftime("%Y-%m-%d")

        if profit < 0:
            self._consecutive_losses += 1
            self._last_loss_time = current_time
            # Track daily losses in R
            self._daily_losses_r[today] = self._daily_losses_r.get(today, 0) - 1
        else:
            self._consecutive_losses = 0
            self._daily_losses_r[today] = self._daily_losses_r.get(today, 0) + profit * 100

        return True
