# source: https://raw.githubusercontent.com/royaldynamo128-gif/claude-tradex.1/0c1a09662ae12719c1ef6ace5be8a9cd1503c1b6/scripts/research/freqtrade_bridge/strategy_import.py
"""
integrations/freqtrade_bridge/strategy_import.py

Imports well-known Freqtrade community strategies purely for benchmarking
in this folder only. Never import them outside this directory.
"""

import logging
import pandas as pd

logger = logging.getLogger(__name__)

import sys
import os
import types
import importlib.util
from typing import Dict, Type


# Fallback IStrategy definition and sys.modules injecting in case Freqtrade is not installed.
class IStrategy:
    """Stub IStrategy class to prevent import crashes when Freqtrade is not installed."""

    INTERFACE_VERSION = 3

    def __init__(self, config: dict):
        self.config = config


if "freqtrade" not in sys.modules or "freqtrade.strategy" not in sys.modules:
    try:
        from freqtrade.strategy import IStrategy as RealIStrategy

        IStrategy = RealIStrategy
    except ImportError:
        # Inject mock modules
        freqtrade_mod = types.ModuleType("freqtrade")
        freqtrade_strategy_mod = types.ModuleType("freqtrade.strategy")
        freqtrade_strategy_mod.IStrategy = IStrategy
        freqtrade_mod.strategy = freqtrade_strategy_mod

        sys.modules["freqtrade"] = freqtrade_mod
        sys.modules["freqtrade.strategy"] = freqtrade_strategy_mod

# Ensure pandas is used correctly
DataFrame = pd.DataFrame


def load_strategies_from_directory(directory_path: str) -> Dict[str, Type[IStrategy]]:
    """
    Scans directory_path for any strategy .py files and loads subclasses of IStrategy.
    """
    strategies = {}
    if not os.path.exists(directory_path):
        return strategies

    # Add directory to sys.path so nested imports work
    if directory_path not in sys.path:
        sys.path.insert(0, directory_path)

    for filename in os.listdir(directory_path):
        if filename.endswith(".py") and not filename.startswith("__"):
            filepath = os.path.join(directory_path, filename)
            module_name = f"dynamic_strategy_{os.path.splitext(filename)[0]}"
            try:
                spec = importlib.util.spec_from_file_location(module_name, filepath)
                if spec is None or spec.loader is None:
                    continue
                module = importlib.util.module_from_spec(spec)
                sys.modules[module_name] = module
                spec.loader.exec_module(module)

                # Scan module for subclasses of IStrategy
                for attr_name in dir(module):
                    attr = getattr(module, attr_name)
                    if (
                        isinstance(attr, type)
                        and attr is not IStrategy
                        and issubclass(attr, IStrategy)
                    ):
                        strategies[attr.__name__] = attr
            except Exception as e:
                logger.error(f"Failed to load dynamic strategy from {filepath}: {e}")

    return strategies


class Github_royaldynamo128_gif_claude_tradex_1__strategy_import__20260711_150558(IStrategy):
    """
    Github_royaldynamo128_gif_claude_tradex_1__strategy_import__20260711_150558 - Freqtrade default community strategy stub.
    Matches standard Freqtrade design.
    """

    INTERFACE_VERSION = 3

    # Strategy parameters
    timeframe = "5m"

    # ROI table:
    minimal_roi = {"0": 0.10, "30": 0.05, "60": 0.02, "120": 0.0}

    # Stoploss:
    stoploss = -0.10

    # Trailing stop:
    trailing_stop = False

    def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        try:
            import talib.abstract as ta

            dataframe["rsi"] = ta.RSI(dataframe, timeperiod=14)
            dataframe["sma"] = ta.SMA(dataframe, timeperiod=20)
        except ImportError:
            # simple pandas fallbacks
            dataframe["rsi"] = 50.0
            dataframe["sma"] = dataframe["close"].rolling(20, min_periods=1).mean()
        return dataframe

    def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        dataframe.loc[
            ((dataframe["rsi"] < 30) & (dataframe["close"] > dataframe["sma"])),
            "enter_long",
        ] = 1

        dataframe.loc[
            ((dataframe["rsi"] > 70) & (dataframe["close"] < dataframe["sma"])),
            "enter_short",
        ] = 1

        return dataframe

    def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        dataframe.loc[((dataframe["rsi"] > 70)), "exit_long"] = 1

        dataframe.loc[((dataframe["rsi"] < 30)), "exit_short"] = 1

        return dataframe


