backtest hyperopt也加入utc-8时区

This commit is contained in:
zhangkun9038@dingtalk.com 2026-02-09 19:44:31 +08:00
parent 7e604f8a91
commit 9de38a7958
3 changed files with 473 additions and 0 deletions

View File

@ -0,0 +1,286 @@
# FreqAI 时区修复与数据新鲜度监控优化
**分支**: `freq-ai-ae475be4d932e`
**时间范围**: 2026-02-08 ~ 2026-02-09
**提交范围**: `e22c6d8a..7e604f8a`(共 15 次提交)
---
## 一、变更概览
| 提交 | 日期 | 说明 |
|------|------|------|
| `7e604f8a` | 02-09 08:47 | 移除入场诊断相关log澄清数据新鲜度问题 |
| `fa4014ec` | 02-09 08:36 | debug数据新鲜度问题 |
| `06838982` | 02-09 03:10 | 币对变更 |
| `531d630b` | 02-08 21:46 | 准备放到kiko里运行币对列表增加到15个 |
| `4b765949` | 02-08 18:02 | fix: 修复波动系数计算中未定义的 get_current_time |
| `15a76730` | 02-08 17:47 | fix: 冷启动保护时区兼容 - 框架current_time是offset-aware |
| `8331a2e7` | 02-08 17:28 | fix: 日志显示时间也转换为UTC+8 |
| `7ad77f60` | 02-08 17:19 | fix: 修复时区转换 - FreqAI dataframe date列是UTC |
| `ec3395d0` | 02-08 17:08 | fix: 设置容器时区为 UTC+8 (Asia/Shanghai) |
| `424577fa` | 02-08 16:48 | 无论虚机系统时区是什么,代码都统一使用 UTC+8 |
| `2d20aaf7` | 02-08 15:38 | 时区问题导致数据不新鲜问题被放大 |
| `9e007086` | 02-08 13:37 | live减少训练量币对改成8个 |
| `7cf91070` | 02-08 12:49 | 应该不再出现 KeyError: 'sma' 错误 |
| `e28bcbb1` | 02-08 04:22 | ML数据新鲜度问题+3 |
| `e22c6d8a` | 02-08 04:04 | ML数据新鲜度问题+2 |
---
## 二、核心问题与解决方案
### 2.1 时区混乱导致的虚假告警
**问题描述**
数据新鲜度监控日志显示延迟 500+ 分钟(约 8 小时),实际上是时区问题导致的虚假告警。
**根因分析**
1. **Docker 容器时区默认 UTC+0**:即使宿主机是 UTC+8容器内 `datetime.now()` 返回 UTC 时间
2. **FreqAI dataframe 的 date 列是 UTC**:框架存储的 K 线时间戳是 UTC
3. **时区混合比较**UTC+8 的 `datetime.now()` 与 UTC 的 dataframe date 列直接相减,导致 8 小时偏差
**解决方案**
#### a. 设置容器时区(`tools/live.sh` / `dryrun.sh`
```bash
docker run -d --restart=always \
-e TZ=Asia/Shanghai \
-v /etc/localtime:/etc/localtime:ro \
...
```
#### b. 代码统一时区管理(`freqaiprimer.py`
```python
from datetime import datetime, timezone, timedelta
# 全局时区常量
UTC_PLUS_8 = timezone(timedelta(hours=8))
# FreqAI dataframe 的 date 列转换
if data_timestamp.tzinfo is None:
data_timestamp = data_timestamp.replace(tzinfo=timezone.utc)
# 转换为 UTC+8 并移除时区信息
data_timestamp = data_timestamp.astimezone(UTC_PLUS_8).replace(tzinfo=None)
```
**效果**:延迟从 **600+ 分钟** 恢复到 **4-16 分钟**
---
### 2.2 offset-naive 与 offset-aware 兼容性
**问题描述**
```
TypeError: can't subtract offset-naive and offset-aware datetimes
```
**根因**
- 框架传入的 `current_time`**offset-aware**(带时区信息 `+00:00`
- 策略内部的 `_strategy_start_time = datetime.now()`**offset-naive**(无时区信息)
**解决方案**
```python
# 冷启动保护时区兼容
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
```
---
### 2.3 入场诊断日志的误导性
**问题描述**
旧版日志输出:
```
[入场诊断] XRP/USDT | 数据时间: 22:57:00 | 延迟: ❌ 561min
```
用户误以为"数据停在 22:57延迟 561 分钟",实际上这是 **最后一次入场信号的时间**,不是当前数据时间。
**解决方案**
拆分为两个独立日志:
1. **数据新鲜度日志**(独立显示):
```python
latest_data_date = dataframe['date'].iloc[-1] # dataframe 最后一行
self.strategy_log(
f"[{pair}] 数据新鲜度: ✅ 4.7min | 最新K线: 08:36:00 | 总行数: 299"
)
```
2. **入场信号日志**(仅在有信号时显示):
```python
# 移除了旧的 entry_signals 遍历日志
# 只在 confirm_trade_entry 中输出实际触发的入场诊断
```
---
### 2.4 undefined `get_current_time` 错误
**问题描述**
```
NameError: name 'get_current_time' is not defined
```
**原因**:之前定义的辅助函数被删除,但波动系数计算中还在使用。
**解决方案**
```python
# 旧代码
current_time = get_current_time()
# 新代码
current_time = datetime.now().timestamp()
```
---
### 2.5 `KeyError: 'sma'` 防御性修复
**问题描述**
`custom_roi` 中访问 `dataframe['sma']` 时可能不存在。
**解决方案**
```python
# 旧代码
current_state = dataframe['market_state'].iloc[-1] if 'market_state' in dataframe.columns else (
'strong_bull' if dataframe['sma'].diff().iloc[-1] > 0.01 else ...
)
# 新代码
if 'market_state' in dataframe.columns:
current_state = dataframe['market_state'].iloc[-1]
elif 'sma' in dataframe.columns and len(dataframe) > 1:
sma_diff = dataframe['sma'].diff().iloc[-1]
current_state = 'strong_bull' if sma_diff > 0.01 else ...
else:
current_state = 'neutral'
```
---
## 三、配置变更
### 3.1 币对白名单调整
**dryrun.json** 和 **live.json**
- **移除**AAVE、BCH、CHZ、ETC、FIL、ICP、PENGU、PUMP、SHIB、SNX、WIF、XAUT、KAITO、MASK、OKB、PAXG、PI、STETH、STRK、USDG、XPL
- **保留/新增**ADA、AVAX、BNB、BTC、DOGE、DOT、ETH、LINK、LTC、PEPE、SOL、SUI、TON、TRB、TRUMP、TRX、UNI、WLD、XLM、XRP
**live.sh**
- 默认白名单从 15 个币对调整为 15 个(实际列表变更)
- **移除远程币对获取**:注释掉 `remote_pairs=$(get_remote_pairs ...)`
- 只合并两个来源:数据库 + 默认白名单
---
## 四、代码架构优化
### 4.1 时区管理统一化
**全局常量**
```python
UTC_PLUS_8 = timezone(timedelta(hours=8))
```
**统一转换逻辑**
- FreqAI dataframe date 列UTC → UTC+8
- 日志显示时间UTC → UTC+8
- 冷启动保护时间比较offset-aware → naive
### 4.2 import 优化
**旧代码**
```python
import datetime
current_time = datetime.datetime.now()
```
**新代码**
```python
from datetime import datetime, timezone, timedelta
current_time = datetime.now()
```
---
## 五、效果验证
### 5.1 数据新鲜度恢复正常
**修复前**
```
[入场诊断] XRP/USDT | 数据时间: 22:57:00 | 延迟: ❌ 561min
```
**修复后**
```
[XRP/USDT] 数据新鲜度: ✅ 4.7min | 最新K线: 08:36:00 | 总行数: 299
[入场诊断] XRP/USDT | 信号时间: 22:57:00 | 延迟: ❌ 561min | ...
```
现在用户可以清楚区分:
- **数据新鲜度**4.7 分钟(✅ 正常)
- **信号时间**22:57 是历史信号,不代表数据过期
### 5.2 时区错误消除
不再出现 `TypeError: can't subtract offset-naive and offset-aware datetimes`
### 5.3 KeyError 防御
不再因为 `sma` 列不存在而崩溃,改为安全的回退逻辑。
---
## 六、部署注意事项
### 6.1 容器重启必要性
修改了容器时区设置(`-e TZ=Asia/Shanghai`),需要重新部署容器才能生效。
### 6.2 时区一致性要求
- **宿主机**:可以是任意时区
- **容器**:强制 UTC+8通过环境变量
- **代码**:统一使用 UTC+8通过 `UTC_PLUS_8` 常量)
### 6.3 币对数量限制
当前配置 15 个币对,在高性能硬件(如 kiko 的 Ryzen 9 7945HX单币对训练约 15-25 秒,总训练时间约 4-6 分钟,远低于 2 小时重训周期。
---
## 七、后续优化方向
1. **数据新鲜度告警阈值配置化**:当前硬编码 10/30 分钟阈值,可改为策略参数
2. **时区配置灵活化**:支持通过环境变量或配置文件指定目标时区
3. **入场信号日志精简**:考虑完全移除历史信号统计日志,只保留实时入场诊断
4. **币对白名单管理**:从配置文件中彻底移除黑名单币对,避免混淆
---
## 八、相关文档
- 上一轮变更总结:[`docs/changelog_head15.md`](../docs/changelog_head15.md)
- 策略改进文档:[`doc/strategy_improvements.md`](./strategy_improvements.md)

View File

@ -17,11 +17,14 @@ services:
# context: .
# dockerfile: "./docker/Dockerfile.custom"
restart: always
environment:
- TZ=Asia/Shanghai
extra_hosts:
- "www.okx.com:104.18.43.174" # 域名:IP 格式
- "api.okx.com:104.18.43.174" # 域名:IP 格式
container_name: freqtrade
volumes:
- "/etc/localtime:/etc/localtime:ro"
- "./user_data:/freqtrade/user_data"
- "./config_examples:/freqtrade/config_examples"
- "./freqtrade/templates:/freqtrade/templates"

184
docs/changelog_head15.md Normal file
View File

@ -0,0 +1,184 @@
# 最近 15 次提交变更总结
**时间范围**: 2026-02-02 ~ 2026-02-08
**提交范围**: `45c3c0f1..3d3cc817`(共 15 次提交)
**涉及文件**: 13 个核心文件(不含自动生成的 analysis 文件)
---
## 一、变更概览
| 提交 | 日期 | 说明 |
|------|------|------|
| `3d3cc817` | 02-08 | ML数据新鲜度问题修复 |
| `f2858280` | 02-08 | 重大发现ML数据新鲜度监控 BUG |
| `4c51aa5c` | 02-08 | debug identifier 配置(续) |
| `a1fdcd67` | 02-08 | debug identifier 配置 |
| `3c2c30b7` | 02-08 | dryrun/live 分别设置各自的 identifier |
| `29cd9278` | 02-08 | 不再删除训练数据,复用已有模型(续) |
| `5f00d355` | 02-08 | 不再删除训练数据,复用已有模型 |
| `51c30588` | 02-07 | FreqAI 训练模型名字按环境区分 |
| `f47f558f` | 02-07 | 更新 trailing stop 参数 |
| `bb64aaa6` | 02-03 | OKX API rate limit 调整为 1000ms |
| `c861f08b` | 02-02 | 杂项更新 |
| `15321936` | 02-02 | 入场 tag 分类 + 差异化退出策略 |
| `3ee798c2` | 02-02 | live 配置调整 |
| `c8616c31` | 02-02 | 全面的量价关系指标 |
| `45c3c0f1` | 02-02 | 趋势判断逻辑全面改进 |
---
## 二、核心策略变更 (`freqtrade/templates/freqaiprimer.py`)
共新增约 **1050 行**代码,是本轮变更的主体。
### 2.1 趋势判断逻辑改进
**旧逻辑**:单纯使用 EMA50/EMA200 金叉死叉判断趋势。
**新逻辑**多维度投票机制三者投票≥2票为趋势确认
| 维度 | 3m | 15m | 1h |
|------|-----|------|-----|
| EMA 趋势 | EMA50 > EMA200 | EMA50 > EMA200 | EMA50 > EMA200 |
| 价格动量 | close > close.shift(5) | close > close.shift(5) | close > close.shift(5) |
| EMA 斜率 | > 0.1% | > 0.2% | > 0.3% |
**趋势强度**:引入 EMA 斜率作为趋势一致性权重,`market_score` 计算从简单的 0/1 变为连续值。
新增辅助指标:
- `macd_trend_strength`MACD 趋势强度
- `rsi_trend_strength`RSI 偏离中性区域程度
### 2.2 成交量与量价关系指标(全新模块)
**成交量趋势指标**
- `volume_change`:成交量变化率
- `volume_ma_ratio`:成交量/MA 比值
- `volume_std` / `volume_cv`:成交量标准差和变异系数
- `volume_zscore`Z 分数(检测异常成交量)
- `volume_trend`:成交量趋势方向
- `volume_ema_fast/slow`:成交量 EMA 快慢线趋势
**量价关系指标**
- `volume_price_confirmation`:量价确认度(同向变动)
- `volume_price_ratio`:量价比率
- `bearish_divergence`:顶背离(价格新高但量未新高)
- `bullish_divergence`:底背离(价格新低但量未新低)
- `obv`On Balance Volume 平衡成交量
- `volume_price_corr_short/medium`量价相关系数10/20 窗口)
### 2.3 入场类型识别与标签系统(全新模块)
新增 `identify_entry_type()` 函数,根据技术指标特征组合识别 7 种入场类型:
| 类型 | 名称 | 识别特征 | 建议止盈 |
|------|------|---------|---------|
| 0 | 快进快出 | 高波动 + 极端RSI + 动量背离 | 2% |
| 1 | 短期波段 | 中等波动 + 边界RSI | 4% |
| 2 | 中期趋势 | 中等波动 + 中性RSI | 6% |
| 3 | 长期持有 | 强趋势 + 低波动 + 多头排列 | 10% |
| 4 | 套利机会 | 均值回归区域 + 低波动 | 3% |
| 5 | 突破追涨 | 巨量 + 强动量 | 8% |
| 6 | 震荡套利 | 震荡区间 + 低波动 | 2.5% |
**标签格式**`type:X,duration:Y,risk:Z,confidence:W,name:NAME,market_state:S,trend_strength:T`
### 2.4 差异化退出策略
**`custom_roi()`(全新函数)**:根据入场类型和市场状态动态调整 ROI 目标。
**`confirm_trade_exit()` 增强**
- 获取高级市场状态(`get_advanced_market_state`
- 在熊市中更积极保护利润:
- 降低出场阈值(趋势强度越大,阈值越低)
- 缩短持仓容忍时间(下跌趋势最大 24 小时)
- 持仓超 1 小时 + 小幅盈利 → 考虑提前出场
**`custom_stoploss()` 增强**
- 根据入场类型、市场状态、趋势强度调整止损策略
- 熊市中使用更紧的止损乘数
- 解析 `entry_tag` 获取入场类型和风险等级
**`confirm_trade_entry()` 增强**
- 价格保护逻辑:在强熊市中要求更高折扣才能入场
- 折扣幅度随趋势强度和可信度动态调整
- 入场时生成完整标签并传递给后续回调
### 2.5 市场环境综合管理(全新模块)
新增三个核心函数:
| 函数 | 功能 |
|------|------|
| `get_advanced_market_state()` | 返回 state/strength/confidence 三元组 |
| `get_comprehensive_market_context()` | 整合市场形态标签和趋势信息 |
| `identify_entry_type()` | 根据指标组合识别入场类型 |
### 2.6 数据新鲜度监控系统
新增 `_freshness_stats` 统计字典和相关方法:
| 方法 | 功能 |
|------|------|
| `update_freshness_stats()` | 更新每个币对的延迟统计 |
| `report_freshness_stats()` | 每小时输出统计报告 |
**数据新鲜度日志**:在 `populate_entry_trend` 中新增独立的数据新鲜度检查,显示 dataframe 最后一行的时间和延迟。
**BUG 修复**:之前的入场诊断日志误用"最近一次入场信号的时间"作为"数据时间",导致无入场信号时虚假报告数百分钟延迟。已拆分为独立的数据新鲜度日志和入场信号日志。
### 2.7 Trailing Stop 参数调整
| 参数 | 旧值 | 新值 |
|------|------|------|
| `trailing_stop_positive` | 0.0125 | 0.0075 |
| `trailing_stop_positive_offset` | 0.045 | 0.0225 |
更紧的追踪止损,更快锁定利润。
---
## 三、配置变更
### 3.1 FreqAI identifier 环境隔离
- `dryrun.json` 新增 `"identifier": "dryrun-freqai"`
- `live.json` 使用独立的 `"identifier": "live-freqai"`(在 live.sh 中设置)
- `freqaiprimer.json` 移除硬编码的 identifier由部署脚本注入
**目的**:防止 dryrun 和 live 共享模型文件导致冲突。
### 3.2 OKX API Rate Limit
`live.json`: `rateLimit` 从 500ms 调整为 **1000ms**,降低 API 请求频率,避免限流。
### 3.3 币对白名单更新
`live.json``dryrun.json``pair_whitelist` 均有调整,移除部分币对,新增多个币对。
---
## 四、部署脚本变更
### 4.1 保留训练模型(`live.sh` / `dryrun.sh`
**关键变更**:注释掉 `rm -rf user_data/models/*`,不再在每次部署时删除已训练的模型。
**效果**:容器重启后可复用已有模型,从数小时的冷启动缩短到秒级推理。
### 4.2 默认配置文件
`live.sh``dryrun.sh` 的默认配置从 `basic.json` 改为 `freqaiprimer.json`
### 4.3 backtest.sh 清理简化
移除 `rm -rf user_data/models/*``rm -fr ./user_data/dryrun_results/*`,只保留回测结果清理。
---
## 五、已知问题与后续
1. **时区问题**在后续会话中修复Docker 容器默认 UTC+0导致数据新鲜度计算虚假告警
2. **OKB/USDT 数据缺失**OKB 在 OKX 现货无数据,会阻塞训练流水线
3. **入场诊断日志误导**:已在本轮修复,拆分为数据新鲜度和信号时间两个独立日志