Files
Chan/live/deploy/status.sh
T
jackandCursor 3bbd92784c 下单失败必须推 Telegram,并让心跳判读「做了却没建上」
实盘首日丢掉 4 个信号、白跑 5 小时,根因是 40774,但**没有任何推送**。
日志里一直在报,可外在表现和"没信号"一模一样——这正是整套通知设计要防的
那类静默经济损失,却恰好漏了下单失败这一条。

现在:入场被拒或止盈挂不上都推。按信号聚合成一条,不按腿推(避免一个信号
两条)。两腿全失败时说明"这个信号丢了"并带累计失败次数与常见原因;部分腿
失败时说明"收益结构已偏离设计"——那种情况仓位是半的,不是设计的两腿结构。

心跳原先只把数字并排列出来:"已做 4 · 在场 0/3" 那 5 小时一直在打,但没有
一处说这是异常。现在分开计 n_built 与 n_order_fail,做了却一次没建上直接
打 。只看 n_took 分不出"做了"和"建上了"。

status.sh 同样加判读:统计 entry_fail 次数并打出最后一条错误与码表。

tp_fail 现在也记进 live_trades.jsonl,原先只打日志不落盘。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-29 00:48:56 +08:00

95 lines
3.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
# 生产机一屏体检。不改任何状态,随时可跑。
set -uo pipefail
APP=/opt/chan
STATE=/var/lib/chan-live/state
CONF=/etc/chan-live/live.env
hr() { printf '─── %s\n' "$1"; }
hr "服务"
for u in chan-live-ship chan-live-exec; do
act="$(systemctl is-active "$u" 2>/dev/null)"
since="$(systemctl show -p ActiveEnterTimestamp --value "$u" 2>/dev/null)"
nrs="$(systemctl show -p NRestarts --value "$u" 2>/dev/null)"
printf ' %-16s %-8s 自 %s · 重启 %s 次\n' \
"$u" "$act" "${since:-?}" "${nrs:-0}"
done
hr "时钟(staleness 闸依赖它)"
if command -v chronyc >/dev/null; then
chronyc tracking 2>/dev/null | grep -E "Reference ID|System time" | sed 's/^/ /'
else
echo " ⚠ 没装 chrony"
fi
hr "信号总线"
bus="${SIGNAL_BUS:-$STATE/signals_live.jsonl}"
[[ -f "$CONF" ]] && bus="$(grep -E '^SIGNAL_BUS=' "$CONF" | tail -1 | cut -d= -f2-)"
bus="${bus:-$STATE/signals_live.jsonl}"
if [[ -s "$bus" ]]; then
n="$(wc -l < "$bus")"
last_ts="$(tail -1 "$bus" | grep -o '"kline_ts":[0-9]*' | cut -d: -f2)"
if [[ -n "$last_ts" ]]; then
age=$(( $(date +%s) - last_ts / 1000 ))
printf ' %s 条 · 最近一条 %d 分钟前\n' "$n" "$((age / 60))"
else
echo " $n 条(最后一行没有 kline_ts"
fi
# 信号 6.8 个/天,所以「几小时没有」是正常的。真要看的是搬运连着没有
echo " 注:6.8 个/天,长时间没有新信号是正常的;要判健康看下面的搬运心跳"
else
echo " 空或不存在:$bus"
fi
hr "闸的状态"
# 在场仓位**不落盘**:重启时由 reconcile 查交易所并平掉(见 live_exec.py
# 的 reconcile 注释)。所以这里只报当日计数,仓位要看交易所或下面的成交流
if [[ -f "$STATE/live_state.json" ]]; then
python3 - "$STATE/live_state.json" <<'PY'
import json, sys, time
d = json.load(open(sys.argv[1]))
today = time.strftime("%Y-%m-%d")
day = d.get("day", "?")
stale = "" if day == today else f" ⚠ 是 {day} 的,跨日后首次开仓时才归零"
print(f" 当日 {day}{stale}")
pnl, n = d.get("pnl_day", 0.0), d.get("n_day", 0)
# pnl 恒为 0 而又确实开过仓,说明 watch() 没在记账 → MAX_DAY_LOSS 是死的
warn = " ⚠ 开过仓但盈亏仍为 0,查 watch() 是否在跑" if n and pnl == 0 else ""
print(f" 已开 {n} 笔 · 盈亏 {pnl:+.2f} USDT{warn}")
print(f" 已处理信号键 {len(d.get('done', []))} 个(幂等去重用,留最近 5000")
PY
else
echo " 还没有状态文件(没开过仓)"
fi
hr "最近成交"
if [[ -s "$STATE/live_trades.jsonl" ]]; then
tail -5 "$STATE/live_trades.jsonl" | sed 's/^/ /'
# 「下过单但一次都没建上」是明确的故障,而它的外在表现和"没信号"一样,
# 不主动判读就会白跑几小时。首日就是这么丢掉 4 个信号的
nf="$(grep -c '"ev": "entry_fail"' "$STATE/live_trades.jsonl" || true)"
nb="$(grep -c '"ev": "opened", "key": [^]]*\[{' "$STATE/live_trades.jsonl" || true)"
ne="$(grep -c '"ev": "entry"' "$STATE/live_trades.jsonl" || true)"
if [[ "${nf:-0}" -gt 0 ]]; then
echo
echo " ⛔ 有 $nf 次入场被拒(共尝试 $ne 个信号)"
echo " 最后一条错误:"
grep '"ev": "entry_fail"' "$STATE/live_trades.jsonl" | tail -1 \
| sed 's/^/ /'
echo " 40774 = 持仓模式不匹配 · 40762/40786 = 保证金不足"
fi
else
echo " 还没有成交记录"
fi
hr "搬运心跳(最近 3 条)"
journalctl -u chan-live-ship -n 200 --no-pager 2>/dev/null \
| grep -F "[心跳]" | tail -3 | sed 's/^/ /' \
|| echo " 还没有心跳(每 5 分钟一条)"
hr "最近报错"
journalctl -u chan-live-exec -u chan-live-ship -n 400 --no-pager -p warning 2>/dev/null \
| tail -8 | sed 's/^/ /' || echo " 无"