# source: https://raw.githubusercontent.com/Eksz3lsi0r/DOLLAMNY/f6178a8e9385159556a6443262fc338e43d6f88c/example_hyperopt_strategy.py
"""
Example Strategy with Comprehensive Hyperopt Parameters

This strategy demonstrates how to use the hyperopt parameters defined in hyperopt_parameters.py
for a complete trading strategy with RSI, EMA, MACD, ROI, Stoploss, and ML integration.
"""

from pandas import DataFrame
import talib.abstract as ta

from freqtrade.persistence import Trade
from freqtrade.strategy import BooleanParameter, CategoricalParameter, DecimalParameter, IntParameter, IStrategy
import freqtrade.vendor.qtpylib.indicators as qtpylib


class Github_Eksz3lsi0r_DOLLAMNY__example_hyperopt_strategy__20250916_061219(IStrategy):
    """
    Example strategy demonstrating comprehensive hyperopt parameter usage.

    This strategy includes:
    - RSI thresholds for buy/sell signals
    - EMA crossovers for trend following
    - MACD signals for momentum
    - Custom ROI table generation
    - Stoploss and trailing stop optimization
    - ML threshold integration for FreqAI
    """

    # Strategy interface version
    INTERFACE_VERSION = 3

    # Timeframe
    timeframe = "5m"

    # Startup candle count
    startup_candle_count: int = 30

    # Order types
    order_types = {"entry": "limit", "exit": "limit", "stoploss": "market", "stoploss_on_exchange": False}

    # Order time in force
    order_time_in_force = {"entry": "gtc", "exit": "gtc"}

    # =============================================================================
    # HYPEROPT PARAMETERS
    # =============================================================================

    # RSI Parameters
    buy_rsi = IntParameter(20, 40, default=30, space="buy")
    sell_rsi = IntParameter(60, 80, default=70, space="sell")
    rsi_period = IntParameter(10, 21, default=14, space="buy")
    buy_rsi_enabled = BooleanParameter(default=True, space="buy")
    sell_rsi_enabled = BooleanParameter(default=True, space="sell")

    # EMA Parameters
    ema_short_period = IntParameter(5, 15, default=9, space="buy")
    ema_long_period = IntParameter(30, 50, default=34, space="buy")
    ema_short_enabled = BooleanParameter(default=True, space="buy")
    ema_long_enabled = BooleanParameter(default=True, space="buy")
    ema_cross_threshold = DecimalParameter(0.001, 0.01, default=0.005, decimals=3, space="buy")

    # MACD Parameters
    macd_fast_period = IntParameter(8, 16, default=12, space="buy")
    macd_slow_period = IntParameter(20, 30, default=26, space="buy")
    macd_signal_period = IntParameter(7, 12, default=9, space="buy")
    macd_histogram_threshold = DecimalParameter(0.0001, 0.01, default=0.001, decimals=4, space="buy")
    macd_buy_trigger = CategoricalParameter(
        ["macd_cross_signal", "macd_histogram_positive", "macd_both"], default="macd_cross_signal", space="buy"
    )

    # ROI Parameters
    roi_t1 = IntParameter(5, 30, default=10, space="roi")
    roi_t2 = IntParameter(10, 60, default=20, space="roi")
    roi_t3 = IntParameter(20, 120, default=40, space="roi")
    roi_p1 = DecimalParameter(0.01, 0.05, default=0.02, decimals=3, space="roi")
    roi_p2 = DecimalParameter(0.01, 0.08, default=0.03, decimals=3, space="roi")
    roi_p3 = DecimalParameter(0.01, 0.15, default=0.05, decimals=3, space="roi")

    # Stoploss Parameters
    stoploss = DecimalParameter(-0.15, -0.02, default=-0.10, decimals=3, space="stoploss")

    # Trailing Stop Parameters
    trailing_stop = BooleanParameter(default=True, space="trailing")
    trailing_stop_positive = DecimalParameter(0.01, 0.05, default=0.02, decimals=3, space="trailing")
    trailing_stop_positive_offset = DecimalParameter(0.01, 0.10, default=0.05, decimals=3, space="trailing")
    trailing_only_offset_is_reached = BooleanParameter(default=True, space="trailing")

    # ML Parameters (for FreqAI integration)
    ml_buy_threshold = DecimalParameter(0.5, 0.9, default=0.7, decimals=2, space="buy")
    ml_sell_threshold = DecimalParameter(0.1, 0.5, default=0.3, decimals=2, space="sell")
    ml_confidence_threshold = DecimalParameter(0.6, 0.95, default=0.8, decimals=2, space="buy")
    ml_model_type = CategoricalParameter(
        ["RandomForest", "XGBoost", "LightGBM", "CatBoost"], default="RandomForest", space="buy"
    )

    # Additional Technical Indicators
    bb_period = IntParameter(15, 25, default=20, space="buy")
    bb_std = DecimalParameter(1.5, 2.5, default=2.0, decimals=1, space="buy")
    adx_period = IntParameter(10, 20, default=14, space="buy")
    adx_threshold = IntParameter(20, 40, default=25, space="buy")

    # Risk Management
    max_risk_per_trade = DecimalParameter(0.01, 0.05, default=0.02, decimals=3, space="buy")
    position_sizing_method = CategoricalParameter(
        ["fixed", "percentage", "volatility"], default="percentage", space="buy"
    )

    # Market Condition Filters
    trend_filter_enabled = BooleanParameter(default=True, space="buy")
    volatility_filter_enabled = BooleanParameter(default=True, space="buy")
    volume_filter_enabled = BooleanParameter(default=True, space="buy")

    # =============================================================================
    # CUSTOM ROI TABLE GENERATION
    # =============================================================================

    def custom_roi_table(self, params: dict) -> dict:
        """
        Generate custom ROI table based on hyperopt parameters.
        """
        roi_table = {}
        roi_table[0] = params["roi_p1"] + params["roi_p2"] + params["roi_p3"]
        roi_table[params["roi_t3"]] = params["roi_p1"] + params["roi_p2"]
        roi_table[params["roi_t3"] + params["roi_t2"]] = params["roi_p1"]
        roi_table[params["roi_t3"] + params["roi_t2"] + params["roi_t1"]] = 0
        return roi_table

    # =============================================================================
    # INDICATOR CALCULATION
    # =============================================================================

    def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        """
        Calculate all technical indicators used in the strategy.
        """
        # RSI
        dataframe["rsi"] = ta.RSI(dataframe, timeperiod=self.rsi_period.value)

        # EMAs
        if self.ema_short_enabled.value:
            dataframe["ema_short"] = ta.EMA(dataframe, timeperiod=self.ema_short_period.value)
        if self.ema_long_enabled.value:
            dataframe["ema_long"] = ta.EMA(dataframe, timeperiod=self.ema_long_period.value)

        # MACD
        macd = ta.MACD(
            dataframe,
            fastperiod=self.macd_fast_period.value,
            slowperiod=self.macd_slow_period.value,
            signalperiod=self.macd_signal_period.value,
        )
        dataframe["macd"] = macd["macd"]
        dataframe["macdsignal"] = macd["macdsignal"]
        dataframe["macdhist"] = macd["macdhist"]

        # Bollinger Bands
        bollinger = qtpylib.bollinger_bands(dataframe["close"], window=self.bb_period.value, stds=self.bb_std.value)
        dataframe["bb_lowerband"] = bollinger["lower"]
        dataframe["bb_middleband"] = bollinger["mid"]
        dataframe["bb_upperband"] = bollinger["upper"]

        # ADX
        dataframe["adx"] = ta.ADX(dataframe, timeperiod=self.adx_period.value)

        # Volume indicators
        dataframe["volume_sma"] = ta.SMA(dataframe["volume"], timeperiod=20)

        # ML predictions (if using FreqAI)
        # These would be populated by FreqAI in a real implementation
        if hasattr(self, "freqai_info"):
            dataframe["prediction"] = dataframe.get("prediction", 0.5)
            dataframe["confidence"] = dataframe.get("confidence", 0.8)
        else:
            # Placeholder values for backtesting without FreqAI
            dataframe["prediction"] = 0.5
            dataframe["confidence"] = 0.8

        return dataframe

    # =============================================================================
    # BUY SIGNAL LOGIC
    # =============================================================================

    def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        """
        Define buy signal logic using hyperopt parameters.
        """
        conditions = []

        # RSI condition
        if self.buy_rsi_enabled.value:
            conditions.append(dataframe["rsi"] < self.buy_rsi.value)

        # EMA crossover condition
        if self.ema_short_enabled.value and self.ema_long_enabled.value:
            conditions.append(dataframe["ema_short"] > dataframe["ema_long"])
            # Additional threshold for crossover strength
            conditions.append((dataframe["ema_short"] - dataframe["ema_long"]) > self.ema_cross_threshold.value)

        # MACD condition
        if self.macd_buy_trigger.value == "macd_cross_signal":
            conditions.append(dataframe["macd"] > dataframe["macdsignal"])
        elif self.macd_buy_trigger.value == "macd_histogram_positive":
            conditions.append(dataframe["macdhist"] > self.macd_histogram_threshold.value)
        elif self.macd_buy_trigger.value == "macd_both":
            conditions.append(
                (dataframe["macd"] > dataframe["macdsignal"])
                & (dataframe["macdhist"] > self.macd_histogram_threshold.value)
            )

        # Bollinger Bands condition (price near lower band)
        conditions.append(dataframe["close"] <= dataframe["bb_lowerband"])

        # ADX condition (trend strength)
        if self.trend_filter_enabled.value:
            conditions.append(dataframe["adx"] > self.adx_threshold.value)

        # Volume condition
        if self.volume_filter_enabled.value:
            conditions.append(dataframe["volume"] > dataframe["volume_sma"])

        # ML condition (if using FreqAI)
        conditions.append(dataframe["prediction"] > self.ml_buy_threshold.value)
        conditions.append(dataframe["confidence"] > self.ml_confidence_threshold.value)

        # Combine all conditions
        if conditions:
            dataframe.loc[
                qtpylib.crossed_above(dataframe["rsi"], 30)  # Additional momentum check
                & qtpylib.AND(*conditions),
                "buy",
            ] = 1

        return dataframe

    # =============================================================================
    # SELL SIGNAL LOGIC
    # =============================================================================

    def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        """
        Define sell signal logic using hyperopt parameters.
        """
        conditions = []

        # RSI condition
        if self.sell_rsi_enabled.value:
            conditions.append(dataframe["rsi"] > self.sell_rsi.value)

        # EMA crossover condition (opposite of buy)
        if self.ema_short_enabled.value and self.ema_long_enabled.value:
            conditions.append(dataframe["ema_short"] < dataframe["ema_long"])

        # MACD condition (opposite of buy)
        conditions.append(dataframe["macd"] < dataframe["macdsignal"])

        # Bollinger Bands condition (price near upper band)
        conditions.append(dataframe["close"] >= dataframe["bb_upperband"])

        # ML condition
        conditions.append(dataframe["prediction"] < self.ml_sell_threshold.value)

        # Combine all conditions
        if conditions:
            dataframe.loc[
                qtpylib.crossed_below(dataframe["rsi"], 70)  # Additional momentum check
                & qtpylib.AND(*conditions),
                "sell",
            ] = 1

        return dataframe

    # =============================================================================
    # CUSTOM STOPLOSS
    # =============================================================================

    def custom_stoploss(
        self, pair: str, trade: Trade, current_time, current_rate, current_profit, after_fill: bool, **kwargs
    ) -> float:
        """
        Custom stoploss logic using hyperopt parameters.
        """
        # Use the hyperopt-optimized stoploss value
        return self.stoploss.value

    # =============================================================================
    # CUSTOM EXIT
    # =============================================================================

    def custom_exit(self, pair: str, trade: Trade, current_time, current_rate, current_profit, **kwargs) -> str:
        """
        Custom exit logic for additional sell conditions.
        """
        # Exit if ML confidence drops below threshold
        if hasattr(trade, "ml_confidence") and trade.ml_confidence < self.ml_confidence_threshold.value:
            return "ml_confidence_low"

        # Exit if profit target reached and RSI overbought
        if current_profit > 0.05 and trade.calc_profit_ratio(current_rate) > 0.02:
            return "profit_target_reached"

        return None

    # =============================================================================
    # PLOT CONFIGURATION
    # =============================================================================

    plot_config = {
        "main_plot": {
            "ema_short": {"color": "blue"},
            "ema_long": {"color": "orange"},
            "bb_upperband": {"color": "red"},
            "bb_lowerband": {"color": "red"},
        },
        "subplots": {
            "RSI": {
                "rsi": {"color": "purple"},
            },
            "MACD": {
                "macd": {"color": "blue"},
                "macdsignal": {"color": "red"},
                "macdhist": {"type": "bar", "plotly": {"opacity": 0.9}},
            },
            "ML": {
                "prediction": {"color": "green"},
                "confidence": {"color": "orange"},
            },
        },
    }


