全系统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.pyget_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 是中长期重构方向。

Read more

跑步的技巧(滚动落地)

“滚动落地(rolling contact / rolling foot strike)”不是一种教条式的“脚法”,而是一种 让冲击沿着整只脚、整条后链逐级传递的落地机制。 它的核心不是“你先用哪儿着地”,而是: 你的脚落地之后,冲击是不是像轮子一样滚过去,而不是像锤子一样砸下去。 这就是滚动落地的本质。 一、什么叫“滚动落地”? 你可以把它理解成两种完全不同的落地方式: 1. 砸地(撞击式) 脚像锤子一样拍到地上: * 要么后跟先砸 * 要么前掌先戳 * 冲击集中在一个点 * 一个结构瞬间吃掉大部分载荷 结果就是: * 后跟砸 → 膝盖难受 * 前掌戳 → 前脚掌磨烂 * 都不是长跑友好模式 这叫 撞击式着地(impact strike)。 2. 滚地(滚动式) 脚像轮胎一样“滚”过地面: * 不是某一点硬砸 * 而是外侧中足先轻触 * 再向前滚到前掌 * 最后从大脚趾蹬离

By SHI XIAOLONG

AMI的优越性

世界模型(World Models)的具体例子 如下,我按类型分类,便于理解。每类都附带实际实现、演示效果和应用场景。 1. Yann LeCun / Meta 的 JEPA 系列(最直接对应“世界模型”概念) 这些是 LeCun 主张的非生成式抽象预测世界模型代表。 * I-JEPA(Image JEPA,2023) 输入一张图像,模型把不同区域(context 和 target)编码成抽象表示,然后预测 target 的表示(不在像素级别重建)。 例子:给定一张遮挡了部分物体的图片,模型能预测“被遮挡物体的大致位置和属性”,构建对物体持久性和空间关系的理解。 这是一个“原始世界模型”,能学习物理常识(如物体不会凭空消失)。 * V-JEPA / V-JEPA 2(Video JEPA,

By SHI XIAOLONG

什么是:“世界模型(World Models)”

世界模型(World Models) 是人工智能领域的一个核心概念,尤其在 Yann LeCun 等研究者推动的下一代 AI 架构中占据中心位置。它指的是 AI 系统在内部构建的对现实世界的抽象模拟或内部表示,让机器能够像人类或动物一样“理解”物理世界、预测未来、规划行动。 简单比喻 想象你闭上眼睛也能“看到”房间里的物体会如何移动、碰撞或掉落——这就是你大脑里的世界模型。AI 的世界模型就是类似的“数字孪生”(digital twin)或“内部模拟器”:它不是简单记住数据,而是学习世界的动态、因果关系和物理直觉(如重力、物体持久性、遮挡、因果等)。 为什么需要世界模型? 当前主流的大型语言模型(LLM) 擅长处理文本(统计模式预测),但存在根本局限: * 缺乏对物理世界的真正理解 → 容易“幻觉”、无法可靠规划。 * 样本效率低 → 人类/

By SHI XIAOLONG

K线周期可配置化设计方案

K线周期可配置化设计方案 1. 背景与目标 当前 Beta 套利策略的 K 线周期硬编码为 "1h",分散在多个文件中。需要: 1. 将 K 线周期从 1h 改为 2h 2. 提取为环境变量 BETA_ARB_KLINE_INTERVAL,使其可在 .env 中配置 2. 影响范围分析 2.1 需要修改的文件(共 6 个) 文件 硬编码位置 修改内容 src/trading/config.py BetaArbConfig dataclass 新增 kline_interval 字段,

By SHI XIAOLONG