# source: https://raw.githubusercontent.com/xielk/freqtrade-test/925e067f080172a4581bc71ae9ee64be247606f4/user_data/strategies/MAVolumeStrategy.py
# pragma pylint: disable=missing-docstring, invalid-name, pointless-string-statement
# flake8: noqa: F401
# isort: skip_file
# --- Do not remove这些 imports ---
import numpy as np
import pandas as pd
from datetime import datetime, timedelta, timezone
from pandas import DataFrame
from typing import Dict, Optional, Union, Tuple

from freqtrade.strategy import (
    IStrategy,
    Trade,
    Order,
    PairLocks,
    informative,  # @informative decorator
    # Hyperopt Parameters
    BooleanParameter,
    CategoricalParameter,
    DecimalParameter,
    IntParameter,
    RealParameter,
    # timeframe helpers
    timeframe_to_minutes,
    timeframe_to_next_date,
    timeframe_to_prev_date,
    timeframe_to_seconds,
    timeframe_to_msecs,
)

import talib.abstract as ta
import freqtrade.vendor.qtpylib.indicators as qtpylib


class Github_xielk_freqtrade_test__MAVolumeStrategy__20250910_084214(IStrategy):
    """
    均线金叉 + 成交量放大策略
    
    策略逻辑：
    买入：均线金叉 + 成交量放大
    卖出：均线死叉或破重要支撑
    止损：宽松止损 15%-20%，甚至不用严格止损
    
    特点：
    1. 基于移动平均线金叉死叉信号
    2. 结合成交量确认信号强度
    3. 宽松的止损设置，减少过早止损
    4. 重点关注趋势转换点
    """
    
    INTERFACE_VERSION = 3

    # 策略时间框架
    timeframe = "15m"

    # 是否可以做空
    can_short: bool = False

    # ROI设置 - 优化为更积极的利润目标
    minimal_roi = {
        "2880": 0.015, # 48小时后1.5%收益
        "1440": 0.02,  # 24小时后2%收益
        "720": 0.025,  # 12小时后2.5%收益
        "480": 0.03,   # 8小时后3%收益
        "360": 0.035,  # 6小时后3.5%收益
        "240": 0.04,   # 4小时后4%收益
        "180": 0.045,  # 3小时后4.5%收益
        "120": 0.05,   # 2小时后5%收益
        "60": 0.06,    # 1小时后6%收益
        "30": 0.07,    # 30分钟后7%收益
        "0": 0.08      # 立即8%收益
    }

    # 止损设置 - 动态止损，减少噪音
    stoploss = -0.08  # 放宽到8%，减少噪音止损

    # 追踪止损 - 更保守的设置
    trailing_stop = True
    trailing_stop_positive = 0.02  # 2%开始追踪
    trailing_stop_positive_offset = 0.025  # 2.5%偏移
    trailing_only_offset_is_reached = True

    # 只处理新K线
    process_only_new_candles = True

    # 策略参数 - 暂时禁用exit_signal，只用高胜率信号
    use_exit_signal = False  # 禁用exit_signal，避免低胜率信号
    exit_profit_only = False
    ignore_roi_if_entry_signal = False

    # 策略启动所需的K线数量
    startup_candle_count: int = 100

    # 仓位调整参数
    position_adjustment_enable = True
    max_entry_position_adjustment = 2  # 最多加仓2次

    # 订单类型
    order_types = {
        "entry": "limit",
        "exit": "limit",
        "stoploss": "market",
        "stoploss_on_exchange": False
    }

    # 订单时间
    order_time_in_force = {
        "entry": "GTC",
        "exit": "GTC"
    }

    # 策略参数 - 优化后的参数
    # 均线参数 - 调整为更敏感的参数
    fast_ma_period = IntParameter(5, 15, default=8, space="buy")
    slow_ma_period = IntParameter(15, 30, default=21, space="buy")
    
    # 成交量参数 - 降低阈值
    volume_ma_period = IntParameter(10, 20, default=15, space="buy")
    volume_threshold = DecimalParameter(1.1, 2.0, default=1.3, space="buy")
    
    # 支撑位参数
    support_ma_period = IntParameter(30, 60, default=50, space="sell")

    def informative_pairs(self):
        return []

    def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        """
        计算技术指标
        """
        # 移动平均线 - 用于金叉死叉判断
        dataframe["ema_fast"] = ta.EMA(dataframe, timeperiod=self.fast_ma_period.value)
        dataframe["ema_slow"] = ta.EMA(dataframe, timeperiod=self.slow_ma_period.value)
        
        # 长期均线 - 用于支撑位判断
        dataframe["sma_support"] = ta.SMA(dataframe, timeperiod=self.support_ma_period.value)
        
        # 成交量指标
        dataframe["volume_ma"] = dataframe["volume"].rolling(window=self.volume_ma_period.value).mean()
        dataframe["volume_ratio"] = dataframe["volume"] / dataframe["volume_ma"]
        
        # 均线金叉死叉信号
        dataframe["ma_golden_cross"] = (
            (dataframe["ema_fast"] > dataframe["ema_slow"]) &
            (dataframe["ema_fast"].shift(1) <= dataframe["ema_slow"].shift(1))
        )
        
        dataframe["ma_death_cross"] = (
            (dataframe["ema_fast"] < dataframe["ema_slow"]) &
            (dataframe["ema_fast"].shift(1) >= dataframe["ema_slow"].shift(1))
        )
        
        # 成交量放大信号
        dataframe["volume_surge"] = dataframe["volume_ratio"] >= self.volume_threshold.value
        
        # 价格位置指标
        dataframe["price_above_fast_ma"] = dataframe["close"] > dataframe["ema_fast"]
        dataframe["price_above_slow_ma"] = dataframe["close"] > dataframe["ema_slow"]
        dataframe["price_above_support"] = dataframe["close"] > dataframe["sma_support"]
        
        # 趋势强度
        dataframe["trend_strength"] = (
            (dataframe["ema_fast"] > dataframe["ema_slow"]).astype(int) +
            (dataframe["close"] > dataframe["ema_fast"]).astype(int) +
            (dataframe["close"] > dataframe["ema_slow"]).astype(int)
        )
        
        # 支撑位强度
        dataframe["support_strength"] = (
            (dataframe["close"] > dataframe["sma_support"]).astype(int) +
            (dataframe["low"] > dataframe["sma_support"]).astype(int)
        )
        
        # RSI - 辅助判断
        dataframe["rsi"] = ta.RSI(dataframe, timeperiod=14)
        
        # MACD - 趋势确认
        macd = ta.MACD(dataframe)
        dataframe["macd"] = macd["macd"]
        dataframe["macdsignal"] = macd["macdsignal"]
        dataframe["macdhist"] = macd["macdhist"]
        
        # 布林带 - 价格位置
        bollinger = qtpylib.bollinger_bands(qtpylib.typical_price(dataframe), window=20, stds=2)
        dataframe["bb_lowerband"] = bollinger["lower"]
        dataframe["bb_middleband"] = bollinger["mid"]
        dataframe["bb_upperband"] = bollinger["upper"]
        dataframe["bb_percent"] = (
            (dataframe["close"] - dataframe["bb_lowerband"]) /
            (dataframe["bb_upperband"] - dataframe["bb_lowerband"])
        )
        
        return dataframe

    def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        """
        入场条件：均线金叉 + 成交量放大
        """
        # 基础条件
        base_conditions = (
            (dataframe["volume"] > 0) &
            (dataframe["ema_fast"].notna()) &
            (dataframe["ema_slow"].notna()) &
            (dataframe["volume_ma"].notna())
        )
        
        # 主要入场条件：均线金叉 - 放宽条件
        ma_golden_cross = (
            dataframe["ma_golden_cross"] |  # 标准金叉
            (dataframe["ema_fast"] > dataframe["ema_slow"] * 0.999)  # 接近金叉
        )
        
        # 成交量确认 - 大幅放宽
        volume_confirmation = (
            dataframe["volume_surge"] |  # 成交量放大
            (dataframe["volume_ratio"] >= 1.1)  # 成交量稍微放大即可
        )
        
        # 价格位置确认 - 大幅放宽
        price_position = (
            dataframe["price_above_fast_ma"] |  # 价格在快线之上
            (dataframe["close"] > dataframe["ema_slow"] * 0.95) |  # 价格接近慢线
            (dataframe["close"] > dataframe["ema_fast"] * 0.98)  # 价格接近快线
        )
        
        # 趋势确认 - 大幅放宽
        trend_confirmation = (
            (dataframe["trend_strength"] >= 0) |  # 任何趋势条件
            (dataframe["ema_fast"] > dataframe["ema_fast"].shift(1)) |  # 快线上升
            (dataframe["ema_slow"] > dataframe["ema_slow"].shift(1)) |  # 慢线上升
            (dataframe["close"] > dataframe["close"].shift(1))  # 价格上升
        )
        
        # 辅助条件 - 大幅放宽
        additional_conditions = (
            (dataframe["rsi"] < 80) &  # RSI不过度超买
            (dataframe["bb_percent"] < 0.98)  # 不在布林带上轨附近
        )
        
        # 综合入场条件 - 大幅简化
        dataframe.loc[
            base_conditions &
            ma_golden_cross &
            volume_confirmation &
            price_position &
            trend_confirmation &
            additional_conditions,
            "enter_long"
        ] = 1

        return dataframe

    def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        """
        出场条件：均线死叉或破重要支撑
        """
        # 基础条件
        base_conditions = (
            (dataframe["volume"] > 0) &
            (dataframe["ema_fast"].notna()) &
            (dataframe["ema_slow"].notna())
        )
        
        # 主要出场条件1：均线死叉 - 非常严格的条件
        ma_death_cross = (
            dataframe["ma_death_cross"] &  # 标准死叉
            (dataframe["close"] < dataframe["ema_slow"] * 0.98) &  # 价格明显低于慢线2%
            (dataframe["ema_fast"] < dataframe["ema_fast"].shift(2))  # 快线持续下降
        )
        
        # 主要出场条件2：跌破重要支撑 - 非常严格的条件
        support_break = (
            (dataframe["close"] < dataframe["sma_support"] * 0.95) &  # 跌破支撑5%
            (dataframe["close"].shift(1) >= dataframe["sma_support"].shift(1) * 0.97) &  # 之前接近支撑
            (dataframe["ema_fast"] < dataframe["ema_fast"].shift(3)) &  # 快线持续下降
            (dataframe["volume_ratio"] < 0.8)  # 成交量萎缩
        )
        
        # 趋势转弱 - 非常严格的条件
        trend_weak = (
            (dataframe["trend_strength"] <= 0) &  # 趋势强度为0
            (dataframe["ema_fast"] < dataframe["ema_fast"].shift(3)) &  # 快线持续下降
            (dataframe["close"] < dataframe["close"].shift(2)) &  # 价格持续下降
            (dataframe["volume_ratio"] < 0.9)  # 成交量萎缩
        )
        
        # 价格跌破快线 - 非常严格的条件
        price_break_fast = (
            (dataframe["close"] < dataframe["ema_fast"] * 0.95) &  # 价格跌破快线5%
            (dataframe["close"].shift(1) >= dataframe["ema_fast"].shift(1) * 0.98) &  # 之前接近快线
            (dataframe["ema_fast"] < dataframe["ema_fast"].shift(2)) &  # 快线持续下降
            (dataframe["volume_ratio"] < 0.8)  # 成交量萎缩
        )
        
        # 综合出场条件 - 极其保守的出场，大幅减少小亏损
        dataframe.loc[
            base_conditions &
            (ma_death_cross | support_break | trend_weak | price_break_fast),
            "exit_long"
        ] = 1

        return dataframe

    def custom_stoploss(self, pair: str, trade: 'Trade', current_time: datetime,
                       current_rate: float, current_profit: float, **kwargs) -> float:
        """
        智能动态止损 - 减少噪音止损，增加确认条件
        """
        # 获取当前数据
        dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe)
        if len(dataframe) == 0:
            return self.stoploss
            
        last_candle = dataframe.iloc[-1].squeeze()
        
        # 获取关键指标
        trend_strength = last_candle.get("trend_strength", 0)
        support_strength = last_candle.get("support_strength", 0)
        volume_ratio = last_candle.get("volume_ratio", 1.0)
        ema_fast = last_candle.get("ema_fast", 0)
        ema_slow = last_candle.get("ema_slow", 0)
        current_price = last_candle.get("close", 0)
        
        # 计算持仓时间
        trade_duration = (current_time - trade.open_date_utc).total_seconds() / 3600  # 小时
        
        # 如果有利润，保护利润
        if current_profit > 0.03:  # 有3%以上利润
            return -0.015  # 1.5%止损（保护大部分利润）
        elif current_profit > 0.02:  # 有2%以上利润
            return -0.02  # 2%止损
        elif current_profit > 0.01:  # 有1%以上利润
            return -0.025  # 2.5%止损
        
        # 根据趋势强度和确认条件调整止损
        if (trend_strength >= 2 and 
            support_strength >= 1 and 
            current_price > ema_fast and 
            volume_ratio >= 0.8):  # 趋势强且确认
            return -0.06  # 6%止损（趋势强，放宽止损）
        elif (trend_strength >= 1 and 
              current_price > ema_slow and 
              volume_ratio >= 0.7):  # 趋势中等且确认
            return -0.07  # 7%止损（趋势中等）
        elif trade_duration > 24:  # 持仓超过24小时
            return -0.05  # 5%止损（长期持仓收紧）
        else:
            return self.stoploss  # -0.08 (8%止损)

    def custom_exit(self, pair: str, trade: 'Trade', current_time: datetime, current_rate: float,
                   current_profit: float, **kwargs) -> Optional[Union[str, bool]]:
        """
        自定义退出逻辑 - 确保3%利润目标
        """
        dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe)
        if len(dataframe) == 0:
            return None
            
        last_candle = dataframe.iloc[-1].squeeze()
        
        # 获取关键指标
        ema_fast = last_candle.get("ema_fast", 0)
        ema_slow = last_candle.get("ema_slow", 0)
        sma_support = last_candle.get("sma_support", 0)
        current_price = last_candle.get("close", 0)
        trend_strength = last_candle.get("trend_strength", 0)
        volume_ratio = last_candle.get("volume_ratio", 1.0)
        rsi = last_candle.get("rsi", 50)
        
        # 计算持仓时间
        trade_duration = (current_time - trade.open_date_utc).total_seconds() / 3600  # 小时
        
        # 如果达到3%利润目标，立即退出
        if current_profit >= 0.03:
            return "profit_target_3%"
        
        # 如果接近3%目标且有强趋势，等待更高利润
        if (current_profit >= 0.025 and trend_strength >= 2 and 
            current_price > ema_fast and current_price > ema_slow and
            volume_ratio >= 0.8):  # 增加成交量确认
            # 继续持有，等待更高利润
            pass
        
        # 如果达到2.5%且有利润，考虑退出
        elif current_profit >= 0.025:
            # 检查是否应该退出（增加确认条件）
            if (trend_strength <= 1 and 
                current_price < ema_fast * 0.995 and 
                volume_ratio < 0.8):  # 趋势弱+价格跌破+成交量萎缩
                return "time_near_target_exit"
        
        # 如果达到2%利润且趋势转弱，退出
        elif (current_profit >= 0.02 and trend_strength <= 0 and 
              current_price < ema_slow * 0.98):  # 增加价格确认
            return "trend_weak_exit"
        
        # 如果均线死叉且有利润，立即退出（增加确认）
        elif (last_candle.get("ma_death_cross", False) and 
              current_profit > 0.015 and 
              current_price < ema_slow * 0.99 and 
              volume_ratio < 0.9):  # 死叉+利润+价格确认+成交量确认
            return "ma_death_cross_exit"
        
        # 如果跌破支撑且有利润，立即退出（增加确认）
        elif (current_price < sma_support * 0.97 and  # 更严格的支撑跌破
              current_profit > 0.01 and 
              volume_ratio < 0.8 and  # 成交量萎缩确认
              trend_strength <= 0):  # 趋势转弱确认
            return "support_break_exit"
        
        # 如果价格跌破快线且有利润，退出（增加确认）
        elif (current_price < ema_fast * 0.97 and  # 更严格的价格跌破
              current_profit > 0.015 and 
              volume_ratio < 0.8 and  # 成交量萎缩确认
              rsi < 40):  # RSI超卖确认
            return "price_break_fast_exit"
        
        # 如果持仓时间过长且有利润，退出
        elif (current_profit > 0.01 and trade_duration > 48):  # 48小时
            return "time_profit_exit"
        
        # 如果持仓时间过长且接近目标，退出
        elif (current_profit > 0.02 and trade_duration > 24):  # 24小时
            return "time_near_target_exit"
        
        # 如果连续亏损且趋势转弱，强制退出
        elif (current_profit < -0.02 and 
              trend_strength <= 0 and 
              current_price < ema_slow * 0.95 and 
              trade_duration > 12):  # 12小时
            return "force_exit_loss"
        
        return None

    def custom_stake_amount(self, pair: str, current_time: datetime, 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) == 0:
            return proposed_stake
            
        last_candle = dataframe.iloc[-1].squeeze()
        
        # 获取信号强度指标
        trend_strength = last_candle.get("trend_strength", 0)
        volume_ratio = last_candle.get("volume_ratio", 1.0)
        support_strength = last_candle.get("support_strength", 0)
        
        # 计算信号强度分数
        signal_strength = (
            trend_strength * 0.4 +  # 趋势强度权重40%
            min(volume_ratio, 3.0) * 0.3 +  # 成交量权重30%
            support_strength * 0.3  # 支撑强度权重30%
        )
        
        # 根据信号强度调整仓位
        if signal_strength >= 2.5:  # 强信号
            return proposed_stake * 1.5  # 增加50%仓位
        elif signal_strength >= 2.0:  # 中等信号
            return proposed_stake * 1.2  # 增加20%仓位
        else:  # 弱信号
            return proposed_stake * 0.8  # 减少20%仓位

    def adjust_trade_position(self, trade: Trade, current_time: datetime,
                            current_rate: float, current_profit: float,
                            min_stake: Optional[float], max_stake: float,
                            current_entry_rate: float, current_exit_rate: float,
                            current_entry_profit: float, current_exit_profit: float,
                            **kwargs) -> Optional[float]:
        """
        加仓逻辑 - 对高胜率单进行加仓
        """
        dataframe, _ = self.dp.get_analyzed_dataframe(trade.pair, self.timeframe)
        if len(dataframe) == 0:
            return None
            
        last_candle = dataframe.iloc[-1].squeeze()
        
        # 获取当前指标
        trend_strength = last_candle.get("trend_strength", 0)
        volume_ratio = last_candle.get("volume_ratio", 1.0)
        ema_fast = last_candle.get("ema_fast", 0)
        ema_slow = last_candle.get("ema_slow", 0)
        current_price = last_candle.get("close", 0)
        
        # 加仓条件1：有利润且趋势强劲
        if (current_profit > 0.01 and  # 有1%以上利润
            trend_strength >= 2 and  # 趋势强劲
            current_price > ema_fast and  # 价格在快线之上
            current_price > ema_slow and  # 价格在慢线之上
            volume_ratio >= 1.2):  # 成交量放大
            
            # 计算加仓金额（不超过原仓位的50%）
            stake_amount = trade.stake_amount * 0.5
            return stake_amount
        
        # 加仓条件2：接近利润目标且趋势持续
        elif (current_profit > 0.02 and  # 有2%以上利润
              trend_strength >= 1 and  # 趋势持续
              current_price > ema_fast * 0.99 and  # 价格接近快线
              volume_ratio >= 1.1):  # 成交量稍微放大
              
            # 计算加仓金额（不超过原仓位的30%）
            stake_amount = trade.stake_amount * 0.3
            return stake_amount
        
        return None

    def leverage(self, pair: str, current_time: datetime, current_rate: float,
                 proposed_leverage: float, max_leverage: float, entry_tag: Optional[str], 
                 side: str, **kwargs) -> float:
        """
        杠杆设置 - 保守的杠杆使用
        """
        # 基于均线策略，使用较低的杠杆
        return min(proposed_leverage, 2.0)  # 最大2倍杠杆
