# source: https://raw.githubusercontent.com/RoboticAutomations/freqtradestrategies/52c4bd40414645f079a980cf4486dc7af4061efb/AIRA%20-%20open%20code%20v2/strategies/rsi-momentum/mk4__2.py
# directory_url: https://github.com/RoboticAutomations/freqtradestrategies/blob/main/AIRA - open code v2/strategies/rsi-momentum/
# User: RoboticAutomations
# Repository: freqtradestrategies
# --------------------"""
###########################
freqtrade hyperopt --hyperopt-loss SharpeHyperOptLossDaily --strategy Github_RoboticAutomations_freqtradestrategies__mk4__2__20260915_233254 --spaces buy sell roi stoploss trailing \
    --config user_data/config_binance_futures_backtest_informative.json --epochs 1000 --timerange 20250616-20250715 --timeframe-detail 5m  --max-open-trades 3

# 838/1000: 179 trades. 102/9/68 Wins/Draws/Losses. Avg profit   1.27%. Median profit   0.68%. Total profit 992.39404689 USDC (  99.24%). Avg duration 3:12:00 min. Objective: -6.57654

    # Buy hyperspace params:
    buy_params = {
        "rsi_entry_long": 57,
        "rsi_entry_short": 24,
        "window": 48,
    }

    # Sell hyperspace params:
    sell_params = {
        "rsi_exit_long": 11,
        "rsi_exit_short": 66,
    }

    # ROI table:
    minimal_roi = {
        "0": 0.121,
        "108": 0.096,
        "246": 0.033,
        "276": 0
    }

    # Stoploss:
    stoploss = -0.189

    # Trailing stop:
    trailing_stop = True
    trailing_stop_positive = 0.149
    trailing_stop_positive_offset = 0.171
    trailing_only_offset_is_reached = False
    

    # Max Open Trades:
    max_open_trades = 3  # value loaded from strategy

###########################
freqtrade hyperopt --hyperopt-loss SharpeHyperOptLossDaily --strategy Github_RoboticAutomations_freqtradestrategies__mk4__2__20260915_233254 --spaces buy sell roi stoploss trailing \
    --config user_data/config_binance_futures_backtest_informative.json --epochs 1000 --timerange 20250616-20250715 --timeframe-detail 5m  --max-open-trades 3 --timeframe 1h

# 292/1000: 12 trades. 9/0/3 Wins/Draws/Losses. Avg profit   7.03%. Median profit   9.28%. Total profit 314.77176421 USDC (  31.48%). Avg duration 9:55:00 min. Objective: -7.96761

    # Buy hyperspace params:
    buy_params = {
        "rsi_entry_long": 55,
        "rsi_entry_short": 90,
        "window": 93,
    }

    # Sell hyperspace params:
    sell_params = {
        "rsi_exit_long": 44,
        "rsi_exit_short": 59,
    }

    # ROI table:
    minimal_roi = {
        "0": 0.181,
        "447": 0.12,
        "1106": 0.06,
        "1557": 0
    }

    # Stoploss:
    stoploss = -0.122

    # Trailing stop:
    trailing_stop = True
    trailing_stop_positive = 0.217
    trailing_stop_positive_offset = 0.292
    trailing_only_offset_is_reached = True
    
    # Max Open Trades:
    max_open_trades = 3  # value loaded from strategy

###########################
####### BEST parameters 
###########################

freqtrade hyperopt --hyperopt-loss SharpeHyperOptLossDaily --strategy Github_RoboticAutomations_freqtradestrategies__mk4__2__20260915_233254 --spaces buy sell roi stoploss trailing \
    --config user_data/config_binance_futures_backtest_informative.json --epochs 1000 --timerange 20250616-20250715 --timeframe-detail 5m  --max-open-trades 3 --timeframe 4h

# 180/1000: 24 trades. 20/0/4 Wins/Draws/Losses. Avg profit   9.63%. Median profit   8.04%. Total profit 1039.63690468 USDC ( 103.96%). Avg duration 1 day, 2:49:00 min. Objective: -10.51142

    # Buy hyperspace params:
    buy_params = {
        "rsi_entry_long": 71,
        "rsi_entry_short": 17,
        "window": 59,
    }

    # Sell hyperspace params:
    sell_params = {
        "rsi_exit_long": 92,
        "rsi_exit_short": 48,
    }

    # ROI table:
    minimal_roi = {
        "0": 0.495,
        "1172": 0.206,
        "3708": 0.064,
        "8031": 0
    }

    # Stoploss:
    stoploss = -0.19

    # Trailing stop:
    trailing_stop = True
    trailing_stop_positive = 0.112
    trailing_stop_positive_offset = 0.169
    trailing_only_offset_is_reached = True
    

    # Max Open Trades:
    max_open_trades = 3  # value loaded from strategy