class Github_royaldynamo128_gif_claude_tradex_1__strategy_import__20260711_150558(IStrategy):
    """
    ClucHustle strategy - well-known community strategy.
    """

    INTERFACE_VERSION = 3
    timeframe = "5m"
    minimal_roi = {"0": 0.05}
    stoploss = -0.05

    def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        try:
            import talib.abstract as ta

            dataframe["rsi"] = ta.RSI(dataframe, timeperiod=14)
            dataframe["ema_100"] = ta.EMA(dataframe, timeperiod=100)
            # Bollinger bands
            bb = ta.BBANDS(dataframe, timeperiod=20, nbdevup=2.0, nbdevdn=2.0)
            dataframe["bb_lowerband"] = bb["lowerband"]
            dataframe["bb_middleband"] = bb["middleband"]
        except ImportError:
            dataframe["rsi"] = 50.0
            dataframe["ema_100"] = dataframe["close"].rolling(100, min_periods=1).mean()
            mean = dataframe["close"].rolling(20, min_periods=1).mean()
            std = dataframe["close"].rolling(20, min_periods=1).std().fillna(0.0)
            dataframe["bb_lowerband"] = mean - 2 * std
            dataframe["bb_middleband"] = mean
        return dataframe

    def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        dataframe.loc[
            (
                (dataframe["close"] < dataframe["bb_lowerband"])
                & (dataframe["close"] < dataframe["ema_100"])
                & (dataframe["rsi"] < 35)
            ),
            "enter_long",
        ] = 1
        return dataframe

    def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        dataframe.loc[
            ((dataframe["close"] > dataframe["bb_middleband"])), "exit_long"
        ] = 1
        return dataframe


class Github_royaldynamo128_gif_claude_tradex_1__strategy_import__20260711_150558(IStrategy):
    """
    BB_RPB_TSL - Freqtrade strategy stub.
    Uses Bollinger Bands (BB), RSI, and EMA trend filters.
    """

    INTERFACE_VERSION = 3
    timeframe = "5m"
    minimal_roi = {"0": 0.05}
    stoploss = -0.05

    def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        try:
            import talib.abstract as ta

            dataframe["rsi"] = ta.RSI(dataframe, timeperiod=14)
            dataframe["ema_50"] = ta.EMA(dataframe, timeperiod=50)
            dataframe["ema_200"] = ta.EMA(dataframe, timeperiod=200)
            bb = ta.BBANDS(dataframe, timeperiod=20, nbdevup=2.0, nbdevdn=2.0)
            dataframe["bb_lowerband"] = bb["lowerband"]
            dataframe["bb_middleband"] = bb["middleband"]
            dataframe["bb_upperband"] = bb["upperband"]
        except ImportError:
            dataframe["rsi"] = 50.0
            dataframe["ema_50"] = dataframe["close"].rolling(50, min_periods=1).mean()
            dataframe["ema_200"] = dataframe["close"].rolling(200, min_periods=1).mean()
            mean = dataframe["close"].rolling(20, min_periods=1).mean()
            std = dataframe["close"].rolling(20, min_periods=1).std().fillna(0.0)
            dataframe["bb_lowerband"] = mean - 2 * std
            dataframe["bb_middleband"] = mean
            dataframe["bb_upperband"] = mean + 2 * std
        return dataframe

    def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        dataframe.loc[
            (
                (dataframe["close"] < dataframe["bb_lowerband"])
                & (dataframe["rsi"] < 30)
                & (dataframe["ema_50"] > dataframe["ema_200"])
            ),
            "enter_long",
        ] = 1
        dataframe.loc[
            (
                (dataframe["close"] > dataframe["bb_upperband"])
                & (dataframe["rsi"] > 70)
                & (dataframe["ema_50"] < dataframe["ema_200"])
            ),
            "enter_short",
        ] = 1
        return dataframe

    def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        dataframe.loc[
            (
                (dataframe["close"] > dataframe["bb_middleband"])
                | (dataframe["rsi"] > 70)
            ),
            "exit_long",
        ] = 1
        dataframe.loc[
            (
                (dataframe["close"] < dataframe["bb_middleband"])
                | (dataframe["rsi"] < 30)
            ),
            "exit_short",
        ] = 1
        return dataframe


