Logo
新用户订阅 RPC,首月享 6.5 折优惠查看优惠
OnFinality Learn
网络与协议指南阅读约 14 分钟

Hyperliquid WebSocket 交易与成交:频道机制

一份实用指南,介绍如何订阅 Hyperliquid 的 trades 和 userFills WebSocket 频道、与 Info API 对账,以及避免重连后出现重复成交。

TL;DR

Hyperliquid 为交易和成交数据提供了两个不同的 WebSocket 频道:公开的 trades 频道,以币种为范围,推送该市场上的每一笔交易;以及 userFills 频道,以用户为范围,仅推送已认证账户的成交。订阅错误的范围是最常见的集成错误。订阅协议使用包含 type 和 subscription 对象的 JSON 消息,subscription 对象包含频道名称及其参数(coin 或 user)。由于 WebSocket 消息仅在连接期间投递,连接断开会产生数据缺口,必须从 Info API 回填,并通过稳定的成交标识(而非仅靠时间戳)进行对账。本指南涵盖频道机制、载荷结构、可运行的 Node.js 示例、用于自测的结果表,以及常见故障模式的排查方法。

交易与成交数据的两种范围

Hyperliquid 的 WebSocket API 将交易数据分为两个范围截然不同的频道。公开的 trades 频道按币种订阅,推送该市场上的每一笔交易,无论发起者是谁。userFills 频道以用户为范围,仅推送属于已认证账户的成交。为用例选择错误的频道是最常见的集成错误:订阅了 userFills 的市场数据消费者,除非以用户身份认证,否则什么也看不到;而订阅了 trades 的账户追踪器则会收到大量无关的市场活动。

这一区别很重要,因为两个频道有不同的认证要求、不同的载荷结构以及不同的对账路径。trades 频道是公开的,无需认证;userFills 频道需要已认证的用户上下文。关于连接生命周期和通用订阅的完整概述,请参阅 Hyperliquid WebSocket 订阅与连接生命周期。关于用户成交的拉取式等价方案,请参阅 通过 Info API 读取 Hyperliquid 用户成交与订单状态

频道列表和订阅消息格式由 Hyperliquid WebSocket 订阅文档 定义,各频道的载荷结构由 WebSocket 数据格式文档 定义,重连后必须对账的拉取接口由 Info 端点文档 定义。请将这些文档视为字段名和频道名的权威来源。

  • trades:以币种为范围,公开,推送市场上的每一笔交易。
  • userFills:以用户为范围,需认证,仅推送你账户的成交。
  • 范围错误是最常见的集成错误。

订阅协议与消息格式

Hyperliquid WebSocket 订阅协议遵循 Hyperliquid 文档(WebSocket 订阅)中记录的 JSON 消息格式。要订阅,客户端发送一条消息,其中 type 字段设为 "subscribe",并包含一个 subscription 对象,内含频道名称及其参数。对于 trades 频道,参数是币种符号;对于 userFills,参数是用户地址。服务器会返回订阅确认,回显订阅详情。要取消订阅,客户端发送 type 为 "unsubscribe" 的消息,并附带相同的 subscription 对象。

确认消息很重要,可用于确认订阅已被接受,以及检测无效币种名称或缺少认证等错误。Hyperliquid 文档(WebSocket POST 请求和数据格式)规定了每个频道的精确载荷结构。典型的订阅消息形如 {"type":"subscribe","subscription":{"type":"trades","coin":"ETH"}}(trades),或 {"type":"subscribe","subscription":{"type":"userFills","user":"0x..."}}(userFills)。确认消息会包含相同的 subscription 对象,使客户端能够将响应与请求关联起来。

const WebSocket = require('ws');
const ws = new WebSocket('wss://api.hyperliquid.xyz/ws');

ws.on('open', () => {
  // Subscribe to trades for ETH
  ws.send(JSON.stringify({
    type: 'subscribe',
    subscription: { type: 'trades', coin: 'ETH' }
  }));
  // Subscribe to userFills for the authenticated account
  ws.send(JSON.stringify({
    type: 'subscribe',
    subscription: { type: 'userFills', user: '0xYourAddress' }
  }));
});

ws.on('message', (data) => {
  const msg = JSON.parse(data);
  if (msg.channel === 'subscriptionResponse') {
    console.log('Subscription acknowledged:', msg.data);
  } else if (msg.channel === 'trades') {
    console.log('Trade:', msg.data);
  } else if (msg.channel === 'userFills') {
    console.log('Fill:', msg.data);
  }
});

载荷结构与成交标识字段

trades 频道载荷包含 coin、price、size、side、time 和 trade id 等字段。userFills 频道载荷包含类似字段,并在适用时增加 fee 和 closed PnL。根据 Hyperliquid 文档(WebSocket POST 请求和数据格式),每个频道的精确字段名和类型均有记录。对于去重,稳定标识通常是 trade id(或 fill id)与 userFills 的用户地址的组合,或 trades 的 trade id 本身。仅靠时间戳不是稳定标识,因为多笔交易可能共享同一毫秒,而且 Info API 报告记录的顺序可能略有不同。

