# source: https://raw.githubusercontent.com/huckhuck12/freqtrade-futures-lab/40a97470897a3ebfe2853eca7586a325598dc0f0/user_data/strategies/DualPositionHedgeV1.py
import numpy as np
import talib.abstract as ta
import pandas as pd
from pandas import DataFrame
from freqtrade.strategy import IStrategy, IntParameter, DecimalParameter, merge_informative_pair
from datetime import datetime
from typing import Optional
import logging

logger = logging.getLogger(__name__)

class Github_huckhuck12_freqtrade_futures_lab__DualPositionHedgeV1__20260122_145205(IStrategy):
    """
    多空双开对冲策略 V1 (Dual Position Hedge Strategy)
    
    核心思路：
    1. 在关键位置同时开多空单，锁定风险
    2. 根据趋势方向，逐步平掉反向单子
    3. 保留顺势单子，获取趋势利润
    4. 通过对冲机制，实现永不爆仓
    
    适用场景：
    - 震荡行情中的突破
    - 重要支撑阻力位
    - 不确定方向但预期有大波动的时候
    """
    
    INTERFACE_VERSION = 3
    timeframe = '5m'
    can_short = True
    
    # 策略参数 - 回到第一轮优化的最佳配置
    rsi_overbought = IntParameter(70, 85, default=78, space='sell')  # 回到最优设置
    rsi_oversold = IntParameter(15, 30, default=22, space='buy')     # 回到最优设置
    ema_period = IntParameter(20, 50, default=25, space='buy')       # 保持不变
    volume_threshold = DecimalParameter(1.5, 2.5, default=2.0, space='buy')  # 回到最优设置
    
    # 新增动态止损参数
    dynamic_stoploss = DecimalParameter(0.05, 0.12, default=0.07, space='buy')  # 动态止损
    
    # 风险控制 - 最优版本设置
    stoploss = -0.08  # 8%止损，最优版本
    
    minimal_roi = {
        "0": 0.04,   # 4% 目标利润（降低以增加获利机会）
        "15": 0.025, # 15分钟 2.5%
        "30": 0.02,  # 30分钟 2%
        "60": 0.015, # 1小时 1.5%
        "120": 0.01  # 2小时 1%
    }

    # 不使用追踪止损，手动管理仓位
    trailing_stop = False
    
    process_only_new_candles = True
    startup_candle_count = 200
    custom_leverage = 6.0  # 最优版本杠杆

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

    def custom_stoploss(self, pair: str, trade, current_time: datetime,
                        current_rate: float, current_profit: float, **kwargs) -> float:
        """
        动态止损：根据持仓时间和盈利情况调整止损
        """
        # 获取持仓时间（分钟）
        trade_duration = (current_time - trade.open_date_utc).total_seconds() / 60
        
        # 如果已经盈利，使用更紧的止损保护利润
        if current_profit > 0.02:  # 盈利超过2%
            return -0.015  # 1.5%止损保护利润
        elif current_profit > 0.01:  # 盈利超过1%
            return -0.03   # 3%止损
        elif trade_duration > 60:  # 持仓超过1小时
            return -0.06   # 6%止损，避免长时间亏损
        else:
            return self.stoploss  # 使用默认7%止损

    def informative_pairs(self) -> list:
        pairs = self.dp.current_whitelist()
        return [(pair, '1h') for pair in pairs]  # 改用1小时数据

    def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        
        # === 5分钟指标 ===
        dataframe['ema_fast'] = ta.EMA(dataframe, timeperiod=self.ema_period.value)
        dataframe['ema_slow'] = ta.EMA(dataframe, timeperiod=self.ema_period.value * 2)
        dataframe['rsi'] = ta.RSI(dataframe, timeperiod=14)
        dataframe['volume_sma'] = ta.SMA(dataframe['volume'], timeperiod=20)
        dataframe['volume_ratio'] = dataframe['volume'] / dataframe['volume_sma']
        
        # ATR for volatility
        dataframe['atr'] = ta.ATR(dataframe, timeperiod=14)
        dataframe['atr_ratio'] = dataframe['atr'] / dataframe['close']
        
        # Bollinger Bands for exit signals
        bollinger = ta.BBANDS(dataframe, timeperiod=20, nbdevup=2.0, nbdevdn=2.0, matype=0)
        dataframe['bb_upper'] = bollinger['upperband']
        dataframe['bb_middle'] = bollinger['middleband']
        dataframe['bb_lower'] = bollinger['lowerband']
        
        # === 1小时趋势指标 ===
        inf_tf = '1h'
        informative = self.dp.get_pair_dataframe(pair=metadata['pair'], timeframe=inf_tf)
        
        informative['ema_trend'] = ta.EMA(informative, timeperiod=50)
        informative['rsi_trend'] = ta.RSI(informative, timeperiod=14)
        informative['macd'], informative['macdsignal'], informative['macdhist'] = ta.MACD(informative)
        
        # 趋势强度判断
        informative['trend_strength'] = np.where(
            informative['close'] > informative['ema_trend'], 1,  # 上涨趋势
            np.where(informative['close'] < informative['ema_trend'], -1, 0)  # 下跌趋势
        )
        
        dataframe = merge_informative_pair(dataframe, informative, self.timeframe, inf_tf, ffill=True)
        
        # 重命名列
        dataframe['trend_strength'] = dataframe[f'trend_strength_{inf_tf}']
        dataframe['rsi_trend'] = dataframe[f'rsi_trend_{inf_tf}']
        dataframe['macd_trend'] = dataframe[f'macd_{inf_tf}']
        
        return dataframe

    def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        
        # === 双开条件：回到第一轮优化的最佳配置 ===
        # 使用已验证有效的参数组合
        
        dual_open_condition = (
            # RSI极值 - 最优阈值
            ((dataframe['rsi'] >= self.rsi_overbought.value) | 
             (dataframe['rsi'] <= self.rsi_oversold.value)) &
            # 成交量放大 - 最优要求
            (dataframe['volume_ratio'] > self.volume_threshold.value) &
            # 波动率较高 - 最优阈值
            (dataframe['atr_ratio'] > 0.025) &
            # 布林带位置确认 - 最优设置
            ((dataframe['close'] > dataframe['bb_upper'] * 0.98) |  # 接近上轨
             (dataframe['close'] < dataframe['bb_lower'] * 1.02))   # 接近下轨
        )
        
        # === 多单入场 ===
        # 在双开条件下，如果趋势偏多，优先开多单
        dataframe.loc[
            (
                dual_open_condition &
                (
                    # 5分钟超跌反弹
                    (dataframe['rsi'] <= self.rsi_oversold.value) |
                    # 1小时趋势向上
                    (dataframe['trend_strength'] > 0) |
                    # EMA金叉
                    ((dataframe['ema_fast'] > dataframe['ema_slow']) & 
                     (dataframe['ema_fast'].shift(1) <= dataframe['ema_slow'].shift(1)))
                )
            ),
            'enter_long'] = 1
            
        # === 空单入场 ===  
        # 在双开条件下，如果趋势偏空，优先开空单
        dataframe.loc[
            (
                dual_open_condition &
                (
                    # 5分钟超买回调
                    (dataframe['rsi'] >= self.rsi_overbought.value) |
                    # 1小时趋势向下
                    (dataframe['trend_strength'] < 0) |
                    # EMA死叉
                    ((dataframe['ema_fast'] < dataframe['ema_slow']) & 
                     (dataframe['ema_fast'].shift(1) >= dataframe['ema_slow'].shift(1)))
                )
            ),
            'enter_short'] = 1

        return dataframe

    def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        
        # === 多单离场 - 更积极的获利策略 ===
        dataframe.loc[
            (
                # RSI超买但阈值更低，更早离场
                (dataframe['rsi'] > self.rsi_overbought.value - 3) |
                # 趋势转弱
                (dataframe['trend_strength'] < 0) |
                # 价格接近布林带中轨（获利回吐信号）
                (dataframe['close'] < dataframe['bb_middle']) |
                # EMA死叉信号
                ((dataframe['ema_fast'] < dataframe['ema_slow']) & 
                 (dataframe['ema_fast'].shift(1) >= dataframe['ema_slow'].shift(1)))
            ),
            'exit_long'] = 1
            
        # === 空单离场 - 更积极的获利策略 ===
        dataframe.loc[
            (
                # RSI超卖但阈值更高，更早离场
                (dataframe['rsi'] < self.rsi_oversold.value + 3) |
                # 趋势转强
                (dataframe['trend_strength'] > 0) |
                # 价格接近布林带中轨（获利回吐信号）
                (dataframe['close'] > dataframe['bb_middle']) |
                # EMA金叉信号
                ((dataframe['ema_fast'] > dataframe['ema_slow']) & 
                 (dataframe['ema_fast'].shift(1) <= dataframe['ema_slow'].shift(1)))
            ),
            'exit_short'] = 1

        return dataframe

    def custom_entry_price(self, pair: str, current_time: datetime, proposed_rate: float,
                          entry_tag: Optional[str], side: str, **kwargs) -> float:
        """
        自定义入场价格，确保在关键位置入场
        """
        dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe)
        if dataframe.empty:
            return proposed_rate
            
        last_candle = dataframe.iloc[-1]
        
        if side == 'long':
            # 多单稍微激进一点入场
            return proposed_rate * 1.001
        else:
            # 空单稍微激进一点入场  
            return proposed_rate * 0.999
            
    def custom_exit_price(self, pair: str, trade, current_time: datetime, 
                         proposed_rate: float, current_profit: float, exit_tag: Optional[str],
                         **kwargs) -> float:
        """
        自定义离场价格，确保及时离场
        """
        if current_profit > 0.02:  # 利润超过2%时，稍微保守离场
            if trade.is_short:
                return proposed_rate * 1.001  # 空单稍高价格平仓
            else:
                return proposed_rate * 0.999  # 多单稍低价格平仓
        
        return proposed_rate

    def confirm_trade_entry(self, pair: str, order_type: str, amount: float, rate: float,
                           time_in_force: str, current_time: datetime, entry_tag: Optional[str],
                           side: str, **kwargs) -> bool:
        """
        确认交易入场，可以在这里实现更复杂的双开逻辑
        """
        # 这里可以添加额外的风控逻辑
        # 比如检查当前持仓情况，确保不会过度开仓
        return True