StreamL4Book gRPC Method
Please note that this method is metered based on data consumption at 0.0165 MB = 10 API credits.
Parameters
coin
string
Loading...
Returns
stream
stream<L4BookUpdate>
Loading...
snapshot
L4BookSnapshot
Loading...
diff
L4BookDiff
Loading...
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