# =============================================================================
# HYPEROPT CONFIGURATION CLASS
# =============================================================================


class Github_Eksz3lsi0r_DOLLAMNY__example_hyperopt_strategy__20250916_061219Hyperopt:
    """
    Custom hyperopt configuration for the example strategy.
    This class can be used to override default hyperopt spaces.
    """

    @staticmethod
    def generate_roi_table(params: dict) -> dict:
        """
        Generate ROI table for hyperopt optimization.
        """
        roi_table = {}
        roi_table[0] = params["roi_p1"] + params["roi_p2"] + params["roi_p3"]
        roi_table[params["roi_t3"]] = params["roi_p1"] + params["roi_p2"]
        roi_table[params["roi_t3"] + params["roi_t2"]] = params["roi_p1"]
        roi_table[params["roi_t3"] + params["roi_t2"] + params["roi_t1"]] = 0
        return roi_table

    @staticmethod
    def generate_trailing_params(params: dict) -> dict:
        """
        Generate trailing stop parameters for hyperopt optimization.
        """
        return {
            "trailing_stop": params["trailing_stop"],
            "trailing_stop_positive": params["trailing_stop_positive"],
            "trailing_stop_positive_offset": (
                params["trailing_stop_positive"] + params["trailing_stop_positive_offset"]
            ),
            "trailing_only_offset_is_reached": params["trailing_only_offset_is_reached"],
        }


