# source: https://raw.githubusercontent.com/tellyoung/money_game/8b12b7fa13559ab44a6a09104292ad75f0cdaeb0/Trading/user_data/strategies/yutyStrategy02.py
from freqtrade.strategy import IStrategy, IntParameter, DecimalParameter
from freqtrade.persistence import Trade
from datetime import datetime, timedelta
from pandas import DataFrame, Series
import talib.abstract as ta
import numpy as np

class Github_tellyoung_money_game__yutyStrategy02__20250519_005942(IStrategy):
    can_short = True

    # 策略基础配置
    timeframe = '5m'  # 策略运行的时间周期为5分钟
    process_only_short = False  # 是否只运行空头策略，False表示支持多头和空头
    process_only_long = False  # 是否只运行多头策略，False表示支持多头和空头
    
    # 风险参数
    stoploss = -0.99  # 动态止损占位符，实际止损逻辑在custom_stoploss中实现
    trailing_stop = True  # 启用追踪止损
    trailing_stop_positive = 0.2  # 当价格达到一定盈利时，触发追踪止损
    trailing_only_offset_is_reached = True  # 只有在达到正向偏移时才启用追踪止损
    
    # 策略参数（添加 hyperopt 参数）
    atr_period = IntParameter(10, 30, default=14, space='buy', optimize=True)  # ATR（平均真实波幅）的计算周期
    volatility_threshold = DecimalParameter(0.01, 0.08, default=0.03, space='buy', optimize=True)  # 波动率阈值
    ma_period = IntParameter(5, 20, default=18, space='buy', optimize=True)  # 移动平均线的周期

    # >>>> from me
    h_w = IntParameter(1, 15, default=7, space='buy', optimize=True)
    l_w = IntParameter(1, 15, default=8, space='buy', optimize=True)
    roc_n = IntParameter(6, 18, default=11, space='buy', optimize=True)
    momentum_threshold_long = DecimalParameter(-0.5, 0.5, default=0.0, space='buy', optimize=True)
    momentum_threshold_short = DecimalParameter(-0.5, 0.5, default=0.0, space='buy', optimize=True)
    sell_1 = IntParameter(0, 5, default=1, space='sell', optimize=True)
    sell_2 = IntParameter(1, 8, default=3, space='sell', optimize=True)
    # 添加 hyperopt 参数
    initial_stoploss_multiplier = DecimalParameter(1.0, 3.0, default=2.0, space='sell', optimize=True)  # 初始止损倍数
    trailing_stop_multiplier_long = DecimalParameter(0.5, 1.0, default=0.8, space='sell', optimize=True)  # 多头追踪止损倍数
    trailing_stop_multiplier_short = DecimalParameter(1.0, 1.5, default=1.2, space='sell', optimize=True)  # 空头追踪止损倍数


    # 状态跟踪
    drawdown_blacklist = {}  # 用于记录熔断机制中被禁止交易的交易对
    last_check_date = None  # 记录最后一次检查熔断机制的日期

    def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        # 计算技术指标
        dataframe['ma7'] = ta.SMA(dataframe, timeperiod=int(self.ma_period.value))  # 7周期简单移动平均线
        dataframe['atr'] = ta.ATR(dataframe, timeperiod=int(self.atr_period.value))  # 平均真实波幅，用于衡量市场波动性
        
        # 三线突破信号
        dataframe['prior_high'] = dataframe['high'].rolling(window=int(self.h_w.value)).max().shift(1)  # 过去3根K线的最高价
        dataframe['prior_low'] = dataframe['low'].rolling(window=int(self.l_w.value)).min().shift(1)  # 过去3根K线的最低价
        
        # 趋势强度指标（用于品种轮动）
        dataframe['momentum'] = ta.ROC(dataframe, timeperiod=24 * int(self.roc_n.value))  # 24小时动量指标
        
        return dataframe

    def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        # 多头入场逻辑
        dataframe.loc[ 
            (dataframe['close'] > dataframe['prior_high']) &  # 收盘价高于过去3根K线的最高价
            (dataframe['close'] > dataframe['ma7']) &  # 收盘价高于7周期移动平均线
            (dataframe['atr'] > self.volatility_threshold.value) &  # ATR超过波动率阈值
            (dataframe['momentum'] > self.momentum_threshold_long.value) &  # 动量为正
            (self.allow_trading(metadata)),  # 熔断机制允许交易
            'enter_long'
        ] = 1

        # 空头入场逻辑
        dataframe.loc[
            (dataframe['close'] < dataframe['prior_low']) &  # 收盘价低于过去3根K线的最低价
            (dataframe['close'] < dataframe['ma7']) &  # 收盘价低于7周期移动平均线
            (dataframe['atr'] > self.volatility_threshold.value) &  # ATR超过波动率阈值
            (dataframe['momentum'] < self.momentum_threshold_short.value) &  # 动量为负
            (self.allow_trading(metadata)),  # 熔断机制允许交易
            'enter_short'
        ] = 1

        return dataframe

    def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        # 基础退出信号（主要退出逻辑在custom_exit中实现）
        return dataframe

    def custom_exit(self, pair: str, trade: Trade, current_time: datetime,
                    current_rate: float, current_profit: float, **kwargs):
        # 分阶段止盈逻辑
        atr = self.get_current_atr(pair)  # 获取当前ATR值
        if trade.is_short:
            price_diff = trade.open_rate - current_rate  # 空头价格变动
        else:
            price_diff = current_rate - trade.open_rate  # 多头价格变动

        if price_diff >= self.sell_1.value * atr:  # 第一阶段止盈：价格变动达到2倍ATR
            return "take_profit_50%", 0.5  # 平仓50%
        elif price_diff >= self.sell_2.value * atr:  # 第二阶段止盈：价格变动达到3倍ATR
            return "take_profit_100%", 1.0  # 平仓100%
        return None

    def custom_stoploss(self, pair: str, trade: Trade, current_time: datetime,
                        current_rate: float, current_profit: float, **kwargs) -> float:
        # 动态止损逻辑
        atr = self.get_current_atr(pair)  # 获取当前ATR值
        
        if trade.is_short:
            return self.calculate_short_stoploss(trade, atr, current_rate)  # 空头止损计算
        else:
            return self.calculate_long_stoploss(trade, atr, current_rate)  # 多头止损计算

    def calculate_long_stoploss(self, trade, atr, current_rate):
        # 多头止损计算
        if len(trade.orders) <= 1:  # 初始止损
            return (trade.open_rate - self.initial_stoploss_multiplier.value * atr) / current_rate
        # 追踪止损
        return (trade.max_rate * self.trailing_stop_multiplier_long.value) / current_rate

    def calculate_short_stoploss(self, trade, atr, current_rate):
        # 空头止损计算
        if len(trade.orders) <= 1:  # 初始止损
            return (trade.open_rate + self.initial_stoploss_multiplier.value * atr) / current_rate
        # 追踪止损
        return (trade.min_rate * self.trailing_stop_multiplier_short.value) / current_rate

    def allow_trading(self, metadata):
        # 熔断机制实现
        pair = metadata['pair']  # 当前交易对
        now = datetime.utcnow()  # 当前时间
        
        # 每日重置检测
        if self.last_check_date != now.date():
            self.drawdown_blacklist = {}  # 清空熔断黑名单
            self.last_check_date = now.date()  # 更新最后检查日期
        
        if pair in self.drawdown_blacklist:  # 如果交易对在黑名单中，禁止交易
            return False
        
        # 计算当日回撤
        trades = Trade.get_trades_proxy(pair=pair)  # 获取交易对的交易记录
        today_trades = [t for t in trades if t.open_date.date() == now.date()]  # 筛选当日交易
        # if len(today_trades) >= 5:  # 每日最多交易3次
        #     return False
        
        if len(today_trades) >= 1:
            drawdown = self.calculate_max_drawdown(today_trades)  # 计算最大回撤
            if drawdown > 0.01:  # 如果回撤超过1%，加入黑名单
                self.drawdown_blacklist[pair] = now
                return False
        return True

    def calculate_max_drawdown(self, trades):
        # 计算最大回撤
        equity_curve = []  # 账户权益曲线
        cumulative = 0  # 累计收益
        for t in sorted(trades, key=lambda x: x.open_date):  # 按交易时间排序
            cumulative += t.calc_profit_ratio(t.close_rate)  # 累加收益
            equity_curve.append(cumulative)
        
        peak = -np.inf  # 初始化峰值
        max_dd = 0  # 初始化最大回撤
        for value in equity_curve:
            if value > peak:
                peak = value  # 更新峰值
            dd = peak - value  # 计算回撤
            if dd > max_dd:
                max_dd = dd  # 更新最大回撤
        return max_dd

    def get_current_atr(self, pair):
        # 获取当前ATR值
        dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe)
        return dataframe['atr'].iat[-1]  # 返回最后一个ATR值

    def version(self) -> str:
        # 返回策略版本号
        return "AdaptiveThreeLineBreak v2.1"