# source: https://raw.githubusercontent.com/henrylin99/freqtrade-tutorials/691c3799846fc7ff6173a3b9241ce5a00b346141/user_data/strategies/HyperliquidMomentumStrategy.py
"""
Hyperliquid动量突破策略
基于专业交易代理提示词实现的freqtrade策略

策略特点：
- 多重技术指标确认（EMA趋势、MACD动量、RSI强弱、ATR波动）
- 严格的风险管理（动态止损、仓位控制）
- 24/7加密货币市场适应性
- 基于置信度的仓位调整
- 突破确认机制减少假信号
"""

from freqtrade.strategy import IStrategy, IntParameter, DecimalParameter
from pandas import DataFrame
from typing import Optional
import talib.abstract as ta
import numpy as np
import logging

logger = logging.getLogger(__name__)

class Github_henrylin99_freqtrade_tutorials__HyperliquidMomentumStrategy__20251212_023733(IStrategy):
    """
    Hyperliquid动量突破策略

    核心逻辑：
    1. EMA趋势过滤 - 确保顺势交易
    2. MACD动量确认 - 捕捉趋势转折点
    3. RSI超买超卖过滤 - 避免极端位置入场
    4. ATR波动性管理 - 动态调整止损和仓位
    5. 多重确认机制 - 减少假突破
    6. 基于置信度的仓位管理 - 高置信度加大仓位
    """

    INTERFACE_VERSION = 3

    # 基础配置
    minimal_roi = {
        "0": 0.40,      # 40%收益立即止盈
        "20": 0.25,     # 20分钟后25%收益
        "40": 0.20,     # 40分钟后20%收益
        "60": 0.15,     # 1小时后15%收益
        "120": 0.10,    # 2小时后10%收益
        "240": 0.06,    # 4小时后6%收益
        "480": 0.04     # 8小时后4%收益
    }

    stoploss = -0.10    # 10%基础止损
    timeframe = '15m'   # 15分钟时间框架，适应提示词的2-3分钟决策频率

    # 策略控制
    can_short = True    # 启用做空
    startup_candle_count = 200  # 需要足够K线计算指标
    process_only_new_candles = True
    use_exit_signal = True
    use_custom_stoploss = True

    # 订单类型配置
    order_types = {
        'entry': 'market',
        'exit': 'market',
        'stoploss': 'market',
        'stoploss_on_exchange': False,
        'stoploss_on_exchange_interval': 60,
    }

    # 可优化参数

    # EMA趋势参数
    ema_fast = IntParameter(10, 25, default=20, space="buy")
    ema_slow = IntParameter(40, 80, default=50, space="buy")
    ema_trend = IntParameter(100, 200, default=150, space="buy")

    # MACD参数
    macd_fast = IntParameter(8, 15, default=12, space="buy")
    macd_slow = IntParameter(20, 30, default=26, space="buy")
    macd_signal = IntParameter(6, 12, default=9, space="buy")

    # RSI参数
    rsi_period = IntParameter(10, 20, default=14, space="buy")
    rsi_overbought = IntParameter(70, 80, default=75, space="sell")
    rsi_oversold = IntParameter(20, 30, default=25, space="sell")
    rsi_neutral_high = IntParameter(60, 70, default=65, space="buy")
    rsi_neutral_low = IntParameter(35, 45, default=40, space="buy")

    # ATR参数
    atr_period = IntParameter(10, 20, default=14, space="sell")
    atr_multiplier_stoploss = DecimalParameter(2.0, 4.0, default=3.0, space="sell")
    atr_multiplier_entry = DecimalParameter(0.5, 2.0, default=1.0, space="buy")

    # 成交量确认参数
    volume_ma_period = IntParameter(15, 35, default=20, space="buy")
    volume_multiplier_min = DecimalParameter(1.2, 2.0, default=1.5, space="buy")
    volume_multiplier_strong = DecimalParameter(2.0, 3.5, default=2.5, space="buy")

    # 价格突破参数
    breakout_threshold = DecimalParameter(0.3, 1.5, default=0.8, space="buy")
    pullback_threshold = DecimalParameter(0.2, 1.0, default=0.5, space="sell")

    # 仓位管理参数
    position_size_base = DecimalParameter(0.01, 0.05, default=0.02, space="buy")  # 基础仓位2%
    position_size_multiplier = DecimalParameter(1.5, 3.0, default=2.0, space="buy")  # 高置信度2倍仓位
    max_position_size = DecimalParameter(0.08, 0.20, default=0.10, space="buy")  # 最大仓位10%

    def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        """
        计算技术指标
        """

        # EMA趋势指标
        dataframe['ema_fast'] = ta.EMA(dataframe, timeperiod=self.ema_fast.value)
        dataframe['ema_slow'] = ta.EMA(dataframe, timeperiod=self.ema_slow.value)
        dataframe['ema_trend'] = ta.EMA(dataframe, timeperiod=self.ema_trend.value)

        # EMA趋势状态
        dataframe['price_above_ema_fast'] = dataframe['close'] > dataframe['ema_fast']
        dataframe['price_above_ema_slow'] = dataframe['close'] > dataframe['ema_slow']
        dataframe['price_above_ema_trend'] = dataframe['close'] > dataframe['ema_trend']
        dataframe['ema_bullish_alignment'] = (dataframe['ema_fast'] > dataframe['ema_slow']) & (dataframe['ema_slow'] > dataframe['ema_trend'])
        dataframe['ema_bearish_alignment'] = (dataframe['ema_fast'] < dataframe['ema_slow']) & (dataframe['ema_slow'] < dataframe['ema_trend'])

        # MACD动量指标
        macd = ta.MACD(dataframe, fastperiod=self.macd_fast.value, slowperiod=self.macd_slow.value, signalperiod=self.macd_signal.value)
        dataframe['macd'] = macd['macd']
        dataframe['macd_signal'] = macd['macdsignal']
        dataframe['macd_hist'] = macd['macdhist']

        # MACD信号
        dataframe['macd_bullish'] = dataframe['macd'] > dataframe['macd_signal']
        dataframe['macd_bearish'] = dataframe['macd'] < dataframe['macd_signal']
        dataframe['macd_cross_above'] = (dataframe['macd'] > dataframe['macd_signal']) & (dataframe['macd'].shift(1) <= dataframe['macd_signal'].shift(1))
        dataframe['macd_cross_below'] = (dataframe['macd'] < dataframe['macd_signal']) & (dataframe['macd'].shift(1) >= dataframe['macd_signal'].shift(1))
        dataframe['macd_hist_positive'] = dataframe['macd_hist'] > 0
        dataframe['macd_hist_growing'] = dataframe['macd_hist'] > dataframe['macd_hist'].shift(1)

        # RSI强弱指标
        dataframe['rsi'] = ta.RSI(dataframe, timeperiod=self.rsi_period.value)
        dataframe['rsi_overbought'] = dataframe['rsi'] > self.rsi_overbought.value
        dataframe['rsi_oversold'] = dataframe['rsi'] < self.rsi_oversold.value
        dataframe['rsi_neutral'] = (dataframe['rsi'] >= self.rsi_neutral_low.value) & (dataframe['rsi'] <= self.rsi_neutral_high.value)
        dataframe['rsi_rising'] = dataframe['rsi'] > dataframe['rsi'].shift(1)

        # ATR波动性指标
        dataframe['atr'] = ta.ATR(dataframe, timeperiod=self.atr_period.value)
        dataframe['atr_percent'] = dataframe['atr'] / dataframe['close']
        dataframe['volatility_high'] = dataframe['atr_percent'] > dataframe['atr_percent'].rolling(20).mean() * 1.5

        # 支撑阻力位
        dataframe['resistance_20'] = dataframe['high'].rolling(20).max()
        dataframe['support_20'] = dataframe['low'].rolling(20).min()
        dataframe['resistance_50'] = dataframe['high'].rolling(50).max()
        dataframe['support_50'] = dataframe['low'].rolling(50).min()

        # 价格位置
        dataframe['price_position_20'] = (dataframe['close'] - dataframe['support_20']) / (dataframe['resistance_20'] - dataframe['support_20'])
        dataframe['near_resistance_20'] = dataframe['close'] > dataframe['resistance_20'].shift(1) * 0.98
        dataframe['near_support_20'] = dataframe['close'] < dataframe['support_20'].shift(1) * 1.02

        # 成交量指标
        dataframe['volume_ma'] = dataframe['volume'].rolling(self.volume_ma_period.value).mean()
        dataframe['volume_ratio'] = dataframe['volume'] / dataframe['volume_ma']
        dataframe['volume_high'] = dataframe['volume_ratio'] > self.volume_multiplier_strong.value
        dataframe['volume_normal'] = dataframe['volume_ratio'] > self.volume_multiplier_min.value

        # 价格突破检测
        dataframe['breakout_up'] = dataframe['close'] > dataframe['resistance_20'].shift(1)
        dataframe['breakout_down'] = dataframe['close'] < dataframe['support_20'].shift(1)
        dataframe['breakout_strength'] = np.where(
            dataframe['breakout_up'],
            (dataframe['close'] - dataframe['resistance_20'].shift(1)) / dataframe['close'] * 100,
            np.where(
                dataframe['breakout_down'],
                (dataframe['support_20'].shift(1) - dataframe['close']) / dataframe['close'] * 100,
                0
            )
        )

        # 布林带辅助指标
        bb = ta.BBANDS(dataframe, timeperiod=20, nbdevup=2.0, nbdevdn=2.0)
        dataframe['bb_upper'] = bb['upperband']
        dataframe['bb_lower'] = bb['lowerband']
        dataframe['bb_middle'] = bb['middleband']
        dataframe['bb_width'] = (dataframe['bb_upper'] - dataframe['bb_lower']) / dataframe['bb_middle']
        dataframe['price_above_bb_upper'] = dataframe['close'] > dataframe['bb_upper']
        dataframe['price_below_bb_lower'] = dataframe['close'] < dataframe['bb_lower']

        # 置信度计算
        dataframe['confidence_score'] = self._calculate_confidence_score(dataframe)

        return dataframe

    def _calculate_confidence_score(self, dataframe: DataFrame) -> DataFrame:
        """
        计算交易置信度分数 (0-1)
        """
        score = np.zeros(len(dataframe))

        # 趋势一致性得分 (0-0.3)
        trend_score = np.where(
            dataframe['ema_bullish_alignment'],
            0.3,
            np.where(
                dataframe['ema_bearish_alignment'],
                -0.3,
                0
            )
        )

        # 动量确认得分 (0-0.25)
        momentum_score = np.where(
            dataframe['macd_bullish'] & dataframe['macd_hist_positive'],
            0.25,
            np.where(
                dataframe['macd_bearish'] & ~dataframe['macd_hist_positive'],
                -0.25,
                0
            )
        )

        # RSI位置得分 (0-0.2)
        rsi_score = np.where(
            dataframe['rsi_neutral'] & dataframe['rsi_rising'],
            0.2,
            np.where(
                dataframe['rsi_oversold'],
                0.15,
                np.where(
                    dataframe['rsi_overbought'],
                    -0.15,
                    0
                )
            )
        )

        # 成交量确认得分 (0-0.15)
        volume_score = np.where(
            dataframe['volume_high'],
            0.15,
            np.where(
                dataframe['volume_normal'],
                0.1,
                0
            )
        )

        # 突破强度得分 (0-0.1)
        breakout_score = np.clip(dataframe['breakout_strength'] / 2.0, -0.1, 0.1)

        # 综合得分
        total_score = trend_score + momentum_score + rsi_score + volume_score + breakout_score

        # 归一化到0-1范围
        normalized_score = (total_score + 1) / 2
        return np.clip(normalized_score, 0, 1)

    def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        """
        定义入场条件
        """

        # 做多入场条件
        long_conditions = (
            # 趋势确认
            dataframe['price_above_ema_trend'] &
            dataframe['ema_bullish_alignment'] &

            # 动量确认
            dataframe['macd_cross_above'] &
            dataframe['macd_hist_positive'] &

            # RSI确认
            (dataframe['rsi'] > self.rsi_neutral_low.value) &
            (dataframe['rsi'] < self.rsi_neutral_high.value) &
            dataframe['rsi_rising'] &

            # 成交量确认
            dataframe['volume_high'] &

            # 突破确认
            (dataframe['breakout_strength'] > self.breakout_threshold.value) &

            # 波动性过滤
            ~dataframe['volatility_high'] &

            # 置信度要求
            (dataframe['confidence_score'] > 0.65)
        )

        dataframe.loc[long_conditions, 'enter_long'] = 1

        # 做空入场条件
        short_conditions = (
            # 趋势确认
            ~dataframe['price_above_ema_trend'] &
            dataframe['ema_bearish_alignment'] &

            # 动量确认
            dataframe['macd_cross_below'] &
            ~dataframe['macd_hist_positive'] &

            # RSI确认
            (dataframe['rsi'] > self.rsi_neutral_low.value) &
            (dataframe['rsi'] < self.rsi_neutral_high.value) &
            ~dataframe['rsi_rising'] &

            # 成交量确认
            dataframe['volume_high'] &

            # 突破确认
            (dataframe['breakout_strength'] > self.breakout_threshold.value) &

            # 波动性过滤
            ~dataframe['volatility_high'] &

            # 置信度要求
            (dataframe['confidence_score'] > 0.65)
        )

        dataframe.loc[short_conditions, 'enter_short'] = 1

        return dataframe

    def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        """
        定义出场条件
        """

        # 多头出场条件
        exit_long_conditions = (
            # 趋势反转
            (~dataframe['price_above_ema_slow']) |
            (dataframe['ema_fast'] < dataframe['ema_slow']) |

            # 动量衰竭
            (dataframe['macd_cross_below']) |

            # RSI过热
            (dataframe['rsi'] > self.rsi_overbought.value) |

            # 破位下跌
            (dataframe['close'] < dataframe['support_20'].shift(1)) |

            # 置信度过低
            (dataframe['confidence_score'] < 0.35)
        )

        dataframe.loc[exit_long_conditions, 'exit_long'] = 1

        # 空头出场条件
        exit_short_conditions = (
            # 趋势反转
            (dataframe['price_above_ema_slow']) |
            (dataframe['ema_fast'] > dataframe['ema_slow']) |

            # 动量衰竭
            (dataframe['macd_cross_above']) |

            # RSI超卖
            (dataframe['rsi'] < self.rsi_oversold.value) |

            # 破位上涨
            (dataframe['close'] > dataframe['resistance_20'].shift(1)) |

            # 置信度过低
            (dataframe['confidence_score'] < 0.35)
        )

        dataframe.loc[exit_short_conditions, 'exit_short'] = 1

        return dataframe

    def custom_stoploss(self, pair: str, trade, current_time, current_rate: float,
                       current_profit: float, **kwargs) -> float:
        """
        动态止损策略
        """

        dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe)
        if len(dataframe) < 1:
            return self.stoploss

        last_candle = dataframe.iloc[-1].squeeze()

        # ATR动态止损
        atr_stop_distance = (last_candle['atr'] * self.atr_multiplier_stoploss.value) / current_rate

        # 支撑/阻力位止损
        if trade.is_short:
            support_stop_distance = abs(last_candle['support_20'] - current_rate) / current_rate
            stop_distance = max(atr_stop_distance, support_stop_distance)
        else:
            resistance_stop_distance = abs(last_candle['resistance_20'] - current_rate) / current_rate
            stop_distance = max(atr_stop_distance, resistance_stop_distance)

        # 根据盈利情况调整止损
        if current_profit > 0.30:  # 盈利30%以上，收紧止损
            return max(-stop_distance * 0.5, -0.03)
        elif current_profit > 0.20:  # 盈利20%以上
            return max(-stop_distance * 0.6, -0.04)
        elif current_profit > 0.10:  # 盈利10%以上
            return max(-stop_distance * 0.7, -0.05)
        elif current_profit > 0.05:  # 盈利5%以上
            return max(-stop_distance * 0.8, -0.06)
        elif current_profit > -0.02:  # 小幅亏损或盈利
            return max(-stop_distance * 1.0, self.stoploss)
        else:  # 较大亏损
            return max(-stop_distance * 1.2, self.stoploss)

    def custom_stake_amount(self, pair: str, current_time: str, current_rate: float,
                          proposed_stake: float, min_stake: Optional[float], max_stake: float,
                          leverage: float, entry_tag: Optional[str], side: str,
                          **kwargs) -> float:
        """
        自定义仓位管理
        """

        dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe)
        if len(dataframe) < 1:
            return proposed_stake

        last_candle = dataframe.iloc[-1].squeeze()

        # 基础仓位计算
        available_capital = self.wallets.get_total_stake_amount()
        base_stake = available_capital * self.position_size_base.value

        # 根据置信度调整仓位
        confidence = last_candle['confidence_score']

        if confidence > 0.85:  # 超高置信度
            position_multiplier = self.position_size_multiplier.value
        elif confidence > 0.75:  # 高置信度
            position_multiplier = 1.5
        elif confidence > 0.65:  # 中等置信度
            position_multiplier = 1.2
        else:  # 低置信度
            position_multiplier = 0.8

        # 根据波动性调整仓位
        if last_candle['volatility_high']:
            position_multiplier *= 0.7  # 高波动时减仓

        # 根据突破强度调整仓位
        if last_candle['breakout_strength'] > 1.5:
            position_multiplier *= 1.2  # 强突破时加仓

        adjusted_stake = base_stake * position_multiplier

        # 限制最大仓位
        max_allowed = available_capital * self.max_position_size.value
        adjusted_stake = min(adjusted_stake, max_allowed)

        return min(max(adjusted_stake, min_stake or 0), max_stake)

    def confirm_trade_entry(self, pair: str, order_type: str, amount: float,
                          rate: float, time_in_force: str, current_time,
                          entry_tag: str, side: str, **kwargs) -> bool:
        """
        交易确认
        """

        dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe)
        if len(dataframe) < 2:
            return False

        last_candle = dataframe.iloc[-1].squeeze()
        prev_candle = dataframe.iloc[-2].squeeze()

        # 置信度检查
        if last_candle['confidence_score'] < 0.65:
            return False

        # 做多交易确认
        if side == 'long':
            # 确保趋势向上
            if not (last_candle['price_above_ema_trend'] and last_candle['ema_bullish_alignment']):
                return False

            # 确保动量向上
            if not (last_candle['macd_hist_positive'] and last_candle['macd_hist_growing']):
                return False

            # 确保RSI合理
            if not (self.rsi_neutral_low.value <= last_candle['rsi'] <= self.rsi_neutral_high.value):
                return False

            # 确保成交量充足
            if last_candle['volume_ratio'] < self.volume_multiplier_min.value:
                return False

        # 做空交易确认
        elif side == 'short':
            # 确保趋势向下
            if not (not last_candle['price_above_ema_trend'] and last_candle['ema_bearish_alignment']):
                return False

            # 确保动量向下
            if not (not last_candle['macd_hist_positive'] and not last_candle['macd_hist_growing']):
                return False

            # 确保RSI合理
            if not (self.rsi_neutral_low.value <= last_candle['rsi'] <= self.rsi_neutral_high.value):
                return False

            # 确保成交量充足
            if last_candle['volume_ratio'] < self.volume_multiplier_min.value:
                return False

        return True

    def custom_exit(self, pair: str, trade, current_time, current_rate: float,
                   current_profit: float, **kwargs) -> str:
        """
        自定义退出逻辑
        """

        dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe)
        if len(dataframe) < 1:
            return None

        last_candle = dataframe.iloc[-1].squeeze()

        # 置信度急剧下降退出
        if last_candle['confidence_score'] < 0.3:
            return "confidence_drop"

        # 动量衰竭退出
        if trade.is_short:
            if (last_candle['macd_cross_above'] and current_profit < 0.02):
                return "momentum_reversal_long"
        else:
            if (last_candle['macd_cross_below'] and current_profit < 0.02):
                return "momentum_reversal_short"

        # 趋势破坏退出
        if trade.is_short:
            if (last_candle['price_above_ema_slow'] and current_profit < 0.01):
                return "trend_breakdown_long"
        else:
            if (not last_candle['price_above_ema_slow'] and current_profit < 0.01):
                return "trend_breakdown_short"

        # 时间止损
        trade_duration = (current_time - trade.open_date_utc).total_seconds() / 3600
        if trade_duration > 48 and abs(current_profit) < 0.02:  # 48小时无显著收益
            return "time_exit"

        return None

    def leverage(self, pair: str, current_time: str, current_rate: float,
                proposed_leverage: float, max_leverage: int, entry_tag: Optional[str],
                side: str, **kwargs) -> float:
        """
        动态杠杆管理
        """

        dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe)
        if len(dataframe) < 1:
            return 1.0

        last_candle = dataframe.iloc[-1].squeeze()

        # 基于置信度调整杠杆
        confidence = last_candle['confidence_score']

        if confidence > 0.85:
            return min(3.0, max_leverage)  # 最高3倍杠杆
        elif confidence > 0.75:
            return min(2.0, max_leverage)  # 2倍杠杆
        elif confidence > 0.65:
            return 1.5  # 1.5倍杠杆
        else:
            return 1.0  # 1倍杠杆