Files
Chan/research/live/deploy/start.sh
T
jackandCursor ef2a85dd8c 币池可配,实测十币的排队代价;标出漂移的 tick 地板
加 --syms(start.sh 传 SYMS),默认仍是三币。TRX 不进十币池:实盘口径 208 天
只有 5 笔,ATR 门控几乎全刷掉。

十币实测(对比三币):

  queue_ms        2 → 169ms(P90 519ms,每刻最大排队中位 533ms)
  inner_ms      144 → 196ms(CPU 争抢也拖慢了纯计算)
  lag_signal_ms 621 → 958ms;同一收盘时刻最后算完的币中位 1376ms

排队从可忽略变成主项,与「10 币 × 196ms ÷ 2 核 ≈ 640ms 突发」吻合。这也是
之前「加核没用」那个结论唯一会翻转的场景:CPU 占用率只有约 3%,问题纯粹是
所有币同一秒收盘的突发,加核压的是并行度而非单币耗时。

但 800ms 哨兵不受影响——它喂的是 t_data − kline_ts(数据腿),十币下逐币
154~532ms 全在线内。加币不碰那道闸。

另外发现一个会被静默误读的东西:ADA/AVAX/DOGE/LINK/LTC 的漂移中位精确等于
半个 tick 且在四个延迟点上完全相同。那不是漂移,是中价的最小变动量——ADA 半
tick 就有 2.34bp。拿这个数去比预算会误判某币不可做。shadow_report 加了
tick_floor() 标注;方向上安全(真实漂移只会更小,这些是上界)。

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

