diff --git a/freqtrade/templates/freqaiprimer.py b/freqtrade/templates/freqaiprimer.py index dcbfce99..4f07dd94 100644 --- a/freqtrade/templates/freqaiprimer.py +++ b/freqtrade/templates/freqaiprimer.py @@ -1352,101 +1352,58 @@ class FreqaiPrimer(IStrategy): self.strategy_log(f"[{pair}] 非多头交易,跳过入场检查") return False - # 获取虚机名字 - import os - virtual_host_name = os.environ.get('VIRTUAL_HOST_NAME') - self.strategy_log(f"[{pair}] 当前虚机名字: {virtual_host_name}") + # 记录所有拒绝条件的列表 + rejected_conditions = [] - # 只有当虚机名字是kiko时,才执行从外部获取数据和RMSE限制的逻辑 - if virtual_host_name == 'kiko': - self.strategy_log(f"[{pair}] 虚机名字是kiko,执行外部数据获取和RMSE限制逻辑") - import requests - import json - - try: - # 构建API请求URL - api_url = f"http://pairlist.xl.home/api/mlpredictionList?pairName={pair}&sortBy=timestamp" - self.strategy_log(f"[{pair}] 请求API数据: {api_url}") - - # 发送API请求 - response = requests.get(api_url, timeout=10) - response.raise_for_status() # 检查请求是否成功 - - # 解析API响应 - data = response.json() - if not data.get('success'): - self.strategy_log(f"[{pair}] API请求失败: {data.get('message', '未知错误')}") - return False - - predictions = data.get('predictions', []) - if not predictions: - self.strategy_log(f"[{pair}] API返回空预测数据") - return False - - # 只取第一条数据 - first_prediction = predictions[0] - self.strategy_log(f"[{pair}] 从API获取到预测数据: 时间戳={first_prediction.get('timestamp')}") - - # 检查时间戳是否不超过现在时间5分钟 - prediction_timestamp = first_prediction.get('timestamp') - if not prediction_timestamp: - self.strategy_log(f"[{pair}] 预测数据缺少时间戳") - return False - - import time - current_timestamp = int(time.time()) - time_diff_seconds = current_timestamp - prediction_timestamp - time_diff_minutes = time_diff_seconds / 60 - - if time_diff_minutes > 5: - self.strategy_log(f"[{pair}] 预测数据过期: 时间戳 {prediction_timestamp} 距离现在 {time_diff_minutes:.1f} 分钟,超过5分钟限制") - return False - - # 检查RMSE是否小于0.6 - rmse = first_prediction.get('rmse') - if rmse is None: - self.strategy_log(f"[{pair}] 预测数据缺少RMSE值") - return False - - if rmse >= 0.6: - self.strategy_log(f"[{pair}] RMSE值过大: {rmse} >= 0.6,拒绝入场") - return False - - # 检查未来波动率,高波动禁止入场 - values = first_prediction.get('values', {}) - future_volatility = values.get('&s-future_volatility') - - if future_volatility is None: - self.strategy_log(f"[{pair}] 预测数据缺少未来波动率值") - return False - - # 定义高波动率阈值(可根据需要调整) - high_volatility_threshold = 0.7 - if future_volatility > high_volatility_threshold: - self.strategy_log(f"[{pair}] 高波动禁止入场: 未来波动率 {future_volatility} > 阈值 {high_volatility_threshold}") - return False - - # 所有条件通过,允许入场 - self.strategy_log(f"[{pair}] 所有条件通过,允许入场") - self.strategy_log(f"[{pair}] 预测数据详情: ") - self.strategy_log(f" - 时间戳: {prediction_timestamp}") - self.strategy_log(f" - RMSE: {rmse}") - self.strategy_log(f" - 未来波动率: {future_volatility}") - self.strategy_log(f" - 入场信号: {values.get('&s-entry_signal')}") - self.strategy_log(f" - 出场信号: {values.get('&s-exit_signal')}") - - return True - - except requests.RequestException as e: - self.strategy_log(f"[{pair}] API请求失败: {str(e)}") - return False - except Exception as e: - self.strategy_log(f"[{pair}] 检查过程中发生错误: {str(e)}") - return False - else: - # 虚机名字不是kiko,跳过外部数据获取和RMSE限制,直接允许入场 - self.strategy_log(f"[{pair}] 虚机名字不是kiko,跳过外部数据获取和RMSE限制,直接允许入场") - return True + # 冷启动保护:程序启动后前20分钟内不允许入场 + cold_start_rejected = False + if hasattr(self, "_strategy_start_time"): + # 在回测模式下不启用冷启动保护 + if not self.config.get("dry_run", False): + warmup_minutes = 20 + # current_time 来自框架,是 offset-aware (UTC) + # _strategy_start_time 是 offset-naive (本地时间) + # 需要统一处理:把 current_time 转换为本地时间并移除时区信息 + if current_time.tzinfo is not None: + local_current_time = current_time.astimezone(UTC_PLUS_8).replace(tzinfo=None) + else: + local_current_time = current_time + elapsed_minutes = (local_current_time - self._strategy_start_time).total_seconds() / 60.0 + if elapsed_minutes < warmup_minutes: + rejected_conditions.append(f"冷启动保护: 策略启动未满 {warmup_minutes} 分钟(已运行 {elapsed_minutes:.1f} 分钟)") + self.strategy_log(f"[{pair}] {rejected_conditions[-1]}") + cold_start_rejected = True + + # 检查1:入场间隔控制(使用hyperopt参数) + interval_rejected = False + if pair in self._last_entry_time and not cold_start_rejected: + last_entry = self._last_entry_time[pair] + time_diff = (current_time - last_entry).total_seconds() * 0.0166666667 # 转换为分钟(使用乘法避免除法) + if time_diff < self.entry_interval_minutes.value: + rejected_conditions.append(f"入场间隔不足: 距离上次入场 {time_diff:.1f}分钟 < {self.entry_interval_minutes.value}分钟") + self.strategy_log(f"[{pair}] {rejected_conditions[-1]}") + interval_rejected = True + + # 检查2:检查是否处于剧烈拉升的不稳固区域 + unstable_rejected = False + if not cold_start_rejected and not interval_rejected: + is_unstable_region = self.detect_h1_rapid_rise(pair) + if is_unstable_region: + rejected_conditions.append("剧烈拉升检测: 检测到1小时图剧烈拉升") + self.strategy_log(f"[{pair}] {rejected_conditions[-1]}") + unstable_rejected = True + + # 如果有任何拒绝条件,直接返回 False + if rejected_conditions: + self.strategy_log(f"[{pair}] 入场被拒绝,原因: {', '.join(rejected_conditions)}") + return False + + # 所有条件通过,允许入场并记录入场时间 + self.strategy_log(f"[{pair}] 所有条件通过,允许入场") + self._last_entry_time[pair] = current_time + self.strategy_log(f"[{pair}] 更新入场时间为: {current_time}") + + return True