Full reference
Field definitions, proto messages, example updates, and comparisons for this stream live on the L4 Order Book dataset page. This page covers the method signature and client snippets.
Please note that this method is metered based on data consumption at 0.0165 MB = 10 API credits.
Parameters
- coinstring
Trading pair symbol (e.g., 'BTC', 'ETH')
Returns
- streamstream<L4BookUpdate>
Stream of L4BookUpdate messages containing either a snapshot or diff
- snapshotL4BookSnapshot
Initial full orderbook state with all active orders (sent once at connection start)
- diffL4BookDiff
Incremental block-level changes containing order statuses and book modifications (sent for each subsequent block)
Request
1// StreamL4Book Example - Stream individual order data via gRPC2package main34import (5"context"6"encoding/json"7"flag"8"fmt"9"io"10"log"11"math"12"strings"13"time"1415"google.golang.org/grpc"16"google.golang.org/grpc/codes"17"google.golang.org/grpc/credentials"18"google.golang.org/grpc/metadata"19"google.golang.org/grpc/status"2021pb "hyperliquid-orderbook-example/proto"22)2324const (25grpcEndpoint = "your-endpoint.hype-mainnet.quiknode.pro:10000"26authToken = "your-auth-token"27maxRetries = 1028baseDelay = 2 * time.Second29)3031func streamL4Orderbook(coin string, maxMessages int) error {32fmt.Println(strings.Repeat("=", 60))33fmt.Printf("Streaming L4 Orderbook for %s\n", coin)34fmt.Println("Auto-reconnect: true")35fmt.Println(strings.Repeat("=", 60) + "\n")3637retryCount := 038totalMsgCount := 03940for retryCount < maxRetries {41creds := credentials.NewClientTLSFromCert(nil, "")42conn, err := grpc.Dial(grpcEndpoint,43grpc.WithTransportCredentials(creds),44grpc.WithDefaultCallOptions(grpc.MaxCallRecvMsgSize(100*1024*1024)))45if err != nil {46return fmt.Errorf("failed to connect: %w", err)47}4849client := pb.NewOrderBookStreamingClient(conn)50ctx := metadata.AppendToOutgoingContext(context.Background(), "x-token", authToken)5152request := &pb.L4BookRequest{53Coin: coin,54}5556if retryCount > 0 {57fmt.Printf("\n🔄 Reconnecting (attempt %d/%d)...\n", retryCount+1, maxRetries)58} else {59fmt.Printf("Connecting to %s...\n", grpcEndpoint)60}6162stream, err := client.StreamL4Book(ctx, request)63if err != nil {64conn.Close()65return fmt.Errorf("failed to start stream: %w", err)66}6768snapshotReceived := false69shouldRetry := false7071for {72update, err := stream.Recv()73if err == io.EOF {74break75}76if err != nil {77st, ok := status.FromError(err)78if ok && st.Code() == codes.DataLoss {79fmt.Printf("\n⚠️ Server reinitialized: %s\n", st.Message())80retryCount++81if retryCount < maxRetries {82delay := baseDelay * time.Duration(math.Pow(2, float64(retryCount-1)))83fmt.Printf("⏳ Waiting %v before reconnecting...\n", delay)84time.Sleep(delay)85shouldRetry = true86break87} else {88fmt.Printf("\n❌ Max retries (%d) reached. Giving up.\n", maxRetries)89conn.Close()90return nil91}92}93conn.Close()94return fmt.Errorf("stream error: %w", err)95}9697totalMsgCount++9899if snapshot := update.GetSnapshot(); snapshot != nil {100snapshotReceived = true101retryCount = 0 // Reset on success102103fmt.Println("\n✓ L4 Snapshot Received!")104fmt.Println(strings.Repeat("─", 60))105fmt.Printf("Coin: %s\n", snapshot.Coin)106fmt.Printf("Height: %d\n", snapshot.Height)107fmt.Printf("Time: %d\n", snapshot.Time)108fmt.Printf("Bids: %d orders\n", len(snapshot.Bids))109fmt.Printf("Asks: %d orders\n", len(snapshot.Asks))110fmt.Println(strings.Repeat("─", 60))111112// Sample bids113if len(snapshot.Bids) > 0 {114fmt.Println("\nSample Bids (first 5):")115bidCount := len(snapshot.Bids)116if bidCount > 5 {117bidCount = 5118}119for i := 0; i < bidCount; i++ {120order := snapshot.Bids[i]121userShort := order.User122if len(userShort) > 10 {123userShort = userShort[:10] + "..."124}125fmt.Printf(" OID: %d | Price: %s | Size: %s | User: %s\n",126order.Oid, order.LimitPx, order.Sz, userShort)127}128}129130// Sample asks131if len(snapshot.Asks) > 0 {132fmt.Println("\nSample Asks (first 5):")133askCount := len(snapshot.Asks)134if askCount > 5 {135askCount = 5136}137for i := 0; i < askCount; i++ {138order := snapshot.Asks[i]139userShort := order.User140if len(userShort) > 10 {141userShort = userShort[:10] + "..."142}143fmt.Printf(" OID: %d | Price: %s | Size: %s | User: %s\n",144order.Oid, order.LimitPx, order.Sz, userShort)145}146}147148} else if diff := update.GetDiff(); diff != nil {149if !snapshotReceived {150fmt.Println("\n⚠ Received diff before snapshot")151}152153var diffData map[string]interface{}154if err := json.Unmarshal([]byte(diff.Data), &diffData); err == nil {155orderStatuses := []interface{}{}156bookDiffs := []interface{}{}157158if os, ok := diffData["order_statuses"].([]interface{}); ok {159orderStatuses = os160}161if bd, ok := diffData["book_diffs"].([]interface{}); ok {162bookDiffs = bd163}164165fmt.Printf("\n[Block %d] L4 Diff:\n", diff.Height)166fmt.Printf(" Time: %d\n", diff.Time)167fmt.Printf(" Order Statuses: %d\n", len(orderStatuses))168fmt.Printf(" Book Diffs: %d\n", len(bookDiffs))169170if len(bookDiffs) > 0 && len(bookDiffs) <= 5 {171pretty, _ := json.MarshalIndent(bookDiffs, " ", " ")172fmt.Printf(" Diffs: %s\n", pretty)173}174}175}176177if maxMessages > 0 && totalMsgCount >= maxMessages {178fmt.Printf("\nReached max messages (%d), stopping...\n", maxMessages)179conn.Close()180return nil181}182}183184conn.Close()185186if !shouldRetry {187break188}189}190191return nil192}193194func main() {195coin := flag.String("coin", "BTC", "Coin symbol to stream")196maxMessages := flag.Int("max-messages", 0, "Maximum messages (0 = unlimited)")197198flag.Parse()199200fmt.Println("\n" + strings.Repeat("=", 60))201fmt.Println("Hyperliquid StreamL4Book Example")202fmt.Printf("Endpoint: %s\n", grpcEndpoint)203fmt.Println(strings.Repeat("=", 60))204205if err := streamL4Orderbook(*coin, *maxMessages); err != nil {206log.Fatal(err)207}208}209
1// StreamL4Book Example - Stream individual order data via gRPC2package main34import (5"context"6"encoding/json"7"flag"8"fmt"9"io"10"log"11"math"12"strings"13"time"1415"google.golang.org/grpc"16"google.golang.org/grpc/codes"17"google.golang.org/grpc/credentials"18"google.golang.org/grpc/metadata"19"google.golang.org/grpc/status"2021pb "hyperliquid-orderbook-example/proto"22)2324const (25grpcEndpoint = "your-endpoint.hype-mainnet.quiknode.pro:10000"26authToken = "your-auth-token"27maxRetries = 1028baseDelay = 2 * time.Second29)3031func streamL4Orderbook(coin string, maxMessages int) error {32fmt.Println(strings.Repeat("=", 60))33fmt.Printf("Streaming L4 Orderbook for %s\n", coin)34fmt.Println("Auto-reconnect: true")35fmt.Println(strings.Repeat("=", 60) + "\n")3637retryCount := 038totalMsgCount := 03940for retryCount < maxRetries {41creds := credentials.NewClientTLSFromCert(nil, "")42conn, err := grpc.Dial(grpcEndpoint,43grpc.WithTransportCredentials(creds),44grpc.WithDefaultCallOptions(grpc.MaxCallRecvMsgSize(100*1024*1024)))45if err != nil {46return fmt.Errorf("failed to connect: %w", err)47}4849client := pb.NewOrderBookStreamingClient(conn)50ctx := metadata.AppendToOutgoingContext(context.Background(), "x-token", authToken)5152request := &pb.L4BookRequest{53Coin: coin,54}5556if retryCount > 0 {57fmt.Printf("\n🔄 Reconnecting (attempt %d/%d)...\n", retryCount+1, maxRetries)58} else {59fmt.Printf("Connecting to %s...\n", grpcEndpoint)60}6162stream, err := client.StreamL4Book(ctx, request)63if err != nil {64conn.Close()65return fmt.Errorf("failed to start stream: %w", err)66}6768snapshotReceived := false69shouldRetry := false7071for {72update, err := stream.Recv()73if err == io.EOF {74break75}76if err != nil {77st, ok := status.FromError(err)78if ok && st.Code() == codes.DataLoss {79fmt.Printf("\n⚠️ Server reinitialized: %s\n", st.Message())80retryCount++81if retryCount < maxRetries {82delay := baseDelay * time.Duration(math.Pow(2, float64(retryCount-1)))83fmt.Printf("⏳ Waiting %v before reconnecting...\n", delay)84time.Sleep(delay)85shouldRetry = true86break87} else {88fmt.Printf("\n❌ Max retries (%d) reached. Giving up.\n", maxRetries)89conn.Close()90return nil91}92}93conn.Close()94return fmt.Errorf("stream error: %w", err)95}9697totalMsgCount++9899if snapshot := update.GetSnapshot(); snapshot != nil {100snapshotReceived = true101retryCount = 0 // Reset on success102103fmt.Println("\n✓ L4 Snapshot Received!")104fmt.Println(strings.Repeat("─", 60))105fmt.Printf("Coin: %s\n", snapshot.Coin)106fmt.Printf("Height: %d\n", snapshot.Height)107fmt.Printf("Time: %d\n", snapshot.Time)108fmt.Printf("Bids: %d orders\n", len(snapshot.Bids))109fmt.Printf("Asks: %d orders\n", len(snapshot.Asks))110fmt.Println(strings.Repeat("─", 60))111112// Sample bids113if len(snapshot.Bids) > 0 {114fmt.Println("\nSample Bids (first 5):")115bidCount := len(snapshot.Bids)116if bidCount > 5 {117bidCount = 5118}119for i := 0; i < bidCount; i++ {120order := snapshot.Bids[i]121userShort := order.User122if len(userShort) > 10 {123userShort = userShort[:10] + "..."124}125fmt.Printf(" OID: %d | Price: %s | Size: %s | User: %s\n",126order.Oid, order.LimitPx, order.Sz, userShort)127}128}129130// Sample asks131if len(snapshot.Asks) > 0 {132fmt.Println("\nSample Asks (first 5):")133askCount := len(snapshot.Asks)134if askCount > 5 {135askCount = 5136}137for i := 0; i < askCount; i++ {138order := snapshot.Asks[i]139userShort := order.User140if len(userShort) > 10 {141userShort = userShort[:10] + "..."142}143fmt.Printf(" OID: %d | Price: %s | Size: %s | User: %s\n",144order.Oid, order.LimitPx, order.Sz, userShort)145}146}147148} else if diff := update.GetDiff(); diff != nil {149if !snapshotReceived {150fmt.Println("\n⚠ Received diff before snapshot")151}152153var diffData map[string]interface{}154if err := json.Unmarshal([]byte(diff.Data), &diffData); err == nil {155orderStatuses := []interface{}{}156bookDiffs := []interface{}{}157158if os, ok := diffData["order_statuses"].([]interface{}); ok {159orderStatuses = os160}161if bd, ok := diffData["book_diffs"].([]interface{}); ok {162bookDiffs = bd163}164165fmt.Printf("\n[Block %d] L4 Diff:\n", diff.Height)166fmt.Printf(" Time: %d\n", diff.Time)167fmt.Printf(" Order Statuses: %d\n", len(orderStatuses))168fmt.Printf(" Book Diffs: %d\n", len(bookDiffs))169170if len(bookDiffs) > 0 && len(bookDiffs) <= 5 {171pretty, _ := json.MarshalIndent(bookDiffs, " ", " ")172fmt.Printf(" Diffs: %s\n", pretty)173}174}175}176177if maxMessages > 0 && totalMsgCount >= maxMessages {178fmt.Printf("\nReached max messages (%d), stopping...\n", maxMessages)179conn.Close()180return nil181}182}183184conn.Close()185186if !shouldRetry {187break188}189}190191return nil192}193194func main() {195coin := flag.String("coin", "BTC", "Coin symbol to stream")196maxMessages := flag.Int("max-messages", 0, "Maximum messages (0 = unlimited)")197198flag.Parse()199200fmt.Println("\n" + strings.Repeat("=", 60))201fmt.Println("Hyperliquid StreamL4Book Example")202fmt.Printf("Endpoint: %s\n", grpcEndpoint)203fmt.Println(strings.Repeat("=", 60))204205if err := streamL4Orderbook(*coin, *maxMessages); err != nil {206log.Fatal(err)207}208}209
1// StreamL4Book Example - Stream individual order data via gRPC2const grpc = require('@grpc/grpc-js');3const protoLoader = require('@grpc/proto-loader');4const path = require('path');56const GRPC_ENDPOINT = 'your-endpoint.hype-mainnet.quiknode.pro:10000';7const AUTH_TOKEN = 'your-auth-token';8const PROTO_PATH = path.join(__dirname, 'proto', 'orderbook.proto');910const packageDefinition = protoLoader.loadSync(PROTO_PATH, {11keepCase: true,12longs: String,13enums: String,14defaults: true,15oneofs: true16});17const proto = grpc.loadPackageDefinition(packageDefinition).hyperliquid;1819function createClient() {20return new proto.OrderBookStreaming(21GRPC_ENDPOINT,22grpc.credentials.createSsl(),23{ 'grpc.max_receive_message_length': 100 * 1024 * 1024 }24);25}2627// Stream L4 (individual orders) orderbook28async function streamL4Orderbook(coin, maxMessages = null, autoReconnect = true, retryCount = 0) {29console.log('='.repeat(60));30console.log(`Streaming L4 Orderbook for ${coin}`);31console.log(`Auto-reconnect: ${autoReconnect}`);32console.log('='.repeat(60) + '\n');3334const maxRetries = 10;35const baseDelay = 2000;36let totalMsgCount = 0;3738while (retryCount < maxRetries) {39const client = createClient();40const metadata = new grpc.Metadata();41metadata.add('x-token', AUTH_TOKEN);4243const request = { coin: coin };4445try {46if (retryCount > 0) {47console.log(`\n🔄 Reconnecting (attempt ${retryCount + 1}/${maxRetries})...`);48} else {49console.log(`Connecting to ${GRPC_ENDPOINT}...`);50}5152let snapshotReceived = false;53const call = client.StreamL4Book(request, metadata);5455call.on('data', (update) => {56totalMsgCount++;5758if (update.snapshot) {59const snapshot = update.snapshot;60snapshotReceived = true;61retryCount = 0; // Reset on success6263console.log('\n✓ L4 Snapshot Received!');64console.log('─'.repeat(60));65console.log(`Coin: ${snapshot.coin}`);66console.log(`Height: ${snapshot.height}`);67console.log(`Time: ${snapshot.time}`);68console.log(`Bids: ${snapshot.bids.length} orders`);69console.log(`Asks: ${snapshot.asks.length} orders`);70console.log('─'.repeat(60));7172// Sample bids73if (snapshot.bids.length > 0) {74console.log('\nSample Bids (first 5):');75snapshot.bids.slice(0, 5).forEach(order => {76console.log(` OID: ${order.oid} | Price: ${order.limit_px} | Size: ${order.sz} | User: ${order.user.substring(0, 10)}...`);77});78}7980// Sample asks81if (snapshot.asks.length > 0) {82console.log('\nSample Asks (first 5):');83snapshot.asks.slice(0, 5).forEach(order => {84console.log(` OID: ${order.oid} | Price: ${order.limit_px} | Size: ${order.sz} | User: ${order.user.substring(0, 10)}...`);85});86}8788} else if (update.diff) {89const diff = update.diff;9091if (!snapshotReceived) {92console.log('\n⚠ Received diff before snapshot');93}9495try {96const diffData = JSON.parse(diff.data);97const orderStatuses = diffData.order_statuses || [];98const bookDiffs = diffData.book_diffs || [];99100console.log(`\n[Block ${diff.height}] L4 Diff:`);101console.log(` Time: ${diff.time}`);102console.log(` Order Statuses: ${orderStatuses.length}`);103console.log(` Book Diffs: ${bookDiffs.length}`);104105if (bookDiffs.length > 0 && bookDiffs.length <= 5) {106console.log(` Diffs: ${JSON.stringify(bookDiffs, null, 2)}`);107}108} catch (e) {109console.log(` Error parsing diff: ${e.message}`);110}111}112113if (maxMessages && totalMsgCount >= maxMessages) {114console.log(`\nReached max messages (${maxMessages}), stopping...`);115call.cancel();116}117});118119call.on('error', (err) => {120if (err.code === grpc.status.DATA_LOSS && autoReconnect) {121console.log(`\n⚠️ Server reinitialized: ${err.message}`);122retryCount++;123if (retryCount < maxRetries) {124const delay = baseDelay * Math.pow(2, retryCount - 1);125console.log(`⏳ Waiting ${delay / 1000}s before reconnecting...`);126setTimeout(() => streamL4Orderbook(coin, maxMessages, autoReconnect, retryCount), delay);127} else {128console.log(`\n❌ Max retries (${maxRetries}) reached. Giving up.`);129}130} else if (err.code !== grpc.status.CANCELLED) {131console.error('\ngRPC error:', err.code, '-', err.message);132}133});134135call.on('end', () => {136console.log('\nStream ended');137});138139// Wait for stream to complete140await new Promise((resolve) => {141call.on('end', resolve);142call.on('error', resolve);143});144145break; // Exit retry loop on success146147} catch (err) {148console.error('Error:', err.message);149break;150}151}152}153154// Parse command line args155const args = process.argv.slice(2);156const coin = args.find(a => a.startsWith('--coin='))?.split('=')[1] || 'BTC';157const maxMessages = parseInt(args.find(a => a.startsWith('--max-messages='))?.split('=')[1]) || null;158159console.log('\n' + '='.repeat(60));160console.log('Hyperliquid StreamL4Book Example');161console.log(`Endpoint: ${GRPC_ENDPOINT}`);162console.log('='.repeat(60));163164streamL4Orderbook(coin, maxMessages);165
1// StreamL4Book Example - Stream individual order data via gRPC2const grpc = require('@grpc/grpc-js');3const protoLoader = require('@grpc/proto-loader');4const path = require('path');56const GRPC_ENDPOINT = 'your-endpoint.hype-mainnet.quiknode.pro:10000';7const AUTH_TOKEN = 'your-auth-token';8const PROTO_PATH = path.join(__dirname, 'proto', 'orderbook.proto');910const packageDefinition = protoLoader.loadSync(PROTO_PATH, {11keepCase: true,12longs: String,13enums: String,14defaults: true,15oneofs: true16});17const proto = grpc.loadPackageDefinition(packageDefinition).hyperliquid;1819function createClient() {20return new proto.OrderBookStreaming(21GRPC_ENDPOINT,22grpc.credentials.createSsl(),23{ 'grpc.max_receive_message_length': 100 * 1024 * 1024 }24);25}2627// Stream L4 (individual orders) orderbook28async function streamL4Orderbook(coin, maxMessages = null, autoReconnect = true, retryCount = 0) {29console.log('='.repeat(60));30console.log(`Streaming L4 Orderbook for ${coin}`);31console.log(`Auto-reconnect: ${autoReconnect}`);32console.log('='.repeat(60) + '\n');3334const maxRetries = 10;35const baseDelay = 2000;36let totalMsgCount = 0;3738while (retryCount < maxRetries) {39const client = createClient();40const metadata = new grpc.Metadata();41metadata.add('x-token', AUTH_TOKEN);4243const request = { coin: coin };4445try {46if (retryCount > 0) {47console.log(`\n🔄 Reconnecting (attempt ${retryCount + 1}/${maxRetries})...`);48} else {49console.log(`Connecting to ${GRPC_ENDPOINT}...`);50}5152let snapshotReceived = false;53const call = client.StreamL4Book(request, metadata);5455call.on('data', (update) => {56totalMsgCount++;5758if (update.snapshot) {59const snapshot = update.snapshot;60snapshotReceived = true;61retryCount = 0; // Reset on success6263console.log('\n✓ L4 Snapshot Received!');64console.log('─'.repeat(60));65console.log(`Coin: ${snapshot.coin}`);66console.log(`Height: ${snapshot.height}`);67console.log(`Time: ${snapshot.time}`);68console.log(`Bids: ${snapshot.bids.length} orders`);69console.log(`Asks: ${snapshot.asks.length} orders`);70console.log('─'.repeat(60));7172// Sample bids73if (snapshot.bids.length > 0) {74console.log('\nSample Bids (first 5):');75snapshot.bids.slice(0, 5).forEach(order => {76console.log(` OID: ${order.oid} | Price: ${order.limit_px} | Size: ${order.sz} | User: ${order.user.substring(0, 10)}...`);77});78}7980// Sample asks81if (snapshot.asks.length > 0) {82console.log('\nSample Asks (first 5):');83snapshot.asks.slice(0, 5).forEach(order => {84console.log(` OID: ${order.oid} | Price: ${order.limit_px} | Size: ${order.sz} | User: ${order.user.substring(0, 10)}...`);85});86}8788} else if (update.diff) {89const diff = update.diff;9091if (!snapshotReceived) {92console.log('\n⚠ Received diff before snapshot');93}9495try {96const diffData = JSON.parse(diff.data);97const orderStatuses = diffData.order_statuses || [];98const bookDiffs = diffData.book_diffs || [];99100console.log(`\n[Block ${diff.height}] L4 Diff:`);101console.log(` Time: ${diff.time}`);102console.log(` Order Statuses: ${orderStatuses.length}`);103console.log(` Book Diffs: ${bookDiffs.length}`);104105if (bookDiffs.length > 0 && bookDiffs.length <= 5) {106console.log(` Diffs: ${JSON.stringify(bookDiffs, null, 2)}`);107}108} catch (e) {109console.log(` Error parsing diff: ${e.message}`);110}111}112113if (maxMessages && totalMsgCount >= maxMessages) {114console.log(`\nReached max messages (${maxMessages}), stopping...`);115call.cancel();116}117});118119call.on('error', (err) => {120if (err.code === grpc.status.DATA_LOSS && autoReconnect) {121console.log(`\n⚠️ Server reinitialized: ${err.message}`);122retryCount++;123if (retryCount < maxRetries) {124const delay = baseDelay * Math.pow(2, retryCount - 1);125console.log(`⏳ Waiting ${delay / 1000}s before reconnecting...`);126setTimeout(() => streamL4Orderbook(coin, maxMessages, autoReconnect, retryCount), delay);127} else {128console.log(`\n❌ Max retries (${maxRetries}) reached. Giving up.`);129}130} else if (err.code !== grpc.status.CANCELLED) {131console.error('\ngRPC error:', err.code, '-', err.message);132}133});134135call.on('end', () => {136console.log('\nStream ended');137});138139// Wait for stream to complete140await new Promise((resolve) => {141call.on('end', resolve);142call.on('error', resolve);143});144145break; // Exit retry loop on success146147} catch (err) {148console.error('Error:', err.message);149break;150}151}152}153154// Parse command line args155const args = process.argv.slice(2);156const coin = args.find(a => a.startsWith('--coin='))?.split('=')[1] || 'BTC';157const maxMessages = parseInt(args.find(a => a.startsWith('--max-messages='))?.split('=')[1]) || null;158159console.log('\n' + '='.repeat(60));160console.log('Hyperliquid StreamL4Book Example');161console.log(`Endpoint: ${GRPC_ENDPOINT}`);162console.log('='.repeat(60));163164streamL4Orderbook(coin, maxMessages);165
1#!/usr/bin/env python32"""3StreamL4Book Example - Stream individual order data via gRPC45Setup:6pip install grpcio grpcio-tools protobuf zstandard7python -m grpc_tools.protoc -I../../proto --python_out=. --grpc_python_out=. ../../proto/orderbook.proto89Usage:10python stream_l4_example.py --coin BTC --max-messages 10011"""1213import grpc14import json15import sys16import time17import argparse18from typing import Optional1920try:21import orderbook_pb2 as pb22import orderbook_pb2_grpc as pb_grpc23except ImportError:24print("Error: Proto files not generated. Run:")25print(" python -m grpc_tools.protoc -I../../proto --python_out=. --grpc_python_out=. ../../proto/orderbook.proto")26sys.exit(1)2728# Configuration29GRPC_ENDPOINT = "your-endpoint.hype-mainnet.quiknode.pro:10000"30AUTH_TOKEN = "your-auth-token"313233def stream_l4_orderbook(coin: str, max_messages: Optional[int] = None, auto_reconnect: bool = True):34"""35Stream L4 (individual orders) orderbook updates for a coin.3637Args:38coin: Symbol to stream (e.g., "BTC", "ETH")39max_messages: Maximum number of messages to receive (None for unlimited)40auto_reconnect: Auto-reconnect on DATA_LOSS errors (default True)41"""42print(f"\n{'='*60}")43print(f"Streaming L4 Orderbook for {coin}")44print(f"Auto-reconnect: {auto_reconnect}")45print(f"{'='*60}\n")4647retry_count = 048max_retries = 1049base_delay = 250total_msg_count = 05152while retry_count < max_retries:53channel = grpc.secure_channel(54GRPC_ENDPOINT,55grpc.ssl_channel_credentials(),56options=[57('grpc.max_receive_message_length', 100 * 1024 * 1024),58('grpc.keepalive_time_ms', 30000),59]60)61stub = pb_grpc.OrderBookStreamingStub(channel)6263request = pb.L4BookRequest(coin=coin)6465msg_count = 066snapshot_received = False6768try:69if retry_count > 0:70print(f"\n🔄 Reconnecting (attempt {retry_count + 1}/{max_retries})...")71else:72print(f"Connecting to {GRPC_ENDPOINT}...")7374for update in stub.StreamL4Book(request, metadata=[('x-token', AUTH_TOKEN)]):75msg_count += 176total_msg_count += 17778if update.HasField('snapshot'):79snapshot = update.snapshot80snapshot_received = True81retry_count = 0 # Reset retry count on successful connection8283print(f"\n✓ L4 Snapshot Received!")84print(f"{'─'*60}")85print(f"Coin: {snapshot.coin}")86print(f"Height: {snapshot.height}")87print(f"Time: {snapshot.time}")88print(f"Bids: {len(snapshot.bids)} orders")89print(f"Asks: {len(snapshot.asks)} orders")90print(f"{'─'*60}")9192# Show sample of orders93if snapshot.bids:94print(f"\nSample Bids (first 5):")95for order in snapshot.bids[:5]:96print(f" OID: {order.oid} | Price: {order.limit_px} | Size: {order.sz} | User: {order.user[:10]}...")9798if snapshot.asks:99print(f"\nSample Asks (first 5):")100for order in snapshot.asks[:5]:101print(f" OID: {order.oid} | Price: {order.limit_px} | Size: {order.sz} | User: {order.user[:10]}...")102103elif update.HasField('diff'):104diff = update.diff105106if not snapshot_received:107print(f"\n⚠ Received diff before snapshot")108109# Parse the JSON diff data110try:111diff_data = json.loads(diff.data)112order_statuses = diff_data.get('order_statuses', [])113book_diffs = diff_data.get('book_diffs', [])114115print(f"\n[Block {diff.height}] L4 Diff:")116print(f" Time: {diff.time}")117print(f" Order Statuses: {len(order_statuses)}")118print(f" Book Diffs: {len(book_diffs)}")119120# Show sample diffs121if book_diffs and len(book_diffs) <= 5:122print(f" Diffs: {json.dumps(book_diffs, indent=4)}")123124except json.JSONDecodeError as e:125print(f" Error parsing diff data: {e}")126print(f" Raw data: {diff.data[:200]}...")127128if max_messages and total_msg_count >= max_messages:129print(f"\nReached max messages ({max_messages}), stopping...")130channel.close()131return132133except grpc.RpcError as e:134if e.code() == grpc.StatusCode.DATA_LOSS and auto_reconnect:135print(f"\n⚠️ Server reinitialized: {e.details()}")136retry_count += 1137if retry_count < max_retries:138delay = base_delay * (2 ** (retry_count - 1)) # Exponential backoff139print(f"⏳ Waiting {delay}s before reconnecting...")140time.sleep(delay)141channel.close()142continue143else:144print(f"\n❌ Max retries ({max_retries}) reached. Giving up.")145break146else:147print(f"\ngRPC error: {e.code()} - {e.details()}")148break149except KeyboardInterrupt:150print("\nStopping L4 stream...")151break152finally:153channel.close()154155# If we get here without error, break the retry loop156break157158159def main():160parser = argparse.ArgumentParser(description='Stream Hyperliquid L4 orderbook data via gRPC')161parser.add_argument('--coin', default='BTC', help='Coin symbol to stream')162parser.add_argument('--max-messages', type=int, default=None, help='Maximum number of messages to receive')163164args = parser.parse_args()165166print(f"\n{'='*60}")167print("Hyperliquid StreamL4Book Example")168print(f"Endpoint: {GRPC_ENDPOINT}")169print(f"{'='*60}")170171try:172stream_l4_orderbook(args.coin, max_messages=args.max_messages)173except Exception as e:174print(f"\nError: {e}")175import traceback176traceback.print_exc()177sys.exit(1)178179180if __name__ == "__main__":181main()182
1#!/usr/bin/env python32"""3StreamL4Book Example - Stream individual order data via gRPC45Setup:6pip install grpcio grpcio-tools protobuf zstandard7python -m grpc_tools.protoc -I../../proto --python_out=. --grpc_python_out=. ../../proto/orderbook.proto89Usage:10python stream_l4_example.py --coin BTC --max-messages 10011"""1213import grpc14import json15import sys16import time17import argparse18from typing import Optional1920try:21import orderbook_pb2 as pb22import orderbook_pb2_grpc as pb_grpc23except ImportError:24print("Error: Proto files not generated. Run:")25print(" python -m grpc_tools.protoc -I../../proto --python_out=. --grpc_python_out=. ../../proto/orderbook.proto")26sys.exit(1)2728# Configuration29GRPC_ENDPOINT = "your-endpoint.hype-mainnet.quiknode.pro:10000"30AUTH_TOKEN = "your-auth-token"313233def stream_l4_orderbook(coin: str, max_messages: Optional[int] = None, auto_reconnect: bool = True):34"""35Stream L4 (individual orders) orderbook updates for a coin.3637Args:38coin: Symbol to stream (e.g., "BTC", "ETH")39max_messages: Maximum number of messages to receive (None for unlimited)40auto_reconnect: Auto-reconnect on DATA_LOSS errors (default True)41"""42print(f"\n{'='*60}")43print(f"Streaming L4 Orderbook for {coin}")44print(f"Auto-reconnect: {auto_reconnect}")45print(f"{'='*60}\n")4647retry_count = 048max_retries = 1049base_delay = 250total_msg_count = 05152while retry_count < max_retries:53channel = grpc.secure_channel(54GRPC_ENDPOINT,55grpc.ssl_channel_credentials(),56options=[57('grpc.max_receive_message_length', 100 * 1024 * 1024),58('grpc.keepalive_time_ms', 30000),59]60)61stub = pb_grpc.OrderBookStreamingStub(channel)6263request = pb.L4BookRequest(coin=coin)6465msg_count = 066snapshot_received = False6768try:69if retry_count > 0:70print(f"\n🔄 Reconnecting (attempt {retry_count + 1}/{max_retries})...")71else:72print(f"Connecting to {GRPC_ENDPOINT}...")7374for update in stub.StreamL4Book(request, metadata=[('x-token', AUTH_TOKEN)]):75msg_count += 176total_msg_count += 17778if update.HasField('snapshot'):79snapshot = update.snapshot80snapshot_received = True81retry_count = 0 # Reset retry count on successful connection8283print(f"\n✓ L4 Snapshot Received!")84print(f"{'─'*60}")85print(f"Coin: {snapshot.coin}")86print(f"Height: {snapshot.height}")87print(f"Time: {snapshot.time}")88print(f"Bids: {len(snapshot.bids)} orders")89print(f"Asks: {len(snapshot.asks)} orders")90print(f"{'─'*60}")9192# Show sample of orders93if snapshot.bids:94print(f"\nSample Bids (first 5):")95for order in snapshot.bids[:5]:96print(f" OID: {order.oid} | Price: {order.limit_px} | Size: {order.sz} | User: {order.user[:10]}...")9798if snapshot.asks:99print(f"\nSample Asks (first 5):")100for order in snapshot.asks[:5]:101print(f" OID: {order.oid} | Price: {order.limit_px} | Size: {order.sz} | User: {order.user[:10]}...")102103elif update.HasField('diff'):104diff = update.diff105106if not snapshot_received:107print(f"\n⚠ Received diff before snapshot")108109# Parse the JSON diff data110try:111diff_data = json.loads(diff.data)112order_statuses = diff_data.get('order_statuses', [])113book_diffs = diff_data.get('book_diffs', [])114115print(f"\n[Block {diff.height}] L4 Diff:")116print(f" Time: {diff.time}")117print(f" Order Statuses: {len(order_statuses)}")118print(f" Book Diffs: {len(book_diffs)}")119120# Show sample diffs121if book_diffs and len(book_diffs) <= 5:122print(f" Diffs: {json.dumps(book_diffs, indent=4)}")123124except json.JSONDecodeError as e:125print(f" Error parsing diff data: {e}")126print(f" Raw data: {diff.data[:200]}...")127128if max_messages and total_msg_count >= max_messages:129print(f"\nReached max messages ({max_messages}), stopping...")130channel.close()131return132133except grpc.RpcError as e:134if e.code() == grpc.StatusCode.DATA_LOSS and auto_reconnect:135print(f"\n⚠️ Server reinitialized: {e.details()}")136retry_count += 1137if retry_count < max_retries:138delay = base_delay * (2 ** (retry_count - 1)) # Exponential backoff139print(f"⏳ Waiting {delay}s before reconnecting...")140time.sleep(delay)141channel.close()142continue143else:144print(f"\n❌ Max retries ({max_retries}) reached. Giving up.")145break146else:147print(f"\ngRPC error: {e.code()} - {e.details()}")148break149except KeyboardInterrupt:150print("\nStopping L4 stream...")151break152finally:153channel.close()154155# If we get here without error, break the retry loop156break157158159def main():160parser = argparse.ArgumentParser(description='Stream Hyperliquid L4 orderbook data via gRPC')161parser.add_argument('--coin', default='BTC', help='Coin symbol to stream')162parser.add_argument('--max-messages', type=int, default=None, help='Maximum number of messages to receive')163164args = parser.parse_args()165166print(f"\n{'='*60}")167print("Hyperliquid StreamL4Book Example")168print(f"Endpoint: {GRPC_ENDPOINT}")169print(f"{'='*60}")170171try:172stream_l4_orderbook(args.coin, max_messages=args.max_messages)173except Exception as e:174print(f"\nError: {e}")175import traceback176traceback.print_exc()177sys.exit(1)178179180if __name__ == "__main__":181main()182
Don't have an account yet?
Create your Quicknode endpoint in seconds and start building
Get started for free