class Github_royaldynamo128_gif_claude_tradex_1__strategy_import__20260711_150558(IStrategy):
    """
    WaveTrend - Freqtrade strategy stub.
    Uses WaveTrend Oscillator (wt1, wt2) for momentum entry/exit.
    """

    INTERFACE_VERSION = 3
    timeframe = "5m"
    minimal_roi = {"0": 0.05}
    stoploss = -0.05

    def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        ap = (dataframe["high"] + dataframe["low"] + dataframe["close"]) / 3.0
        esa = ap.ewm(span=10, adjust=False).mean()
        d = (ap - esa).abs().ewm(span=10, adjust=False).mean()
        d = d.replace(0.0, 1e-5)
        ci = (ap - esa) / (0.015 * d)
        dataframe["wt1"] = ci.ewm(span=21, adjust=False).mean()
        dataframe["wt2"] = dataframe["wt1"].rolling(4, min_periods=1).mean()
        return dataframe

    def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        dataframe.loc[
            (dataframe["wt1"] < -60)
            | ((dataframe["wt1"] < -50) & (dataframe["wt1"] > dataframe["wt2"])),
            "enter_long",
        ] = 1
        dataframe.loc[
            (dataframe["wt1"] > 60)
            | ((dataframe["wt1"] > 50) & (dataframe["wt1"] < dataframe["wt2"])),
            "enter_short",
        ] = 1
        return dataframe

    def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        dataframe.loc[
            (dataframe["wt1"] > 50) | (dataframe["wt1"] < dataframe["wt2"]), "exit_long"
        ] = 1
        dataframe.loc[
            (dataframe["wt1"] < -50) | (dataframe["wt1"] > dataframe["wt2"]),
            "exit_short",
        ] = 1
        return dataframe


class Github_royaldynamo128_gif_claude_tradex_1__strategy_import__20260711_150558(IStrategy):
    """
    NostalgiaForInfinity - Freqtrade strategy stub.
    Implements multiple buy conditions using EMA, RSI, and Bollinger Bands.
    """

    INTERFACE_VERSION = 3
    timeframe = "5m"
    minimal_roi = {"0": 0.05}
    stoploss = -0.05

    def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        try:
            import talib.abstract as ta

            dataframe["rsi"] = ta.RSI(dataframe, timeperiod=14)
            dataframe["ema_20"] = ta.EMA(dataframe, timeperiod=20)
            dataframe["ema_50"] = ta.EMA(dataframe, timeperiod=50)
            dataframe["ema_100"] = ta.EMA(dataframe, timeperiod=100)
            bb = ta.BBANDS(dataframe, timeperiod=20, nbdevup=2.0, nbdevdn=2.0)
            dataframe["bb_lowerband"] = bb["lowerband"]
            dataframe["bb_middleband"] = bb["middleband"]
            dataframe["bb_upperband"] = bb["upperband"]
        except ImportError:
            dataframe["rsi"] = 50.0
            dataframe["ema_20"] = dataframe["close"].rolling(20, min_periods=1).mean()
            dataframe["ema_50"] = dataframe["close"].rolling(50, min_periods=1).mean()
            dataframe["ema_100"] = dataframe["close"].rolling(100, min_periods=1).mean()
            mean = dataframe["close"].rolling(20, min_periods=1).mean()
            std = dataframe["close"].rolling(20, min_periods=1).std().fillna(0.0)
            dataframe["bb_lowerband"] = mean - 2 * std
            dataframe["bb_middleband"] = mean
            dataframe["bb_upperband"] = mean + 2 * std
        return dataframe

    def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        dataframe.loc[
            (
                (dataframe["close"] < dataframe["bb_lowerband"])
                & (dataframe["rsi"] < 35)
                & (dataframe["close"] < dataframe["ema_100"])
            ),
            "enter_long",
        ] = 1
        dataframe.loc[
            (
                (dataframe["close"] > dataframe["bb_upperband"])
                & (dataframe["rsi"] > 65)
                & (dataframe["close"] > dataframe["ema_100"])
            ),
            "enter_short",
        ] = 1
        return dataframe

    def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        dataframe.loc[
            (
                (dataframe["close"] > dataframe["bb_middleband"])
                & (dataframe["rsi"] > 55)
            ),
            "exit_long",
        ] = 1
        dataframe.loc[
            (
                (dataframe["close"] < dataframe["bb_middleband"])
                & (dataframe["rsi"] < 45)
            ),
            "exit_short",
        ] = 1
        return dataframe


