# source: https://raw.githubusercontent.com/WaritDev/freqtrade-data-driven/2c4d5a30dcf71a09c581a72087c5d442974f1df4/user_data/strategies/long_zigzag_strategy_optimize.py
import numpy as np
import pandas as pd
from pandas import DataFrame

from freqtrade.strategy import IStrategy, IntParameter
import talib.abstract as ta
from technical import qtpylib

from scipy.signal import argrelextrema


class Github_WaritDev_freqtrade_data_driven__long_zigzag_strategy_optimize__20250910_081903(IStrategy):
    INTERFACE_VERSION = 3

    can_short: bool = False
    timeframe = "15m"
    startup_candle_count = 300 

    minimal_roi = {"0": 0.2} 
    stoploss = -0.1
    trailing_stop = True
    trailing_stop_positive = 0.02
    trailing_stop_positive_offset = 0.06
    trailing_only_offset_is_reached = True

    use_exit_signal = True

    adx_min = IntParameter(15, 25, default=20, optimize=True, space="buy")
    zz_window = IntParameter(10, 30, default=20, optimize=True, space="buy")

    def _ema_slope(self, series: pd.Series, period: int = 200) -> pd.Series:
        ema = pd.Series(ta.EMA(series, timeperiod=period), index=series.index)
        slope = ema - ema.shift(1)
        return slope

    def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        dataframe["ema200"] = pd.Series(
            ta.EMA(dataframe["close"], timeperiod=200), index=dataframe.index
        )
        dataframe["ema200_slope"] = self._ema_slope(dataframe["close"], 200)

        dataframe["adx"] = ta.ADX(dataframe)

        dataframe["atr"] = ta.ATR(dataframe, timeperiod=14)
        dataframe["atr_pct"] = (dataframe["atr"] / dataframe["close"]) * 100.0

        stoch_rsi = ta.STOCHRSI(
            dataframe, timeperiod=14, fastk_period=3, fastd_period=3
        )
        dataframe["stoch_rsi_k"] = stoch_rsi["fastk"]
        dataframe["stoch_rsi_d"] = stoch_rsi["fastd"]

        dataframe["vol_sma20"] = ta.SMA(dataframe["volume"], timeperiod=20)

        window = int(self.zz_window.value) if hasattr(self, "zz_window") else 20
        highs_idx = argrelextrema(
            dataframe["high"].values, np.greater_equal, order=window
        )[0]
        lows_idx = argrelextrema(
            dataframe["low"].values, np.less_equal, order=window
        )[0]

        dataframe["zigzag_high"] = np.nan
        dataframe["zigzag_low"] = np.nan
        if len(highs_idx) > 0:
            dataframe.loc[
                dataframe.index[highs_idx], "zigzag_high"
            ] = dataframe["high"].iloc[highs_idx]
        if len(lows_idx) > 0:
            dataframe.loc[
                dataframe.index[lows_idx], "zigzag_low"
            ] = dataframe["low"].iloc[lows_idx]

        dataframe["resistance"] = dataframe["zigzag_high"].ffill()
        dataframe["support"] = dataframe["zigzag_low"].ffill()

        dataframe["dist_to_support"] = dataframe["close"] - dataframe["support"]
        dataframe["near_support"] = (
            (dataframe["dist_to_support"] >= 0)
            & (dataframe["dist_to_support"] <= 0.5 * dataframe["atr"])
        )

        dataframe["breakout_ok"] = (
            (dataframe["close"] > dataframe["resistance"] * 1.001)
            & (dataframe["volume"] > 1.2 * dataframe["vol_sma20"])
        )

        dataframe["bullish_close"] = dataframe["close"] > dataframe["open"]
        dataframe["stoch_cross_up_low"] = (
            qtpylib.crossed_above(dataframe["stoch_rsi_k"], dataframe["stoch_rsi_d"])
        ) & (dataframe["stoch_rsi_k"] < 30)

        return dataframe

    def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        adx_min = int(self.adx_min.value) if hasattr(self, "adx_min") else 20

        dataframe.loc[
            (
                (dataframe["close"] > dataframe["ema200"])
                & (dataframe["ema200_slope"] > 0)
                & (dataframe["adx"] >= adx_min)
                & (dataframe["atr_pct"] > 0.8)
                & (
                    dataframe["breakout_ok"]
                    | (
                        dataframe["near_support"]
                        & dataframe["bullish_close"]
                        & dataframe["stoch_cross_up_low"]
                    )
                )
                & (dataframe["volume"] > 0)
            ),
            "enter_long",
        ] = 1

        return dataframe

    def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        dataframe.loc[
            (
                (dataframe["stoch_rsi_k"] > 70)
                & (dataframe["stoch_rsi_k"] < dataframe["stoch_rsi_d"])
            ),
            "exit_long",
        ] = 1
        return dataframe

    @property
    def protections(self):
        return [
            {"method": "CooldownPeriod", "stop_duration_candles": 3},
            {
                "method": "MaxDrawdown",
                "lookback_period_candles": 30,
                "trade_limit": 5,
                "stop_duration_candles": 6,
                "max_allowed_drawdown": 0.2,
            },
            {
                "method": "StoplossGuard",
                "lookback_period_candles": 24,
                "trade_limit": 2,
                "stop_duration_candles": 6,
                "only_per_pair": True,
            },
        ]