# source: https://raw.githubusercontent.com/realm520/FreqBot/a24897ec69f16a9a06b6cd051050fe2ec5bc2583/strategies/MeanReversionProfitStrategy.py
# pragma pylint: disable=missing-docstring, invalid-name, pointless-string-statement
# flake8: noqa: F401
# isort: skip_file
import numpy as np
import pandas as pd
from datetime import datetime, timedelta
from pandas import DataFrame
from typing import Optional, Union
from collections import deque

from freqtrade.strategy import (
    IStrategy,
    Trade,
    Order,
    DecimalParameter,
    IntParameter,
    BooleanParameter,
)

import talib.abstract as ta
from technical import qtpylib


class Github_realm520_FreqBot__MeanReversionProfitStrategy__20250906_062156(IStrategy):
    """
    均值回归盈利策略 - 专门在震荡市场中获利
    
    核心思想：
    1. 识别震荡市场环境
    2. 在价格偏离均值时进场
    3. 快速止盈，严格止损
    4. 高频交易，积少成多
    """
    
    INTERFACE_VERSION = 3
    can_short: bool = False
    
    # 激进盈利设置
    minimal_roi = {
        "0": 0.03,   # 3%止盈
        "30": 0.02,  # 30分钟后2%止盈  
        "60": 0.01,  # 1小时后1%止盈
        "120": 0    # 2小时后平仓
    }
    stoploss = -0.025  # 2.5%止损
    trailing_stop = False
    timeframe = '15m'
    use_custom_stoploss = False  # 使用固定止损
    
    # 策略参数
    bb_period = IntParameter(15, 25, default=20, space="buy", optimize=True)
    bb_std = DecimalParameter(1.8, 2.5, default=2.0, space="buy", optimize=True)
    rsi_period = IntParameter(10, 21, default=14, space="buy", optimize=True)
    rsi_oversold = IntParameter(25, 35, default=30, space="buy", optimize=True)
    rsi_overbought = IntParameter(65, 80, default=70, space="buy", optimize=True)
    
    # 均值回归参数
    bb_entry_threshold = DecimalParameter(0.05, 0.25, default=0.1, space="buy", optimize=True)  # 进入布林带下轨的程度
    volume_threshold = DecimalParameter(0.8, 1.5, default=1.0, space="buy", optimize=True)  # 成交量阈值
    
    # 震荡市场识别参数
    volatility_period = IntParameter(20, 50, default=30, space="buy", optimize=True)
    trend_strength_threshold = DecimalParameter(15, 30, default=20, space="buy", optimize=True)  # ADX阈值
    
    # 快速出场参数
    quick_profit_target = DecimalParameter(0.008, 0.025, default=0.015, space="sell", optimize=True)  # 1.5%快速止盈
    volume_exit_threshold = DecimalParameter(0.3, 0.8, default=0.5, space="sell", optimize=True)  # 成交量萎缩出场
    
    startup_candle_count: int = 200
    
    def __init__(self, config: dict):
        super().__init__(config)
        
    def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        """
        技术指标计算
        """
        # 布林带 - 均值回归核心指标
        bollinger = qtpylib.bollinger_bands(dataframe['close'], window=self.bb_period.value, stds=self.bb_std.value)
        dataframe['bb_lower'] = bollinger['lower']
        dataframe['bb_middle'] = bollinger['mid']
        dataframe['bb_upper'] = bollinger['upper']
        dataframe['bb_percent'] = (dataframe['close'] - dataframe['bb_lower']) / (dataframe['bb_upper'] - dataframe['bb_lower'])
        dataframe['bb_width'] = (dataframe['bb_upper'] - dataframe['bb_lower']) / dataframe['bb_middle']
        
        # RSI - 超买超卖确认
        dataframe['rsi'] = ta.RSI(dataframe, timeperiod=self.rsi_period.value)
        
        # ADX - 趋势强度 (震荡市场识别)
        dataframe['adx'] = ta.ADX(dataframe, timeperiod=14)
        dataframe['is_ranging'] = dataframe['adx'] < self.trend_strength_threshold.value
        
        # EMA - 趋势过滤
        dataframe['ema_20'] = ta.EMA(dataframe, timeperiod=20)
        dataframe['ema_50'] = ta.EMA(dataframe, timeperiod=50)
        
        # 成交量指标
        dataframe['volume_sma'] = dataframe['volume'].rolling(20).mean()
        dataframe['volume_ratio'] = dataframe['volume'] / dataframe['volume_sma']
        
        # 波动率计算
        dataframe['price_change'] = dataframe['close'].pct_change()
        dataframe['volatility'] = dataframe['price_change'].rolling(self.volatility_period.value).std()
        
        # 均值回归信号强度
        dataframe['mean_reversion_strength'] = (
            (dataframe['bb_percent'] < self.bb_entry_threshold.value).astype(int) +
            (dataframe['rsi'] < self.rsi_oversold.value).astype(int) +
            (dataframe['close'] < dataframe['ema_20'] * 0.99).astype(int) +
            (dataframe['volume_ratio'] > self.volume_threshold.value).astype(int) +
            (dataframe['is_ranging']).astype(int)
        )
        
        return dataframe
    
    def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        """
        入场条件 - 均值回归信号
        """
        # 强均值回归信号：多个条件同时满足
        dataframe['enter_long'] = (
            # 核心条件：价格远离布林带下轨
            (dataframe['bb_percent'] < self.bb_entry_threshold.value) &
            
            # RSI超卖确认
            (dataframe['rsi'] < self.rsi_oversold.value) &
            
            # 震荡市场确认 (非强趋势)
            (dataframe['is_ranging'] == True) &
            
            # 成交量确认
            (dataframe['volume_ratio'] > self.volume_threshold.value) &
            
            # 价格低于短期均线 (进一步确认超卖)
            (dataframe['close'] < dataframe['ema_20']) &
            
            # 布林带宽度适中 (避免极端波动)
            (dataframe['bb_width'] > 0.02) & (dataframe['bb_width'] < 0.08)
        )
        
        return dataframe
    
    def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        """
        出场条件 - 快速止盈
        """
        dataframe['exit_long'] = (
            # 快速反转信号
            (dataframe['rsi'] > self.rsi_overbought.value) |
            
            # 回到布林带中轨以上
            (dataframe['close'] > dataframe['bb_middle'] * 1.01) |
            
            # 成交量萎缩
            (dataframe['volume_ratio'] < self.volume_exit_threshold.value) |
            
            # 趋势转强 (不再是震荡市场)
            (dataframe['adx'] > self.trend_strength_threshold.value * 1.5)
        )
        
        return dataframe


