# source: https://raw.githubusercontent.com/camster91/crypto-bots/1012ef382a65864d994f255551815206e8c5d2d3/strategies/HighTimeframeStrategy.py
# pragma pylint: disable=missing-docstring, invalid-name, pointless-string-statement
import numpy as np
import pandas as pd
from pandas import DataFrame
from freqtrade.strategy import IStrategy, IntParameter, DecimalParameter, informative
import talib.abstract as ta
import freqtrade.vendor.qtpylib.indicators as qtpylib


class Github_camster91_crypto_bots__HighTimeframeStrategy__20260525_100135(IStrategy):
    """
    Higher Timeframe Confirmation Strategy.

    Uses 1H chart for trend direction and 5m chart for precise entries.
    This filters out the majority of false signals in the 5m mean reversion.

    Architecture:
    - 1H: EMA-50 slope determines trend direction (up/flat/down)
    - 1H: RSI > 50 = bullish regime, RSI < 50 = bearish
    - 5m: Mean reversion entries ONLY when 1H says oversold is valid
    - 5m: Trend entries ONLY when 1H confirms uptrend

    Expected improvement: +15-25% win rate vs pure 5m strategy
    """

    INTERFACE_VERSION = 3

    # 1H filter params
    htf_ema_period = IntParameter(30, 100, default=50, space="buy")
    htf_rsi_bull = IntParameter(45, 60, default=50, space="buy")

    # 5m entry params
    entry_rsi_buy = IntParameter(20, 40, default=28, space="buy")
    entry_zscore = DecimalParameter(-3.0, -1.0, default=-1.5, space="buy", decimals=1)
    entry_volume = DecimalParameter(1.5, 4.0, default=2.5, space="buy", decimals=1)
    exit_rsi_sell = IntParameter(65, 85, default=75, space="sell")
    exit_zscore = DecimalParameter(0.0, 2.0, default=0.5, space="sell", decimals=1)

    minimal_roi = {"120": 0, "60": 0.015, "30": 0.035, "0": 0.10}
    stoploss = -0.12
    trailing_stop = False
    timeframe = "5m"
    process_only_new_candles = True
    startup_candle_count: int = 200

    @informative("1h")
    def populate_indicators_1h(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        """1H indicators — used for higher timeframe trend filter."""
        dataframe["ema_50"] = ta.EMA(dataframe, timeperiod=self.htf_ema_period.value)
        dataframe["ema_slope"] = dataframe["ema_50"] - dataframe["ema_50"].shift(3)
        dataframe["rsi"] = ta.RSI(dataframe, timeperiod=14)
        dataframe["adx"] = ta.ADX(dataframe, timeperiod=14)
        # Trend up: price above EMA + EMA rising + RSI bullish
        dataframe["trend_up"] = (
            (dataframe["close"] > dataframe["ema_50"]) &
            (dataframe["ema_slope"] > 0) &
            (dataframe["rsi"] > self.htf_rsi_bull.value)
        ).astype(int)
        # Sideways: ADX low
        dataframe["trend_sideways"] = (dataframe["adx"] < 20).astype(int)
        return dataframe

    def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        # 5m indicators
        dataframe["rsi"] = ta.RSI(dataframe, timeperiod=14)
        dataframe["atr"] = ta.ATR(dataframe, timeperiod=14)

        # Z-score mean reversion
        dataframe["sma_80"] = ta.SMA(dataframe, timeperiod=80)
        dataframe["std_80"] = dataframe["close"].rolling(window=80).std()
        dataframe["zscore"] = (dataframe["close"] - dataframe["sma_80"]) / dataframe["std_80"]

        # Volume spike
        dataframe["volume_mean"] = dataframe["volume"].rolling(window=20).mean()

        # MACD
        macd = ta.MACD(dataframe)
        dataframe["macd"] = macd["macd"]
        dataframe["macdsignal"] = macd["macdsignal"]

        return dataframe

    def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        # 1H says sideways or uptrend AND 5m shows oversold Z-score
        entry = (
            # Higher timeframe filter: either sideways or uptrend on 1H
            (
                (dataframe["trend_up_1h"] == 1) |
                (dataframe["trend_sideways_1h"] == 1)
            ) &
            # 5m: mean reversion signal
            (dataframe["zscore"] < self.entry_zscore.value) &
            (dataframe["rsi"] < self.entry_rsi_buy.value) &
            (dataframe["volume"] > self.entry_volume.value * dataframe["volume_mean"]) &
            (dataframe["std_80"] > 0) &
            (dataframe["volume"] > 0)
        )
        dataframe.loc[entry, "enter_long"] = 1
        return dataframe

    def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        exit_signal = (
            (dataframe["zscore"] > self.exit_zscore.value) &
            (dataframe["rsi"] > self.exit_rsi_sell.value)
        )
        dataframe.loc[exit_signal, "exit_long"] = 1
        return dataframe
