Skip to main content

Documentation Index

Fetch the complete documentation index at: https://hypernode-docs.polynode.dev/llms.txt

Use this file to discover all available pages before exploring further.

Summary

By merging pre-consensus gossip data (cancel actions) with confirmed order flow (fills and cancellations), we can measure how often market makers fail to cancel orders before they get filled. This produces a real-time “market stress” signal that no single data source can provide alone.

The two data sources

SourceWhat it carriesLatency
Gossip protocol (pre-consensus)Cancel and modify requests before block commit~1.6s ahead of confirmation
Confirmed event streamEvery order status change: filled, canceled, rejectedDefinitive, post-matching
A cancel in gossip means a user wants to pull an order. A confirmed order_canceled means it succeeded. A confirmed order_filled on the same oid means it didn’t — the order was taken before the cancel reached the validator.

Correlation methodology

The oid (order ID) is assigned by HyperLiquid’s matching engine and is globally unique. It appears in both:
  • Gossip cancel events: data.cancels[].oid
  • Confirmed order_canceled events: data.oid
  • Confirmed order_filled events: data.oid
By tracking oids across both layers, every gossip cancel resolves to one of three outcomes:
OutcomeWhat happenedSignal
order_canceled confirmedCancel succeededNormal market making
order_filled confirmedOrder was taken before cancel arrivedPick-off: maker lost the race
No confirmationOrder was already in terminal stateRedundant cancel

Measured results

Over a 16-second sample on mainnet (April 14, 2026): Cancel confirmation rate: 49.7% of gossip cancels result in a confirmed cancellation. The remaining 50.3% arrive too late — the order was already filled, previously canceled, or expired. Pre-consensus lead time (cancel oid correlation):
MetricValue
Minimum0 ms
Median1,614 ms
Mean1,664 ms
P903,010 ms
Maximum3,579 ms
The gossip cancel arrives 1.6 seconds before its confirmed outcome on median.

Cancel velocity as a market signal

Cancel velocity (cancels per second per asset) spikes when market conditions shift. In a 20-second observation window, BTC cancel velocity jumped from 19/sec to 242/sec — a 12x surge involving 264 unique addresses pulling orders simultaneously. This corresponds to a price movement where market makers collectively repriced. Sample stress readings (5-second windows):
AssetCancel/secFill/secUnique cancelersStress score
BTC (spike)242.63.0264970
xyz:CL33.221.076730
ETH136.61.2246301
SOL43.21.677112
High cancel velocity with low fill velocity = market makers pulling quotes (defensive). High cancel velocity with high fill velocity = active repricing under pressure (aggressive).

Cross-user correlation

When a maker cancels and gets filled, the taker is a different address. By joining cancels and fills on the same asset within a time window:
  • 16 BTC addresses were both canceling AND receiving fills in a 16-second window
  • The same heavy market maker address (0x31ca8395...) appeared across BTC, BCH, TRX, SKY, and LTC — quoting every asset, constantly canceling
  • Fill events come in matched pairs: one taker buy, one maker sell at the same price and size

Implementing this signal

Subscribe to cancels and fills on the unified stream:
const ws = new WebSocket("wss://hyper.polynode.dev/ws?key=YOUR_KEY");

ws.send(JSON.stringify({
  action: "subscribe",
  filters: { action_types: ["cancel", "cancelByCloid", "order", "fill"] }
}));

// Track cancel oids per asset
const cancelOids = new Map(); // asset → Set<oid>
const cancelVelocity = new Map(); // asset → count in current window

ws.on("message", (raw) => {
  const event = JSON.parse(raw);

  if (event.consensus === "pre" && event.type === "cancel") {
    const asset = event.asset;
    for (const c of event.data.cancels) {
      if (!cancelOids.has(asset)) cancelOids.set(asset, new Set());
      cancelOids.get(asset).add(c.oid);
      cancelVelocity.set(asset, (cancelVelocity.get(asset) || 0) + 1);
    }
  }

  if (event.consensus === "confirmed" && event.type === "order_filled") {
    const oid = event.data.oid;
    const asset = event.asset;
    // Was this order being canceled?
    if (cancelOids.get(asset)?.has(oid)) {
      console.log("PICK-OFF:", asset, "oid", oid,
        "filled at", event.data.px, "x", event.data.sz,
        "user", event.user);
    }
  }
});

Why this only works on hypernode

This signal requires both data layers merged in one stream:
  • Gossip cancels are not available through any public HyperLiquid API
  • Confirmed fills with oids come from the confirmed event stream
  • The oid correlation between layers is only possible when both sources are tagged and delivered together
No other HyperLiquid data provider combines pre-consensus gossip with confirmed order flow.