diff --git a/freqtrade/templates/freqaiprimer.py b/freqtrade/templates/freqaiprimer.py index 7d4eaffe..2358ff22 100644 --- a/freqtrade/templates/freqaiprimer.py +++ b/freqtrade/templates/freqaiprimer.py @@ -397,49 +397,18 @@ class FreqaiPrimer(IStrategy): def adjust_trade_position(self, trade: Trade, current_time: datetime, current_rate: float, current_profit: float, min_roi: float, max_profit: float, **kwargs): + pair = trade.pair hold_time = (current_time - trade.open_date_utc).total_seconds() / 60 - # 确定市场状态 - labels_mean = self.pair_stats.get(trade.pair, {}).get("labels_mean", {"&-future_adx": 25}) - labels_std = self.pair_stats.get(trade.pair, {}).get("labels_std", {"&-future_adx": 1}) - k_adx = 0.5 if labels_std["&-future_adx"] <= 9 and labels_std["&-future_adx"] > 5 else (0.3 if labels_std["&-future_adx"] > 9 else 0.7) - future_adx_threshold = labels_mean["&-future_adx"] + k_adx * labels_std["&-future_adx"] - future_adx_threshold = max(future_adx_threshold, 20) - future_adx_threshold = min(future_adx_threshold, 35) - - market_state = "trending" if ((trade.dataframe["adx"].iloc[-1] > 25) or (trade.dataframe["&-future_adx"].iloc[-1] > future_adx_threshold)) and \ - (trade.dataframe["ema200_slope"].iloc[-1] > 0) and \ - (trade.dataframe["atr"].iloc[-1] > trade.dataframe["atr_median"].iloc[-1]) else "ranging" - - # 最小持仓时间 - if hold_time < 15: - logger.info(f"[{trade.pair}] 持仓时间 {hold_time:.1f} 分钟,未达到最小持仓时间 15 分钟,暂不退出") + # 获取当前币对的 dataframe + dataframe = self.dp.get_pair_dataframe(pair=pair, timeframe=self.timeframe) + if dataframe.empty: + logger.warning(f"[{pair}] 无法获取 dataframe,跳过调整持仓") return None - # Trailing Stop - profit_ratio = (current_rate - trade.open_rate) / trade.open_rate - if profit_ratio >= self.trailing_stop_start and not self.trailing_stop_enabled: - self.trailing_stop_enabled = True - trade.adjust_max_rate(current_rate) - logger.info(f"[{trade.pair}] 价格上涨超过 {self.trailing_stop_start*100:.1f}%,启动 Trailing Stop") + # 确保 dataframe 已包含所需指标 + dataframe = self.populate_indicators(dataframe, {"pair": pair}) - if self.trailing_stop_enabled: - max_rate = trade.max_rate - trailing_stop_price = max_rate * (1 - self.trailing_stop_distance) - if current_rate < trailing_stop_price: - logger.info(f"[{trade.pair}] 价格回落至 Trailing Stop 点 {trailing_stop_price:.6f},触发卖出") - return -1 - trade.adjust_max_rate(current_rate) - - # 根据市场状态调整持仓时间限制 - max_hold_time = 60 if market_state == "trending" else 30 - if hold_time > max_hold_time: - logger.info(f"[{trade.pair}] 持仓时间超过{max_hold_time}分钟(市场状态:{market_state}),强制平仓") - return -1 - return None - - def confirm_trade_entry(self, pair: str, order_type: str, amount: float, rate: float, - time_in_force: str, current_time: datetime, **kwargs) -> bool: # 确定市场状态 labels_mean = self.pair_stats.get(pair, {}).get("labels_mean", {"&-future_adx": 25}) labels_std = self.pair_stats.get(pair, {}).get("labels_std", {"&-future_adx": 1}) @@ -448,9 +417,59 @@ class FreqaiPrimer(IStrategy): future_adx_threshold = max(future_adx_threshold, 20) future_adx_threshold = min(future_adx_threshold, 35) - market_state = "trending" if ((self.dataframe["adx"].iloc[-1] > 25) or (self.dataframe["&-future_adx"].iloc[-1] > future_adx_threshold)) and \ - (self.dataframe["ema200_slope"].iloc[-1] > 0) and \ - (self.dataframe["atr"].iloc[-1] > self.dataframe["atr_median"].iloc[-1]) else "ranging" + market_state = "trending" if ((dataframe["adx"].iloc[-1] > 25) or (dataframe["&-future_adx"].iloc[-1] > future_adx_threshold)) and \ + (dataframe["ema200_slope"].iloc[-1] > 0) and \ + (dataframe["atr"].iloc[-1] > dataframe["atr_median"].iloc[-1]) else "ranging" + + # 最小持仓时间 + if hold_time < 15: + logger.info(f"[{pair}] 持仓时间 {hold_time:.1f} 分钟,未达到最小持仓时间 15 分钟,暂不退出") + return None + + # Trailing Stop + profit_ratio = (current_rate - trade.open_rate) / trade.open_rate + if profit_ratio >= self.trailing_stop_start and not self.trailing_stop_enabled: + self.trailing_stop_enabled = True + trade.adjust_max_rate(current_rate) + logger.info(f"[{pair}] 价格上涨超过 {self.trailing_stop_start*100:.1f}%,启动 Trailing Stop") + + if self.trailing_stop_enabled: + max_rate = trade.max_rate + trailing_stop_price = max_rate * (1 - self.trailing_stop_distance) + if current_rate < trailing_stop_price: + logger.info(f"[{pair}] 价格回落至 Trailing Stop 点 {trailing_stop_price:.6f},触发卖出") + return -1 + trade.adjust_max_rate(current_rate) + + # 根据市场状态调整持仓时间限制 + max_hold_time = 60 if market_state == "trending" else 30 + if hold_time > max_hold_time: + logger.info(f"[{pair}] 持仓时间超过{max_hold_time}分钟(市场状态:{market_state}),强制平仓") + return -1 + return None + + def confirm_trade_entry(self, pair: str, order_type: str, amount: float, rate: float, + time_in_force: str, current_time: datetime, **kwargs) -> bool: + # 获取当前币对的 dataframe + dataframe = self.dp.get_pair_dataframe(pair=pair, timeframe=self.timeframe) + if dataframe.empty: + logger.warning(f"[{pair}] 无法获取 dataframe,跳过交易确认") + return False + + # 确保 dataframe 已包含所需指标 + dataframe = self.populate_indicators(dataframe, {"pair": pair}) + + # 确定市场状态 + labels_mean = self.pair_stats.get(pair, {}).get("labels_mean", {"&-future_adx": 25}) + labels_std = self.pair_stats.get(pair, {}).get("labels_std", {"&-future_adx": 1}) + k_adx = 0.5 if labels_std["&-future_adx"] <= 9 and labels_std["&-future_adx"] > 5 else (0.3 if labels_std["&-future_adx"] > 9 else 0.7) + future_adx_threshold = labels_mean["&-future_adx"] + k_adx * labels_std["&-future_adx"] + future_adx_threshold = max(future_adx_threshold, 20) + future_adx_threshold = min(future_adx_threshold, 35) + + market_state = "trending" if ((dataframe["adx"].iloc[-1] > 25) or (dataframe["&-future_adx"].iloc[-1] > future_adx_threshold)) and \ + (dataframe["ema200_slope"].iloc[-1] > 0) and \ + (dataframe["atr"].iloc[-1] > dataframe["atr_median"].iloc[-1]) else "ranging" # 根据市场状态调整冷却期 cooldown_minutes = 3 if market_state == "trending" else 5 @@ -465,11 +484,21 @@ class FreqaiPrimer(IStrategy): return False self.trailing_stop_enabled = False + logger.info(f"[{pair}] 交易确认通过,市场状态:{market_state}") return True def confirm_trade_exit(self, pair: str, trade: Trade, order_type: str, amount: float, rate: float, time_in_force: str, exit_reason: str, current_time: datetime, **kwargs) -> bool: + # 获取当前币对的 dataframe + dataframe = self.dp.get_pair_dataframe(pair=pair, timeframe=self.timeframe) + if dataframe.empty: + logger.warning(f"[{pair}] 无法获取 dataframe,默认允许退出") + return True + + # 确保 dataframe 已包含所需指标 + dataframe = self.populate_indicators(dataframe, {"pair": pair}) + # 确定市场状态 labels_mean = self.pair_stats.get(pair, {}).get("labels_mean", {"&-future_adx": 25}) labels_std = self.pair_stats.get(pair, {}).get("labels_std", {"&-future_adx": 1}) @@ -478,9 +507,9 @@ class FreqaiPrimer(IStrategy): future_adx_threshold = max(future_adx_threshold, 20) future_adx_threshold = min(future_adx_threshold, 35) - market_state = "trending" if ((trade.dataframe["adx"].iloc[-1] > 25) or (trade.dataframe["&-future_adx"].iloc[-1] > future_adx_threshold)) and \ - (trade.dataframe["ema200_slope"].iloc[-1] > 0) and \ - (trade.dataframe["atr"].iloc[-1] > trade.dataframe["atr_median"].iloc[-1]) else "ranging" + market_state = "trending" if ((dataframe["adx"].iloc[-1] > 25) or (dataframe["&-future_adx"].iloc[-1] > future_adx_threshold)) and \ + (dataframe["ema200_slope"].iloc[-1] > 0) and \ + (dataframe["atr"].iloc[-1] > dataframe["atr_median"].iloc[-1]) else "ranging" # 调整卖出价格 adjusted_rate = rate * (1 + 0.0025) @@ -492,7 +521,7 @@ class FreqaiPrimer(IStrategy): drop_percentage = (max_rate - rate) / max_rate * 100 additional_info = f"最大价格:{max_rate:.6f},回落:{drop_percentage:.2f}%" elif market_state == "ranging" and "rsi" in exit_reason.lower(): - price_value_divergence = trade.dataframe["&-price_value_divergence"].iloc[-1] + price_value_divergence = dataframe["&-price_value_divergence"].iloc[-1] additional_info = f"&-price_value_divergence:{price_value_divergence:.6f}" logger.info(f"[{pair}] 退出交易,原因:{exit_reason}, 市场状态:{market_state}, "