Nautilus Trader Hyperliquid 适配器测试数据指南:真实 API 样本的采集、加载与测试实践
【免费下载链接】nautilus_traderProduction-grade Rust-native trading engine with deterministic event-driven architecture项目地址: https://gitcode.com/GitHub_Trending/na/nautilus_trader
本指南围绕 Nautilus Trader 开源仓库中 Hyperliquid 适配器的test_data目录(crates/adapters/hyperliquid/test_data)展开,系统讲解该目录中真实 API 响应样本的组织方式、采集方法、测试加载模式与数据维护规范。读完本文,你将掌握如何用仓库自带的采集二进制抓取主网真实 JSON 样本、如何在 Rust 单元测试中通过load_test_data加载并反序列化这些样本,以及如何遵循数据大小策略保持测试夹具(fixture)轻量可控。
一、为什么需要真实 API 样本作为测试数据
Hyperliquid 适配器(crate 名为nautilus-hyperliquid,源码位于 crates/adapters/hyperliquid/src)通过 HTTP 与 WebSocket 两条通道与 Hyperliquid 交易所交互。交易系统对消息格式的解析正确性要求极高:字段缺失、精度不一致、数值溢出都可能导致行情或成交解析失败。为此,适配器将真实主网 API 响应以 JSON 快照形式固化在test_data目录中,供单元测试直接加载断言。
该目录的定位在 README.md 中写得很明确:“This directory contains real API response samples for testing.”即:这里的文件不是手工编造的模拟数据,而是真实交易所响应的精简样本,用于在不依赖网络、不消耗 API 配额的前提下验证解析逻辑。
二、测试数据文件清单与用途
test_data目录实际包含 18 个 JSON 文件,README 按数据通道分为两类:
HTTP 公共数据(无需账户)
| 文件 | 用途 |
|---|---|
http_meta_perp_sample.json | 永续合约市场元数据(抽样 3 个市场) |
http_meta_spot_sample.json | 现货市场元数据(抽样 3 个市场) |
http_l2_book_btc.json | BTC 订单簿快照(每侧 5 档) |
http_l2_book_snapshot.json | 已有订单簿测试数据 |
此外,目录中还沉淀了更多实测样本(README 之外,源码测试实际引用的文件):
http_all_perp_metas_non_usdc_collateral.json/http_spot_meta_non_usdc_collateral.json:非 USDC 保证金市场的完整元数据,用于校验保证金模式的解析分支(见 http/parse.rs);http_funding_history.json:资金费率历史,被 data.rs 的测试引用;http_recent_trades_btc.json:最近成交记录(data.rs);http_clearinghouse_state_negative_total_raw_usd.json:负余额清算状态的边界样本,直接以内联include_str!方式用于 common/parse.rs 的测试;http_user_fills_dust_conversion.json:dust 转换类成交填充样本(http/parse.rs);http_order_status_frontend_market.json、http_historical_order_liquidation_market.json:订单状态与历史清算订单样本(http/models.rs)。
WebSocket 公共数据(无需账户)
| 文件 | 用途 |
|---|---|
ws_trades_sample.json | 实时成交消息样本 |
ws_l2_book_sample.json | 订单簿更新消息样本 |
ws_book_data.json | 已有簿数据测试样本 |
WebSocket 侧同样有 README 之外的重要样本:
ws_user_fill_liquidation.json:用户成交强平消息,用于 websocket/messages.rs 的解析测试;ws_user_twap_history.json、ws_user_twap_slice_fills.json:TWAP 历史与切片成交,被 websocket/parse.rs 引用;ws_all_dexs_asset_ctxs.json:DEX 资产上下文,用于 websocket/handler.rs 的处理测试。
样本内容示例
以http_meta_perp_sample.json为例,它仅保留universe数组的前 3 个市场,每个条目包含isDelisted、maxLeverage、name、onlyIsolated、szDecimals等字段:
{ "universe": [ { "isDelisted": null, "maxLeverage": 40, "name": "BTC", "onlyIsolated": null, "szDecimals": 5 }, { "isDelisted": null, "maxLeverage": 25, "name": "ETH", "onlyIsolated": null, "szDecimals": 4 }, { "isDelisted": null, "maxLeverage": 5, "name": "ATOM", "onlyIsolated": null, "szDecimals": 2 } ] }http_l2_book_btc.json则展示了订单簿快照结构:coin+levels(买/卖两档数组)+time,每个 level 由字符串精度的px(价格)与sz(数量)组成,例如{"px": "110427.0", "sz": "4.11882"}。字符串精度格式正是 Hyperliquid API 的真实返回形式,测试用它来验证适配器对十进制精度的处理。
三、采集新测试数据:从主网抓取真实响应
3.1 HTTP 数据采集
README 给出了采集命令:
cargo run --bin capture-test-data需要说明的是,该命令在 README 中写作capture-test-data,而 Cargo.toml 中实际声明的二进制名为hyperliquid-capture-test-data,推荐使用完整包名运行:
cargo run -p nautilus-hyperliquid --bin hyperliquid-capture-test-data该二进制源码位于 bin/capture_test_data.rs,其核心逻辑展示了“抓取 → 裁剪 → 落盘”的完整流程:
let client = HyperliquidHttpClient::new(HyperliquidEnvironment::Mainnet, 60, None)?; // 1. 拉取永续合约元数据,仅保留前 3 个市场控制文件体积 let meta = client.info_meta().await?; let sample_meta = serde_json::json!({ "universe": meta.universe.iter().take(3).collect::<Vec<_>>() }); fs::write( "test_data/http_meta_perp_sample.json", serde_json::to_string_pretty(&sample_meta)?, )?; // 2. 拉取 BTC 订单簿,仅保留每侧前 5 档 let book = client.info_l2_book("BTC").await?; let sample_book = serde_json::json!({ "coin": book.coin, "levels": vec![ book.levels.first().unwrap().iter().take(5).collect::<Vec<_>>(), book.levels[1].iter().take(5).collect::<Vec<_>>() ], "time": book.time }); fs::write("test_data/http_l2_book_btc.json", serde_json::to_string_pretty(&sample_book)?)?;值得注意的细节:
- 客户端通过
HyperliquidHttpClient::new(HyperliquidEnvironment::Mainnet, 60, None)构造,第一个参数指定主网环境,第二个参数为超时秒数(60s),第三个为可选凭据——因为采集的是公共数据,无需签名; - 采集脚本刻意在源头裁剪:
take(3)与take(5)保证写入磁盘的 JSON 体积最小化; - 脚本注释明确说明现货元数据端点“尚未在客户端实现”,因此
http_meta_spot_sample.json需要手工从其他渠道或后续版本补充。
3.2 WebSocket 数据采集
README 中对应的命令为:
cargo run --bin capture-ws-test-data不过从当前仓库源码结构看(bin 目录与 Cargo.toml 中声明的全部 11 个二进制),并未发现capture-ws-test-data对应的源文件,WebSocket 样本目前主要通过 bin/ws_data.rs 等订阅脚本实时观察后手动固化为 fixture。因此建议以 Cargo.toml 中实际存在的二进制为准,WebSocket 样本的采集可按“订阅 → 观察消息结构 → 写入test_data/”的流程手工完成。
3.3 采集后的位置约定
采集脚本将文件写入相对路径test_data/...,这是因为二进制在 crate 根目录下运行(cargo run -p nautilus-hyperliquid时工作目录即 crate 根)。这也与测试加载器load_test_data的路径约定保持一致——它同样使用相对路径test_data/{filename}。
四、在测试中加载样本:load_test_data 与两种引用方式
4.1 共享加载器
README 给出的核心工具函数是load_test_data,其真实实现位于 src/common/testing.rs:
/// Loads and deserializes a JSON test fixture from the `test_data/` directory. pub fn load_test_data<T>(filename: &str) -> T where T: serde::de::DeserializeOwned, { let path = format!("test_data/{filename}"); let content = std::fs::read_to_string(&path) .unwrap_or_else(|e| panic!("Failed to read test data at {path}: {e}")); serde_json::from_str(&content) .unwrap_or_else(|e| panic!("Failed to parse test data at {path}: {e}")) }该函数是一个泛型 fixture 加载器:泛型参数T约束为serde::de::DeserializeOwned,意味着任何实现了Deserialize的模型类型(如PerpMeta、HyperliquidL2Book)都可以直接传入;读取或解析失败时通过panic!快速失败,便于在测试中立刻暴露 fixture 与模型定义不一致的问题。
4.2 README 中的测试写法
README 给出了一种便捷的测试内局部封装方式:
fn load_test_data<T>(filename: &str) -> T where T: serde::de::DeserializeOwned, { let path = format!("test_data/{}", filename); let content = std::fs::read_to_string(path).expect("Failed to read test data"); serde_json::from_str(&content).expect("Failed to parse test data") } #[rstest] fn test_parse_perpetuals_metadata() { let meta: PerpMetadata = load_test_data("http_meta_perp_sample.json"); // assertions... }通过rstest属性宏组织测试,直接调用加载器即可获得类型化数据,然后针对模型字段编写断言。
4.3 源码中的两种真实引用模式
仓库源码实际采用了两种方式消费这些 fixture,各有适用场景:
模式一:运行时文件加载(load_test_data),用于较重的解析集成测试。例如 src/http/parse.rs 中,用http_meta_perp_sample.json验证永续元数据解析、用http_l2_book_btc.json验证订单簿解析:
let meta: PerpMeta = load_test_data("http_meta_perp_sample.json"); // ...解析与断言 let book: HyperliquidL2Book = load_test_data("http_l2_book_btc.json"); // ...解析与断言模式二:编译期内联(include_str!),用于对关键边界样本的零 IO 引用。例如 src/common/parse.rs 中的负余额样本,以及 src/websocket/messages.rs、src/websocket/parse.rs 中的 WebSocket 消息样本:
let raw = include_str!("../../test_data/ws_all_dexs_asset_ctxs.json");include_str!在编译期把文件内容嵌入二进制,测试运行时零文件系统依赖、天然免路径问题,适合在#[cfg(test)]模块中直接断言原始 JSON 的解析结果。
五、数据大小策略:维护夹具的硬性规范
README 明确了测试数据目录的维护纪律,这是保证仓库长期可维护的关键约定:
- 保持文件小体积(每个文件 < 50KB):避免把完整 API 响应整个塞进仓库,防止二进制与克隆体积膨胀;
- 大数组只抽样 3–5 个元素:如元数据
universe只保留 3 个市场、订单簿每侧只保留 5 档,采集脚本中的take(3)/take(5)正是这条策略的代码化落实; - 优先使用真实主网数据:真实数据能覆盖 API 实际返回的各种边界(如字符串精度、null 字段、非 USDC 保证金),远比手工编造数据可靠;
- API 响应格式变化时及时更新对应文件:Hyperliquid 端格式升级后,若 fixture 过期,
load_test_data会直接 panic,反而起到了“格式变更哨兵”的作用,倒逼适配器同步升级解析逻辑。
此外,从 benches/common/mod.rs 的注释可以看到,性能基准(benchmark)刻意保持自包含、不依赖test_data中的真实抓取数据,说明该目录的定位是纯测试夹具,与基准测试的数据供给是分离的。
六、延伸阅读
若想深入理解这些样本背后的解析与通信实现,可以继续阅读:
- HTTP 客户端与模型:crates/adapters/hyperliquid/src/http/client.rs、crates/adapters/hyperliquid/src/http/models.rs、crates/adapters/hyperliquid/src/http/parse.rs;
- WebSocket 订阅与解析:crates/adapters/hyperliquid/src/websocket/parse.rs、crates/adapters/hyperliquid/src/websocket/messages.rs;
- 公共模型与工具:crates/adapters/hyperliquid/src/common/models.rs、crates/adapters/hyperliquid/src/common/testing.rs;
- 采集二进制声明:crates/adapters/hyperliquid/Cargo.toml。
七、小结
test_data目录是 Nautilus Trader Hyperliquid 适配器测试体系的“数据基座”:它用真实主网响应的小体积样本,支撑起从 HTTP 元数据、订单簿到 WebSocket 成交、TWAP 的各类解析测试;load_test_data与include_str!两种加载模式覆盖了运行时集成测试与编译期内联断言两种场景;采集脚本与大小策略则保证了样本可再生成、可维护、不膨胀。理解这套 fixture 规范,不仅能帮你快速读懂适配器测试,也为在自有项目中建立“真实数据驱动”的解析测试管线提供了可直接复制的范式。
【免费下载链接】nautilus_traderProduction-grade Rust-native trading engine with deterministic event-driven architecture项目地址: https://gitcode.com/GitHub_Trending/na/nautilus_trader
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考