# source: https://raw.githubusercontent.com/luv-robot/QuantOdyssey/c38ce8fdb401e88935ddc2518bdf544042415c27/freqtrade_user_data/strategies/TrendPullbackReversionGridL48Z125H24C6.py
from __future__ import annotations

from datetime import timedelta

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


class Github_luv_robot_QuantOdyssey__TrendPullbackReversionGridL48Z125H24C6__20260522_092146(IStrategy):
    """Materialized paper candidate for the Harness trend_pullback_reversion winner.

    This is a Freqtrade dry-run artifact for paper certification. It mirrors the
    low-turnover z-score recovery candidate that the Harness found inside
    liquid-trend contexts, but it does not replace MarketContext/Risk Auditor
    gating in QuantOdyssey.
    """

    timeframe = "1h"
    startup_candle_count = 72
    can_short = True
    process_only_new_candles = True

    stoploss = -0.12
    minimal_roi = {"0": 0.08, "24": 0.03, "48": 0}

    lookback_bars = 48
    entry_zscore = 1.25
    horizon_bars = 24
    cooldown_bars = 6
    min_trend_abs_return = 0.015

    protections = [
        {
            "method": "CooldownPeriod",
            "stop_duration_candles": cooldown_bars,
        }
    ]

    def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        close = dataframe["close"]
        rolling_mean = close.rolling(self.lookback_bars).mean()
        rolling_std = close.rolling(self.lookback_bars).std(ddof=0)

        dataframe["qo_center"] = rolling_mean
        dataframe["qo_zscore"] = (close - rolling_mean) / rolling_std.replace(0, float("nan"))
        dataframe["qo_trend_return"] = close / close.shift(self.lookback_bars) - 1
        dataframe["qo_liquid_trend_proxy"] = dataframe["qo_trend_return"].abs() >= self.min_trend_abs_return
        return dataframe

    def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        base = dataframe["qo_liquid_trend_proxy"] & (dataframe["volume"] > 0)
        dataframe.loc[
            base & (dataframe["qo_zscore"] <= -self.entry_zscore),
            "enter_long",
        ] = 1
        dataframe.loc[
            base & (dataframe["qo_zscore"] >= self.entry_zscore),
            "enter_short",
        ] = 1
        return dataframe

    def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        dataframe.loc[dataframe["qo_zscore"] >= 0, "exit_long"] = 1
        dataframe.loc[dataframe["qo_zscore"] <= 0, "exit_short"] = 1
        return dataframe

    def custom_exit(
        self,
        pair: str,
        trade: Trade,
        current_time,
        current_rate: float,
        current_profit: float,
        **kwargs,
    ):
        if current_time - trade.open_date_utc >= timedelta(hours=self.horizon_bars):
            return "horizon_exit"
        return None
