全系统key配对升级 bug10

系统缺陷分析报告:配对维度 Key 优化后的状态

Context

系统已完成全局 Key 改为配对维度(PairKey = (symbol, base_symbol))的优化。
本报告分析优化后的代码中仍然存在的缺陷和不足,按严重程度分级。


🔴 高严重性缺陷

缺陷 1:symbol_blacklist 未做大写转换(config.py:425-432)

问题:黑名单解析时没有调用 .upper(),但检查时使用的是已大写的 coin 名称,导致大小写不一致时黑名单完全失效

# load_trading_config() 中(config.py 425-432)
symbol_blacklist = {
    s.strip()              # ❌ 未调用 .upper()
    for s in blacklist_raw.split(",")
    if s.strip()
}
# is_symbol_allowed() 中(config.py 253-254)
alt = symbol.split('/')[0]   # alt = "PURR"(来自交易所,天然大写)
# 检查:alt in self.symbol_blacklist
# 若用户配置 TRADING_SYMBOL_BLACKLIST=purr,则 "PURR" in {"purr"} = False

对比_parse_close_disabled_symbols_load_symbol_strategy_overrides_load_pair_strategy_overrides 都正确地做了 .upper()

位置src/trading/config.py 第 425-432 行
影响:用户输入小写 coin 名称时黑名单静默失效,无任何错误提示


缺陷 2:监控线程在策略引擎初始化之前启动(orchestrator.py:128-144)

问题:止损监控线程和仓位同步线程在 AdaptiveBollingerStrategy 初始化之前启动,期间 self._strategy = None

# orchestrator.py start() 中
# 1. 启动监控线程(self._strategy = None)
self._stop_loss_thread.start()    # 第128行
self._sync_thread.start()         # 第136行

# 2. 才初始化策略引擎
self._strategy = AdaptiveBollingerStrategy(self._config)  # 第144行

# 3. 再同步仓位到策略
for pos in self._position_manager.open_positions:
    self._strategy.sync_position(...)   # 第181行

风险场景:若在步骤 1-3 之间,同步线程发现幽灵仓位并关闭,策略引擎 on_position_closed 由于 self._strategy is None 跳过。此后策略引擎不会获得该仓位关闭的通知,状态不一致。

虽然 closed_pairs 中的仓位不会出现在 open_positions(已删除),所以步骤 3 的 sync_position 不会重新注册它——结果实际上是 OK 的。但这是一个脆弱的初始化顺序,存在逻辑上的竞态窗口。

位置src/trading/orchestrator.py 第 128-144 行
建议修复:先初始化策略引擎,再启动监控线程


🟡 中等严重性缺陷

缺陷 3:_close_ops 告警丢失 base_symbol(position_manager.py:906-910)

问题:同步关闭仓位时,_close_ops 元组中没有存储 base_symbol,导致告警中看不到配对信息。

# position_manager.py 第906-910行
_close_ops.append((
    pos.position_id, now, symbol,
    pos.direction, pos.alt_entry_price, pos.alt_size,
    # ❌ 缺少 pos.base_symbol
))

# 告警循环(第963行)
for _, _, symbol, direction, entry_price, size in _close_ops:
    sender_colourful(
        title=f"仓位对账: {symbol}",   # ❌ 只有 symbol,无 base_symbol
        ...
    )

影响PURR|HYPE 配对仓位被自动关闭时,运营只能看到 "仓位对账: PURR/USDC:USDC",不知道是哪个配对被关闭,排查困难。


缺陷 4:_close_with_retry 告警不含 base_symbol(orchestrator.py:703-710)

# orchestrator.py 第703-710行
sender_colourful(
    title=f"🚨 平仓重试全部失败: {symbol_to_coin(pos.symbol)}",
    content=(
        f"**币种**: {pos.symbol}\n"
        f"**方向**: {pos.direction}\n"
        # ❌ 缺少 pos.base_symbol
        ...
    ),
)

缺陷 5:on_entry_signal 可被外部调用绕过 base_symbol 非空验证

问题process_analysis 强制要求 base_symbol 非空(第296-299行),但 on_entry_signal 是公开方法,可直接被外部调用,构造 base_symbol="" 的信号,导致 key 为 (symbol, "") 的仓位与配对维度设计不符。

# orchestrator.py process_analysis(第296-299行)
base_symbol = multi_period_result.get("base_symbol", "")
if not base_symbol:
    logger.error(f"process_analysis: base_symbol 缺失,跳过本次 tick | {symbol}")
    return False   # ✅ 有验证

