# source: https://raw.githubusercontent.com/mlsys-io/PortfolioBench/8d20eec9103ee4b622538b522c94753160701580/strategy/EmaCrossStrategy.py
# pragma pylint: disable=missing-docstring, invalid-name, pointless-string-statement
# flake8: noqa: F401
# isort: skip_file
# --- Do not remove these imports ---
import numpy as np
import pandas as pd
from datetime import datetime, timedelta, timezone
from pandas import DataFrame
from typing import Dict, Optional, Union, Tuple

from freqtrade.strategy import (
    IStrategy,
    Trade,
    Order,
    PairLocks,
    informative,  # @informative decorator
    # Hyperopt Parameters
    BooleanParameter,
    CategoricalParameter,
    DecimalParameter,
    IntParameter,
    RealParameter,
    # timeframe helpers
    timeframe_to_minutes,
    timeframe_to_next_date,
    timeframe_to_prev_date,
    # Strategy helper functions
    merge_informative_pair,
    stoploss_from_absolute,
    stoploss_from_open,
    AnnotationType,
)

from alpha.SimpleEmaFactors import EmaAlpha

# Add your lib to import here
import talib.abstract as ta
from technical import qtpylib


class Github_mlsys_io_PortfolioBench__EmaCrossStrategy__20260220_164748(IStrategy):
    """
    Strategy adapted from paper: http://arxiv.org/abs/2511.00665 
    """

    # Strategy interface version - allow new iterations of the strategy interface.
    # Check the documentation or the Sample strategy to get the latest version.
    INTERFACE_VERSION = 3

    # Can this strategy go short?
    can_short: bool = False

    # Minimal ROI designed for the strategy.
    # This attribute will be overridden if the config file contains "minimal_roi".
    minimal_roi = {}

    # Optimal stoploss designed for the strategy.
    # This attribute will be overridden if the config file contains "stoploss".
    stoploss = -99

    # Trailing stoploss
    trailing_stop = False
    # trailing_only_offset_is_reached = False
    # trailing_stop_positive = 0.01
    # trailing_stop_positive_offset = 0.0  # Disabled / not configured

    # Run "populate_indicators()" only for new candle.
    process_only_new_candles = True

    # These values can be overridden in the config.
    use_exit_signal = True
    exit_profit_only = False
    ignore_roi_if_entry_signal = False
    startup_candle_count: int = 30

    ema_fast_length = IntParameter(5, 15, default=12, space="buy")
    ema_slow_length = IntParameter(20, 30, default=26, space="buy")
    ema_exit_length = IntParameter(5, 10, default=6, space="sell")

    def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        # dataframe["ema_fast"] = ta.EMA(dataframe, timeperiod=self.ema_fast_length.value)
        # dataframe["ema_slow"] = ta.EMA(dataframe, timeperiod=self.ema_slow_length.value)
        # dataframe["ema_exit"] = ta.EMA(dataframe, timeperiod=self.ema_exit_length.value)
        # dataframe["mean-volume"] = dataframe["volume"].rolling(20).mean()
        dataframe = EmaAlpha(dataframe, metadata).process()
        return dataframe

    def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        dataframe.loc[
            (
                (qtpylib.crossed_above(dataframe["ema_fast"], dataframe["ema_slow"]))
                & (dataframe["mean-volume"] > 0.75)
            ),
            "enter_long",
        ] = 1

        return dataframe

    def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        dataframe.loc[
            (qtpylib.crossed_below(dataframe["ema_exit"], dataframe["ema_fast"])), "exit_long"
        ] = 1
        return dataframe

    def confirm_trade_entry(self, pair: str, order_type: str, amount: float, rate: float,
                            time_in_force: str, current_time: datetime, entry_tag: str | None,
                            side: str, **kwargs) -> bool:
        dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe)
        last_close = dataframe.iloc[-1]["close"]
        max_deviation = 0.01  # 1% deviation allowed
        deviation = abs(rate - last_close) / last_close

        if deviation > max_deviation:
            return False

        return True