class Github_realm520_FreqBot__MeanReversionProfitStrategy__20250906_062156(IStrategy):
    """
    趋势跟踪盈利策略 - 专门在趋势市场中获利
    
    核心思想：
    1. 识别趋势市场环境
    2. 在趋势确认后进场
    3. 金字塔加仓，长期持有
    4. 追踪止盈，保护利润
    """
    
    INTERFACE_VERSION = 3
    can_short: bool = False
    
    # 趋势跟踪设置
    minimal_roi = {"0": 10}  # 不使用固定ROI
    stoploss = -0.08  # 8%止损
    trailing_stop = True
    trailing_stop_positive = 0.02  # 2%启动追踪
    trailing_stop_positive_offset = 0.03  # 3%追踪距离
    timeframe = '15m'
    use_custom_stoploss = True
    
    # 趋势识别参数
    ema_fast = IntParameter(12, 21, default=15, space="buy", optimize=True)
    ema_slow = IntParameter(26, 55, default=35, space="buy", optimize=True)
    adx_period = IntParameter(10, 21, default=14, space="buy", optimize=True)
    adx_threshold = IntParameter(25, 40, default=30, space="buy", optimize=True)
    
    # 趋势确认参数
    trend_confirmation_period = IntParameter(3, 8, default=5, space="buy", optimize=True)
    momentum_threshold = DecimalParameter(0.003, 0.015, default=0.008, space="buy", optimize=True)
    
    # 加仓参数
    enable_pyramid = BooleanParameter(default=True, space="buy", optimize=True)
    pyramid_distance = DecimalParameter(0.02, 0.05, default=0.03, space="buy", optimize=True)
    max_pyramid_levels = IntParameter(2, 4, default=3, space="buy", optimize=True)
    
    # 利润目标
    profit_target_1 = DecimalParameter(0.05, 0.10, default=0.08, space="sell", optimize=True)
    profit_target_2 = DecimalParameter(0.15, 0.25, default=0.20, space="sell", optimize=True)
    profit_target_3 = DecimalParameter(0.30, 0.50, default=0.40, space="sell", optimize=True)
    
    startup_candle_count: int = 200
    
    def __init__(self, config: dict):
        super().__init__(config)
        self.pyramid_levels = {}
        
    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'] = dataframe['ema_fast'] > dataframe['ema_slow']
        
        # ADX趋势强度
        dataframe['adx'] = ta.ADX(dataframe, timeperiod=self.adx_period.value)
        dataframe['strong_trend'] = dataframe['adx'] > self.adx_threshold.value
        
        # MACD动量确认
        macd = ta.MACD(dataframe)
        dataframe['macd'] = macd['macd']
        dataframe['macdsignal'] = macd['macdsignal']
        dataframe['macd_positive'] = dataframe['macd'] > dataframe['macdsignal']
        
        # 动量计算
        dataframe['momentum'] = dataframe['close'].pct_change(self.trend_confirmation_period.value)
        dataframe['strong_momentum'] = dataframe['momentum'] > self.momentum_threshold.value
        
        # 成交量确认
        dataframe['volume_sma'] = dataframe['volume'].rolling(20).mean()
        dataframe['volume_surge'] = dataframe['volume'] > dataframe['volume_sma'] * 1.2
        
        # 综合趋势信号
        dataframe['trend_score'] = (
            dataframe['ema_trend'].astype(int) +
            dataframe['strong_trend'].astype(int) +
            dataframe['macd_positive'].astype(int) +
            dataframe['strong_momentum'].astype(int) +
            dataframe['volume_surge'].astype(int)
        )
        
        return dataframe
    
    def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        """
        趋势跟踪入场
        """
        dataframe['enter_long'] = (
            # 强趋势信号：至少4个确认
            (dataframe['trend_score'] >= 4) &
            
            # EMA排列确认
            (dataframe['ema_fast'] > dataframe['ema_slow']) &
            
            # 价格在EMA之上
            (dataframe['close'] > dataframe['ema_fast']) &
            
            # ADX确认强趋势
            (dataframe['strong_trend'] == True) &
            
            # 动量确认
            (dataframe['strong_momentum'] == True)
        )
        
        return dataframe
    
    def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        """
        趋势跟踪出场
        """
        dataframe['exit_long'] = (
            # 趋势转弱
            (dataframe['ema_fast'] < dataframe['ema_slow']) |
            
            # ADX下降
            (dataframe['adx'] < self.adx_threshold.value * 0.8) |
            
            # MACD转负
            (dataframe['macd'] < dataframe['macdsignal']) |
            
            # 动量转负
            (dataframe['momentum'] < -self.momentum_threshold.value)
        )
        
        return dataframe
    
    def custom_stoploss(self, pair: str, trade: 'Trade', current_time: datetime,
                       current_rate: float, current_profit: float, **kwargs) -> float:
        """
        动态追踪止损
        """
        # 分阶段追踪止损
        if current_profit >= self.profit_target_3.value:
            return current_profit - 0.15  # 保护85%利润
        elif current_profit >= self.profit_target_2.value:
            return current_profit - 0.08  # 保护92%利润
        elif current_profit >= self.profit_target_1.value:
            return current_profit - 0.04  # 保护96%利润
        elif current_profit >= 0.03:
            return current_profit - 0.02  # 保护部分利润
        else:
            return self.stoploss
    
    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] = None, side: str = 'long', **kwargs) -> bool:
        """
        趋势确认入场
        """
        dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe)
        if dataframe.empty:
            return False
        
        latest = dataframe.iloc[-1]
        
        # 确认趋势信号强度
        if latest['trend_score'] < 3:
            return False
        
        return True