# =============================================================================
# USAGE INSTRUCTIONS
# =============================================================================

"""
To use this strategy with hyperopt:

1. Save this file as your strategy (e.g., in user_data/strategies/)

2. Run hyperopt with the strategy:
   freqtrade hyperopt --strategy Github_Eksz3lsi0r_DOLLAMNY__example_hyperopt_strategy__20250916_061219 \
       --hyperopt-loss SharpeHyperOptLoss \
       --spaces buy sell roi stoploss trailing --epochs 100

3. For ML integration with FreqAI:
   freqtrade hyperopt --strategy Github_Eksz3lsi0r_DOLLAMNY__example_hyperopt_strategy__20250916_061219 \
       --freqai-model-type RandomForest \
       --freqai-backtest-live-models \
       --spaces buy sell roi stoploss trailing --epochs 100

4. The hyperopt results will show optimized parameters for:
   - RSI thresholds
   - EMA periods
   - MACD parameters
   - ROI table values
   - Stoploss and trailing stop values
   - ML thresholds (if using FreqAI)

5. Copy the best parameters from hyperopt results into your strategy or config file.

Example hyperopt command with all spaces:
freqtrade hyperopt --strategy Github_Eksz3lsi0r_DOLLAMNY__example_hyperopt_strategy__20250916_061219 \
    --hyperopt-loss SharpeHyperOptLoss \
    --spaces buy sell roi stoploss trailing --epochs 200 \
    --timerange 20230101-20231201
"""
