# source: https://raw.githubusercontent.com/waleedba19/trading-bot-live/f7eef7897ed63f7c33203aa06a4725d7a8e3c42b/user_data/strategies/FreqaiClfRegime.py
# directory_url: https://github.com/waleedba19/trading-bot-live/blob/main/user_data/strategies/
# User: waleedba19
# Repository: trading-bot-live
# --------------------import logging
from functools import reduce
from pathlib import Path

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

from freqtrade.strategy import DecimalParameter, IStrategy, informative


logger = logging.getLogger(__name__)


class Github_waleedba19_trading_bot_live__FreqaiClfRegime__20261026_210640(IStrategy):
    """
    FreqAI spot long-only classifier strategy used with
    `--freqaimodel LightGBMClassifier` and `user_data/config_freqai_clf.json`.

    Predicts whether the +2% target (`close * 1.02`) is hit before the -5% stop
    (`close * 0.95`) within the label period and enters long when the predicted
    probability exceeds a hyperoptable threshold and BTC trades above its daily
    EMA200.
    """

    INTERFACE_VERSION = 3

    timeframe = "1h"
    can_short = False

    process_only_new_candles = True
    startup_candle_count = 4900

    stoploss = -0.05
    trailing_stop = False
    minimal_roi = {"0": 0.02}

    entry_prob = DecimalParameter(0.50, 0.85, decimals=2, default=0.60, space="buy")

    _fng = None

    def feature_engineering_expand_all(
        self, dataframe: DataFrame, period: int, metadata: dict, **kwargs
    ) -> DataFrame:
        dataframe["%-rsi-period"] = ta.RSI(dataframe, timeperiod=period)
        dataframe["%-adx-period"] = ta.ADX(dataframe, timeperiod=period)
        dataframe["%-atr_pct-period"] = ta.ATR(dataframe, timeperiod=period) / dataframe["close"]
        dataframe["%-roc-period"] = ta.ROC(dataframe, timeperiod=period)
        return dataframe

    def feature_engineering_expand_basic(
        self, dataframe: DataFrame, metadata: dict, **kwargs
    ) -> DataFrame:
        dataframe["%-pct-change"] = dataframe["close"].pct_change()
        dataframe["%-ret_4"] = dataframe["close"].pct_change(4)
        dataframe["%-volume_z"] = dataframe["volume"] / dataframe["volume"].rolling(48).mean()
        return dataframe

    def feature_engineering_standard(
        self, dataframe: DataFrame, metadata: dict, **kwargs
    ) -> DataFrame:
        dataframe["%-hour_of_day"] = dataframe["date"].dt.hour
        dataframe["%-day_of_week"] = dataframe["date"].dt.dayofweek

        if self._fng is None:
            path = Path(self.config["user_data_dir"]) / "external" / "fng.csv"
            fng = pd.read_csv(path)
            fng["avail"] = pd.to_datetime(fng["date"], utc=True) + pd.Timedelta(days=1)
            fng["chg7"] = fng["fng"].diff(7)
            self._fng = fng[["avail", "fng", "chg7"]].sort_values("avail")

        merged = dataframe.copy()
        merged["_orig_order"] = range(len(merged))
        merged = merged.sort_values("date")
        merged = pd.merge_asof(
            merged,
            self._fng.assign(avail=self._fng["avail"].astype(merged["date"].dtype)),
            left_on="date",
            right_on="avail",
            direction="backward",
        )
        merged = merged.sort_values("_orig_order")

        dataframe["%-fng"] = merged["fng"].fillna(50).to_numpy()
        dataframe["%-fng_chg7"] = merged["chg7"].fillna(0).to_numpy()
        return dataframe

    def set_freqai_targets(self, dataframe: DataFrame, metadata: dict, **kwargs) -> DataFrame:
        self.freqai.class_names = ["down", "up"]
        n = self.freqai_info["feature_parameters"]["label_period_candles"]

        close = dataframe["close"].to_numpy()
        up_price = close * 1.02
        dn_price = close * 0.95
        first_up = np.full(len(dataframe), n + 1, dtype=int)
        first_dn = np.full(len(dataframe), n + 1, dtype=int)

        for k in range(1, n + 1):
            hi = dataframe["high"].shift(-k).to_numpy()
            lo = dataframe["low"].shift(-k).to_numpy()
            dn_hit = (lo <= dn_price) & (first_dn == n + 1)
            first_dn[dn_hit] = k
            up_hit = (hi >= up_price) & (first_up == n + 1)
            first_up[up_hit] = k

        dataframe["&-hit"] = np.where(first_up < first_dn, "up", "down")
        return dataframe

    def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        dataframe = self.freqai.start(dataframe, metadata, self)
        return dataframe

    @informative("1d", "BTC/USDT")
    def populate_indicators_btc_1d(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        dataframe["ema200"] = ta.EMA(dataframe, timeperiod=200)
        return dataframe

    def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        enter_long_conditions = [
            dataframe["do_predict"] == 1,
            dataframe["up"] > self.entry_prob.value,
            dataframe["btc_usdt_close_1d"] > dataframe["btc_usdt_ema200_1d"],
            dataframe["volume"] > 0,
        ]
        if enter_long_conditions:
            dataframe.loc[
                reduce(lambda x, y: x & y, enter_long_conditions), ["enter_long", "enter_tag"]
            ] = (1, "clf_hit_long")
        return dataframe

    def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        exit_long_conditions = [
            dataframe["do_predict"] == 1,
            dataframe["up"] < 0.35,
        ]
        if exit_long_conditions:
            dataframe.loc[reduce(lambda x, y: x & y, exit_long_conditions), "exit_long"] = 1
        return dataframe
