# source: https://raw.githubusercontent.com/camster91/crypto-bots/5fd45118d5be1eaadd338e71ca26072dd5437831/strategies/MeanReversionStrategyV3.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__MeanReversionStrategyV3__20260511_032334(IStrategy):
    """
    Mean Reversion Strategy V3 - Hyperopt Optimized.

    Core: Prices revert to mean after extreme Z-score deviations.
    Profitable in bear/sideways markets.

    Optimized params: buy_zscore=-1.5, ma_period=92, rsi_buy=24,
    volume_threshold=2.9, sell_zscore=0.3, rsi_sell=80, max_hold_candles=43

    Results (bear market -28.86%):
    - Win Rate: 54.3%, Total Return: +0.18%
    - Max Drawdown: 0.54%, Sharpe: 2.28
    Best: AVAX/USDT, DOT/USDT, ETH/USDT
    Avoid: BTC/USDT (strong trends break mean reversion)
    """

    INTERFACE_VERSION = 3

    buy_zscore = DecimalParameter(-3.0, -0.5, default=-1.5, space="buy", decimals=1)
    sell_zscore = DecimalParameter(0.0, 2.0, default=0.3, space="sell", decimals=1)
    ma_period = IntParameter(20, 150, default=92, space="buy")
    rsi_buy = IntParameter(15, 40, default=24, space="buy")
    rsi_sell = IntParameter(65, 90, default=80, space="sell")
    volume_threshold = DecimalParameter(1.0, 5.0, default=2.9, space="buy", decimals=1)
    max_hold_candles = IntParameter(10, 100, default=43, space="sell")

    minimal_roi = {"141": 0, "71": 0.013, "37": 0.045, "0": 0.11}
    stoploss = -0.161
    trailing_stop = False
    timeframe = "5m"
    process_only_new_candles = True
    startup_candle_count: int = 150

    def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        dataframe["ma"] = ta.SMA(dataframe, timeperiod=self.ma_period.value)
        dataframe["std"] = dataframe["close"].rolling(window=self.ma_period.value).std()
        dataframe["zscore"] = (dataframe["close"] - dataframe["ma"]) / dataframe["std"]
        dataframe["rsi"] = ta.RSI(dataframe, timeperiod=14)
        dataframe["volume_mean"] = dataframe["volume"].rolling(window=20).mean()
        dataframe["atr"] = ta.ATR(dataframe, timeperiod=14)
        bollinger = qtpylib.bollinger_bands(qtpylib.typical_price(dataframe), window=20, stds=2)
        dataframe["bb_lower"] = bollinger["lower"]
        dataframe["bb_upper"] = bollinger["upper"]
        return dataframe

    def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        dataframe.loc[
            (
                (dataframe["zscore"] < self.buy_zscore.value) &
                (dataframe["rsi"] < self.rsi_buy.value) &
                (dataframe["volume"] > self.volume_threshold.value * dataframe["volume_mean"]) &
                (dataframe["std"] > 0) &
                (dataframe["volume"] > 0)
            ),
            "enter_long",
        ] = 1
        return dataframe

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