Files
Chan/research/live/deploy
jackandCursor 6c843639d5 影子路径开 lean 模式,实测完整链路 1410 → 683ms
拉到新引擎后在服务器侧直接实测,没有沿用 3.4 倍那个换算——那是在旧代码上量的。

先做等价性验证。不能直接引用 step46 的对拍结论:它固化的是 bsp_list 那条链的
哈希,而影子路径走 find_fast_bsp3 + build_htf_zones + htf_fx_timeline +
attach_htf_context,两条链读的东西不一样。所以新建 verify_lean_parity.py 在
这条路径上逐根对拍 compute() 的每个返回字段。

其中一个坑:随机取窗口测不到信号分支。信号密度约 1/2000 根,头 12 个窗口命中
0 个,「一致」只覆盖了早退路径。改成一半窗口对齐到已知信号根,命中率才上来。
最终 180 窗口 / 两模式各 90 命中 / 零分歧。

生产实测(inner_ms,容器内同口径):

  compute_ms      646 → 132ms   4.89x
  inner_ms        612 → 128ms   4.80x
  lag_signal_ms  1410 → 683ms   完整链路,落回 800ms 线内

比 3.4 倍更好,因为是引擎 ~3.5x 叠 lean ~1.35x。

两点判读上的订正:

- lag_data_ms 那 576→490ms 是噪声,不要记在引擎账上。均值 721±36 vs
  740±127,重叠;而且引擎本来就影响不到交易所与网络那一段。
- 仍有 44% 的根超 800ms,但尾部现在完全由数据腿主导(lag_data P90 1482ms
  vs compute P90 237ms)。计算既不是瓶颈也不是尾部主因了,继续压计算换不到
  尾部改善。800ms 那道闸取的是最近 30 根的中位数,683ms 已满足。

start.sh 加 SHADOW_LEAN(默认 1),设 0 可退回 full 复量两模式差异。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-28 04:24:08 +08:00
..

影子采集器部署

在第二台机器上跑一套完全相同的采集,用来看不同地理位置的数据差异。

先看这一节:延迟的瓶颈不在机房

滑点里最大的一项是延迟漂移,所以延迟越低越好。但延迟拆开之后,可优化的 地方和直觉不一样。腾讯云新加坡实测(2026-08-28):

构成 中位 随机房位置变化吗
数据到达(交易所推送 + 回源 + 网络) 506ms 只有网络那段,2ms
信号计算(本机 CPU 646ms 不变,取决于 CPU
合计到可下单 1315ms

ws.bitget.com 解析出来是 CloudFrontdxotqhr62n6z4.cloudfront.net), 落在新加坡的 AS16509(Amazon)。所以我们连的是 AWS 的 CDN 边缘,不是 Bitget 自己的机房。从腾讯云新加坡到这个边缘:

ICMP 往返      2.1ms
TCP 握手       3.3ms
TLS 完成       9.0ms
首字节        87.8ms   ← 减去 TLS 的 9ms,约 79ms 是 CloudFront 回源开销

回源那 79ms 和交易所自己的推送节奏,不管我们坐在哪都一样。而随位置变化的 只有那 2ms 往返。换机房的全部空间是 1~2ms,占总延迟 1315ms 的 0.1%。

真正的大头是本机 646ms 的信号计算:每根 K 线要在 2000 根 1m 加 800 根 5m 上 重建缠论结构,三个币同时收盘而本机只有 2 核、2 个计算进程,第三个币还要排队。

所以第二台机器该测的是 CPU 规格,不是地理位置。compute_ms,不是 lag_data_ms。3 个币至少要 3 个 worker,--workers 给到核数减一。

三个必须一致,一个必须不同

必须一致,否则差异分不清是地理位置还是环境造成的:

  • 代码版本git_commit)——同一个 commit
  • 镜像摘要image_digest)——同一个 hummingbot 镜像
  • 时钟——两台都同步到 NTP,偏移都在 10ms 内

必须不同:

  • SHADOW_SITE——写进每一行数据,是合并后区分来源的唯一依据

start.sh 会把这四项连同内核、核数、内存一起写进 research/out/run_meta_<site>.json。两地数据对不上时先看这个文件。

时钟为什么是硬门槛