# 但 on_entry_signal 无此验证(第425行)
def on_entry_signal(self, symbol, multi_period_result, ...):
    signal = PairTradeSignal(
        base_symbol=multi_period_result.get("base_symbol", ""),  # ❌ 可为空
        ...
    )

缺陷 6:_signal_history 信号去重键设计与注释不符(strategy.py:542)

问题:代码注释说 "同一配对+方向+K线时间内不重复入场",但实现用的是 tick 处理时间(秒级) 而非 kline_time:

# strategy.py 第542行
signal_key = f"{symbol}:{base_symbol}:{entry_signal.direction}:{int(timestamp.timestamp())}"
#                                                                ^^^ tick处理时间,非kline_time

实际效果:同一 K 线在不同 tick 时间到来时会产生不同的 signal_key,去重几乎没有实际效果(真正的防重依赖 _prev_above_threshold 的"非首次突破"逻辑)。

位置src/trading/strategy.py 第 542 行


缺陷 7:_load_pair_strategy_overrides 回退逻辑只考虑 alt 资产(config.py:393-408)

问题:配对级参数的回退来源只检查 alt 资产(如 PURR)的币种级覆盖,完全忽略 base 资产(如 HYPE)的币种级覆盖:

# config.py 第393-408行
alt_asset = parts[0]
if alt_asset in symbol_overrides:   # 只检查 alt
    base_cfg = {symbol_overrides[alt_asset]}
else:
    base_cfg = global_cfg           # ❌ 未考虑 base 资产的覆盖

用户配置了 TRADING_STRATEGY_OVERRIDE_SYMBOLS=HYPE 后,PURR|HYPE 配对不会继承 HYPE 的参数。


🟢 低严重性 / 信息完整性问题

缺陷 8:any_ready 属性读取未加锁(strategy.py:139-145)

@property
def any_ready(self) -> bool:
    for key, bl in self._baselines.items():  # ❌ 无 self._lock
        ...

实际上只在 start() 日志中使用一次,此时后台线程尚未运行,风险极低。但从代码规范角度,应与其他只读操作保持一致。


缺陷 9:on_exit_signal 使用 O(n) 遍历而非 O(1) dict 查找(orchestrator.py:558-561)

# orchestrator.py 第558-561行
if (symbol, base_symbol) not in {
    (p.symbol, p.base_symbol or "") for p in self._position_manager.open_positions
}:

open_positions 返回的是副本列表,查找复杂度为 O(n)。PositionManager._positions 是 dict,应暴露 has_position(symbol, base_symbol) 方法实现 O(1) 查找。


缺陷 10:孤儿仓位均值回归退出被永久禁用

设计意图但存在风险:孤儿仓位(base_symbol=""entry_adaptive_z=0.0)在策略层永远不会产生退出信号:

# strategy.py 第587-589行
baseline = tracker.entry_adaptive_z
if abs(baseline) < 1e-9:
    return None  # 孤儿仓位直接跳过

孤儿仓位只能靠固定止损 / 移动止损 / 持仓超时退出,这是设计意图。但持仓超时默认 72h,在市场不利时风险敞口较大。


缺陷 11:_calculate_pnl_pct 相对名义价值而非保证金(risk_manager.py:199)

止损百分比 stop_loss_pct=0.03 是相对名义价值,不是保证金。在 3 倍杠杆下,名义价值的 3% = 保证金的 9% 亏损。这可能与用户直觉不符(用户可能认为 3% 是保证金止损),需文档明确。


关键文件位置速查

缺陷编号 文件 行号
1(黑名单大小写) src/trading/config.py 425-432
2(线程顺序) src/trading/orchestrator.py 128-144
3(close_ops 缺 base_symbol) src/trading/position_manager.py 906-910, 963
4(retry 告警缺 base_symbol) src/trading/orchestrator.py 703-710
5(on_entry_signal 验证) src/trading/orchestrator.py 425, 461
6(signal_history 键) src/trading/strategy.py 542
7(pair 参数回退) src/trading/config.py 393-408
8(any_ready 无锁) src/trading/strategy.py 139-145
9(O(n) 查找) src/trading/orchestrator.py 558-561
10(孤儿退出) src/trading/strategy.py 587-589
11(PnL 计算语义) src/trading/risk_manager.py 199

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