# source: https://raw.githubusercontent.com/Meiyerlove/lhjy/c2933ef7b5dac1bf9f3150b8f312c5164ec6232d/freqtrade_bot/user_data/strategies/BbRsiStrategy.py
import talib.abstract as ta
from pandas import DataFrame
from freqtrade.strategy import IStrategy, IntParameter, DecimalParameter


class Github_Meiyerlove_lhjy__BbRsiStrategy__20260429_064223(IStrategy):
    """
    布林带均值回归策略（4h）

    逻辑：
      买入：价格跌破布林带下轨 + RSI 超卖 + 成交量放大
            → 预期价格回归中轨

      卖出：价格触及布林带中轨（止盈）或上轨（激进止盈）

    适合震荡行情，与趋势策略互补。
    """

    INTERFACE_VERSION = 3
    timeframe = "1h"
    can_short = False

    # 均值回归目标：价格回归布林带中轨约有 2~3% 收益
    minimal_roi = {
        "0": 0.06,   # 立即止盈 6%
        "240": 0.03, # 持仓 240 分钟（1根4h K线）达到 3% 止盈
        "480": 0.01, # 持仓 2 根 K线达到 1% 止盈
    }

    stoploss = -0.06  # 止损 6%

    # 不用追踪止损，均值回归到目标就走
    trailing_stop = False

    startup_candle_count = 30

    # Hyperopt 参数范围
    buy_rsi = IntParameter(20, 40, default=30, space="buy")
    sell_rsi = IntParameter(55, 80, default=65, space="sell")
    bb_std = DecimalParameter(1.5, 3.0, default=2.0, decimals=1, space="buy")

    def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        # RSI
        dataframe["rsi"] = ta.RSI(dataframe, timeperiod=14)

        # 布林带：预计算所有 Hyperopt 可能用到的标准差档位（1.5~3.0，步长0.1）
        import numpy as np
        for std in np.arange(1.5, 3.1, 0.1):
            std = round(std, 1)
            upper, mid, lower = ta.BBANDS(
                dataframe["close"], timeperiod=20, nbdevup=std, nbdevdn=std
            )
            dataframe[f"bb_upper_{std}"] = upper
            dataframe[f"bb_mid_{std}"]   = mid
            dataframe[f"bb_lower_{std}"] = lower

        # 成交量均线（过滤低流动性）
        dataframe["volume_mean"] = dataframe["volume"].rolling(20).mean()

        # ATR（衡量波动率，避免在低波动期入场）
        dataframe["atr"] = ta.ATR(dataframe, timeperiod=14)

        return dataframe

    def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        std = self.bb_std.value
        dataframe.loc[
            (
                (dataframe["close"] < dataframe[f"bb_lower_{std}"]) &  # 价格跌破下轨
                (dataframe["rsi"] < self.buy_rsi.value) &              # RSI 超卖
                (dataframe["volume"] > dataframe["volume_mean"]) &     # 成交量放大，信号有效
                (dataframe["atr"] > 0)                                 # 确保有波动
            ),
            "enter_long",
        ] = 1
        return dataframe

    def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        std = self.bb_std.value
        dataframe.loc[
            (
                (dataframe["close"] > dataframe[f"bb_mid_{std}"]) |   # 价格回归中轨，止盈
                (dataframe["rsi"] > self.sell_rsi.value)              # RSI 超买
            ),
            "exit_long",
        ] = 1
        return dataframe
