一句话概括
我给 AlphaTrace 加数据源的时候,顺手实测了一下线上真实数据,发现一个我从未怀疑过的地方出了问题:平台在读取链上成交的那一刻,就把 92% 的现货交易丢掉了,并且什么都没说。 关于这次自我纠错,以及我差点犯下的第二个错误。
事情从一篇别人的文章开始
前几天读到一篇复盘:两个人在 Hyperliquid 的股票永续合约和传统券商 IBKR 之间做跨市场套利,十个月赚了一千万美元。文章最有价值的部分不是炫耀,而是那次亏掉 110 万美元的事故——券商行情接口刷新故障,机器人误以为两边仓位有偏差,不断做空去"修正"一个根本不存在的敞口,最后裸空 1.2 亿美元的黄金。
我当时的想法很直接:AlphaTrace 能不能也识别出"这个地址在做跨市场套利"?
AlphaTrace 是我自己写的一个量化分析平台。它不做交易,只做一件事:把链上某个公开地址的历史交易搬进来,还原每笔交易发生时的市场状态,然后让一批经典策略假设(动量、突破、均值回归……)互相竞争,看哪个最能解释这些交易为什么发生在那些时刻。它给出的永远是"最可能的解释",不是"真相",更不是"能赚钱"。
要识别跨市场套利,前提是能同时看见两个市场。于是我准备去改数据层——结果在动手之前,先做了一件本来只是走个流程的事:拿线上真实数据验一下。
实测:数字对不上
我调了 Hyperliquid 的公开接口,拉取平台正在分析的那个地址的全部历史成交。
返回 493 笔。
而打开 AlphaTrace 自己的数据库,这个地址名下只有 12 笔交易。
差了 41 倍。我第一反应是接口拉多了,或者时间范围不对。于是把返回数据按市场类型分了组:
- 现货成交 454 笔,时间跨度 2024-04 到 2026-09
- 永续成交 39 笔,时间跨度 2023-08 到 2024-03
再回头看平台里那 12 笔——它们全部来自永续。现货那 454 笔,一笔都没进来。
根因:一行很正常的代码
问题在读取链上成交的那个函数里,有一行:
| |
读起来毫无问题:永续币种列表里没有的,跳过。这个函数当初就是为永续合约写的,写的时候还想着"过滤掉无关数据,很干净"。
但 Hyperliquid 的现货成交,在接口返回里长这样:@150、@107、@4。它们不在永续列表里,于是全部被这个 return None 吃掉了。不是报错,不是警告,是安静地什么都没发生。
更麻烦的是:@150 这种编号也不是币名,它是交易所内部的索引。要把它变成人能看懂的 USDE/USDC,得额外查一张映射表。我实测确认了这条链路能走通——@107 解析出来是 HYPE/USDC,@150 是 USDE/USDC。

也就是说,这个地址 92% 的行为,平台从来没有看见过。 而它对外给出结论的时候,语气一如既往地笃定。
第二个错误:我差点把推断当事实
修法很清楚了:加两个字段区分"现货/永续"和"哪个交易所",再把现货成交接进来。我写完设计文档,还顺手在文档里写了一句自我表扬式的话——因为两条腿都在 Hyperliquid 链上,所以"这个地址在做双市场操作"可以算作事实级证据,比原文章里第二条腿在券商(看不见)的场景强。
写完之后我又核了一遍数据,然后发现这句话是错的。
永续活动集中在 2023 年 8 月到 2024 年 3 月,现货活动集中在 2024 年 4 月到 2026 年 9 月。中间断了一个月,两段几乎没有重叠。
这个地址是先后参与过两个市场,不是同时持有两条腿。它根本不是我想要找的那种跨市场套利者。
我删掉了那句话,在文档里单独写了一段自我更正,并标注:下一期假设的数据验证不能依赖这个地址,得另找真正有双腿重叠的样本。
这次侥幸在于:我是在写完之后多核了一遍数据,而不是等分析结论发布之后。如果那句话留在了文档里,后续所有基于它的推理都会建立在一个不存在的样本上——这比丢掉 92% 的数据更危险,因为丢数据至少是安静的,而编造出来的因果会一路污染下去。
这次修复真正值多少
我原本以为这只是一次"为将来的功能铺路"的地基工作:加字段、接现货、以后再写套利假设。
但实测把它的性质改了。这不是新功能的前置准备,这是在修复一个正在损坏全部分析结论的数据丢失问题。
过去所有基于这个地址的分析,都是在 8% 的数据上做的。样本量小得可怜——这恰好解释了为什么之前的假设竞赛里,排第一的策略只靠 3 笔有效数据撑着。当时我以为是数据源本身就没数据,现在知道了:是我自己把数据丢在了门口。
这带来一个必须直面的推论:历史结论需要重跑。修复之后,同一个地址应该有约 493 笔成交进入分析,而不是 39 笔。假设排名很可能变化,之前"谁是冠军"的结论可能被推翻。
这听起来像坏消息,但它其实是这次修复里最有价值的部分——一个会推翻自己历史结论的修复,才是真的修复了东西。
几个可以直接拿走的教训
一、return None 是最危险的一类筛选。 它不报错、不打日志、不打招呼,只是让数据消失。如果当初写成 raise 或者至少 log.warning,这个问题不会潜伏这么久。过滤掉"不重要"的数据这个动作本身没错,错的是让它静默发生。
二、先测真实数据,再写设计。 我的设计文档第一版是基于代码阅读写的,第二版基于实测——两版结论不一样。代码告诉你"它是怎么写的",实测告诉你"它实际做成了什么",两者经常不是一回事。
三、把自己的推断写下来之后,再核一遍。 我在文档里几乎留下了一句"这个地址在同时持有两条腿"的断言。它不是撒谎,是推断得太顺了——因为我在找跨市场套利的例子,而眼前正好有一个地址同时有现货和永续记录,就很自然地假设了它们重叠。核数据这件事,只花了不到一分钟。
四、区分"看不见"和"不存在"。 这个地址 92% 的现货活动,在平台里表现出的状态和"从未发生"完全一样。任何分析系统里,被静默过滤掉的数据,和根本不存在的数据,在结论上的表现是一致的——除非你专门去核对原始数据源。
最后一件事,也是我现在还在想的:如果我没打算改数据层,我就永远不会去核对这份数据。 我这次发现它,纯粹是因为要动那里的代码,顺手验了一下。
那些我没打算改的地方呢?
