# source: https://raw.githubusercontent.com/tellyoung/money_game/8b12b7fa13559ab44a6a09104292ad75f0cdaeb0/Trading/user_data/strategies/yutyStrategy01.py
from freqtrade.strategy import IStrategy, merge_informative_pair
from pandas import DataFrame, Series, DatetimeIndex
import talib.abstract as ta
import pandas as pd
from datetime import datetime
import logging

# 配置日志（通常在策略外部或 __init__ 方法中）
logger = logging.getLogger(__name__)


class Github_tellyoung_money_game__yutyStrategy01__20250519_005942(IStrategy):
    can_short = True

    timeframe = '5m'
    
    # 策略参数
    stoploss = -0.20  # 初始值，实际在custom_stoploss中动态计算
    trailing_stop = False
    process_only_new_candles = True
    use_custom_stoploss = True
    use_exit_signal = False  # 关闭内置退出信号

    def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        # 计算多周期均线
        dataframe['ema5'] = ta.EMA(dataframe, timeperiod=5)
        dataframe['ema20'] = ta.EMA(dataframe, timeperiod=20)
        dataframe['ema50'] = ta.EMA(dataframe, timeperiod=50)
        
        # 计算ATR(14)
        dataframe['atr'] = ta.ATR(dataframe, timeperiod=14)
        
        # 计算RSI
        dataframe['rsi'] = ta.RSI(dataframe, timeperiod=14)
        
        # 计算布林带
        bollinger = ta.BBANDS(dataframe, timeperiod=20)
        dataframe['bb_lowerband'] = bollinger['lowerband']
        dataframe['bb_upperband'] = bollinger['upperband']
        dataframe['bb_width'] = (dataframe['bb_upperband'] - dataframe['bb_lowerband']) / dataframe['close']
        
        # 计算ADX
        dataframe['adx'] = ta.ADX(dataframe, timeperiod=14)
        
        # 计算Stochastic RSI
        stoch = ta.STOCH(dataframe)
        dataframe['stoch_k'] = stoch['slowk']
        dataframe['stoch_d'] = stoch['slowd']
        
        return dataframe

    def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        dataframe = self.create_signals(dataframe)
        
        # 多头条件
        dataframe.loc[
            (dataframe['ema5'] > dataframe['ema20']) &  # 短期趋势向上
            (dataframe['rsi'] > 40) &  # RSI 放宽条件
            (dataframe['stoch_k'] < 30) &  # Stochastic RSI 超卖区域
            (dataframe['adx'] > 15) &  # 趋势强度放宽
            (dataframe['close'] > dataframe['bb_lowerband']) &  # 接近布林带下轨
            (dataframe['volume'] > 0),
            'enter_long'
        ] = 1
        
        # 空头条件
        dataframe.loc[
            (dataframe['ema5'] < dataframe['ema20']) &
            (dataframe['rsi'] < 60) &  # RSI 放宽条件
            (dataframe['stoch_k'] > 70) &  # Stochastic RSI 超买区域
            (dataframe['adx'] > 15) &
            (dataframe['close'] < dataframe['bb_upperband']) &  # 接近布林带上轨
            (dataframe['volume'] > 0),
            'enter_short'
        ] = 1
        
        return dataframe

    def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        # 由于使用移动止损，此处保持空退出信号
        dataframe = self.create_signals(dataframe)
        dataframe['exit_long'] = 0  # 不主动触发退出
        dataframe['exit_short'] = 0
        return dataframe

    def create_signals(self, dataframe):
        # 初始化信号列
        for col in ['enter_long', 'enter_short', 'exit_long', 'exit_short']:
            if col not in dataframe:
                dataframe[col] = 0
        return dataframe

    def custom_stoploss(self, pair: str, trade: 'Trade', current_time: datetime,
                        current_rate: float, current_profit: float, **kwargs) -> float:
        """
        动态止损逻辑：
        - 初始止损为 1 倍 ATR
        - 当利润达到 1.5 倍 ATR 时，移动止损到开仓价
        - 当利润达到 2 倍 ATR 时，锁定 50% 利润
        """
        dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe)
        open_date = trade.open_date_utc.replace(tzinfo=None)
        mask = dataframe['date'] == open_date
        if not dataframe[mask].empty:
            atr_entry = dataframe[mask]['atr'].iloc[0]
        else:
            return 1  # 异常情况返回默认值

        if not trade.is_short:
            price_diff = current_rate - trade.open_rate
            if price_diff >= 2 * atr_entry:
                # 锁定 50% 利润
                return (current_rate - trade.open_rate * 0.5) / current_rate
            elif price_diff >= 1.5 * atr_entry:
                # 移动到保本
                return (current_rate - trade.open_rate) / current_rate
            else:
                # 初始止损
                stop_price = trade.open_rate - 1 * atr_entry
                return (current_rate - stop_price) / current_rate
        else:
            price_diff = trade.open_rate - current_rate
            if price_diff >= 2 * atr_entry:
                return (trade.open_rate - current_rate * 0.5) / current_rate
            elif price_diff >= 1.5 * atr_entry:
                return (trade.open_rate - current_rate) / current_rate
            else:
                stop_price = trade.open_rate + 1 * atr_entry
                return (stop_price - current_rate) / current_rate

    def custom_stake_amount(self, pair: str, current_time: datetime, current_rate: float,
                            proposed_stake: float, min_stake: float, max_stake: float,
                            **kwargs) -> float:
        """
        动态仓位管理：
        - 根据波动率动态调整仓位
        - 高波动行情中增加仓位，低波动行情中减少仓位
        """
        try:
            # 获取当前ATR
            dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe)
            atr = dataframe['atr'].iloc[-1]
            
            # 动态调整仓位
            atr_factor = min(max(atr / current_rate, 0.01), 0.05)  # ATR占比限制在1%-5%
            adjusted_stake = proposed_stake * atr_factor
            
            return max(min_stake, min(adjusted_stake, max_stake))
        except Exception as e:
            logger.info(f"仓位计算错误: {str(e)}", exc_info=True)
            return proposed_stake
    
    @property
    def protections(self):
        return [
            {
                "method": "CooldownPeriod",
                "stop_duration_candles": 5
            }
        ]



