""" 移动平均线交叉趋势跟随策略 基于双移动平均线或多移动平均线的经典趋势跟随策略 """ from freqtrade.strategy import IStrategy, IntParameter, DecimalParameter from pandas import DataFrame import talib.abstract as ta from technical import qtpylib import numpy as np import logging logger = logging.getLogger(__name__) class MovingAverageCrossStrategy(IStrategy): """ 移动平均线交叉趋势跟随策略 策略逻辑: 1. 使用快速移动平均线和慢速移动平均线 2. 当快线上穿慢线时买入(金叉) 3. 当快线下穿慢线时卖出(死叉) 4. 结合成交量确认和趋势强度过滤 5. 使用ATR动态止损 """ INTERFACE_VERSION = 3 # 基础配置 minimal_roi = { "0": 0.15, # 15%收益立即止盈 "60": 0.08, # 1小时后8%收益 "120": 0.04, # 2小时后4%收益 "240": 0.02 # 4小时后2%收益 } stoploss = -0.08 # 8%止损 timeframe = '1h' # 1小时时间框架 # 策略控制 can_short = False startup_candle_count = 100 process_only_new_candles = True use_exit_signal = True use_custom_stoploss = False # 禁用 custom stoploss,避免 trailing stop 导致的大量亏损 # 可优化参数 # 移动平均线参数 fast_ma_period = IntParameter(5, 20, default=10, space="buy") slow_ma_period = IntParameter(20, 50, default=30, space="buy") # 移动平均线类型 ma_type_fast = IntParameter(0, 8, default=1, space="buy") # 0=SMA, 1=EMA, 2=WMA等 ma_type_slow = IntParameter(0, 8, default=1, space="buy") # 成交量确认参数 volume_factor = DecimalParameter(1.0, 3.0, default=1.5, space="buy") # 趋势强度过滤参数 adx_period = IntParameter(10, 20, default=14, space="buy") adx_threshold = IntParameter(15, 30, default=18, space="buy") # 止损参数 atr_period = IntParameter(10, 20, default=14, space="sell") atr_multiplier = DecimalParameter(1.5, 3.0, default=2.0, space="sell") # 趋势确认参数 trend_ema_period = IntParameter(50, 100, default=75, space="buy") def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: """ 计算技术指标 """ # 移动平均线 dataframe['ma_fast'] = ta.MA(dataframe, timeperiod=self.fast_ma_period.value, matype=self.ma_type_fast.value) dataframe['ma_slow'] = ta.MA(dataframe, timeperiod=self.slow_ma_period.value, matype=self.ma_type_slow.value) # 移动平均线距离 dataframe['ma_distance'] = (dataframe['ma_fast'] - dataframe['ma_slow']) / dataframe['ma_slow'] dataframe['ma_distance_abs'] = abs(dataframe['ma_distance']) # 移动平均线斜率 dataframe['ma_fast_slope'] = (dataframe['ma_fast'] - dataframe['ma_fast'].shift(3)) / dataframe['ma_fast'].shift(3) dataframe['ma_slow_slope'] = (dataframe['ma_slow'] - dataframe['ma_slow'].shift(5)) / dataframe['ma_slow'].shift(5) # 趋势确认EMA dataframe['trend_ema'] = ta.EMA(dataframe, timeperiod=self.trend_ema_period.value) # ADX趋势强度 dataframe['adx'] = ta.ADX(dataframe, timeperiod=self.adx_period.value) dataframe['di_plus'] = ta.PLUS_DI(dataframe, timeperiod=self.adx_period.value) dataframe['di_minus'] = ta.MINUS_DI(dataframe, timeperiod=self.adx_period.value) # ATR波动率 dataframe['atr'] = ta.ATR(dataframe, timeperiod=self.atr_period.value) dataframe['atr_percent'] = dataframe['atr'] / dataframe['close'] # 成交量指标 dataframe['volume_sma'] = dataframe['volume'].rolling(window=20).mean() dataframe['volume_ratio'] = dataframe['volume'] / dataframe['volume_sma'] # MACD确认 macd = ta.MACD(dataframe) dataframe['macd'] = macd['macd'] dataframe['macd_signal'] = macd['macdsignal'] dataframe['macd_hist'] = macd['macdhist'] # RSI过滤 dataframe['rsi'] = ta.RSI(dataframe, timeperiod=14) # 布林带 bollinger = qtpylib.bollinger_bands(dataframe['close'], window=20, stds=2) dataframe['bb_upper'] = bollinger['upper'] dataframe['bb_middle'] = bollinger['mid'] dataframe['bb_lower'] = bollinger['lower'] dataframe['bb_percent'] = (dataframe['close'] - dataframe['bb_lower']) / (dataframe['bb_upper'] - dataframe['bb_lower']) # 价格相对于移动平均线的位置 dataframe['price_above_fast_ma'] = dataframe['close'] > dataframe['ma_fast'] dataframe['price_above_slow_ma'] = dataframe['close'] > dataframe['ma_slow'] dataframe['price_above_trend_ema'] = dataframe['close'] > dataframe['trend_ema'] # 移动平均线交叉信号 dataframe['ma_cross_up'] = qtpylib.crossed_above(dataframe['ma_fast'], dataframe['ma_slow']) dataframe['ma_cross_down'] = qtpylib.crossed_below(dataframe['ma_fast'], dataframe['ma_slow']) return dataframe def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: """ 定义买入条件 - 趋势跟随策略 """ # 简化条件:金叉 + 趋势确认 + DI确认 dataframe.loc[ ( dataframe['ma_cross_up'] & # 金叉信号 (dataframe['close'] > dataframe['trend_ema']) & # 价格在长期趋势之上 (dataframe['di_plus'] > dataframe['di_minus']) & # DI确认上升趋势 (dataframe['rsi'] < 80) # RSI不能过度超买 ), 'enter_long' ] = 1 return dataframe def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: """ 定义卖出条件 """ conditions = [ # 主要信号:快线下穿慢线(死叉) dataframe['ma_cross_down'], # 趋势转弱信号 dataframe['adx'] < 20, # 移动平均线开始走平或下降 dataframe['ma_fast_slope'] < -0.001, # DI转向 dataframe['di_minus'] > dataframe['di_plus'], # MACD转弱 dataframe['macd'] < dataframe['macd_signal'], # RSI超买 dataframe['rsi'] > 75, # 价格跌破趋势线 qtpylib.crossed_below(dataframe['close'], dataframe['trend_ema']), ] dataframe.loc[ ( # 主要退出:死叉 + ADX弱化(需要2个条件同时满足) (conditions[0] & conditions[1]) | # 或者:趋势明确转向(快线下降 + MACD转弱) (conditions[2] & conditions[4]) | # 或者:明确跌破趋势线 + DI转向 (conditions[6] & conditions[3]) ), 'exit_long' ] = 1 return dataframe def custom_stoploss(self, pair: str, trade, current_time, current_rate: float, current_profit: float, **kwargs) -> float: """ 动态止损策略 """ dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe) last_candle = dataframe.iloc[-1].squeeze() # ATR动态止损 atr_distance = last_candle['atr'] * self.atr_multiplier.value atr_stop_distance = atr_distance / current_rate # 趋势跟随止损策略 if current_profit > 0.08: # 盈利超过8% # 紧跟快速移动平均线 ma_fast_distance = abs(current_rate - last_candle['ma_fast']) / current_rate return max(-ma_fast_distance * 1.5, -0.02) elif current_profit > 0.04: # 盈利超过4% # 中等紧密止损 return max(-atr_stop_distance * 0.7, -0.03) elif current_profit > 0.02: # 盈利超过2% return max(-atr_stop_distance * 0.8, -0.04) else: # 正常ATR止损,但更宽松适合趋势跟随 return max(-atr_stop_distance * 1.2, self.stoploss) def custom_exit(self, pair: str, trade, current_time, current_rate: float, current_profit: float, **kwargs) -> str: """ 自定义退出逻辑 """ dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe) last_candle = dataframe.iloc[-1].squeeze() # 趋势反转退出 if (last_candle['ma_fast'] < last_candle['ma_slow'] and last_candle['adx'] < 20): return "trend_reversal" # 动量衰减退出 if (last_candle['macd_hist'] < 0 and last_candle['rsi'] < 40 and current_profit > 0.02): return "momentum_weakness" # 时间止损(趋势策略允许更长持仓) trade_duration = (current_time - trade.open_date_utc).total_seconds() / 3600 if trade_duration > 72 and current_profit < 0.01: # 72小时无显著盈利 return "time_exit" return None def confirm_trade_entry(self, pair: str, order_type: str, amount: float, rate: float, time_in_force: str, current_time, entry_tag: str, side: str, **kwargs) -> bool: """ 交易确认 """ dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe) if len(dataframe) < 1: return False last_candle = dataframe.iloc[-1].squeeze() # 最终确认检查 # 确保趋势足够强劲 if last_candle['adx'] < self.adx_threshold.value: return False # 确保移动平均线排列正确 if not (last_candle['ma_fast'] > last_candle['ma_slow'] > last_candle['trend_ema']): return False # 确保不是假突破 if last_candle['ma_distance'] < 0.003: # 距离太小可能是假信号 return False # 波动率检查 if last_candle['atr_percent'] > 0.06: # 波动率过大 return False return True