""" 临时占位策略 - 仅用于验证 Freqtrade 环境是否可用 后续会替换为 AICommitteeStrategy(接入 5 人 AI 委员会) 交易逻辑: - RSI < 30 + MACD 金叉 → 做多 - RSI > 70 + MACD 死叉 → 做空 - 硬止损 2.5%(即杠杆前敞口的 2.5%,相当于本金的 5%) - ROI 目标 1.5%(杠杆前 = 本金 30%) - 20x 杠杆,单笔保证金 50 USDT """ from datetime import datetime from typing import Optional import pandas as pd import talib.abstract as ta from freqtrade.strategy import ( IStrategy, informative, merge_informative_pair, ) class SampleStrategy(IStrategy): INTERFACE_VERSION = 3 # ============ 基础配置 ============ timeframe = "15m" can_short = True # 支持做空 process_only_new_candles = True # ============ 风控(硬规则) ============ # 止盈表:持有时间越长要求 ROI 越低 minimal_roi = { "0": 0.015, # 开单立即:1.5% 止盈(保证金层面即 30%) "30": 0.01, # 30 分钟后:1% "60": 0.005, # 60 分钟后:0.5% "120": 0, # 2 小时后:保本出 } # 硬止损:2.5% 敞口损失 = 50% 保证金损失 = 5% 本金损失 stoploss = -0.025 # 追踪止损 trailing_stop = True trailing_stop_positive = 0.005 # 达到 0.5% 盈利时启动追踪 trailing_stop_positive_offset = 0.012 # 追踪距离 1.2% trailing_only_offset_is_reached = True # 订单类型 order_types = { "entry": "limit", "exit": "limit", "stoploss": "market", "stoploss_on_exchange": True, # 止损挂在交易所侧,避免本地进程挂掉 "stoploss_on_exchange_interval": 60, } # 保护机制 protections = [ { "method": "StoplossGuard", "lookback_period_candles": 24, # 过去 24 根 15m K 线(6 小时) "trade_limit": 2, # 2 次止损 "stop_duration_candles": 12, # 停 3 小时 "only_per_pair": False, }, { "method": "CooldownPeriod", "stop_duration_candles": 4, # 每笔交易后冷却 1 小时 }, { "method": "MaxDrawdown", "lookback_period_candles": 48, "trade_limit": 10, "stop_duration_candles": 48, "max_allowed_drawdown": 0.10, # 回撤 10% 停机 }, ] # 启动时间(给数据加载留时间) startup_candle_count: int = 100 # 杠杆配置(交易所侧) 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 20.0 # 强制 20x # ============ 指标计算 ============ def populate_indicators(self, dataframe: pd.DataFrame, metadata: dict) -> pd.DataFrame: # MACD macd = ta.MACD(dataframe, fastperiod=12, slowperiod=26, signalperiod=9) dataframe["macd"] = macd["macd"] dataframe["macdsignal"] = macd["macdsignal"] dataframe["macdhist"] = macd["macdhist"] # RSI dataframe["rsi"] = ta.RSI(dataframe, timeperiod=14) # EMA dataframe["ema9"] = ta.EMA(dataframe, timeperiod=9) dataframe["ema21"] = ta.EMA(dataframe, timeperiod=21) dataframe["ema50"] = ta.EMA(dataframe, timeperiod=50) # ATR dataframe["atr"] = ta.ATR(dataframe, timeperiod=14) return dataframe # ============ 入场信号 ============ def populate_entry_trend(self, dataframe: pd.DataFrame, metadata: dict) -> pd.DataFrame: # 做多条件:RSI 从超卖反弹 + MACD 金叉 + 价格在 EMA50 上方 dataframe.loc[ ( (dataframe["rsi"] > 30) & (dataframe["rsi"].shift(1) <= 30) # 刚从超卖反弹 & (dataframe["macd"] > dataframe["macdsignal"]) & (dataframe["macd"].shift(1) <= dataframe["macdsignal"].shift(1)) # 金叉 & (dataframe["close"] > dataframe["ema50"]) # 趋势向上 & (dataframe["volume"] > 0) ), "enter_long", ] = 1 # 做空条件:RSI 从超买回落 + MACD 死叉 + 价格在 EMA50 下方 dataframe.loc[ ( (dataframe["rsi"] < 70) & (dataframe["rsi"].shift(1) >= 70) # 刚从超买回落 & (dataframe["macd"] < dataframe["macdsignal"]) & (dataframe["macd"].shift(1) >= dataframe["macdsignal"].shift(1)) # 死叉 & (dataframe["close"] < dataframe["ema50"]) # 趋势向下 & (dataframe["volume"] > 0) ), "enter_short", ] = 1 return dataframe # ============ 出场信号 ============ def populate_exit_trend(self, dataframe: pd.DataFrame, metadata: dict) -> pd.DataFrame: # 做多平仓:RSI 进入超买区间或 MACD 死叉 dataframe.loc[ (dataframe["rsi"] > 75) | (dataframe["macd"] < dataframe["macdsignal"]), "exit_long", ] = 1 # 做空平仓:RSI 进入超卖区间或 MACD 金叉 dataframe.loc[ (dataframe["rsi"] < 25) | (dataframe["macd"] > dataframe["macdsignal"]), "exit_short", ] = 1 return dataframe