# source: https://raw.githubusercontent.com/NorthStartLabas/crypto-llm-trader/2baa23b931375de2f0caeb5e2557ec8f8cf1c4bb/user_data/strategies/BaselineRsiEmaStrategy.py
"""
Github_NorthStartLabas_crypto_llm_trader__BaselineRsiEmaStrategy__20260520_011426 — Phase 1.7

Changes from 1.6:
  - RSI lookback extended to 8 candles (was 6): catches slower dip-and-
    recover setups where RSI takes 5-7 candles to climb back to the entry
    threshold. Same signal quality, more samples for hyperopt.
  - Bullish candle filter removed: too restrictive; a neutral/slightly red
    candle at the exact RSI crossover moment is still a valid entry in a
    triple-EMA-aligned trend. Was cutting ~40 % of valid signals.
  - These changes target 180-220 trades so hyperopt has reliable statistics.

Not financial advice. Do not use with real funds without independent review.
"""

from typing import Dict, List

import talib.abstract as ta
from pandas import DataFrame

from freqtrade.optimize.space import SKDecimal
from freqtrade.strategy import IntParameter, IStrategy


class Github_NorthStartLabas_crypto_llm_trader__BaselineRsiEmaStrategy__20260520_011426(IStrategy):
    INTERFACE_VERSION = 3

    can_short = False
    timeframe = "1h"
    startup_candle_count = 200

    minimal_roi = {
        "0": 0.06,
        "120": 0.04,
        "360": 0.02,
        "720": 0.01,
    }

    stoploss = -0.03

    # Trailing stop: activates once the trade is up +2 %, then trails 1 %
    # below the running peak. Trades that "almost made it" exit at +1 %
    # instead of waiting for the full ROI or falling to the hard stop.
    trailing_stop = True
    trailing_stop_positive = 0.01
    trailing_stop_positive_offset = 0.02
    trailing_only_offset_is_reached = True

    process_only_new_candles = True

    # ------------------------------------------------------------------ #
    # Hyperopt parameters                                                  #
    # ------------------------------------------------------------------ #
    buy_rsi_entry = IntParameter(20, 55, default=40, space="buy", optimize=True)
    sell_rsi_exit = IntParameter(60, 90, default=70, space="sell", optimize=True)

    # ------------------------------------------------------------------ #
    # Constrained Hyperopt spaces                                         #
    # ------------------------------------------------------------------ #
    @staticmethod
    def stoploss_space() -> List:
        return [SKDecimal(-0.06, -0.02, decimals=3, name="stoploss")]

    @staticmethod
    def generate_roi_table(params: Dict) -> Dict[int, float]:
        roi_table: Dict[int, float] = {}
        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.01
        return roi_table

    # ------------------------------------------------------------------ #
    # Fixed indicator periods                                              #
    # ------------------------------------------------------------------ #
    ema_long_period: int = 200
    ema_medium_period: int = 50
    ema_short_period: int = 20
    rsi_period: int = 14

    def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        dataframe["ema200"] = ta.EMA(dataframe, timeperiod=self.ema_long_period)
        dataframe["ema50"] = ta.EMA(dataframe, timeperiod=self.ema_medium_period)
        dataframe["ema20"] = ta.EMA(dataframe, timeperiod=self.ema_short_period)
        dataframe["rsi"] = ta.RSI(dataframe, timeperiod=self.rsi_period)
        return dataframe

    # ------------------------------------------------------------------ #
    # Entry — triple EMA alignment filters out weak uptrends              #
    # ------------------------------------------------------------------ #
    def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        dataframe.loc[
            (
                # All three EMAs stacked bullish: short > medium > long
                (dataframe["ema20"] > dataframe["ema50"])
                & (dataframe["ema50"] > dataframe["ema200"])
                & (dataframe["close"] > dataframe["ema200"])
                # RSI must have been genuinely oversold within the last 8
                # candles — filters out mid-range noise crossovers
                & (dataframe["rsi"].rolling(8).min() < 35)
                # RSI crossover signal
                & (dataframe["rsi"] > self.buy_rsi_entry.value)
                & (dataframe["rsi"].shift(1) <= self.buy_rsi_entry.value)
                & (dataframe["volume"] > 0)
            ),
            ["enter_long", "enter_tag"],
        ] = [1, "triple_ema_rsi_dip"]

        return dataframe

    # ------------------------------------------------------------------ #
    # Exit — RSI overbought only (EMA cross exit removed)                 #
    # ------------------------------------------------------------------ #
    def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        dataframe.loc[
            (
                (dataframe["rsi"] > self.sell_rsi_exit.value)
                & (dataframe["volume"] > 0)
            ),
            ["exit_long", "exit_tag"],
        ] = [1, "rsi_overbought"]

        return dataframe