在将实时 WebSocket 成交与 Info API userFills 响应进行对账时,应基于共享的标识字段(如 fill id 或 trade id)匹配,而不是基于时间戳。Info API userFills 端点返回的记录结构类似,但可能包含额外字段或不同的排序。以成交标识为键的去重集合可确保边界记录不被重复。要深入了解 Info API 接口,请参阅 通过 Info API 读取 Hyperliquid 用户成交与订单状态

  • trades:coin、price、size、side、time、trade id。
  • userFills:增加 fee、closed PnL 和用户特定字段。
  • 稳定标识:trade id 或 fill id,而非时间戳。

重连缺口与回填策略

WebSocket 连接仅在打开期间投递消息。如果连接断开——由于网络问题、服务器重启或客户端错误——缺口期间发生的任何交易或成交都会丢失,因为频道不会重放错过的消息。这是流式模型的根本局限。要恢复,客户端必须检测断开、记录最后收到消息的时间戳,然后从 Info API 回填。对于 userFills,可以使用时间范围查询 Info API userFills 端点,或获取最近的成交。对于 trades,可以使用 Info API 中的币种 trades 视图。

回填必须与实时流对账以避免重复。由于 Info API 和 WebSocket 可能以略有不同的顺序或时间戳报告同一笔成交,去重必须基于成交标识。有界去重集合(例如有最大容量的 Set)可以跟踪最近的 fill id,并在边界处抑制重复。Hyperliquid 文档(Info 端点 userFills)是拉取式等价方案的权威来源。关于回填期间的速率限制注意事项,请参阅 Hyperliquid API 速率限制:Info 与 Exchange

const WebSocket = require('ws');
const fetch = require('node-fetch');

const WS_URL = 'wss://api.hyperliquid.xyz/ws';
const INFO_URL = 'https://api.hyperliquid.xyz/info';
const USER = '0xYourAddress';
const COIN = 'ETH';

const seenFills = new Set();
const MAX_SEEN = 10000;
let lastDisconnectTime = null;

function addSeen(id) {
  if (seenFills.size >= MAX_SEEN) {
    const first = seenFills.values().next().value;
    seenFills.delete(first);
  }
  seenFills.add(id);
}

async function backfillUserFills(startTime) {
  const res = await fetch(INFO_URL, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      type: 'userFills',
      user: USER,
      startTime: startTime
    })
  });
  const fills = await res.json();
  for (const fill of fills) {
    const id = fill.tid || fill.hash;
    if (!seenFills.has(id)) {
      addSeen(id);
      console.log('Backfilled fill:', fill);
    }
  }
}

function connect() {
  const ws = new WebSocket(WS_URL);

  ws.on('open', () => {
    console.log('Connected');
    ws.send(JSON.stringify({
      type: 'subscribe',
      subscription: { type: 'trades', coin: COIN }
    }));
    ws.send(JSON.stringify({
      type: 'subscribe',
      subscription: { type: 'userFills', user: USER }
    }));
    if (lastDisconnectTime) {
      backfillUserFills(lastDisconnectTime);
    }
  });

  ws.on('message', (data) => {
    const msg = JSON.parse(data);
    if (msg.channel === 'userFills') {
      for (const fill of msg.data) {
        const id = fill.tid || fill.hash;
        if (!seenFills.has(id)) {
          addSeen(id);
          console.log('Live fill:', fill);
        }
      }
    } else if (msg.channel === 'trades') {
      console.log('Trade:', msg.data);
    }
  });

  ws.on('close', () => {
    console.log('Disconnected');
    lastDisconnectTime = Date.now();
    setTimeout(connect, 1000);
  });

  ws.on('error', (err) => {
    console.error('WebSocket error:', err);
  });
}

connect();

实时与拉取接口之间的对账

对账是将实时 WebSocket 成交与其 Info API 对应记录进行匹配的过程。两个接口可能以略有不同的顺序或时间戳报告同一笔成交,因为它们由不同的内部路径生成。仅基于时间戳的去重策略要么会丢弃共享同一时间戳的合法成交,要么会重复时间戳略有不同的成交。正确的做法是基于成交标识去重——通常是 trade id(tid)或交易哈希——它在两个接口之间是稳定的。

断开后回填时,获取缺口期间的 Info API userFills,并插入任何标识尚未在去重集合中的成交。由于 Info API 可能以不同顺序返回成交,客户端不应假设第一条记录就是最早的。相反,应处理所有记录并依赖标识集合。对于 trades,同样的原则适用:使用 trade id 在实时 trades 频道和 Info API 币种 trades 视图之间去重。关于相关机制,请参阅 Hyperliquid 资金费率机制

  • 基于成交标识(tid/hash)匹配,而非时间戳。
  • 处理所有回填记录;不要假设顺序。
  • 使用有界集合限制内存。

