订单跟踪系统BUG31

订单跟踪系统严重 Bug 分析报告

审计范围: websocket_order_manager.pyexecutor.pyposition_manager.pyorchestrator.pyrealtime_kline_service_base.py

日期: 2026-02-22


Bug 1: 平仓冷却机制阻止止损执行(严重度:危急

位置: position_manager.py:295-306

cooldown_until = self._close_cooldown.get(key)
if cooldown_until is not None:
    if time.time() < cooldown_until:
        return None  # ← 止损也被拒绝

问题: close_position() 失败后会设置 30 秒冷却期(line 335, 645)。在此期间,所有 平仓请求都被拒绝——包括止损监控线程发起的紧急止损。

影响路径:

  1. 信号触发平仓 → 交易所 API 异常 → 设置 30s 冷却
  2. 在这 30s 内,市场剧烈波动触发止损
  3. _close_with_retry 调用 close_position → 被冷却拒绝 → 返回 None
  4. 重试 3 次(1s + 2s + 4s = 7s),全部在冷却期内 → 全部失败
  5. 止损监控放弃,等下一个监控周期(默认间隔 + 30s 冷却)
  6. 结果: 仓位在极端行情下暴露长达 30+ 秒无保护

修复建议: close_position 应区分冷却原因,对 force_market=True 的止损请求跳过冷却检查:

if cooldown_until is not None and not force_market:
    ...

Bug 2: 单 WebSocket 架构导致 Testnet 订单事件完全丢失(严重度:

位置: config.py:104 + enhanced_ws_manager.py:275 + realtime_kline_service_base.py:443-457

# config.py - WS 地址硬编码为主网
WS_URL = "wss://api.hyperliquid.xyz/ws"

# enhanced_ws_manager.py - 所有 WS 订阅都走这一个连接
self.ws_url = WS_URL  # 永远是主网

# realtime_kline_service_base.py - 订单订阅也加到同一个 WS
subscriptions.extend([
    {"type": "orderUpdates", "user": user_address},
    {"type": "userFills", "user": user_address},
])

问题: 系统只有一个 WebSocket 连接(主网),但交易可能在测试网执行。当 TRADING_NETWORK=testnet 时:

  • 订单通过测试网 HTTP API 提交
  • orderUpdates/userFills 订阅在主网 WS 上
  • 主网 WS 不会推送测试网的订单事件

影响:

  • 所有限价单的 WS 实时追踪完全失效
  • _on_order_update_on_user_fill 永远不会被触发
  • 每笔限价单必须等待完整 timeout(默认 600s)+ HTTP 兜底
  • 系统降级为纯 HTTP 轮询模式,延迟从毫秒级退化到秒级
  • 虽然 docs 中有"双WebSocket架构设计"文档,但未实际实现

Bug 3: userFills 部分到达时的过早解析(严重度:中高

位置: websocket_order_manager.py:416-421

# _on_user_fill 中
if tracking._ws_status is not None:  # orderUpdates 已先到
    should_resolve = True            # ← 只收到1笔fill就立即resolve
    resolve_tracking = tracking

问题: 一笔订单可能被分成多笔 partial fills。当 orderUpdates 的 "filled" 先到达后,第一笔 userFill 到达就触发立即解析。如果订单有 3 笔 partial fills,只有第 1 笔被计入加权均价。

竞态时序:

T=0:  orderUpdates "filled" → _ws_status=FILLED, 启动5s grace timer
T=1:  userFill #1 (200 PURR @ $0.01) → has_fill_price=True + _ws_status≠None → 立即resolve!
T=2:  userFill #2 (300 PURR @ $0.012) → oid已从_tracking移除,丢弃
T=3:  userFill #3 (500 PURR @ $0.011) → oid已从_tracking移除,丢弃

影响: avg_price 只反映第 1 笔 fill 的价格,而非全部 fills 的加权均价。

缓解因素: _verify_fill_completeness (executor.py:713) 会通过 HTTP 对比校验并修正。但:

  • 如果 HTTP 查询失败,错误价格将被持久化
  • 开仓时的 alt_entry_price 可能不准确,影响后续 PnL 计算和止损判断

Bug 4: 幽灵仓位清理时 PnL 数据失真(严重度:

位置: position_manager.py:1159

# sync_with_exchange 中
ghost_pnl = pos.unrealized_pnl or 0.0  # ← 使用上一次sync的unrealizedPnl
pos.status = PositionStatus.CLOSED

问题: 当 sync_with_exchange 发现交易所已无某仓位(幽灵仓位),使用 pos.unrealized_pnl(上一个 sync 周期的值)作为 realized_pnl 写入 DB。

影响场景:

  • 仓位被交易所强制平仓(清算/自动减仓)
  • 上一次 sync 时 unrealizedPnl = -$5
  • 实际清算价格导致 realized_pnl = -$50
  • DB 记录的 PnL = -$5,严重失真
  • RiskManager.update_daily_pnl 接收错误值 → 日亏损统计不准 → 风控决策基础数据错误

修复建议: 使用当前市场价格重新计算 PnL,或至少标记为"估算值":

mid = all_mids.get(coin, 0.0) if all_mids else 0.0
if mid > 0 and pos.alt_entry_price > 0:
    ghost_pnl = (mid - pos.alt_entry_price) * pos.alt_size * (1 if pos.alt_side == "buy" else -1)

Bug 5: base 腿消失后的强制平仓与冷却冲突(严重度:

位置: position_manager.py:1255-1261

# sync_with_exchange 锁外处理
for lost_key in _base_lost_pairs:
    close_result = self.close_position(
        lost_symbol, lost_base, reason="base_leg_lost", force_market=True
    )

问题: 与 Bug 1 相关。当 base 腿在交易所消失(对冲保护失效),系统尝试强制平仓 alt 腿。但如果该配对之前有平仓失败记录(处于 30s 冷却中),close_position 会因冷却而拒绝此紧急请求。

影响: alt 腿失去对冲保护后,在冷却期内无法被平仓,直到:

  • 冷却到期(最长 30s)
  • 或下一个 sync 周期再次检测到 base 缺失

叠加风险: 如果 base 腿被清算是因为剧烈波动,alt 腿在同样的波动中也可能亏损,但无法及时退出。


Bug 6: 平仓 DB 更新失败后的数据不一致窗口(严重度:中低

位置: position_manager.py:605-629

try:
    self._repo.update_position_status(...)  # DB更新
    self._repo.save_order(...)
except Exception as e:
    logger.error(f"平仓 DB 更新失败: {position.position_id} | {e}")

问题: 平仓已在交易所执行成功,但 DB 更新失败。此时:

  • 内存状态:已移除仓位(line 602: self._positions.pop(key, None))
  • 交易所:仓位已平
  • DB:仓位仍为 OPEN

影响: 系统重启后 recover_positions_from_db 会从 DB 加载该仓位,发现交易所无持仓后标记为 CLOSED(幽灵清理)。但在重启前:

  • 日 PnL 统计可能缺失这笔已平仓的收益/亏损
  • 如果 save_order 也失败,订单记录永久丢失

缓解因素: risk_manager.update_daily_pnl(realized_pnl) 在 DB 写入之后执行(line 632),且不受 DB 异常影响。重启后有自动修复机制。


总结风险矩阵

Bug 严重度 触发条件 影响范围 现有缓解
#1 止损冷却冲突 危急 平仓失败+极端行情 资金损失
#2 Testnet WS 失效 TRADING_NETWORK=testnet 性能严重退化 HTTP 兜底
#3 Fill 过早解析 中高 多笔 partial fills 价格数据不准 HTTP 校验
#4 幽灵 PnL 失真 仓位被交易所清算 风控统计错误
#5 Base 消失+冷却 对冲腿被清算+近期平仓失败 资金暴露 下个 sync 周期
#6 DB 更新失败 中低 数据库异常 数据不一致 重启自动修复

优先修复顺序: Bug #1 > Bug #3 > Bug #4 > Bug #5 > Bug #2 > Bug #6

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