双程序 AZ 值差异分析报告

双程序 AZ 值差异分析报告

分析日期:2026-03-10
涉及配对:PURR/USDC:USDC | HYPE/USDC:USDC
涉及程序:全币种通用版 (RealtimeKlineService) vs HYPE-PURR 专用版 (RealtimeKlineServiceHypePurr)

问题描述

同一配对 PURR|HYPE,在同一时刻、同样的价格下,两个独立运行的程序产出的 AZ(adaptive_z)值不一致

日志示例:

2026-03-10 05:16:32 - 📉 移动止损触发 (峰值: 1.4% → 当前: 0.6%, 回撤: 0.8%):
    PURR/USDC:USDC|HYPE/USDC:USDC | 当前价=$0.080539 | 浮动盈亏=$+0.21

2026-03-10 04:59:42 - ⚡ 近阈值 | PURR/USDC:USDC|HYPE/USDC:USDC |
    az=+1.6205 (81%thresh) | z4h=+0.3761 | raw_std=0.2810 reg_std=0.3761 (x1.3)

AZ 计算链路

K线数据 (WS → cache)
    ↓
OLS 回归 (dual-window)
    ↓
z4h = (current_spread - spread_mean) / spread_std     ← 第一层:analysis_core.py
    ↓
EMA 增量更新: ema = α * z4h + (1-α) * ema
Welford 增量标准差: effective_std = √(raw_var + floor²)
    ↓
az = (z4h - ema) / effective_std                       ← 第二层:strategy.py

关键代码位置:

  • z4h 计算:src/utils/analysis/analysis_core.pycalculate_zscore_ols()
  • az 计算:src/trading/strategy.py:419-454_update_adaptive_z()
  • 历史灌入:src/trading/orchestrator.py:174-227prime_buffer()

差异来源分析

第一层:z4h 本身不一致(已验证)

DB 实证:对同一 kline_time,两个程序在 <1 秒内写入的 z4h 存在差异。

SELECT kline_time, analysis_time, zscore_4h
FROM analysis_results
WHERE symbol = 'PURR/USDC:USDC' AND base_symbol = 'HYPE/USDC:USDC'
  AND kline_time >= NOW() - INTERVAL '1 day'
ORDER BY kline_time DESC, analysis_time DESC;

实测数据(最近 24 小时采样):

kline_time time_a time_b z4h_a z4h_b diff gap(s)
01:20:00 01:21:42.490 01:21:42.493 +0.3875 +0.3870 -0.0005 0.003
01:00:00 01:04:12.872 01:04:12.874 +0.3053 +0.3094 +0.0041 0.002
01:00:00 01:01:12.429 01:01:12.994 +0.3433 +0.3369 -0.0064 0.566
00:25:00 00:29:12.374 00:29:12.981 +0.1599 +0.1692 +0.0093 0.607
00:20:00 00:20:26.829 00:20:27.364 -0.0022 -0.0103 -0.0080 0.535
23:10:00 23:11:53.638 23:11:53.944 +0.1335 +0.1245 -0.0090 0.306

统计

  • 差异频率:几乎每个 5min candle 都至少出现 1 次 z4h 不一致
  • 差异幅度:0.0003 ~ 0.0093
  • 写入间隔:0.002s ~ 0.6s

原因:两个程序各自维护独立的 K 线内存缓存 (_klines_cache)。WS tick 到达后,_refresh_kline_in_cache() 更新当前 forming candle 的 close 价格。两个程序的分析 worker 在不同瞬间取到的 close 价格不同 → OLS 回归结果不同 → z4h 不同。

第二层:az 的 EMA/std 状态分化(主因)

即使 z4h 只差 0.001~0.009,经过以下内存状态的累积,az 差异会被放大:

# strategy.py — 每个程序独立维护
bl.ema = alpha * z4h + (1 - alpha) * bl.ema          # EMA 累积偏差
bl.welford_m2 += delta * delta2                        # Welford 方差累积偏差
az = (z4h - ema) / effective_std                       # 除以 std 放大差异

放大机制

  1. EMA 累积:每次 z4h 的微小差异通过 α * z4h 注入 EMA,永不衰减到零
  2. std 分化:Welford 增量方差对输入序列的差异敏感,两套 std 独立演化
  3. 除法放大az = (z4h - ema) / std,分子分母都在分化,结果差异非线性增长
  4. 正向反馈:不同的 az → 不同的开仓决策 → 不同的后续行为

第三层:启动时历史灌入的交叉污染

# orchestrator.py:177-190 — 启动时从 analysis_results 灌入历史 z4h
SELECT symbol, base_symbol, kline_time, zscore_4h
FROM (
    SELECT ..., ROW_NUMBER() OVER (
        PARTITION BY symbol, base_symbol, kline_time
        ORDER BY analysis_time DESC    -- 取最新写入的那条
    ) AS rn
    FROM analysis_results
    WHERE zscore_4h IS NOT NULL AND kline_time >= NOW() - INTERVAL '30 days'
) sub WHERE rn = 1

两个程序写入同一张表,ORDER BY analysis_time DESC 去重后选到的可能是任一程序写入的 z4h。不同启动时刻 → prime_buffer 灌入不同的 z4h 序列 → EMA/std 初始状态不同。

结论

层级 差异来源 影响 是否 Bug
第一层 z4h 计算差异(K线缓存时差) 每 tick ±0.001~0.009 否(并发时序的固有特性)
第二层 EMA/std 内存状态累积分化 az 随时间越跑越远 否(有状态计算的数学特性)
第三层 启动灌入交叉污染 初始状态即不同 否(架构设计层面的问题)

本质:不是代码 Bug,而是 同一配对不应由两个独立进程同时计算 的架构问题。

建议方案

方案 A:配对互斥(推荐)

让同一配对只由一个程序分析和交易,避免状态分化。

实现方式:在全币种程序中跳过 HYPE-PURR 专用程序已覆盖的配对,或反之。

# realtime_kline_service.py — 全币种程序跳过专用配对
EXCLUSIVE_PAIRS = {('PURR/USDC:USDC', 'HYPE/USDC:USDC')}

# 在分析任务入队时过滤
if (symbol, base_symbol) in EXCLUSIVE_PAIRS:
    continue

方案 B:analysis_results 去重写入

对同一 (symbol, base_symbol, kline_time) 在短时间内只保留第一次写入:

-- 写入时加 ON CONFLICT 跳过
INSERT INTO analysis_results (symbol, base_symbol, kline_time, ...)
VALUES (...)
ON CONFLICT (symbol, base_symbol, kline_time) DO NOTHING;

此方案仅解决第三层问题(灌入交叉污染),不解决第二层的内存状态分化。

方案 C:共享策略状态(复杂度高)

通过 Redis 等共享存储同步 EMA/std 状态,使两个程序对同一配对维护一致的策略引擎状态。复杂度高,不推荐。

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