본문으로 건너뛰기

Setting Up Node.js for OrderBook (L2/L4)

업데이트 날짜:
2026년 6월 10일

개요

This guide demonstrates how to stream real-time orderbook data from Hyperliquid using gRPC in Node.js. You'll learn to:

  • Stream L2 orderbook (aggregated price levels with depths)
  • Stream L4 orderbook (individual orders with full details)
  • Load Protocol Buffer definitions dynamically
  • Handle reconnections and errors gracefully

The orderbook streaming API uses a separate proto file (orderbook.proto) from the core streaming API, providing specialized methods optimized for market data.

Related Guides

For core streaming API (StreamData, StreamBlocks, Ping), see Node.js Setup Guide

Plan Requirement

Access to /하이퍼코어 (JSON-RPC and WebSocket) and HyperCore gRPC streaming requires a Quicknode Build plan or higher. View pricing to upgrade.

필수 조건

1. Install Node.js

Node.js 16 or later is required. Follow the official installation guide or verify your installation:

node --version
npm --version

프로젝트 설정

Step 1: Create Project Directory

mkdir hyperliquid-orderbook-nodejs
cd hyperliquid-orderbook-nodejs
npm init -y

Your directory structure:

hyperliquid-orderbook-nodejs/
├── package.json
├── proto/ # Protocol buffer definitions
└── examples/ # Example scripts

Step 2: Install Dependencies

Install gRPC and Protocol Buffers packages:

npm install @grpc/grpc-js @grpc/proto-loader

These packages provide:

  • @grpc/grpc-js - Pure JavaScript gRPC implementation
  • @grpc/proto-loader - Loads .proto files at runtime

Step 3: Download Proto File

Create directories and download the orderbook protocol definition:

mkdir -p proto examples

curl -o proto/orderbook.proto \
https://raw.githubusercontent.com/quiknode-labs/hypercore-grpc-examples/main/proto/orderbook.proto

Verify the file was downloaded:

ls -la proto/
# Should show: orderbook.proto

Implementation

StreamL2Book Example

Create examples/stream_l2.js:

const grpc = require('@grpc/grpc-js');
const protoLoader = require('@grpc/proto-loader');
const path = require('path');

// Parse command-line arguments
const args = process.argv.slice(2);
let endpoint = 'your-endpoint.hype-mainnet.quiknode.pro:10000';
let token = 'your-auth-token';
let coin = 'BTC';
let levels = 20;

for (let i = 0; i < args.length; i++) {
if (args[i].startsWith('--endpoint=')) endpoint = args[i].split('=')[1];
else if (args[i].startsWith('--token=')) token = args[i].split('=')[1];
else if (args[i].startsWith('--coin=')) coin = args[i].split('=')[1];
else if (args[i].startsWith('--levels=')) levels = parseInt(args[i].split('=')[1]);
}

const PROTO_PATH = path.join(__dirname, '..', 'proto', 'orderbook.proto');

const packageDefinition = protoLoader.loadSync(PROTO_PATH, {
keepCase: true,
longs: String,
enums: String,
defaults: true,
oneofs: true
});
const proto = grpc.loadPackageDefinition(packageDefinition).hyperliquid;

function createClient() {
return new proto.OrderBookStreaming(
endpoint,
grpc.credentials.createSsl(),
{ 'grpc.max_receive_message_length': 100 * 1024 * 1024 }
);
}

