# source: https://raw.githubusercontent.com/Saadzwak/trading_bot/df850884ca08473e19d3a3d0e173160127c1e1af/user_data/strategies/B2XsMomentum7d.py
# directory_url: https://github.com/Saadzwak/trading_bot/blob/main/user_data/strategies/
# User: Saadzwak
# Repository: trading_bot
# --------------------"""B2 — Momentum cross-sectionnel 7j, top 3, rebalancement hebdomadaire (famille B).

Règles exactes pré-enregistrées dans HYPOTHESES.md (commit de gel edbb439) :
identiques à B1 avec lookback 7 jours. Fichier autonome (pas d'héritage) pour que
chaque candidate soit auditable isolément contre son enregistrement.
"""

import pandas as pd
from pandas import DataFrame

from freqtrade.strategy import IStrategy


class Github_Saadzwak_trading_bot__B2XsMomentum7d__20261001_212855(IStrategy):
    INTERFACE_VERSION = 3

    timeframe = "1h"
    can_short = False
    minimal_roi = {}
    stoploss = -0.25
    trailing_stop = False
    process_only_new_candles = True
    use_exit_signal = True

    LOOKBACK_DAYS = 7
    TOP_N = 3

    # +25 : marge d'une journée au-delà du lookback pour les bords de fenêtre.
    startup_candle_count = 7 * 24 + 25

    def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        lb = self.LOOKBACK_DAYS * 24
        closes = {}
        for p in self.dp.current_whitelist():
            pdf = self.dp.get_pair_dataframe(pair=p, timeframe=self.timeframe)
            if pdf is not None and not pdf.empty:
                closes[p] = pdf.set_index("date")["close"]
        panel = pd.DataFrame(closes)
        # Grille horaire complète + ffill court : voir B1 et JOURNAL.md 2026-08-10
        # (biais de décalage positionnel sur index troué, détecté par
        # lookahead-analysis avant tout essai).
        grid = pd.date_range(panel.index.min(), panel.index.max(),
                             freq="1h", tz="UTC")
        panel = panel.reindex(grid).ffill(limit=6)
        rets = panel / panel.shift(lb) - 1
        ranks = rets.rank(axis=1, ascending=False, method="min")

        pair = metadata["pair"]
        dataframe["xs_ret"] = rets[pair].reindex(dataframe["date"]).values
        dataframe["xs_rank"] = ranks[pair].reindex(dataframe["date"]).values
        d = dataframe["date"]
        dataframe["rebalance"] = (
            (d.dt.dayofweek == 6) & (d.dt.hour == 23)).astype(int)
        return dataframe

    def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        dataframe.loc[
            (dataframe["rebalance"] == 1)
            & (dataframe["xs_rank"] <= self.TOP_N)
            & (dataframe["xs_ret"] > 0),
            "enter_long"] = 1
        return dataframe

    def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        dataframe.loc[
            (dataframe["rebalance"] == 1)
            & ((dataframe["xs_rank"] > self.TOP_N) | (dataframe["xs_ret"] <= 0)),
            "exit_long"] = 1
        return dataframe
