# source: https://raw.githubusercontent.com/RoboticAutomations/freqtradestrategies/52c4bd40414645f079a980cf4486dc7af4061efb/AIRA%20-%20open%20code%20v2/strategies/rsi-momentum/RSI_Futures9.py
# directory_url: https://github.com/RoboticAutomations/freqtradestrategies/blob/main/AIRA - open code v2/strategies/rsi-momentum/
# User: RoboticAutomations
# Repository: freqtradestrategies
# --------------------from datetime import datetime
from typing import Dict, Any

import numpy as np  # noqa
import pandas as pd  # noqa

from freqtrade.strategy import IStrategy
from pandas import DataFrame


class Github_RoboticAutomations_freqtradestrategies__RSI_Futures9__20260915_233254(IStrategy):
    """
    周期：5m
    指标：RSI(14)
    做多：RSI < 25 开多，RSI > 75 且 ROE > 0 平多；
    做空：RSI > 75 开空，RSI < 25 且 ROE > 0 平空；
    止损：由 config.json 中的 stoploss 参数控制；
    杠杆：5x（期货/合约）
    """

    timeframe = "5m"

    # 允许做多和做空
    can_short: bool = True

    # 期货模式（如使用现货可关闭）
    is_futures_trading = True

    # 启用加仓（DCA）功能
    position_adjustment_enable = True

    # 最多加仓次数（不含首单）
    MAX_DCA_ENTRIES = 3

    # 触发加仓的 ROE 阈值（亏损 65%）
    DCA_TRIGGER_ROE = -0.65

    # 默认杠杆倍数（在 leverage() 方法中使用）
    DEFAULT_LEVERAGE = 5.0
    trailing_stop = False

    # ROI 表：设为极大值，让 ROI 不主动触发止盈（主要依赖全局止损）
    minimal_roi = {
        "0": 100.0,
    }

    process_only_new_candles = True

    def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        """
        计算 RSI14 指标
        """
        import talib.abstract as ta

        dataframe["rsi"] = ta.RSI(dataframe["close"], timeperiod=14)
        return dataframe

    def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        """
        开仓条件：
        - 做多：RSI < 25 开多；
        - 做空：RSI > 75 开空。
        """
        dataframe.loc[:, "enter_long"] = 0
        dataframe.loc[:, "enter_short"] = 0

        rsi = dataframe["rsi"]

        # 做多：RSI < 25
        long_entry = rsi < 25
        # 做空：RSI > 75
        short_entry = rsi > 75

        dataframe.loc[long_entry, "enter_long"] = 1
        dataframe.loc[short_entry, "enter_short"] = 1

        return dataframe

    def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        """
        这里不再使用固定的 RSI 平仓信号，
        实际平仓逻辑放在 custom_exit 中根据 RSI + ROE 决定。
        """
        dataframe.loc[:, "exit_long"] = 0
        dataframe.loc[:, "exit_short"] = 0
        return dataframe

    def custom_exit(
        self,
        pair: str,
        trade,
        current_time: datetime,
        current_rate: float,
        current_profit: float,
        **kwargs,
    ) -> Dict[str, Any] | None:
        """
        平仓逻辑（结合 RSI + ROE）：
        - 做多：RSI > 75 且当前 ROE > 0 时平多；
        - 做空：RSI < 25 且当前 ROE > 0 时平空；
        - 若 RSI 已达阈值但 ROE < 0，则暂不平仓，等待之后再次满足 RSI 阈值且 ROE 转正再平仓。
        """
        # 无数据提供器则不做自定义平仓
        if self.dp is None:
            return None

        dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe)
        if dataframe is None or dataframe.empty:
            return None

        last_candle = dataframe.iloc[-1].squeeze()
        rsi = last_candle.get("rsi", None)
        if rsi is None or np.isnan(rsi):
            return None

        # 做多：RSI > 75 且 ROE > 0 平仓
        if not trade.is_short:
            if rsi > 75 and current_profit > 0:
                return {"exit_tag": "rsi_long_take_profit"}
            return None

        # 做空：RSI < 25 且 ROE > 0 平仓
        if trade.is_short:
            if rsi < 25 and current_profit > 0:
                return {"exit_tag": "rsi_short_take_profit"}
            return None

    def leverage(
        self,
        pair: str,
        current_time: datetime,
        current_rate: float,
        proposed_leverage: float,
        max_leverage: float,
        entry_tag: str | None,
        side: str,
        **kwargs,
    ) -> float:
        """
        自定义杠杆：固定使用 5 倍（不超过交易所允许的最大杠杆）。
        """
        return min(self.DEFAULT_LEVERAGE, float(max_leverage))

    def adjust_trade_position(
        self,
        trade,
        current_time: datetime,
        current_rate: float,
        current_profit: float,
        min_stake: float,
        max_stake: float,
        **kwargs,
    ) -> float | None:
        """
        DCA 加仓逻辑：
        - 当整笔仓位 ROE（current_profit）<= -65% 时触发加仓；
        - 每次加仓额度 = 当前持仓总额度（按当前持仓额度加仓，相当于仓位翻倍）；
        - 最多加仓 3 次（不含首单）；
        - 加仓后按均价（加权平均价）计算整笔仓位 ROE，若之后再次整体亏损到 -65% 再加下一次。
        """
        # 达到触发阈值才考虑加仓
        if current_profit > self.DCA_TRIGGER_ROE:
            return None

        entry_side = "sell" if trade.is_short else "buy"
        filled_entries = trade.select_filled_orders(entry_side)
        entries_count = len(filled_entries)
        if entries_count <= 0:
            return None

        # 已加仓次数（不含首单）
        dca_count = entries_count - 1
        if dca_count >= self.MAX_DCA_ENTRIES:
            return None

        # 当前持仓总额度（首单 + 之前所有加仓）
        total_cost = sum(o.cost for o in filled_entries)
        if total_cost <= 0:
            return None

        # 每次加仓额度：按当前持仓额度加仓（翻倍）
        additional_stake = total_cost

        # 遵守 min_stake / max_stake 限制
        additional_stake = max(min_stake, min(additional_stake, max_stake))
        if additional_stake < min_stake:
            return None

        return additional_stake

