全系统key配对升级 bug7
系统分析报告:全系统 Key 改为配对维度后的缺陷与不足
分析日期:2026-02-19
分析范围:src/trading/全部核心文件(strategy.py、position_manager.py、orchestrator.py、models.py、config.py)
🔴 高危 Bug
Bug 1:配对级黑名单在 process_analysis 入口完全失效
位置: src/trading/orchestrator.py:293
问题:黑名单检查时 base_symbol 尚未提取,导致配对级黑名单条目永远不会被命中。
# ❌ 当前代码(第 293 行):检查时 base_symbol 尚未提取
allowed, reason = self._config.is_symbol_allowed(symbol) # 没传 base_symbol
# ... 中间省略 ...
# base_symbol 在第 320 行才被提取——已经太晚了
base_symbol = multi_period_result.get("base_symbol", "")
影响:配对级黑名单条目(如 "PURR|HYPE")完全无效。即使用户配置了
TRADING_SYMBOL_BLACKLIST=PURR|HYPE,该配对信号依然会进入策略引擎处理并可能触发交易。
只有币种级黑名单(如 "PURR")在此处有效。
修复方向:在黑名单检查前先提取 base_symbol,并传入 is_symbol_allowed(symbol, base_symbol)。
🟡 中危问题
Bug 2:debug 日志的 min_required 使用 // 4,而实际检查用 // 2
位置: src/trading/strategy.py:459
# is_ready() 实际使用(第 135 行):max(10, maxlen // 2)
min_required = max(10, bl.std_window.maxlen // 2)
return len(bl.std_window) >= min_required
# _process_tick_unlocked debug 日志(第 459 行):使用 // 4
if not self.is_ready(symbol, base_symbol):
if _DEBUG:
min_required = max(10, bl.std_window.maxlen // 4) # ← 是实际需求量的一半!
logger.debug(f"⏳ 缓冲区未就绪 | ... buf={len(bl.std_window)}/{min_required}")
影响:std_window=72 时,实际需要 36 条数据才能就绪,但 debug 日志显示只需 18 条。
严重误导调试,开发者会误以为缓冲区已就绪。
Bug 3:孤儿仓位收纳时持锁执行数据库 I/O
位置: src/trading/position_manager.py,_detect_and_adopt_orphans 方法
问题:sync_with_exchange() 在持有 self._lock 时调用 _detect_and_adopt_orphans,
后者内部会调用 self._repo.save_position()(数据库写入操作)。
# sync_with_exchange(第 869 行)
with self._lock: # ← 持锁
...
_adopted = self._detect_and_adopt_orphans(...) # ← 调用链最终执行 DB I/O
# 内部路径:
# _restore_orphans_from_db → save_position() ← I/O in lock!
# _adopt_paired_orphans → save_position() ← I/O in lock!
# _adopt_single_orphans → save_position() ← I/O in lock!
影响:每次仓位同步(每 60 秒)如有孤儿仓位,会在持锁状态下执行 DB 写入,
阻塞其他线程(止损监控、信号处理),持锁时间可能长达数百毫秒。
🟠 设计层面的不足
设计问题 1:系统中并存 4 种 配对 Key 格式,缺乏统一规范
全系统 Key 改为配对维度后,各层仍使用不同格式表示同一个"配对"概念:
| 使用场景 | Key 格式 | 示例 |
|---|---|---|
| 策略层/仓位层内部状态 | tuple[str, str] |
("PURR/USDC:USDC", "HYPE/USDC:USDC") |
| 配置覆盖(pair_strategy_overrides) | "ALT|BASE" 字符串 |
"PURR|HYPE" |
| 服务层配对缓存(_pair_cache) | dict[str, List[str]] |
{"PURR/USDC:USDC": ["HYPE/USDC:USDC"]} |
| 数据库(analysis_results 表) | 两列 (symbol, base_symbol) |
独立列存储 |
影响:
- 层与层之间传递数据时需要手动格式转换(完整符号 → coin 名 →
ALT|BASE字符串) config.py中get_strategy_params需要多步转换:"PURR/USDC:USDC"→"PURR"→"PURR|HYPE"- 新开发者容易混用格式,难以感知哪种格式是"规范"
设计问题 2:pair 模式下 get_all_mids() 可能被调用两次
位置: src/trading/position_manager.py,_open_position_inner 第 99-114 行
# 若 latest_alt_price 有值,all_mids 初始化为空 {}
all_mids = self._executor.get_all_mids() if not signal.latest_alt_price else {}
alt_price = signal.latest_alt_price or all_mids.get(alt_coin, 0.0)
# pair 模式下发现 all_mids 为空,触发第二次 API 调用
if self._config.pair_mode == "pair":
base_coin = symbol_to_coin(signal.base_symbol)
if not all_mids: # ← 此时 all_mids 为 {}
all_mids = self._executor.get_all_mids() # ← 重复调用!
影响:在 pair 模式且信号携带 latest_alt_price 时,每次开仓都会多发起一次不必要的 API 请求。
🟢 小问题
小问题 1:退场信号中 zscore_5m / zscore_1h 均为 0
位置: src/trading/orchestrator.py:560-566
signal = PairTradeSignal(
signal_type=SignalType.EXIT,
symbol=symbol,
base_symbol=base_symbol,
direction=reversion_info.direction,
zscore_4h=reversion_info.current_value,
# zscore_5m=0.0, zscore_1h=0.0 ← 使用默认值,丢失实际市场状态
)
影响:历史数据分析时,exit 信号记录中 zscore_5m=0, zscore_1h=0 会造成误导,
无法重现退场时的完整多周期市场状态。
小问题 2:_pair_cache 无定时刷新机制
位置: src/services/realtime_kline_service_base.py
_pair_cache(alt_symbol → [base_symbols])仅在服务启动时从数据库加载一次,
运行期间若数据库中新增或删除了配对关系,服务无法感知。
小问题 3:配对级参数覆盖 key 解析未防御多 __ 的边缘情况
位置: src/trading/config.py:391
alt_asset = pair.split("__")[0] # "A__B__C" → alt_asset="A" ✓
pair_key = pair.replace("__", "|", 1) # "A__B__C" → "A|B__C" ← 错误 key
若环境变量配置为 TRADING_STRATEGY_OVERRIDE_PAIRS=A__B__C,不会报错,而是静默产生
错误的 key "A|B__C",导致配对级参数覆盖完全不生效。
总结
| 严重程度 | 问题 | 文件 | 行号 |
|---|---|---|---|
| 🔴 高危 | 配对级黑名单在入口失效 | orchestrator.py | 293 |
| 🟡 中危 | debug 日志 min_required 与实际不一致 | strategy.py | 459 |
| 🟡 中危 | 持锁执行 DB I/O | position_manager.py | 869 |
| 🟠 设计 | 4 种配对 Key 格式并存 | 全局 | — |
| 🟠 设计 | get_all_mids() 被重复调用 | position_manager.py | 99 |
| 🟢 小问题 | 退场信号丢失多周期 Z-score | orchestrator.py | 560 |
| 🟢 小问题 | _pair_cache 无刷新机制 | realtime_kline_service_base.py | — |
| 🟢 小问题 | 配对参数覆盖 key 解析不健壮 | config.py | 391 |
最优先修复:Bug 1(配对级黑名单失效)是功能性 Bug,用户配置了黑名单却不生效,需立即处理。
Bug 2 影响调试效率。Bug 3 和设计问题 1 是中长期重构方向。