带去重与回填的可运行 Node.js 示例

以下 Node.js 示例使用 ws 库订阅一个币种的 trades 和一个账户的 userFills。它维护一个有界去重集合、检测断开、通过 Info API 回填并重新订阅。代码是自包含的,安装 ws 和 node-fetch 后可用 node 运行。请将 USER 和 COIN 常量替换为你自己的值。

该示例演示了核心机制:在 open 时发送订阅消息;在 message 时解析频道并处理成交;在 close 时记录断开时间并安排重连;在重连时使用记录的时间从 Info API 回填。去重集合防止边界处的重复成交。对于任何需要成交精确一次处理的生产集成,此模式都至关重要。

// Full example is provided in the previous section. This section repeats the key logic for clarity.
// See the code block above for the complete runnable script.

用于自测的结果表

要针对你自己的端点验证集成,请测量以下指标。将观察到的值填入表中。这是一种由读者自行验证的方法;此处不提供基准数字,因为它们取决于你的网络、提供商和市场活动。使用一致的测量窗口(例如 5 分钟)并记录结果。

表格应包含:消息吞吐量(每秒消息数)、每条记录观察到的字段(列出你在 trades 和 userFills 载荷中看到的字段)、强制重连时的缺口时长(从断开到成功重新订阅之间的时间)、回填记录数(回填期间从 Info API 获取的成交数量)以及抑制的重复数(被去重集合跳过的成交数量)。这些数据有助于你调整去重集合大小和回填策略。

  • 消息吞吐量:___ 条消息/秒。
  • 每条记录观察到的字段:___。
  • 强制重连时的缺口时长:___ 毫秒。
  • 回填记录数:___。
  • 抑制的重复数:___。

故障模式与排查

与 Hyperliquid 的 WebSocket 频道集成时,有几种常见的故障模式。未认证就订阅 userFills 将导致没有数据或报错;确保 user 参数是有效地址,并在需要时确保连接已认证。币种名称大小写或命名不匹配(例如 "eth" 与 "ETH")将导致没有交易;始终使用文档中记录的精确符号。连接静默、停止投递消息可能表明网络问题或服务器端空闲超时;实现 ping/心跳以及期望在超时窗口内收到消息的存活检查。

重连后出现重复成交,是因为边界处未去重。如果你从 Info API 回填,同时又在实时流上收到同一笔成交,去重集合必须能捕获它。如果看到重复,请验证你的标识键在两个接口之间是否一致。关于突发重新订阅期间的速率限制问题,请参阅 Hyperliquid API 速率限制:Info 与 Exchange。关于端点可用性,请参阅 Hyperliquid RPC 端点(RPC Assistant)

  • userFills 未认证:无数据。
  • 币种名称不匹配:无交易。
  • 连接静默:添加心跳。
  • 重复:检查去重键。

局限与权衡

Info API 的历史深度有限;它可能不提供早于某个窗口的成交。长时间中断可能需要大范围回填,消耗大量速率限制预算。大范围回填的成本随你跟踪的币种和用户数量增加而增加。此外,突发重新订阅可能超出连接或请求限制,具体限制因提供商而异。请始终参考速率限制指南,并使用指数退避设计重连逻辑。

WebSocket 频道不会重放错过的消息,因此客户端负责缺口恢复。这意味着你的应用必须将最后看到的成交标识和时间戳持久化到磁盘或数据库,以在进程重启后存活。对 Info API 回填的依赖引入了必须对账的第二个接口,增加了复杂性。对于 OHLCV 历史,使用 candleSnapshot 的不同方法可能更合适;请参阅 从 candleSnapshot 构建 Hyperliquid OHLCV 历史

  • Info API 历史深度有限。
  • 大范围回填消耗速率限制。
  • 无重放:客户端必须持久化状态。
  • 提供商限制各不相同。

后续步骤与延伸阅读

要加深理解,请查阅 Hyperliquid 关于 WebSocket 订阅和数据格式以及 userFills 的 Info 端点的权威文档。要更广泛地了解 Hyperliquid 网络,请参阅 Hyperliquid 网络页面。关于定价和服务详情,请参阅 RPC 定价API 服务OnFinality Learn 中心 包含更多关于 Hyperliquid API 使用的指南。

构建生产系统时,考虑使用托管 RPC 提供商来处理连接可靠性和速率限制。Hyperliquid RPC 端点(RPC Assistant) 页面可帮助你找到合适的端点。始终针对你自己的端点测试集成,并测量结果表中的指标,以确保正确性和性能。

  • 查阅 Hyperliquid 文档了解订阅和数据格式。
  • 使用 Info API 进行回填和对账。
  • 测量你自己的吞吐量和缺口恢复。
  • 考虑使用托管 RPC 以提高可靠性。

永远不用担心基础设施

OnFinality 消除了 DevOps 的繁重工作,让您能够更聪明、更快地构建。

开始