全系统Key改为配对维度bug1
配对维度优化后系统缺陷与不足分析
一、当前已正确按配对维度的部分(简要)
- 核心交易与策略:position_manager.py、strategy.py、trade_repository.py、orchestrator.py、risk_manager.py 均以
PairKey = (symbol, base_symbol)存储与查询。 - 实时服务:黑名单校验、持仓保护、分析触发、
_has_open_position、分析结果写入与query_avg_zscore_4h均使用配对维度(见 realtime_kline_service_base.py、timescaledb.py)。 - 数据库:迁移 20260219_fix_pairkey_indexes.sql 已把
trading_signals、pair_positions索引及analysis_results压缩分段改为(symbol, base_symbol);symbol_blacklist表与BlacklistRepository已按配对维度设计。
二、缺陷与不足(按优先级)
1. 数据自愈加载历史 Z-score 未按配对过滤(Bug)
位置:src/utils/data_healing/orchestrator.py 中 _load_zscore_history(约 461–476 行)。
问题:查询 analysis_results 时仅使用 WHERE symbol = %s,未加 AND base_symbol = %s。编排器构造时已传入 base_symbol,但 SQL 未使用,导致同一 symbol 对不同 base 的 zscore 混在一起,自愈结果可能错配、缓冲区被错误序列污染。
建议:在查询中增加 AND base_symbol = %s,并在 params 中传入 self.base_symbol。
2. 新币黑名单为 symbol 维度,与配对维度不一致
位置:src/services/realtime_kline_service_base.py:new_coin_blacklist: set[str](约 244 行)、加入逻辑(1115–1121)、使用处(1498–1499)。
问题:当某 symbol 对 base A 的 4H 数据不足时,会执行 self.new_coin_blacklist.add(symbol),此后该 symbol 对任意 base 的分析都会被跳过。在「同一 symbol 对多 base」场景下,会误伤与 base B 的正常配对。
建议:将 new_coin_blacklist 改为按配对维度存储,例如 set[tuple[str, str]] 或 set 中存 f"{symbol}|{base_symbol}",加入与判断处均使用 (symbol, base_symbol)(或统一字符串 key),并在取消订阅逻辑中区分「仅该配对」还是「全 symbol」(当前为全 symbol 取消订阅,若保留需在文档中说明)。
3. 数据自愈触发列表仅按 symbol 枚举,未按配对
位置:src/services/realtime_kline_service_base.py 中 _run_data_healing(约 1686–1692 行)。
问题:用 SELECT DISTINCT symbol FROM analysis_results WHERE zscore_4h IS NOT NULL 得到「有数据的 symbol」,再与当前订阅的 symbols 取交集,对每个 symbol 仅用 self.base_symbol 调用一次自愈。因此:
- 只自愈「当前配置的 base」下的配对;
- 若同一 symbol 对应多个 base(例如多 base 或后续扩展),其他配对的数据断层不会触发自愈。
建议(可选,视是否支持多 base):
- 若当前仅单 base,可保持现状,但在注释中说明「仅自愈当前 base_symbol 的配对」;
- 若未来支持多 base,应改为按配对枚举,例如
SELECT DISTINCT symbol, base_symbol FROM analysis_results WHERE zscore_4h IS NOT NULL,再与「当前服务的活跃配对集合」求交,对每个(symbol, base_symbol)调用自愈。
4. 脚本与工具类查询未带 base_symbol(一致性/多配对风险)
以下脚本或 SQL 片段在查询 analysis_results 时仅使用 symbol,在配对维度下可能混用多 base 数据或语义不明确:
| 文件 | 说明 |
|---|---|
| src/scripts/fix_buffer_loading.py | SOLUTION_1_SQL 中 WHERE symbol = %s,无 base_symbol |
| src/scripts/query_analyze_result/check_buffer_continuity.py | 查询 WHERE symbol = %s,模拟启动加载逻辑,应与 orchestrator 一致带 base_symbol |
| src/scripts/backfill_analysis_results.py | get_existing_kline_times 与历史查询仅按 symbol(脚本内 SYMBOL/BASE_Symbol 为常量,若固定单配对可接受,但建议显式带 base_symbol 以与表语义一致) |
| src/scripts/query_analyze_result/check_missing_purr_zscore.py | WHERE symbol = %s |
| src/scripts/query_analyze_result/query_purr_zscore.py | 同上 |
| src/scripts/query_analyze_result/query_eth_zscore.py | 同上 |
| src/scripts/optimize_adaptive_zscore*.py | 从 analysis_results 取数时仅按 symbol |
建议:凡涉及「某一配对」的启动模拟、连续性检查、回填、优化,查询应带 AND base_symbol = %s(或脚本参数传入 base_symbol),与生产逻辑和 PairKey 维度一致。
5. 历史已压缩 Chunk 与新区间压缩维度不一致
位置:database/migrations/20260219_fix_pairkey_indexes.sql 注释(44–46 行)。
问题:迁移已将 analysis_results 的压缩分段改为 (symbol, base_symbol),但已压缩的历史 chunk 仍是按旧分段(仅 symbol)压缩的,因此:
- 同一表中存在两种压缩分段方式;
- 对历史区间的按配对查询/压缩效果与新区间不一致。
建议:在维护窗口内对需要一致性的历史 chunk 执行 decompress_chunk + recompress_chunk(或按文档说明批量重压缩);若可接受「仅新数据按配对压缩」,则保留现状并在运维文档中写明。
6. 配置与文档层面的澄清
- config.symbol_blacklist(config.py):为「静态配置的币种黑名单」,按币种名(如 PURR、HYPE)禁止交易,与 DB 的「配对维度 24h 校验黑名单」是两套机制,建议在配置或架构文档中区分说明。
- backfill / 自愈:若 backfill 或自愈脚本长期只支持「单 base」或「单配对」,建议在 README 或脚本注释中写明,避免多配对场景下误用。
三、小结(按建议修复优先级)
- 必须修:数据自愈
_load_zscore_history增加base_symbol条件,避免混用不同配对数据。 - 建议修:
new_coin_blacklist改为配对维度,避免误伤其他 base 的同一 symbol。 - 建议做:自愈触发列表若未来支持多 base,改为按
(symbol, base_symbol)枚举并只自愈当前服务的活跃配对。 - 一致性:脚本与工具中凡「按配对」使用
analysis_results的查询,统一增加base_symbol条件或参数。 - 运维/文档:明确历史压缩 chunk 是否重压缩、以及两套黑名单(配置 vs DB)的职责与使用场景。
以上为配对维度优化后仍存在的缺陷与不足,以及对应的修复与改进建议。