class Github_royaldynamo128_gif_claude_tradex_1__strategy_import__20260711_150558(IStrategy):
    """
    Ichimoku / ichi - Freqtrade strategy stub.
    Uses Ichimoku cloud signals.
    """

    INTERFACE_VERSION = 3
    timeframe = "5m"
    minimal_roi = {"0": 0.05}
    stoploss = -0.05

    def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        high_9 = dataframe["high"].rolling(9).max()
        low_9 = dataframe["low"].rolling(9).min()
        dataframe["tenkan_sen"] = (high_9 + low_9) / 2.0

        high_26 = dataframe["high"].rolling(26).max()
        low_26 = dataframe["low"].rolling(26).min()
        dataframe["kijun_sen"] = (high_26 + low_26) / 2.0

        dataframe["senkou_span_a"] = (
            (dataframe["tenkan_sen"] + dataframe["kijun_sen"]) / 2.0
        ).shift(26)

        high_52 = dataframe["high"].rolling(52).max()
        low_52 = dataframe["low"].rolling(52).min()
        dataframe["senkou_span_b"] = ((high_52 + low_52) / 2.0).shift(26)

        dataframe["chikou_span"] = dataframe["close"].shift(-26)

        dataframe["tenkan_sen"] = (
            dataframe["tenkan_sen"].ffill().fillna(dataframe["close"])
        )
        dataframe["kijun_sen"] = (
            dataframe["kijun_sen"].ffill().fillna(dataframe["close"])
        )
        dataframe["senkou_span_a"] = (
            dataframe["senkou_span_a"].ffill().fillna(dataframe["close"])
        )
        dataframe["senkou_span_b"] = (
            dataframe["senkou_span_b"].ffill().fillna(dataframe["close"])
        )
        dataframe["chikou_span"] = (
            dataframe["chikou_span"].ffill().fillna(dataframe["close"])
        )
        return dataframe

    def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        dataframe.loc[
            (
                (dataframe["close"] > dataframe["senkou_span_a"])
                & (dataframe["close"] > dataframe["senkou_span_b"])
                & (dataframe["tenkan_sen"] > dataframe["kijun_sen"])
            ),
            "enter_long",
        ] = 1
        dataframe.loc[
            (
                (dataframe["close"] < dataframe["senkou_span_a"])
                & (dataframe["close"] < dataframe["senkou_span_b"])
                & (dataframe["tenkan_sen"] < dataframe["kijun_sen"])
            ),
            "enter_short",
        ] = 1
        return dataframe

    def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        dataframe.loc[
            (dataframe["tenkan_sen"] < dataframe["kijun_sen"])
            | (dataframe["close"] < dataframe["kijun_sen"]),
            "exit_long",
        ] = 1
        dataframe.loc[
            (dataframe["tenkan_sen"] > dataframe["kijun_sen"])
            | (dataframe["close"] > dataframe["kijun_sen"]),
            "exit_short",
        ] = 1
        return dataframe


ichi = Github_royaldynamo128_gif_claude_tradex_1__strategy_import__20260711_150558
