# source: https://raw.githubusercontent.com/Chris6622/class-AdvancedFuturesSwingStrategy-IStrategy-/786cfa2c323feab11c072706b06cb9e4243af820/user_data/strategies/SampleEWOStrategy.py
# pragma pylint: disable=missing-docstring, invalid-name, pointless-string-statement
# flake8: noqa: F401
# isort: skip_file
import numpy as np
import pandas as pd
from pandas import DataFrame

from freqtrade.strategy import (
    IStrategy,
    BooleanParameter,
    CategoricalParameter,
    IntParameter,
)
import talib.abstract as ta
from technical import qtpylib

class Github_Chris6622_class_AdvancedFuturesSwingStrategy_IStrategy___SampleEWOStrategy__20260208_084829(IStrategy):
    """
    Sample Freqtrade strategy with indicator sources as hyperoptable CategoricalParameters
    where possible. Indicators that require OHLCV (ADX, SAR, MFI) are always called with the full
    dataframe and are NOT hyperoptable for source.
    """

    INTERFACE_VERSION = 3
    can_short = False

    minimal_roi = {
        "60": 0.01,
        "30": 0.02,
        "0": 0.04,
    }
    stoploss = -0.10
    trailing_stop = False
    timeframe = "5m"
    process_only_new_candles = True
    use_exit_signal = True
    exit_profit_only = False
    ignore_roi_if_entry_signal = False
    startup_candle_count = 200

    # --- Hyperoptable Indicator Source Parameters (only for 1D price indicators) ---
    rsi_src = CategoricalParameter(['close', 'open', 'high', 'low'], default='close', space="buy", optimize=True, load=True)
    macd_src = CategoricalParameter(['close', 'open', 'high', 'low'], default='close', space="buy", optimize=True, load=True)
    tema_src = CategoricalParameter(['close', 'open', 'high', 'low'], default='close', space="buy", optimize=True, load=True)
    boll_src = CategoricalParameter(
        ['close', 'open', 'high', 'low', 'typical'], 
        default='typical', space="buy", optimize=True, load=True
    )
    ewo_src = CategoricalParameter(['close', 'open', 'high', 'low'], default='close', space="buy", optimize=True, load=True)

    # --- Other Hyperoptable Parameters ---
    buy_rsi = IntParameter(1, 50, default=30, space="buy", optimize=True, load=True)
    sell_rsi = IntParameter(50, 100, default=70, space="sell", optimize=True, load=True)
    short_rsi = IntParameter(51, 100, default=70, space="sell", optimize=True, load=True)
    exit_short_rsi = IntParameter(1, 50, default=30, space="buy", optimize=True, load=True)
    ewo_sma1 = IntParameter(2, 20, default=5, space="buy", optimize=True, load=True)
    ewo_sma2 = IntParameter(15, 60, default=35, space="buy", optimize=True, load=True)
    ewo_use_percent = BooleanParameter(default=True, space="buy", optimize=True, load=True)

    order_types = {
        "entry": "limit",
        "exit": "limit",
        "stoploss": "market",
        "stoploss_on_exchange": False,
    }
    order_time_in_force = {"entry": "GTC", "exit": "GTC"}

    plot_config = {
        "main_plot": {
            "tema": {},
            "sar": {"color": "white"},
            "ewo": {"color": "green"},
        },
        "subplots": {
            "MACD": {
                "macd": {"color": "blue"},
                "macdsignal": {"color": "orange"},
            },
            "RSI": {
                "rsi": {"color": "red"},
            },
        },
    }

    def informative_pairs(self):
        return []

    def get_src(self, dataframe: DataFrame, src: str):
        if src == 'typical':
            return qtpylib.typical_price(dataframe)
        return dataframe[src]

    def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        # --- INDICATORS WITH HYPEROPTABLE SOURCE ---
        rsi_src = self.get_src(dataframe, self.rsi_src.value)
        macd_src = self.get_src(dataframe, self.macd_src.value)
        tema_src = self.get_src(dataframe, self.tema_src.value)
        boll_src = self.get_src(dataframe, self.boll_src.value)
        ewo_src = self.get_src(dataframe, self.ewo_src.value)

        dataframe["rsi"] = ta.RSI(rsi_src)
        dataframe["tema"] = ta.TEMA(tema_src, timeperiod=9)
        # EWO
        sma1 = ta.SMA(ewo_src, timeperiod=self.ewo_sma1.value)
        sma2 = ta.SMA(ewo_src, timeperiod=self.ewo_sma2.value)
        if self.ewo_use_percent.value:
            dataframe["ewo"] = (sma1 - sma2) / ewo_src * 100
        else:
            dataframe["ewo"] = sma1 - sma2

        # --- MACD handling (robust to output types) ---
        macd = ta.MACD(macd_src)
        # MACD can return a DataFrame (preferred) or a tuple/list/array
        if isinstance(macd, pd.DataFrame):
            dataframe["macd"] = macd["macd"]
            dataframe["macdsignal"] = macd["macdsignal"]
            dataframe["macdhist"] = macd["macdhist"]
        elif isinstance(macd, (tuple, list, np.ndarray)):
            # MACD returns a tuple/list/array (macd, macdsignal, macdhist)
            dataframe["macd"] = macd[0]
            dataframe["macdsignal"] = macd[1]
            dataframe["macdhist"] = macd[2]
        else:
            # Fallback for dict
            dataframe["macd"] = macd.get("macd", np.nan)
            dataframe["macdsignal"] = macd.get("macdsignal", np.nan)
            dataframe["macdhist"] = macd.get("macdhist", np.nan)

        # --- INDICATORS THAT REQUIRE OHLCV (DO NOT HYPEROPT SOURCE) ---
        dataframe["adx"] = ta.ADX(dataframe)
        dataframe["sar"] = ta.SAR(dataframe)
        dataframe["mfi"] = ta.MFI(dataframe)

        # Bollinger Bands (window=20, stds=2) - source is hyperoptable!
        bollinger = qtpylib.bollinger_bands(boll_src, window=20, stds=2)
        dataframe["bb_lowerband"] = bollinger["lower"]
        dataframe["bb_middleband"] = bollinger["mid"]
        dataframe["bb_upperband"] = bollinger["upper"]
        dataframe["bb_percent"] = (dataframe["close"] - dataframe["bb_lowerband"]) / (
            dataframe["bb_upperband"] - dataframe["bb_lowerband"]
        )
        dataframe["bb_width"] = (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) / dataframe["bb_middleband"]

        return dataframe

    def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        # Entry: EWO crosses above 0
        dataframe.loc[
            (
                qtpylib.crossed_above(dataframe["ewo"], 0)
                & (dataframe["volume"] > 0)
            ),
            "enter_long",
        ] = 1
        return dataframe

    def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        # Exit: EWO crosses below 0
        dataframe.loc[
            (
                qtpylib.crossed_below(dataframe["ewo"], 0)
                & (dataframe["volume"] > 0)
            ),
            "exit_long",
        ] = 1
        return dataframe