修复回测与实盘差异问题

- 调整冷启动保护逻辑,使其在回测模式下不启用
- 修复入场时间记录逻辑,确保入场间隔控制在回测中生效
- 移除外部数据校验逻辑,提高回测与实盘一致性
- 优化confirm_trade_entry函数结构
This commit is contained in:
zhangkun9038@dingtalk.com 2026-02-24 17:45:56 +08:00
parent 419c1ae813
commit 6446aa5dc1

View File

@ -1352,100 +1352,57 @@ class FreqaiPrimer(IStrategy):
self.strategy_log(f"[{pair}] 非多头交易,跳过入场检查") self.strategy_log(f"[{pair}] 非多头交易,跳过入场检查")
return False return False
# 获取虚机名字 # 记录所有拒绝条件的列表
import os rejected_conditions = []
virtual_host_name = os.environ.get('VIRTUAL_HOST_NAME')
self.strategy_log(f"[{pair}] 当前虚机名字: {virtual_host_name}")
# 只有当虚机名字是kiko时才执行从外部获取数据和RMSE限制的逻辑 # 冷启动保护程序启动后前20分钟内不允许入场
if virtual_host_name == 'kiko': cold_start_rejected = False
self.strategy_log(f"[{pair}] 虚机名字是kiko执行外部数据获取和RMSE限制逻辑") if hasattr(self, "_strategy_start_time"):
import requests # 在回测模式下不启用冷启动保护
import json if not self.config.get("dry_run", False):
warmup_minutes = 20
try: # current_time 来自框架,是 offset-aware (UTC)
# 构建API请求URL # _strategy_start_time 是 offset-naive (本地时间)
api_url = f"http://pairlist.xl.home/api/mlpredictionList?pairName={pair}&sortBy=timestamp" # 需要统一处理:把 current_time 转换为本地时间并移除时区信息
self.strategy_log(f"[{pair}] 请求API数据: {api_url}") if current_time.tzinfo is not None:
local_current_time = current_time.astimezone(UTC_PLUS_8).replace(tzinfo=None)
# 发送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: else:
# 虚机名字不是kiko跳过外部数据获取和RMSE限制直接允许入场 local_current_time = current_time
self.strategy_log(f"[{pair}] 虚机名字不是kiko跳过外部数据获取和RMSE限制直接允许入场") 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 return True