###########################
"""
import numpy as np
import talib.abstract as ta
import freqtrade.vendor.qtpylib.indicators as qtpylib
from pandas import DataFrame
from datetime import datetime
from typing import Optional
from freqtrade.persistence import Trade
from freqtrade.strategy.interface import IStrategy
from freqtrade.strategy import (
    IStrategy, IntParameter
)


class Github_RoboticAutomations_freqtradestrategies__mk4__2__20260915_233254(IStrategy):
    INTERFACE_VERSION = 3

    timeframe = '15m'
    can_short = True
    process_only_new_candles = True
    use_custom_exit = True

    minimal_roi = {
        "0": 0.128,
        "16": 0.102,
        "31": 0.078,
        "46": 0.046,
        "61": 0.038,
    }

    stoploss = -0.213
    trailing_stop = True
    trailing_stop_positive = 0.028
    trailing_stop_positive_offset = 0.128
    trailing_only_offset_is_reached = True

    max_stake_per_trade = 100
    max_portfolio_percentage_per_trade = 0.05
    max_entry_position_adjustment = 3
    max_dca_orders = 3
    max_total_stake_per_pair = 250
    max_single_dca_amount = 50

    buy_params = {
        "rsi_entry_long": 41,
        "rsi_entry_short": 59,
        "window": 24,
    }

    sell_params = {
        "rsi_exit_long": 17,
        "rsi_exit_short": 83,
    }

    max_open_trades = 20

    rsi_entry_long = IntParameter(0, 100, default=buy_params.get('rsi_entry_long'), space='buy', optimize=True)
    rsi_exit_long = IntParameter(0, 100, default=sell_params.get('rsi_exit_long'), space='sell', optimize=True)
    rsi_entry_short = IntParameter(0, 100, default=buy_params.get('rsi_entry_short'), space='buy', optimize=True)
    rsi_exit_short = IntParameter(0, 100, default=sell_params.get('rsi_exit_short'), space='sell', optimize=True)
    window = IntParameter(0, 100, default=buy_params.get('window'), space='buy', optimize=True)

    @property
    def protections(self):
        return [{
            "method": "CooldownPeriod",
            "stop_duration_candles": 12,
            "protection_per_coin": True
        }]

    @property
    def plot_config(self):
        return {
            'main_plot': {
                'ema_fast': {'color': 'orange'},
                'ema_slow': {'color': 'pink'},
                'ema_long': {'color': 'blue'},
                'rsi_ema': {},
            },
            'subplots': {
                'MACD': {
                    'macd': {'color': 'orange'},
                    'macdsignal': {'color': 'pink'},
                },
                'Misc': {
                    'rsi': {},
                    'rsi_gra': {},
                    'mom': {},
                    'plus_di': {},
                    'minus_di': {},
                },
            }
        }

    def leverage(self, pair: str, current_time: datetime, current_rate: float,
                 proposed_leverage: float, max_leverage: float, side: str, **kwargs) -> float:
        return 5

    def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        dataframe['rsi'] = ta.RSI(dataframe, timeperiod=14)
        dataframe['rsi_ema'] = dataframe['rsi'].ewm(span=self.window.value).mean()
        dataframe['rsi_gra'] = dataframe['rsi_ema'].diff().ewm(span=3).mean()

        dataframe['ema_fast'] = ta.EMA(dataframe, timeperiod=12)
        dataframe['ema_slow'] = ta.EMA(dataframe, timeperiod=26)
        dataframe['ema_long'] = ta.EMA(dataframe, timeperiod=100)

        macd = ta.MACD(dataframe, fastperiod=12, slowperiod=26, signalperiod=9)
        dataframe['macd'] = macd['macd']
        dataframe['macdsignal'] = macd['macdsignal']

        dataframe['macd_golden_cross'] = qtpylib.crossed_above(dataframe['macd'], dataframe['macdsignal']).astype(int)
        dataframe['macd_dead_cross'] = qtpylib.crossed_below(dataframe['macd'], dataframe['macdsignal']).astype(int)

        # 计算KDJ指标
        low_min = dataframe['low'].rolling(window=9).min()
        high_max = dataframe['high'].rolling(window=9).max()
        rsv = (dataframe['close'] - low_min) / (high_max - low_min) * 100
        dataframe['kdj_k'] = rsv.ewm(com=2).mean()
        dataframe['kdj_d'] = dataframe['kdj_k'].ewm(com=2).mean()
        dataframe['kdj_j'] = 3 * dataframe['kdj_k'] - 2 * dataframe['kdj_d']

        # 保留DI和动量指标计算（出场信号仍可能用）
        dataframe['plus_di'] = ta.PLUS_DI(dataframe, timeperiod=14)
        dataframe['minus_di'] = ta.MINUS_DI(dataframe, timeperiod=14)
        dataframe['mom'] = ta.MOM(dataframe, timeperiod=10)

        return dataframe

    def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        #dataframe['macd_slope'] = np.gradient(dataframe['macd'])

        # 新增：MACD 背离计算
        dataframe['macd_bullish_div'] = (
            (dataframe['low'] < dataframe['low'].shift(1)) &
            (dataframe['macd'] > dataframe['macd'].shift(1))
        ).astype(int)

        dataframe['macd_bearish_div'] = (
            (dataframe['high'] > dataframe['high'].shift(1)) &
            (dataframe['macd'] < dataframe['macd'].shift(1))
        ).astype(int)

        # 反转做多
        cond_reversal_long = (
            (dataframe['rsi'] < self.rsi_entry_long.value) &
            qtpylib.crossed_above(dataframe['rsi_gra'], 0) &
            (dataframe['macd'] < dataframe['macdsignal']) &
            (dataframe['macd_bullish_div'] == 1)
        )
        dataframe.loc[cond_reversal_long, 
            ["enter_long", "enter_tag"]
            ] = [1, "cond_reversal_long"]

        # 反转做空
        cond_reversal_short = (
            (dataframe['rsi'] > self.rsi_entry_short.value) &
            qtpylib.crossed_below(dataframe['rsi_gra'], 0) &
            (dataframe['macd'] > dataframe['macdsignal']) &
            (dataframe['macd_bearish_div'] == 1)
        )
        dataframe.loc[cond_reversal_short,
            ["enter_short", "enter_tag"]
            ] = [1, "cond_reversal_short"]

        return dataframe

    def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        dataframe['exit_long'] = 0
        dataframe['exit_short'] = 0

        dataframe.loc[
            (
                (dataframe['rsi'] > self.rsi_exit_long.value) &
                qtpylib.crossed_below(dataframe['rsi_gra'], 0) &
                (dataframe['low'] < dataframe['low'].rolling(window=20).min())
            ),
            'exit_long'
        ] = 1

        dataframe.loc[
            (
                (dataframe['rsi'] < self.rsi_exit_short.value) &
                qtpylib.crossed_above(dataframe['rsi_gra'], 0) &
                (dataframe['high'] > dataframe['high'].rolling(window=20).max())
            ),
            'exit_short'
        ] = 1

        return dataframe

    def custom_exit(
        self,
        pair: str,
        trade: Trade,
        current_time: datetime,
        current_rate: float,
        current_profit: float,
        **kwargs
    ) -> Optional[str]:
        dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe)
        if dataframe is None or len(dataframe) < 20:
            return None

        last_candle = dataframe.iloc[-1]
        opened_at = trade.open_date_utc
        df_since_entry = dataframe[dataframe['date'] >= opened_at]

        kdj_golden_cross = qtpylib.crossed_above(dataframe['kdj_k'], dataframe['kdj_d']).astype(int)
        kdj_dead_cross = qtpylib.crossed_below(dataframe['kdj_k'], dataframe['kdj_d']).astype(int)

        if trade.trade_direction == 'long':
            had_golden_cross = df_since_entry['macd_golden_cross'].sum() > 0
            current_macd_goldencross = last_candle['macd'] > last_candle['macdsignal']
            if had_golden_cross and current_macd_goldencross and kdj_dead_cross.iloc[-1] == 1:
                return "exit_long_custom"

        if trade.trade_direction == 'short':
            had_dead_cross = df_since_entry['macd_dead_cross'].sum() > 0
            current_macd_deadcross = last_candle['macd'] < last_candle['macdsignal']
            if had_dead_cross and current_macd_deadcross and kdj_golden_cross.iloc[-1] == 1:
                return "exit_short_custom"

        return None