132 lines
5.8 KiB
Bash
Executable File
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env bash
# 启动影子采集器。跑之前先 setup.sh。
#
# SHADOW_SITE=aws-tokyo bash research/live/deploy/start.sh
# SHADOW_SITE=aws-tokyo HOURS=168 WORKERS=2 bash research/live/deploy/start.sh
#
# 为什么 SHADOW_SITE 必填:它写进每一行数据,是两台机器的数据合起来之后
# 唯一的来源区分。缺了就只能靠文件路径猜,一合并就分不清了。
#
# 为什么时钟不同步就拒绝启动:所有延迟数字都是「本地时钟 − 交易所 K 线收盘
# 时间戳」。时钟偏 50ms,全部延迟就凭空偏 50ms,而这**不会有任何报错**,
# 只会让跨地对比得出一个完全错误的结论。这类静默错误必须在启动就挡掉。
set -euo pipefail
NAME="${NAME:-shadow}"
# lean 模式让 TF_DF 只构建到中枢,跳过线段/走势中枢/MACD 状态机。等价性由
# verify_lean_parity.py 在影子这条路径上逐根验过(180 窗口 / 90 命中零分歧)。
# 设 0 可退回 full,用来复量两模式的耗时差。
SHADOW_LEAN="${SHADOW_LEAN:-1}"
# 币池。默认三个流动性最好的做滑点测量;十币池是实际要交易的那批(TRX 剔除,
# ATR 门控几乎全刷掉)。币数直接决定排队:所有币同一秒收盘,2 核上 10 个币
# 需要约 640ms 墙钟才算完,最后一个币的信号会落在 800ms 哨兵线之外。
SYMS="${SYMS:-BTC,ETH,SOL}"
IMAGE="${SHADOW_IMAGE:-hummingbot/hummingbot:latest}"
HOURS="${HOURS:-168}"
WORKERS="${WORKERS:-2}"
MAX_OFFSET_MS="${MAX_OFFSET_MS:-10}"
REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../../.." && pwd)"
OUT="$REPO_ROOT/research/out"
die() { printf '\033[31m错误:%s\033[0m\n' "$*" >&2; exit 1; }
say() { printf '\n\033[1m==> %s\033[0m\n' "$*"; }
[[ -n "${SHADOW_SITE:-}" ]] || die "必须设 SHADOW_SITE,例如 SHADOW_SITE=aws-tokyo。
它写进每一行数据,是跨地对比时区分来源的唯一依据。"
say "站点 $SHADOW_SITE"
say "校验时钟同步"
offset_ms=""
if command -v chronyc >/dev/null 2>&1; then
if ! chronyc tracking >/dev/null 2>&1; then
die "chrony 没在跑。先 sudo systemctl start chrony(或 chronyd"
fi
# System time 那行形如 "0.000058703 seconds fast of NTP time"
line="$(chronyc tracking | grep '^System time' || true)"
secs="$(awk '{print $4}' <<<"$line")"
offset_ms="$(awk -v s="$secs" 'BEGIN{printf "%.3f", s*1000}')"
echo "$line"
leap="$(chronyc tracking | awk -F': *' '/Leap status/{print $2}')"
[[ "$leap" == "Normal" ]] || die "chrony leap status = $leap,尚未收敛。等几分钟再试"
over="$(awk -v o="$offset_ms" -v m="$MAX_OFFSET_MS" 'BEGIN{print (o>m)?1:0}')"
[[ "$over" == "0" ]] || die "时钟偏移 ${offset_ms}ms 超过阈值 ${MAX_OFFSET_MS}ms。
所有延迟测量都会同向偏这么多且不报错,跨地对比会得出错误结论。
先等 chrony 收敛,或调 MAX_OFFSET_MS(不建议)。"
echo "偏移 ${offset_ms}ms,在 ${MAX_OFFSET_MS}ms 阈值内"
elif command -v timedatectl >/dev/null 2>&1; then
timedatectl | grep -qi 'synchronized: yes' \
|| die "系统时钟未同步。装 chrony:见 setup.sh"
echo "timedatectl 报已同步(无 chronyc,拿不到具体偏移)"
else
die "既无 chronyc 也无 timedatectl,无法确认时钟。装 chrony 后再启动"
fi
say "检查镜像"
docker image inspect "$IMAGE" >/dev/null 2>&1 || die "没有镜像 $IMAGE,先跑 setup.sh"
digest="$(docker image inspect "$IMAGE" --format '{{if .RepoDigests}}{{index .RepoDigests 0}}{{end}}' 2>/dev/null || true)"
say "停掉旧容器"
docker rm -f "$NAME" >/dev/null 2>&1 || true
mkdir -p "$OUT"
say "量网络距离"
netprobe="$(bash "$(dirname "${BASH_SOURCE[0]}")/netprobe.sh" 2>/dev/null || echo '{}')"
echo "$netprobe" | grep -E 'edge_org|self_org|self_city|icmp_min_ms|tcp_connect' || true
# 运行元数据。两地数据对不上时,先看这个文件——镜像摘要、代码版本、时钟偏移
# 三者任一不同都足以解释差异,不必去猜。网络探测并在这里,因为「换机房能省
# 多少」这个问题只有它能回答(到达延迟里网络只占约 2ms,用它比等于用公斤秤称克)。
meta="$OUT/run_meta_${SHADOW_SITE}.json"
cat >"$meta" <<EOF
{
"site": "$SHADOW_SITE",
"hostname": "$(hostname)",
"started_utc": "$(date -u +%Y-%m-%dT%H:%M:%SZ)",
"tz": "$(date +%Z%z)",
"clock_offset_ms": ${offset_ms:-null},
"git_commit": "$(git -C "$REPO_ROOT" rev-parse --short HEAD 2>/dev/null || echo unknown)",
"git_dirty": $(git -C "$REPO_ROOT" diff --quiet 2>/dev/null && echo false || echo true),
"image": "$IMAGE",
"image_digest": "${digest:-unknown}",
"hours": $HOURS,
"workers": $WORKERS,
"kernel": "$(uname -r)",
"nproc": $(nproc),
"cpu_model": "$(awk -F': ' '/model name/{print $2; exit}' /proc/cpuinfo 2>/dev/null || echo unknown)",
"mem_gb": $(free -g | awk '/^Mem:/{print $2}'),
"net": $netprobe
}
EOF
echo "元数据已写 $meta"
say "启动容器 $NAME"
docker run -d --name "$NAME" -w /home/hummingbot \
--restart unless-stopped \
-e PYTHONPATH=/home/hummingbot:/repo/research:/repo/research/live:/repo \
-e SHADOW_SITE="$SHADOW_SITE" \
-e SHADOW_LEAN="$SHADOW_LEAN" \
-v "$REPO_ROOT:/repo:ro" \
-v "$OUT:/out" \
--entrypoint /opt/conda/envs/hummingbot/bin/python \
"$IMAGE" /repo/research/live/shadow_hb.py \
--hours "$HOURS" --workers "$WORKERS" --syms "$SYMS" >/dev/null
echo "已启动。等启动自检(补丁断言 + 历史回填,约 60 秒)…"
sleep 45
docker logs "$NAME" 2>&1 | tail -12
cat <<EOF
看日志: docker logs -f $NAME
看状态: bash research/live/deploy/status.sh
停止: docker rm -f $NAME
启动日志里应能看到:
[补丁] 覆盖生效 —— 换根解析补丁有效(缺了会静默慢 1.06 秒)
成交流已挂 [...] —— maker 成交率要用
就绪 … 根 —— 历史回填完成
EOF