# source: https://raw.githubusercontent.com/camster91/crypto-bots/5fd45118d5be1eaadd338e71ca26072dd5437831/strategies/ImprovedStrategy.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
import talib.abstract as ta
import freqtrade.vendor.qtpylib.indicators as qtpylib


class Github_camster91_crypto_bots__ImprovedStrategy__20260511_032334(IStrategy):
    """
    Improved RSI + EMA + MACD Strategy.

    Enhancements over SampleStrategy:
    - ADX-adaptive EMA trend filter (EMA-168 strong trend, EMA-50 sideways)
    - RSI crossover entry (momentum shift, not just level)
    - MACD bullish confirmation
    - Volume filter (70% of average)
    - ATR-based dynamic stop loss (1.5x ATR, capped 2-8%)
    - Trailing stop (activates after 18.5% profit)

    Hyperopt results (50 epochs): buy_rsi=30, sell_rsi=78, ema_period=168
    ROI: 22.5% at 0m, 3.7% at 39m, 2.2% at 73m, 0% at 110m
    """

    INTERFACE_VERSION = 3

    buy_rsi = IntParameter(20, 40, default=30, space="buy")
    sell_rsi = IntParameter(60, 85, default=78, space="sell")
    ema_period = IntParameter(50, 250, default=168, space="buy")
    adx_threshold = IntParameter(20, 35, default=25, space="buy")
    volume_factor = DecimalParameter(0.5, 1.5, default=0.7, space="buy")

    minimal_roi = {"110": 0, "73": 0.022, "39": 0.037, "0": 0.225}
    stoploss = -0.08
    trailing_stop = True
    trailing_stop_positive = 0.185
    trailing_stop_positive_offset = 0.313
    trailing_only_offset_is_reached = True
    timeframe = "5m"
    process_only_new_candles = True
    startup_candle_count: int = 250

    def custom_stoploss(self, pair: str, trade, current_time, current_rate: float,
                        current_profit: float, after_fill: bool, **kwargs) -> float:
        dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe)
        if len(dataframe) < 1:
            return self.stoploss
        last_candle = dataframe.iloc[-1]
        if "atr" in last_candle and last_candle["atr"] > 0:
            atr_stop = -(1.5 * last_candle["atr"] / current_rate)
            return max(min(atr_stop, -0.02), -0.08)
        return self.stoploss

    def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        dataframe["rsi"] = ta.RSI(dataframe, timeperiod=14)
        dataframe["rsi_prev"] = dataframe["rsi"].shift(1)
        dataframe["atr"] = ta.ATR(dataframe, timeperiod=14)
        dataframe["adx"] = ta.ADX(dataframe, timeperiod=14)
        dataframe["ema_strong"] = ta.EMA(dataframe, timeperiod=self.ema_period.value)
        dataframe["ema_weak"] = ta.EMA(dataframe, timeperiod=50)
        dataframe["ema_filter"] = np.where(
            dataframe["adx"] > self.adx_threshold.value,
            dataframe["ema_strong"],
            dataframe["ema_weak"]
        )
        macd = ta.MACD(dataframe)
        dataframe["macd"] = macd["macd"]
        dataframe["macdsignal"] = macd["macdsignal"]
        dataframe["macdhist"] = macd["macdhist"]
        dataframe["volume_mean"] = dataframe["volume"].rolling(window=20).mean()
        return dataframe

    def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        dataframe.loc[
            (
                (dataframe["rsi"] > self.buy_rsi.value) &
                (dataframe["rsi_prev"] <= self.buy_rsi.value) &
                (dataframe["close"] > dataframe["ema_filter"]) &
                (dataframe["macd"] > dataframe["macdsignal"]) &
                (dataframe["volume"] > self.volume_factor.value * dataframe["volume_mean"]) &
                (dataframe["volume"] > 0)
            ),
            "enter_long",
        ] = 1
        return dataframe

    def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        dataframe.loc[
            (
                (dataframe["rsi"] > self.sell_rsi.value) &
                (dataframe["volume"] > 0)
            ),
            "exit_long",
        ] = 1
        return dataframe
