# source: https://raw.githubusercontent.com/hdlproject/quant-trading/447a1006898eb88d123039728d609a50fc64187d/src/quant_trading/strategies/ema_cross_rsi.py
"""A simple and popular Freqtrade strategy: EMA crossover with RSI filter.

This strategy demonstrates basic structure expected by Freqtrade:
- Adds a few technical indicators (EMA fast/slow, RSI)
- Enters on bullish EMA crossover with RSI below an overbought threshold
- Exits on bearish crossover or RSI overbought

DISCLAIMER: For educational purposes only. Not optimized for live trading.
"""
from __future__ import annotations

from typing import Dict, Any

import pandas as pd

try:  # Graceful fallback if freqtrade libs not installed at dev time
    from freqtrade.strategy import IStrategy
    from freqtrade.vendor.qtpylib import indicator_extensions as qtpylib
except ImportError:  # pragma: no cover - only for environments without freqtrade
    class IStrategy:  # type: ignore
        pass

    qtpylib = None  # type: ignore


class Github_hdlproject_quant_trading__ema_cross_rsi__20260518_034301(IStrategy):
    """EMA crossover with RSI confirmation.

    Parameters / attributes:
    timeframe: Candle timeframe used for backtesting/live (string like "5m", "1h").
    minimal_roi: Dict of minute offsets -> target ROI fraction; governs dynamic take-profit.
    stoploss: Hard stop loss as a negative fraction (e.g. -0.08 = -8%).
    trailing_stop: Enable/disable trailing stop mechanism (bool).
    ema_fast_length: Period length for fast EMA (short-term trend).
    ema_slow_length: Period length for slow EMA (long-term trend).
    rsi_length: Lookback period for RSI calculation.
    rsi_overbought: RSI threshold considered overbought; used to filter entries / force exits.
    startup_candle_count: Minimum candles required before signals (ensures indicators initialized).
    """

    # --- Required class attributes --- #
    timeframe = "5m"  # Trading timeframe for this strategy.

    # Minimal ROI table (example). Keys are minutes since trade open, values are min acceptable profit ratio.
    minimal_roi: Dict[str, float] = {
        "0": 0.02,   # Immediately after entry aim for 2%.
        "30": 0.01,  # After 30 minutes accept 1%.
        "120": 0.0,  # After 120 minutes accept break-even (0%).
    }

    stoploss = -0.08  # Hard stop: exit if loss reaches -8%.

    trailing_stop = False  # Trailing stop disabled for simplicity; set True to enable.

    # Indicator configuration (can be tuned / hyperoptimized).
    ema_fast_length = 12      # Fast EMA period (reacts quickly).
    ema_slow_length = 26      # Slow EMA period (smoother baseline).
    rsi_length = 14           # RSI lookback period (standard default).
    rsi_overbought = 70       # RSI threshold to deem market overbought.

    startup_candle_count = max(ema_slow_length, rsi_length) + 1  # Ensure enough candles to compute indicators.

    def populate_indicators(self, dataframe: pd.DataFrame, metadata: Dict[str, Any]) -> pd.DataFrame:  # type: ignore[override]
        """Add EMA and RSI indicators to the dataframe.

        Freqtrade calls this once per candle batch.
        """
        if dataframe.empty:
            return dataframe

        # Exponential moving averages
        dataframe["ema_fast"] = dataframe["close"].ewm(span=self.ema_fast_length, adjust=False).mean()
        dataframe["ema_slow"] = dataframe["close"].ewm(span=self.ema_slow_length, adjust=False).mean()

        # RSI calculation (manual implementation avoiding external dependency for portability)
        delta = dataframe["close"].diff()
        gain = (delta.where(delta > 0, 0)).ewm(alpha=1 / self.rsi_length, adjust=False).mean()
        loss = (-delta.where(delta < 0, 0)).ewm(alpha=1 / self.rsi_length, adjust=False).mean()
        rs = gain / loss.replace(0, 1e-10)
        dataframe["rsi"] = 100 - (100 / (1 + rs))

        # Crossover helpers (qtpylib has a cross function; reimplement if not available)
        if qtpylib:
            dataframe["ema_cross_up"] = qtpylib.crossed_above(dataframe["ema_fast"], dataframe["ema_slow"])  # type: ignore[attr-defined]
            dataframe["ema_cross_down"] = qtpylib.crossed_below(dataframe["ema_fast"], dataframe["ema_slow"])  # type: ignore[attr-defined]
        else:  # Fallback simple cross detection
            prev_fast = dataframe["ema_fast"].shift(1)
            prev_slow = dataframe["ema_slow"].shift(1)
            dataframe["ema_cross_up"] = (dataframe["ema_fast"] > dataframe["ema_slow"]) & (prev_fast <= prev_slow)
            dataframe["ema_cross_down"] = (dataframe["ema_fast"] < dataframe["ema_slow"]) & (prev_fast >= prev_slow)

        return dataframe

    def populate_entry_trend(self, dataframe: pd.DataFrame, metadata: Dict[str, Any]) -> pd.DataFrame:  # type: ignore[override]
        """Define entry (buy) signals.
        Long when EMA fast crosses above EMA slow and RSI below overbought threshold.
        """
        dataframe.loc[
            (
                (dataframe["ema_cross_up"]) &
                (dataframe["rsi"] < self.rsi_overbought) &
                (dataframe["volume"] > 0)  # Basic volume guard ensuring candle isn't empty.
            ),
            ["enter_long", "enter_tag"],
        ] = (1, "ema_cross_rsi")
        return dataframe

    def populate_exit_trend(self, dataframe: pd.DataFrame, metadata: Dict[str, Any]) -> pd.DataFrame:  # type: ignore[override]
        """Define exit (sell) signals.
        Exit when bearish crossover or RSI > overbought threshold.
        """
        dataframe.loc[
            (
                (dataframe["ema_cross_down"]) |
                (dataframe["rsi"] > self.rsi_overbought) &
                (dataframe["volume"] > 0)
            ),
            ["exit_long", "exit_tag"],
        ] = (1, "exit_condition")
        return dataframe

    # (Optional) If you wanted to support shorting, you would implement enter_short/exit_short similarly.


__all__ = ["Github_hdlproject_quant_trading__ema_cross_rsi__20260518_034301"]
