Bug 修复报告:币对大小写匹配问题

Bug 修复报告:币对大小写匹配问题

日期:2026-02-20
文件src/trading/config.py
严重程度:高(功能性 Bug,导致配置项完全失效)


问题概述

config.py 中存在两类大小写处理不一致问题,导致多个配置项在运行时静默失效。


根因分析

Bug 1:is_close_disabled() 永远返回 False(高危)

位置_parse_close_disabled_symbols() + is_close_disabled()

复现路径

环境变量: TRADING_CLOSE_DISABLED_SYMBOLS=purr
↓
_parse_close_disabled_symbols() 存储: {"PURR"}   ← 强制 .upper()
↓
交易所返回 symbol: "purr/usdc:usdc"
↓
is_close_disabled() 比较: "purr" in {"PURR"}     ← 永远 False
↓
结果: 平仓保护失效,系统持续尝试平仓并产生告警

根本原因_parse_close_disabled_symbols().upper() 存储配置值,但 is_close_disabled() 直接用交易所原始符号(可能是小写)做精确比较,两侧大小写不一致导致匹配永远失败。


Bug 2:get_strategy_params() 破坏交易所原始数据(中危)

位置get_strategy_params() 第 124–125 行(修复前)

# 修复前(错误)
alt_asset = symbol.split('/')[0].upper()   # 强制大写交易所符号

问题:交易所返回的 symbol 是系统的原始数据,强制转换违反了"交易所数据永不修改"原则。虽然在当前代码路径中因 override keys 也是大写而偶然正确,但属于脆弱实现,在大小写敏感场景下会静默失败。


设计缺陷:两个黑名单字段行为不一致

字段 存储方式(修复前) 比较方式(修复前)
symbol_blacklist 原始大小写(用户输入) 精确匹配
close_disabled_symbols 强制大写 精确匹配

两个功能相似的字段行为不一致,增加了使用和维护的认知负担。


修复方案

核心原则

  • 交易所 API 数据:保持原始大小写,永不修改
  • 环境变量配置 keys:规范化为大写(匹配 Linux ENV 命名惯例,如 TRADING_STRATEGY_PURR_EMA_SPAN
  • 所有字符串比较:统一使用 casefold() 大小写不敏感匹配

具体改动

1. 新增 _casefold_get() 辅助函数

def _casefold_get(d: dict, key: str):
    """大小写不敏感字典查找(先精确匹配,再 casefold 匹配)"""
    if key in d:
        return d[key]
    key_cf = key.casefold()
    for k, v in d.items():
        if k.casefold() == key_cf:
            return v
    return None

精确匹配优先,保证性能;回退到 casefold 全扫描,保证正确性。


2. get_strategy_params() — 删除对交易所符号的 .upper()

# 修复前
alt_asset = symbol.split('/')[0].upper()

# 修复后
alt_asset = symbol.split('/')[0]          # 保持原始大小写
result = _casefold_get(self.symbol_strategy_overrides, alt_asset)  # 不敏感查找

3. is_symbol_allowed() — casefold 集合比较

# 修复前
if alt in self.symbol_blacklist:           # 精确匹配,大小写敏感

# 修复后
blacklist_lower = {s.casefold() for s in self.symbol_blacklist}
if alt.casefold() in blacklist_lower:      # casefold 不敏感匹配

4. is_close_disabled() — casefold 集合比较

# 修复前
if alt in self.close_disabled_symbols:     # 精确匹配,大小写敏感

# 修复后
disabled_lower = {s.casefold() for s in self.close_disabled_symbols}
if alt.casefold() in disabled_lower:       # casefold 不敏感匹配

5. _parse_close_disabled_symbols() — 删除强制大写

# 修复前
return {s.strip().upper() for s in raw.split(",") if s.strip()}

# 修复后
return {s.strip() for s in raw.split(",") if s.strip()}
# 保持原始大小写,比较由调用方用 casefold 完成

修复后行为对照

场景 修复前 修复后
TRADING_CLOSE_DISABLED_SYMBOLS=purr,交易所返回 "purr" ❌ 匹配失败,平仓保护失效 ✅ 正确禁止平仓
TRADING_CLOSE_DISABLED_SYMBOLS=PURR,交易所返回 "purr" ❌ 匹配失败 ✅ 正确禁止平仓
TRADING_SYMBOL_BLACKLIST=PURR,交易所返回 "purr" ❌ 匹配失败,黑名单失效 ✅ 正确拦截
TRADING_STRATEGY_OVERRIDE_SYMBOLS=PURR,查询 "purr/usdc:usdc" 偶然正确(脆弱) ✅ 明确正确

未改动部分

_load_symbol_strategy_overrides()_load_pair_strategy_overrides() 中的 .upper() 保留

原因:这两个函数处理的是环境变量配置 keys,用于构建 Linux 大小写敏感的环境变量前缀(如 TRADING_STRATEGY_PURR_EMA_SPAN)。规范化为大写是正确且必要的。查找侧已由 _casefold_get() 负责不敏感匹配,两者配合无需改动加载逻辑。


数据流设计总结

环境变量输入 (用户配置)
  ├── override keys       → .upper() 规范化  → casefold 查找
  ├── symbol_blacklist    → 原始大小写保留   → casefold 比较
  └── close_disabled      → 原始大小写保留   → casefold 比较

交易所 API 数据 (symbol)
  └── 永不转换,原样传入各方法 → casefold 比较

验证

运行内联验证脚本,覆盖以下场景:

✅ _casefold_get:精确匹配 / casefold 回退 / 无匹配返回 None
✅ get_strategy_params:purr/PURR 均命中 override,ETH 回退全局默认
✅ is_symbol_allowed:小写/大写/配对级黑名单均正确拦截,非黑名单放行
✅ is_close_disabled:小写/大写/配对级均正确禁止,非配置符号放行

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