所有延迟数字都是「本地时钟 − 交易所 K 线收盘时间戳」。时钟偏 50ms,全部 延迟就同向偏 50ms,而且不会有任何报错——只会让跨地对比得出一个干净、 自信、且完全错误的结论。所以 start.sh 在时钟未同步或偏移超阈值时直接 拒绝启动,而不是打个警告了事。

步骤

在新机器上:

git clone ssh://jack@git.jackyu66.com:2222/jack/chan.git
cd chan && git checkout chan

bash research/live/deploy/setup.sh          # 装 docker + chrony,拉镜像

# 站点名带上机型,因为要比的是 CPU 而不是位置
SHADOW_SITE=aws-sg-c7a4x WORKERS=3 bash research/live/deploy/start.sh

WORKERS 按核数减一给。3 个币同时收盘,worker 少于 3 就会排队,而排队时间 直接计入 compute_ms。本机 2 核只能给 2,这本身就是 646ms 里的一部分。

确认健康:

bash research/live/deploy/status.sh

启动日志里应当看到:

[补丁] 覆盖生效:基类取首元素 … 本地取末元素 …
成交流已挂 ['BTC', 'ETH', 'SOL']
就绪 3.0s · 1m [2001, 2001, 2001] 根 · 5m [801, 801, 801] 根

第一行尤其重要。上游 Bitget 连接器的换根解析有 bug(只取多根消息的首元素), 补丁把它修掉拿回约 1.06 秒。补丁若失效是静默的——不崩不报错,只是延迟悄悄 退回 1.4 秒,所以启动时做了断言。

对比

把两站的 research/out/ 收到一处(同名文件会覆盖,所以分目录放):

mkdir -p collected/sg collected/aws
rsync -av sg-box:chan/research/out/   collected/sg/
rsync -av aws-box:chan/research/out/  collected/aws/

python research/live/compare_sites.py \
  --glob 'collected/*/shadow_latency.csv' \
  --drift-glob 'collected/*/shadow_drift.csv' \
  --meta-dir collected/sg --meta-dir collected/aws

对比脚本做两件事值得说明:

  • 只取各站都有的 K 线做配对比较。不取交集就可能在比不同时段,而延迟对 市场活跃度敏感。
  • 配对差的符号占比而不只是两个中位数相减。同根配对消掉了市场状态, 「A 比 B 慢的根占多少」比「两个中位数差多少」更能说明有无系统性差异。

判读上有一条自检:同一个固定延迟点上,两站的漂移应当几乎相同——漂移是 市场性质,与机器位置无关。若漂移也差很多,先怀疑时钟或时段没对齐,而不是 急着下结论。

同理,lag_data_ms 两站也应当几乎相同(网络那段只有 2ms 空间)。真正该出现 差异的是 compute_ms。如果 lag_data_ms 差很多,先查时钟——比查网络更可能。

⚠ 但先读下面「资源占用」一节:compute_ms 的差异几乎全部来自单核性能,而 现役机型之间单核差距很小。跨站点比 CPU 这件事本身收益有限,本节流程保留 是为了比网络与时钟,不建议为了比 CPU 单独开机器。

资源占用

本机实测(2 vCPU EPYC 9K65 / 3 个币 / 2 worker):内存 410MiB,CPU 均值 1~3%。盘口与成交流落盘约 15MB/天(gzip),一周在百 MB 内。

内存和平均 CPU 都不是约束。约束是单根 K 线的计算延迟,而它是纯单线程的:

compute_ms  中位 764ms   父进程测的墙钟,含排队
  queue_ms  中位   3ms   等空闲 worker
  inner_ms  中位 695ms   进程内真正在算

queue_ms 只有 3ms,说明 2 个 worker 跑 3 个币并不排队——三个币的收盘消息 错峰到达(SOL 最晚,排 66ms),没有真正的并发争抢。

这条结论直接否掉了「换更强机器」这个方向。 加核只能压 queue_ms,而它已经 是 3ms;695ms 全在单线程里,取决于单核性能。t3a.mediumZen 12017)单核比 本机 Zen 5 慢 1.8~2 倍,换过去 compute_ms 会涨到 1200ms 以上。c7a / c6a 这类 现代机型单核与本机相当,也换不到东西。

要压这 695ms 只有算法一条路:现在每分钟把 2001 根从头算一遍,其中 2000 根的 结构与上一分钟完全相同。

--workers 2 是因为信号计算走独立进程池、不能阻塞事件循环。币数超过 worker 数才会看到 queue_ms 上来;届时加 worker 有效,加到与币数相等即可。