main分支开仓告警BUG

"开仓信号详细告警"模块 Bug 分析报告

分析范围

文件 职责
[signal_alert_formatter.py](file:///Users/test/Downloads/Trading-in-websocket/src/utils/monitoring/signal_alert_formatter.py) 告警内容格式化(三级降级)
[risk_evaluator.py](file:///Users/test/Downloads/Trading-in-websocket/src/utils/analysis/risk_evaluator.py) 5维度加权风险评估
[alert_sender.py](file:///Users/test/Downloads/Trading-in-websocket/src/utils/monitoring/alert_sender.py) 告警发送(限流 + 去重)
[orchestrator.py](file:///Users/test/Downloads/Trading-in-websocket/src/trading/orchestrator.py) 调用入口 _send_entry_alert

🔴 严重 BUG

BUG #1:_section_health_monitor 缺少空 dict 防护 — 必崩

[!CAUTION]
严重性:高 — 生产环境中当 (4h, 60d) 周期数据缺失时,函数必定抛出异常。

[signal_alert_formatter.py:313-314](file:///Users/test/Downloads/Trading-in-websocket/src/utils/monitoring/signal_alert_formatter.py#L313-L314):

period_data = _get_period_data(details, ("4h", "60d"))
monitor = period_data.get("health_monitor", {})  # ← period_data 可能是 {}

_get_period_data 在找不到 key 时返回 {}.get("health_monitor", {}) 返回 {},后续检查 if not monitor 会进入早期返回分支。这一段本身不会崩。

但对比同一文件中 [_section_window_comparison:346-348](file:///Users/test/Downloads/Trading-in-websocket/src/utils/monitoring/signal_alert_formatter.py#L346-L348),逻辑完全相同 — 同样安全。

结论:经再次审查,此处实际没有 bug,有防护,不会崩。撤销此项。


BUG #1(更正):_section_risk_assessment 趋势风险得分展示语义反转

[!WARNING]
严重性:中高 — 用户看到的分数与其直觉理解相反,可能导致交易决策失误。

[signal_alert_formatter.py:375](file:///Users/test/Downloads/Trading-in-websocket/src/utils/monitoring/signal_alert_formatter.py#L375):

rows.append(f"| 趋势风险 | {100 - d['trend_risk']['score']:.0f} | {d['trend_risk']['level']} | 25% |")
  • trend_risk['score'] risk_evaluator.py 中的定义是:score 越高 → 风险越大(见 [L102](file:///Users/test/Downloads/Trading-in-websocket/src/utils/analysis/risk_evaluator.py#L102))
  • 告警展示时对 trend_risk 做了 100 - score 反转(变成"质量分"),但表头列名是"趋势风险"
  • 其他 4 个维度(协整质量、相关性、Z-score 一致性、协整健康)的 score 本身就是"质量分"(越高越好),直接展示无反转

结果:5 行表格中,4 个维度的"得分"列含义是"越高越好",但"趋势风险"行的含义也被反转为"越高越好"(反转后),跟列头"趋势风险"的字面含义相矛盾。用户看到 趋势风险 | 80 会以为风险很高,实际是 100 - 20 = 80,代表风险很

建议:将行名改为"趋势安全性"或者不做反转直接展示原始风险分。


🟡 中等 BUG

BUG #2:alert_sender 限流后被去重遗漏记录 — 可能丢失告警

[alert_sender.py:59-67](file:///Users/test/Downloads/Trading-in-websocket/src/utils/monitoring/alert_sender.py#L59-L67):

# 1. 限流
if priority != "high" and not self._check_rate_limit(pair_name):
    return AlertStatus.THROTTLED    # ← 限流时直接返回

# 2. 去重
content_hash = hashlib.md5(content.encode()).hexdigest()
if self._is_duplicate(content_hash):
    return AlertStatus.THROTTLED    # ← 去重时直接返回

被限流的消息没有被记录到 _recent_hashes。这意味着:

  1. 第一条消息被限流 → 没记录 hash
  2. 限流窗口过后,相同内容的消息不会被去重 → 可能重复发送

但在实际场景中,由于每次开仓的 position_idsignal_id 不同,内容 hash 几乎不会重复,所以影响有限


BUG #3:_format_full 超长截断时索引越界风险

[signal_alert_formatter.py:186](file:///Users/test/Downloads/Trading-in-websocket/src/utils/monitoring/signal_alert_formatter.py#L186):

core = [sections[1], sections[7], sections[8], sections[9]] if len(sections) >= 10 else sections[:4]

builders 列表总是有 10 个元素,所以 sections 在不出异常时长度固定为 10,条件 len(sections) >= 10 总为真。但如果某个 builder 抛异常后被跳过(虽然当前代码用了 try/except 不会跳过),或者未来修改了 builders 列表,这里的硬编码索引就会出问题。当前不会触发,但代码脆弱。


BUG #4:_section_zscore_validation — 符号一致性判断逻辑有缺陷

[signal_alert_formatter.py:236](file:///Users/test/Downloads/Trading-in-websocket/src/utils/monitoring/signal_alert_formatter.py#L236):

majority_sign = sum(1 if z > 0 else -1 for z in non_zero) if non_zero else 0

majority_sign 是一个整数(例如 3、1、-1、-3),而非严格的多数符号标志。

[L241](file:///Users/test/Downloads/Trading-in-websocket/src/utils/monitoring/signal_alert_formatter.py#L241):

sign_ok = "✅" if (majority_sign > 0 and z > 0) or (majority_sign < 0 and z < 0) or z == 0 else "⚠️"

边界情况:当 majority_sign == 0(例如 zscore_list = [1.0, -1.0, 0],non_zero = [1.0, -1.0],sum = 0),所有非零 zscore 都会被标记为 ⚠️,即使这种分歧本身就是中性信号的正常状态。这并不是严重 bug,但展示效果有误导性。


🟢 设计层面的注意事项(非 BUG,但值得关注)

注意 #1:降级链的递归调用可能掩盖真实错误

[signal_alert_formatter.py:102-120](file:///Users/test/Downloads/Trading-in-websocket/src/utils/monitoring/signal_alert_formatter.py#L102-L120):

except Exception as e:
    if level == AlertLevel.FULL:
        return format_signal_alert(..., level=AlertLevel.SIMPLIFIED)
    elif level == AlertLevel.SIMPLIFIED:
        return format_signal_alert(..., level=AlertLevel.BASIC)

三级降级是递归调用 format_signal_alert 本身。如果 传入参数本身有问题(例如 signal 对象属性缺失),降级到 SIMPLIFIED 和 BASIC 时仍然会访问相同的属性,可能连续三级都失败,最终落入 L121 的最终兜底。三级降级机制对格式化逻辑内部的错误有效,但对入参数据错误无效。

不过这并不是严重 bug,因为最终兜底(L123-L127)只访问了 signal.symbolsignal.directionsignal.zscore_4h 这三个核心属性,在 PairTradeSignal 构造时已保证存在。

注意 #2:evaluate_risk 的异常吞没

[risk_evaluator.py:87-95](file:///Users/test/Downloads/Trading-in-websocket/src/utils/analysis/risk_evaluator.py#L87-L95):

风险评估异常时返回默认"中性评估"(50 分、中风险),不会阻断告警发送。但 用户无法从告警中看出这是默认值而非真实评估结果,可能误导交易决策。


总结

# 严重性 描述 文件
1 🔴 中高 趋势风险得分展示语义反转,用户看到的分数与直觉理解相反 signal_alert_formatter.py:375
2 🟡 低 限流消息未记录 hash,理论上可能在限流窗口过后重复发送 alert_sender.py:59-67
3 🟡 低 超长截断使用硬编码索引,代码脆弱 signal_alert_formatter.py:186
4 🟡 低 zscore 符号一致性 majority_sign==0 时展示误导 signal_alert_formatter.py:236

[!IMPORTANT]
整体评估:该模块不存在会导致资金损失或系统崩溃的严重 BUG。三级降级机制 + 区块级 try/except 的防御性编程做得比较完善,即使格式化某个区块失败也只会显示"数据缺失",不会影响开仓执行本身。最值得关注的是 BUG #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