Compare commits

...

2 Commits

Author SHA1 Message Date
zhangkun9038@dingtalk.com
c4c549766b timeframe 3m改成了5m 2026-02-24 17:48:34 +08:00
zhangkun9038@dingtalk.com
6446aa5dc1 修复回测与实盘差异问题
- 调整冷启动保护逻辑,使其在回测模式下不启用
- 修复入场时间记录逻辑,确保入场间隔控制在回测中生效
- 移除外部数据校验逻辑,提高回测与实盘一致性
- 优化confirm_trade_entry函数结构
2026-02-24 17:45:56 +08:00
2 changed files with 52 additions and 95 deletions

View File

@ -13,7 +13,7 @@
"fiat_display_currency": "USD",
"dry_run": true,
"enable_strategy_log": true,
"timeframe": "3m",
"timeframe": "5m",
"additional_timeframes": ["4h"],
"dry_run_wallet": 2000,
"cancel_open_orders_on_exit": true,

View File

@ -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