# 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 MAVolumeStrategy(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倍杠杆