全系统key配对升级 bug18
配对维度优化后系统缺陷与不足分析
当前状态概览
迁移 database/migrations/20260219_fix_pairkey_indexes.sql 与 database/migrations/20260220_bugfix_batch.sql 已完成:
- trading_signals / analysis_results / pair_positions:索引与压缩分段已改为
(symbol, base_symbol)维度 - 策略、仓位、编排器、黑名单、分析服务:内部状态与查询均按
PairKey = (symbol, base_symbol)使用 - 配置层:
get_strategy_params(symbol, base_symbol)、is_symbol_allowed、is_close_disabled均支持配对级与币种级,且与字符串 key"ALT|BASE"一致
以下为仍存在的缺陷与不足。
一、明确缺陷(Bug)
1. backfill_all_data 中 get_existing_analysis_times 未按配对过滤
位置:src/scripts/backfill_all_data.py 第 200–207 行
问题:查询已有 analysis_results 的 kline_time 时仅使用 WHERE symbol = %s,未加 base_symbol。在配对维度下,同一 symbol 可能对应多个 base_symbol(如 PURR 对 HYPE、PURR 对 BTC),会混入其他配对的 kline_time,导致:
- 去重/跳过逻辑错误(误判某时间点“已存在”而跳过回补)
- 若未来该脚本支持多配对回补,错误会更明显
建议修改:
- 查询改为
WHERE symbol = %s AND base_symbol = %s,并传入当前脚本的(SYMBOL, BASE_SYMBOL)(与脚本内单配对常量一致)。
二、设计/一致性不足
2. daily_trading_stats 的 symbol/base_symbol 列未使用
位置:
- 表结构:database/init_timescaledb.sql 与 database/migrations/20260220_bugfix_batch.sql 为
daily_trading_stats增加了symbol、base_symbol列 - 写入:src/trading/trade_repository.py 的
update_daily_stats()仅按(stat_date, network)做 INSERT/ON CONFLICT,未写入symbol/base_symbol
问题:统计仍是“按日+网络”的全局汇总,与“配对维度”的表结构扩展不一致;两列始终为默认空串,易造成误解。
可选方向:
- A:若不需要按配对统计,在文档或注释中明确“两列为预留、当前未使用”,避免误用。
- B:若需要按配对维度统计,则需:主键或唯一约束改为包含
(symbol, base_symbol)(或按配对聚合),并在所有调用update_daily_stats的地方传入symbol/base_symbol(见 position_manager.py、orchestrator.py 等处)。
3. get_positions_by_symbols 仅按 symbol 过滤
位置:src/trading/trade_repository.py get_positions_by_symbols(symbols, network)
问题:按 symbol IN (...) 查询活跃仓位,返回的是“这些 symbol 下所有 base_symbol 的仓位”。当前无调用方,但若将来需要“只查指定 (symbol, base_symbol) 的仓位”,现有 API 无法表达。
建议:保持现有方法用于“按 symbol 列表查”;若需按配对查,可新增如 get_open_positions_by_pairs(pairs: list[tuple[str, str]], network=...),内部用 (symbol, base_symbol) = ANY(%s) 或等价条件。
三、可选改进与说明
4. 历史压缩 Chunk 分段方式
迁移 20260219_fix_pairkey_indexes.sql 已说明:已压缩的历史 chunk 仍为旧分段方式,仅新写入数据使用 (symbol, base_symbol) 分段。若希望全库一致,需对历史 chunk 执行 decompress_chunk + recompress_chunk(运维操作,非必须)。
5. 配置 Key 格式与代码中 PairKey 类型
- 配置层:
pair_strategy_overrides、黑名单/禁止平仓等使用字符串"ALT|BASE"(如PURR|HYPE)。 - 业务层:普遍使用元组
(symbol, base_symbol)。
当前通过 get_strategy_params(symbol, base_symbol)、is_symbol_allowed、is_close_disabled 统一转换,无直接拿 tuple 当 key 查配置的 bug。若后续有代码直接以 tuple 查 pair_strategy_overrides,会失败,建议保持“仅通过上述方法访问”、或在一处做类型/格式说明,避免后续扩展混淆。
6. 节流维度 (symbol, timeframe)
realtime_kline_service_base.py 中分析节流 key 为 (symbol, timeframe),不含 base_symbol。设计为“同一 K 线只调度一次,任务内对该 symbol 的所有 base 循环分析”,行为一致且合理,无需改为配对维度节流。
7. cointegrated_pairs 字段命名
表 cointegrated_pairs 使用 (base_symbol, alt_symbol),与系统内常见的 (symbol, base_symbol) 对应关系为:alt_symbol = 目标 = symbol,base_symbol = 基准 = base_symbol。语义一致,仅命名风格不同,可在文档中注明以便维护。
四、已正确使用配对维度的部分(简要)
- 策略 strategy.py:
_baselines、_positions、_prev_above_threshold、信号去重 key 等均按PairKey。 - 仓位 position_manager.py:内存与 DB 读写均按
(symbol, base_symbol)。 - 编排器 orchestrator.py:历史 z4h 加载、信号处理、平仓/开仓均带配对。
- 实时分析 realtime_kline_service_base.py:黑名单、分析记录、策略触发均按配对。
- DB:
trading_signals、analysis_results、pair_positions、symbol_blacklist的索引/压缩/查询均已配对维度;AnalysisResultRepository、BlacklistRepository等按(symbol, base_symbol)读写。
五、总结与建议优先级
| 类型 | 项 | 建议 |
|---|---|---|
| 必修 | backfill_all_data 中 get_existing_analysis_times 缺少 base_symbol | 在 WHERE 中增加 AND base_symbol = %s 并传入 BASE_SYMBOL |
| 设计 | daily_trading_stats 的 symbol/base_symbol 未写入 | 明确“预留未用”或实现按配对统计并改主键/调用方 |
| 可选 | get_positions_by_symbols 无法按配对查 | 需要时新增 get_open_positions_by_pairs |
| 可选 | 历史压缩 chunk 与新区段不一致 | 需要全库一致时再对历史 chunk 重压缩 |
| 文档 | 配置 key 字符串 vs 代码 tuple、cointegrated_pairs 命名 | 在配置/表说明中注明,避免后续误用 |
完成上述“必修”项后,配对维度在逻辑与数据层面即可保持一致;其余为一致性与可维护性改进,可按需求分批做。