# source: https://raw.githubusercontent.com/Saadzwak/trading_bot/df850884ca08473e19d3a3d0e173160127c1e1af/user_data/strategies/C2LiquidityShockFixed.py
# directory_url: https://github.com/Saadzwak/trading_bot/blob/main/user_data/strategies/
# User: Saadzwak
# Repository: trading_bot
# --------------------"""C2 — Choc de liquidité, sortie fixe (famille C, mean reversion conditionnelle).

Règles exactes pré-enregistrées dans HYPOTHESES.md (commit de gel edbb439) :
- Entrée identique à C1 (chute <= -3×ATR%(24) pré-choc + volume quote >= 4× MM24h).
- Sorties : take-profit à +1 × ATR%(24) mesuré à l'entrée ; OU timeout 24 bougies ;
  OU stop -2 × ATR%(24) à l'entrée.

Mêmes résolutions d'ambiguïté que C1 (ATR pré-choc via shift(1), volume quote
approximé par volume×close). Le take-profit est un niveau de PRIX
(open_rate × (1 + ATR%)), évalué par custom_exit à la granularité du
--timeframe-detail (5m). Fichier autonome pour auditabilité.
"""

from datetime import timedelta

import numpy as np
import talib.abstract as ta
from pandas import DataFrame

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


class Github_Saadzwak_trading_bot__C2LiquidityShockFixed__20261001_212855(IStrategy):
    INTERFACE_VERSION = 3

    timeframe = "1h"
    can_short = False
    minimal_roi = {}
    stoploss = -0.25            # plancher catastrophe ; le stop actif est -2×ATR
    trailing_stop = False
    process_only_new_candles = True
    use_exit_signal = True
    use_custom_stoploss = True

    ATR_LEN = 24
    DROP_MULT = 3.0
    VOL_MULT = 4.0
    STOP_ATR = 2.0
    TP_ATR = 1.0
    TIMEOUT_HOURS = 24

    startup_candle_count = 30

    def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        atr_abs = ta.ATR(dataframe, timeperiod=self.ATR_LEN)
        dataframe["atr_pct"] = atr_abs.shift(1) / dataframe["close"].shift(1)
        dataframe["candle_ret"] = dataframe["close"] / dataframe["open"] - 1
        dataframe["qvol"] = dataframe["volume"] * dataframe["close"]
        dataframe["qvol_ma24"] = dataframe["qvol"].rolling(24).mean()
        return dataframe

    def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        dataframe.loc[
            (dataframe["atr_pct"] > 0)
            & (dataframe["candle_ret"] <= -self.DROP_MULT * dataframe["atr_pct"])
            & (dataframe["qvol"] >= self.VOL_MULT * dataframe["qvol_ma24"]),
            "enter_long"] = 1
        return dataframe

    def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        return dataframe  # aucune sortie sur signal : TP / timeout / stop uniquement

    def _entry_atr_pct(self, pair: str, trade: Trade):
        df, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe)
        signal_time = (trade.open_date_utc.replace(minute=0, second=0, microsecond=0)
                       - timedelta(hours=1))
        row = df.loc[df["date"] == signal_time]
        if row.empty:
            return None
        val = float(row["atr_pct"].iloc[0])
        return val if np.isfinite(val) and val > 0 else None

    def custom_stoploss(self, pair: str, trade: Trade, current_time,
                        current_rate: float, current_profit: float,
                        after_fill: bool, **kwargs):
        atr = self._entry_atr_pct(pair, trade)
        if atr is None:
            return None
        return stoploss_from_open(-self.STOP_ATR * atr, current_profit,
                                  is_short=False, leverage=1.0)

    def custom_exit(self, pair: str, trade: Trade, current_time,
                    current_rate: float, current_profit: float, **kwargs):
        atr = self._entry_atr_pct(pair, trade)
        if atr is not None and current_rate >= trade.open_rate * (1 + self.TP_ATR * atr):
            return "tp_1atr"
        if current_time - trade.open_date_utc >= timedelta(hours=self.TIMEOUT_HOURS):
            return "timeout_24h"
        return None