async function streamL2Orderbook() {
console.log('='.repeat(60));
console.log(`Streaming L2 Orderbook for ${coin}`);
console.log(`Levels: ${levels}`);
console.log('='.repeat(60) + '\n');

let retryCount = 0;
const maxRetries = 10;
const baseDelay = 2000;

while (retryCount < maxRetries) {
const client = createClient();
const metadata = new grpc.Metadata();
metadata.add('x-token', token);

const request = {
coin: coin,
n_levels: levels
};

try {
if (retryCount > 0) {
console.log(`\n🔄 Reconnecting (attempt ${retryCount + 1}/${maxRetries})...`);
} else {
console.log(`Connecting to ${endpoint}...`);
}

let msgCount = 0;
const call = client.StreamL2Book(request, metadata);

call.on('data', (update) => {
msgCount++;

if (msgCount === 1) {
console.log('✓ First L2 update received!\n');
retryCount = 0;
}

console.log('\n' + '─'.repeat(60));
console.log(`Block: ${update.block_number} | Time: ${update.time} | Coin: ${update.coin}`);
console.log('─'.repeat(60));

// Display asks (reversed for display)
if (update.asks && update.asks.length > 0) {
console.log('\n ASKS:');
update.asks.slice(0, 10).reverse().forEach(level => {
console.log(` ${level.px.padStart(12)} | ${level.sz.padStart(12)} | (${level.n} orders)`);
});
}

// Display spread
if (update.bids && update.bids.length > 0 && update.asks && update.asks.length > 0) {
const bestBid = parseFloat(update.bids[0].px);
const bestAsk = parseFloat(update.asks[0].px);
const spread = bestAsk - bestBid;
const spreadBps = (spread / bestBid) * 10000;
console.log('\n ' + '─'.repeat(44));
console.log(` SPREAD: ${spread.toFixed(2)} (${spreadBps.toFixed(2)} bps)`);
console.log(' ' + '─'.repeat(44));
}

// Display bids
if (update.bids && update.bids.length > 0) {
console.log('\n BIDS:');
update.bids.slice(0, 10).forEach(level => {
console.log(` ${level.px.padStart(12)} | ${level.sz.padStart(12)} | (${level.n} orders)`);
});
}

console.log(`\n Messages received: ${msgCount}`);
});

call.on('error', (err) => {
if (err.code === grpc.status.DATA_LOSS) {
console.log(`\n⚠️ Server reinitialized: ${err.message}`);
retryCount++;
if (retryCount < maxRetries) {
const delay = baseDelay * Math.pow(2, retryCount - 1);
console.log(`⏳ Waiting ${delay / 1000}s before reconnecting...`);
setTimeout(() => streamL2Orderbook(), delay);
}
} else {
console.error('\ngRPC error:', err.code, '-', err.message);
}
});

call.on('end', () => {
console.log('\nStream ended');
});

await new Promise((resolve) => {
call.on('end', resolve);
call.on('error', resolve);
});

break;

} catch (err) {
console.error('Error:', err.message);
break;
}
}
}

streamL2Orderbook().catch(console.error);
More Method Examples

For individual order streaming with full details, see the StreamL4Book method documentation which includes complete code examples. The other OrderBookStreaming methods (StreamBboBook, StreamL2BookDiff, StreamL4BookUpdates, 그리고 StreamTpslUpdates) also include complete code examples on their method pages.

Run the Example

Execute the script with your credentials:

cd examples

node stream_l2.js \
--endpoint="your-endpoint.hype-mainnet.quiknode.pro:10000" \
--token="your-auth-token" \
--coin="BTC" \
--levels=20

Options:

  • --endpoint - gRPC endpoint with port 10000
  • --token - Authentication token from Quicknode
  • --coin - Trading pair (BTC, ETH, SOL, etc.)
  • --levels - Number of price levels (default: 20, max: 100)

Output:

============================================================
Streaming L2 Orderbook for BTC
Levels: 20
============================================================

Connecting to your-endpoint.hype-mainnet.quiknode.pro:10000...
✓ First L2 update received!

────────────────────────────────────────────────────────────
Block: 904333900 | Time: 1771916717433 | Coin: BTC
────────────────────────────────────────────────────────────

ASKS:
62916 | 1.03755 | (5 orders)
62915 | 2.8545 | (1 orders)
62914 | 0.05136 | (3 orders)
...

────────────────────────────────────────────
SPREAD: 1.00 (0.16 bps)
────────────────────────────────────────────

BIDS:
62906 | 4.42562 | (23 orders)
62905 | 0.20558 | (4 orders)
62904 | 0.11454 | (2 orders)
...

Messages received: 1

Understanding the Output:


  • Block/Time - Blockchain block number and timestamp
  • Asks - Sell orders (displayed high to low)
  • Spread - Difference between best bid and ask in basis points
  • Bids - Buy orders (displayed high to low)
  • Order Count - Number of individual orders at each price level

여러분의 피드백을 ❤️ 환영합니다!

이 문서에 대한 의견이나 질문이 있으시면 언제든지 알려주세요. 여러분의 의견을 기다리고 있습니다!

이 문서 공유하기