记录一次 PC 反复卡死诊断结项(2026-10-08)
记录一次 PC 反复卡死诊断结项(2026-10-08)既然 HL_DRY_RUN=1,蓝屏就不是故障,是等 Verifier 点名的预期行为。
今天把这台从 6 月到 10 月反复硬锁死的交易机收了个尾:硬件根因已经修掉,软件根因还在监控,剩下就是让 Driver Verifier 布防,等它自己指名。整个过程踩了不少 Windows 运维的坑,值得原样记下来。
一、主机与运行环境这台机器 PC-20260408JPNQ 是 7x24 交易栈宿主,跟 craw_app1_wz 同机。核心配置:Intel Core i5-14600KF(Raptor Lake Refresh,Family6 Model183),Maxsun MS-Terminator B760M D5 主板,单条 16GB Asgard DDR5 内存,1TB NVMe 系统盘加 8TB SATA 数据盘,RTX 2080 Ti,Windows 11 26H2 build 26300.9457。上面跑着 defi_dashboard(Streamlit+MySQL+多个 python/node)、 ...
记录一次从假 PnL 修到真根因的 11 天(2026-09-22)
记录一次从假 PnL 修到真根因的 11 天(2026-09-22)真正让 trader 停摆的不是某个策略赚不到钱,而是链上有一批它自己根本不知道的仓位,正死死占着保证金。 今天这份复盘,本质上是把 09-11 那笔 -$98.57 的 orphan_reconcile 假损失给翻了个底朝天——最后发现我们之前一直在跟自己的影子打架。
一、早上先查盘面:11 天没开新仓今天 09:58 我启动诊断的时候,第一反应是“别又是策略逻辑写挂了”。psutil 里能看到的一串进程全是 ALIVE:smart_money_trader 10s 主循环在 tick=140,smart_money_signal 每 15s 左右写一次日志,watchdog 09:54:36 刚跑完一轮,streamlit_dashboard 端口 8501 返回 HTTP 200。看起来系统全活。
再看 sm_account 表,09:47:40 的快照写着 current_usdc=10045.98,realized_pnl=+51.19,W/L=58/ ...
记录一次心跳的修复(2026-09-10)
记录一次心跳的修复(2026-09-10)最危险的不是进程死了,而是它还活着,却已经很久没说过一句话
这天前后处理了几件看起来不相干的事:换掉一个 VLM 模型、给一个本地转发进程补 timeout、修交易 worker 的心跳、核正一份自动判定、读一份策略审计。等它们在记录里排到一起才发现,说的其实是同一件事——每一个长期运行的环节,都得持续向系统证明自己还活着。一旦它不再说话,系统不会立刻崩,它只会安静地做出一个错误的判断。
下面按当天实际处理的顺序记。
从一条 130 秒的规律开始监控里最先扎眼的是这个数:交易 worker 每隔约 130 秒就重连一次,节律几乎不抖。
反复核过之后,这个 130 秒不是巧合——它刚好等于连接守卫的超时阈值 + 平均一次重连所需的耗时。也就是说,重连不是网络断了,是被守卫"误判"成断了:它等了太久没等到该有的时间戳刷新,就按超时处理,断线,重连,然后 130 秒后再来一遍。日志里这种由"陈旧"引发的心跳超时,一次会话里数到了 3885 次。
顺着 _last_success_ts 往下挖连接守卫的判定本身 ...
记录一次重连风暴的排查(2026-09-09)
记录一次重连风暴的排查(2026-09-09)心跳超时说的从来不是"连接断了",而是"没人告诉它还活着"
smart_money_trader 的日志里堆着几千次心跳超时。表面看是网络不稳,但一台机器如果真烂到每两分钟断一次,它早该被停掉了——交易进程却一直在跑,仓位也没乱。所以这次不猜,先把它拆开看。
这一天的活,从头到尾只有一件事:把"到底是谁在超时"查清楚。 修不修得好是第二步。
先看清风暴的形状先把原始日志预解析了一遍,而不是凭印象判断。
reconnect_preanalysis.json 记录的这场风暴,时间跨度是 2026-08-18T22:00:00 到 2026-09-05T13:57:21。进程侧的 _HEARTBEAT_TIMEOUT 是 120.0 秒,stall_profile.jsonl 里的分析确认了一件事:重连的节奏大约是 每 130 秒一次——正好对上"超时阈值 + 重连周期"。
预解析里还埋着一个更早的坑。第一次统计出来的心跳数是 4355,但这个数字是错的:它把 47 ...
记录一次夜里的异常(2026-08-23)
记录一次夜里的异常(2026-08-23)一晚上三条"死因",长得像同一个症状
有些夜里,你会以为在处理一个 bug,其实是四个。那天从 OOS 停写查起,查到最后发现:一条 3 天的数据空洞、一个设错位置的超时、一笔拖了 4.6 天才被硬止损的仓,还有一行缺了 global 的赋值。它们互不相干,却在同一晚一起发作。
一、停写三天的 OOS先看最表面的:factor_ic_oos.db.oos_ticks 从 08-20 18:08 之后就再没写入了,空了 3 天。
runtime_state 给出了另一半答案:next_run_at=None,last_run_at=1787220565(≈08-20 18:08)——调度器在那之后就没再排下一次运行。automation 配置本身还是 ACTIVE,状态显示正常,那只是假象。
代码侧的根因,是 compute_regime()(L177)里的 fetch_candles 没有任何 try/except:
沙箱/网络异常 → fetch_candles 抛异常 → 冒泡到 main()(无 ...
记录一次单腿的确认(2026-08-15)
记录一次单腿的确认(2026-08-15)数据库里说开着一笔空单,链上却是多单——那一刻要确认的,是这一条腿到底是不是真的。
08-15 例行对账时,发现 hl_testnet.db 里躺着一条很老的 OPEN 记录。对账这件事平时看起来很枯燥,但它偶尔会拽出一笔"幽灵仓"——数据库以为还开着的仓位,链上其实早就不存在了。
一条 25 天的 SOL 空单最先跳出来的是这一条:
hl_testnet.db 有 1 个 OPEN 仓:SOL SHORT @ $77.246,开仓时间 2026-07-22,距今约 25 天。
25 天没动的仓位,本身就不正常。这极可能就是 07-30 那一波"半平仓" 遗留下来的——当时 BTC / ETH / SOL 处理过一轮,通过数据库看,只有 SOL 这一笔没清掉。
DB 和链上,一张一张对判断它是不是幽灵仓,做法只有一个:把数据库里的每一个 OPEN,和链上的实际仓位逐一对照。
DB OPEN(sm_positions)
链上真实
一致?
ETH LONG @1891.3 ...
记录一次模拟盘的排障(2026-07-10)
记录一次模拟盘的排障(2026-07-10)模拟盘最坑的地方,不是它亏了钱,而是它让你以为策略已经能跑了
这天打开 http://0.0.0.0:8501/,主流币模拟交易那条曲线几乎全是红的。接下来的几个小时基本都在同一个循环里转:发现一处数据不对,翻代码,改掉,重启,然后发现下一处不对。
下面按当时踩到的顺序记。
第一次运行就崩maincoin_paper.py 里 scan_signals() 返回的是 dict,但 run_tick() 全程用属性访问:sig.confidence、sig.signal.value。所以第一轮 mc_paper_loop 一起来就崩了。
改成 sig["confidence"]、sig["signal"],顺手删掉没用的 Signal import。这个 bug 的性质其实很典型——函数签名和调用方之间没有契约,写的时候两边各自都对,合起来就炸。
开仓时间和平仓时间一模一样翻交易记录时发现一件事:每一笔的 opened_at 和 closed_at 是同一秒,大量交易 duration=0s。
根因在 ...
记录一次 sim_loop 被境外网络掐死 17h56m(2026-06-20)
记录一次 sim_loop 被境外网络掐死 17h56m(2026-06-20)网络断了就别反复激活版本,激活不等于启动。 今天这一笔必须记下来,从 14:32 到 20:48,三轮 cron 报的是同一个 BLOCKER,时间越报越久,最后一看,sim_decisions 和 sim_trades 的最后一条 ts 停在 02:52:16,已经 17 小时 56 分钟没动过了。
一、20:48 那轮巡检,差距已经拉到了 17h56m20:48 是今天第四轮 cron。前两轮其实已经报警了:14:32 报 gap 11h40m,17:40 报 14.8h。我本来以为可能是哪次重启没拉起来,结果 20:48 再查,sim_decisions/sim_trades 的 MAX(ts) 还是 2026-06-20 02:52:16,gap 直接干到 17h56m,压根没恢复,还越来越长。
不止这一个:sim_dexscreener_signals 整表 last_ts 停在 6/18 11:43,1h/24h 计数全是 0,已经 stale 2 天 9 小时;b ...
记录一次看板数据丢失的修复(2026-06-15)
记录一次看板数据丢失的修复(2026-06-15)数据"消失"的时候,先看它是被删了,还是根本没写进去
TVL 看板某天开始是空的。不是加载慢,是图表直接报错,一张干干净净的空白。
这天把这块看板修回来。
一块空的图页面 pages/1_tvl_rank.py 里的 Plotly 柱状图,x 轴取的是 protocol 字段:
1px.bar(x='protocol')
它读到的是一个空列,于是图表报错、数据全丢。
前端报错看着像渲染问题,但真正的疑点应该往后推一步:数据是什么时候没的?是同步进来的数据本来就空,还是本来有、后来被写没了?
根因:往一个不存在的列里写查下来,问题在写入端,而且比想象中更基础。
tvl_rank 表原本就没有 protocol 列。而 save_tvl_rank 在做 INSERT 的时候,把协议名写进了这个不存在的列——结果数据全丢。
同时,页面又反过来用 px.bar(x='protocol') 去引用这个空列,于是一起报错。
这条链子很典型:写入端写了个不存在的目标,读取端读了个空的结果,两 ...
记录一次回测链路的抢修(2026-06-10)
记录一次回测链路的抢修(2026-06-10)一个数字离谱到没人信的时候,问题多半不在那个数字上
新做的 12 号页跑完之后,v1.16.4 的 BUY 数量是 15254。而同一时间,11 号页里真实成交只有两百多笔。
差了将近两个数量级。这天做的事就是把这个数字修回来。
一张对不上的表:11 页 215,12 页 15254起因很朴素:11 号页显示"买的币很少",12 号页却显示买了 15254 次。策略筛选出要买的币本来应该非常少,两个页面却给出了完全不同的结论。
这里先要分清两个页面在干什么。11 号页读的是 sim_trades,真实跑出来的成交;12 号页是反事实回测,用历史数据按每个版本的规则重放一遍。回测的意义在于"把历史数据和样本重新过一遍,快速筛出最近真正赚钱的策略,顺带看出优化方向"——它能跑出的笔数本来就该和实盘一个量级,绝不该多出几十倍。
15254 这个数字本身就在喊"我不对"。
根因一:combo 的 max() 被避雷策略钻了空子第一层根因在 engine.py 的 score_combo( ...