StreamL2Book gRPC Method
Please note that this method is metered based on data consumption at 0.0165 MB = 10 API credits.
Parameters
coin
string
Loading...
n_levels
uint32
Loading...
n_sig_figs
uint32
Loading...
mantissa
uint64
Loading...
Returns
stream
stream<L2BookUpdate>
Loading...
coin
string
Loading...
time
uint64
Loading...
block_number
uint64
Loading...
bids
array<L2Level>
Loading...
asks
array<L2Level>
Loading...
Request
1// StreamL2Book Example - Stream aggregated orderbook data via gRPC2package main34import (5"context"6"flag"7"fmt"8"io"9"log"10"math"11"strings"12"time"1314"google.golang.org/grpc"15"google.golang.org/grpc/codes"16"google.golang.org/grpc/credentials"17"google.golang.org/grpc/metadata"18"google.golang.org/grpc/status"1920pb "hyperliquid-orderbook-example/proto"21)2223const (24grpcEndpoint = "your-endpoint.hype-mainnet.quiknode.pro:10000"25authToken = "your-auth-token"26maxRetries = 1027baseDelay = 2 * time.Second28)2930func streamL2Orderbook(coin string, nLevels uint32) error {31fmt.Println(strings.Repeat("=", 60))32fmt.Printf("Streaming L2 Orderbook for %s\n", coin)33fmt.Printf("Levels: %d\n", nLevels)34fmt.Println("Auto-reconnect: true")35fmt.Println(strings.Repeat("=", 60) + "\n")3637retryCount := 03839for retryCount < maxRetries {40creds := credentials.NewClientTLSFromCert(nil, "")41conn, err := grpc.Dial(grpcEndpoint,42grpc.WithTransportCredentials(creds),43grpc.WithDefaultCallOptions(grpc.MaxCallRecvMsgSize(100*1024*1024)))44if err != nil {45return fmt.Errorf("failed to connect: %w", err)46}4748client := pb.NewOrderBookStreamingClient(conn)49ctx := metadata.AppendToOutgoingContext(context.Background(), "x-token", authToken)5051request := &pb.L2BookRequest{52Coin: coin,53NLevels: nLevels,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.StreamL2Book(ctx, request)63if err != nil {64conn.Close()65return fmt.Errorf("failed to start stream: %w", err)66}6768msgCount := 069shouldRetry := 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}9697msgCount++98if msgCount == 1 {99fmt.Println("ā First L2 update received!\n")100retryCount = 0 // Reset on success101}102103// Display orderbook104fmt.Println("\n" + strings.Repeat("ā", 60))105fmt.Printf("Block: %d | Time: %d | Coin: %s\n", update.BlockNumber, update.Time, update.Coin)106fmt.Println(strings.Repeat("ā", 60))107108// Display asks (reversed)109if len(update.Asks) > 0 {110fmt.Println("\n ASKS:")111askCount := len(update.Asks)112if askCount > 10 {113askCount = 10114}115for i := askCount - 1; i >= 0; i-- {116level := update.Asks[i]117fmt.Printf(" %12s | %12s | (%d orders)\n", level.Px, level.Sz, level.N)118}119}120121// Display spread122if len(update.Bids) > 0 && len(update.Asks) > 0 {123fmt.Println("\n " + strings.Repeat("ā", 44))124fmt.Printf(" SPREAD: (best bid: %s, best ask: %s)\n", update.Bids[0].Px, update.Asks[0].Px)125fmt.Println(" " + strings.Repeat("ā", 44))126}127128// Display bids129if len(update.Bids) > 0 {130fmt.Println("\n BIDS:")131bidCount := len(update.Bids)132if bidCount > 10 {133bidCount = 10134}135for i := 0; i < bidCount; i++ {136level := update.Bids[i]137fmt.Printf(" %12s | %12s | (%d orders)\n", level.Px, level.Sz, level.N)138}139}140141fmt.Printf("\n Messages received: %d\n", msgCount)142}143144conn.Close()145146if !shouldRetry {147break148}149}150151return nil152}153154func main() {155coin := flag.String("coin", "BTC", "Coin symbol to stream")156levels := flag.Uint("levels", 20, "Number of price levels")157158flag.Parse()159160fmt.Println("\n" + strings.Repeat("=", 60))161fmt.Println("Hyperliquid StreamL2Book Example")162fmt.Printf("Endpoint: %s\n", grpcEndpoint)163fmt.Println(strings.Repeat("=", 60))164165if err := streamL2Orderbook(*coin, uint32(*levels)); err != nil {166log.Fatal(err)167}168}169
1// StreamL2Book Example - Stream aggregated orderbook data via gRPC2package main34import (5"context"6"flag"7"fmt"8"io"9"log"10"math"11"strings"12"time"1314"google.golang.org/grpc"15"google.golang.org/grpc/codes"16"google.golang.org/grpc/credentials"17"google.golang.org/grpc/metadata"18"google.golang.org/grpc/status"1920pb "hyperliquid-orderbook-example/proto"21)2223const (24grpcEndpoint = "your-endpoint.hype-mainnet.quiknode.pro:10000"25authToken = "your-auth-token"26maxRetries = 1027baseDelay = 2 * time.Second28)2930func streamL2Orderbook(coin string, nLevels uint32) error {31fmt.Println(strings.Repeat("=", 60))32fmt.Printf("Streaming L2 Orderbook for %s\n", coin)33fmt.Printf("Levels: %d\n", nLevels)34fmt.Println("Auto-reconnect: true")35fmt.Println(strings.Repeat("=", 60) + "\n")3637retryCount := 03839for retryCount < maxRetries {40creds := credentials.NewClientTLSFromCert(nil, "")41conn, err := grpc.Dial(grpcEndpoint,42grpc.WithTransportCredentials(creds),43grpc.WithDefaultCallOptions(grpc.MaxCallRecvMsgSize(100*1024*1024)))44if err != nil {45return fmt.Errorf("failed to connect: %w", err)46}4748client := pb.NewOrderBookStreamingClient(conn)49ctx := metadata.AppendToOutgoingContext(context.Background(), "x-token", authToken)5051request := &pb.L2BookRequest{52Coin: coin,53NLevels: nLevels,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.StreamL2Book(ctx, request)63if err != nil {64conn.Close()65return fmt.Errorf("failed to start stream: %w", err)66}6768msgCount := 069shouldRetry := 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}9697msgCount++98if msgCount == 1 {99fmt.Println("ā First L2 update received!\n")100retryCount = 0 // Reset on success101}102103// Display orderbook104fmt.Println("\n" + strings.Repeat("ā", 60))105fmt.Printf("Block: %d | Time: %d | Coin: %s\n", update.BlockNumber, update.Time, update.Coin)106fmt.Println(strings.Repeat("ā", 60))107108// Display asks (reversed)109if len(update.Asks) > 0 {110fmt.Println("\n ASKS:")111askCount := len(update.Asks)112if askCount > 10 {113askCount = 10114}115for i := askCount - 1; i >= 0; i-- {116level := update.Asks[i]117fmt.Printf(" %12s | %12s | (%d orders)\n", level.Px, level.Sz, level.N)118}119}120121// Display spread122if len(update.Bids) > 0 && len(update.Asks) > 0 {123fmt.Println("\n " + strings.Repeat("ā", 44))124fmt.Printf(" SPREAD: (best bid: %s, best ask: %s)\n", update.Bids[0].Px, update.Asks[0].Px)125fmt.Println(" " + strings.Repeat("ā", 44))126}127128// Display bids129if len(update.Bids) > 0 {130fmt.Println("\n BIDS:")131bidCount := len(update.Bids)132if bidCount > 10 {133bidCount = 10134}135for i := 0; i < bidCount; i++ {136level := update.Bids[i]137fmt.Printf(" %12s | %12s | (%d orders)\n", level.Px, level.Sz, level.N)138}139}140141fmt.Printf("\n Messages received: %d\n", msgCount)142}143144conn.Close()145146if !shouldRetry {147break148}149}150151return nil152}153154func main() {155coin := flag.String("coin", "BTC", "Coin symbol to stream")156levels := flag.Uint("levels", 20, "Number of price levels")157158flag.Parse()159160fmt.Println("\n" + strings.Repeat("=", 60))161fmt.Println("Hyperliquid StreamL2Book Example")162fmt.Printf("Endpoint: %s\n", grpcEndpoint)163fmt.Println(strings.Repeat("=", 60))164165if err := streamL2Orderbook(*coin, uint32(*levels)); err != nil {166log.Fatal(err)167}168}169
1// StreamL2Book Example - Stream aggregated orderbook 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 L2 (aggregated) orderbook28async function streamL2Orderbook(coin, nLevels = 20, autoReconnect = true, retryCount = 0) {29console.log('='.repeat(60));30console.log(`Streaming L2 Orderbook for ${coin}`);31console.log(`Levels: ${nLevels}`);32console.log(`Auto-reconnect: ${autoReconnect}`);33console.log('='.repeat(60) + '\n');3435const maxRetries = 10;36const baseDelay = 2000;3738while (retryCount < maxRetries) {39const client = createClient();40const metadata = new grpc.Metadata();41metadata.add('x-token', AUTH_TOKEN);4243const request = {44coin: coin,45n_levels: nLevels46};4748try {49if (retryCount > 0) {50console.log(`\nš Reconnecting (attempt ${retryCount + 1}/${maxRetries})...`);51} else {52console.log(`Connecting to ${GRPC_ENDPOINT}...`);53}5455let msgCount = 0;56const call = client.StreamL2Book(request, metadata);5758call.on('data', (update) => {59msgCount++;6061if (msgCount === 1) {62console.log('ā First L2 update received!\n');63retryCount = 0; // Reset on success64}6566console.log('\n' + 'ā'.repeat(60));67console.log(`Block: ${update.block_number} | Time: ${update.time} | Coin: ${update.coin}`);68console.log('ā'.repeat(60));6970// Display asks (reversed for display)71if (update.asks && update.asks.length > 0) {72console.log('\n ASKS:');73update.asks.slice(0, 10).reverse().forEach(level => {74console.log(` ${level.px.padStart(12)} | ${level.sz.padStart(12)} | (${level.n} orders)`);75});76}7778// Display spread79if (update.bids && update.bids.length > 0 && update.asks && update.asks.length > 0) {80const bestBid = parseFloat(update.bids[0].px);81const bestAsk = parseFloat(update.asks[0].px);82const spread = bestAsk - bestBid;83const spreadBps = (spread / bestBid) * 10000;84console.log('\n ' + 'ā'.repeat(44));85console.log(` SPREAD: ${spread.toFixed(2)} (${spreadBps.toFixed(2)} bps)`);86console.log(' ' + 'ā'.repeat(44));87}8889// Display bids90if (update.bids && update.bids.length > 0) {91console.log('\n BIDS:');92update.bids.slice(0, 10).forEach(level => {93console.log(` ${level.px.padStart(12)} | ${level.sz.padStart(12)} | (${level.n} orders)`);94});95}9697console.log(`\n Messages received: ${msgCount}`);98});99100call.on('error', (err) => {101if (err.code === grpc.status.DATA_LOSS && autoReconnect) {102console.log(`\nā ļø Server reinitialized: ${err.message}`);103retryCount++;104if (retryCount < maxRetries) {105const delay = baseDelay * Math.pow(2, retryCount - 1);106console.log(`ā³ Waiting ${delay / 1000}s before reconnecting...`);107setTimeout(() => streamL2Orderbook(coin, nLevels, autoReconnect, retryCount), delay);108} else {109console.log(`\nā Max retries (${maxRetries}) reached. Giving up.`);110}111} else {112console.error('\ngRPC error:', err.code, '-', err.message);113}114});115116call.on('end', () => {117console.log('\nStream ended');118});119120// Wait for stream to complete121await new Promise((resolve) => {122call.on('end', resolve);123call.on('error', resolve);124});125126break; // Exit retry loop on success127128} catch (err) {129console.error('Error:', err.message);130break;131}132}133}134135// Parse command line args136const args = process.argv.slice(2);137const coin = args.find(a => a.startsWith('--coin='))?.split('=')[1] || 'BTC';138const levels = parseInt(args.find(a => a.startsWith('--levels='))?.split('=')[1]) || 20;139140console.log('\n' + '='.repeat(60));141console.log('Hyperliquid StreamL2Book Example');142console.log(`Endpoint: ${GRPC_ENDPOINT}`);143console.log('='.repeat(60));144145streamL2Orderbook(coin, levels);146
1// StreamL2Book Example - Stream aggregated orderbook 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 L2 (aggregated) orderbook28async function streamL2Orderbook(coin, nLevels = 20, autoReconnect = true, retryCount = 0) {29console.log('='.repeat(60));30console.log(`Streaming L2 Orderbook for ${coin}`);31console.log(`Levels: ${nLevels}`);32console.log(`Auto-reconnect: ${autoReconnect}`);33console.log('='.repeat(60) + '\n');3435const maxRetries = 10;36const baseDelay = 2000;3738while (retryCount < maxRetries) {39const client = createClient();40const metadata = new grpc.Metadata();41metadata.add('x-token', AUTH_TOKEN);4243const request = {44coin: coin,45n_levels: nLevels46};4748try {49if (retryCount > 0) {50console.log(`\nš Reconnecting (attempt ${retryCount + 1}/${maxRetries})...`);51} else {52console.log(`Connecting to ${GRPC_ENDPOINT}...`);53}5455let msgCount = 0;56const call = client.StreamL2Book(request, metadata);5758call.on('data', (update) => {59msgCount++;6061if (msgCount === 1) {62console.log('ā First L2 update received!\n');63retryCount = 0; // Reset on success64}6566console.log('\n' + 'ā'.repeat(60));67console.log(`Block: ${update.block_number} | Time: ${update.time} | Coin: ${update.coin}`);68console.log('ā'.repeat(60));6970// Display asks (reversed for display)71if (update.asks && update.asks.length > 0) {72console.log('\n ASKS:');73update.asks.slice(0, 10).reverse().forEach(level => {74console.log(` ${level.px.padStart(12)} | ${level.sz.padStart(12)} | (${level.n} orders)`);75});76}7778// Display spread79if (update.bids && update.bids.length > 0 && update.asks && update.asks.length > 0) {80const bestBid = parseFloat(update.bids[0].px);81const bestAsk = parseFloat(update.asks[0].px);82const spread = bestAsk - bestBid;83const spreadBps = (spread / bestBid) * 10000;84console.log('\n ' + 'ā'.repeat(44));85console.log(` SPREAD: ${spread.toFixed(2)} (${spreadBps.toFixed(2)} bps)`);86console.log(' ' + 'ā'.repeat(44));87}8889// Display bids90if (update.bids && update.bids.length > 0) {91console.log('\n BIDS:');92update.bids.slice(0, 10).forEach(level => {93console.log(` ${level.px.padStart(12)} | ${level.sz.padStart(12)} | (${level.n} orders)`);94});95}9697console.log(`\n Messages received: ${msgCount}`);98});99100call.on('error', (err) => {101if (err.code === grpc.status.DATA_LOSS && autoReconnect) {102console.log(`\nā ļø Server reinitialized: ${err.message}`);103retryCount++;104if (retryCount < maxRetries) {105const delay = baseDelay * Math.pow(2, retryCount - 1);106console.log(`ā³ Waiting ${delay / 1000}s before reconnecting...`);107setTimeout(() => streamL2Orderbook(coin, nLevels, autoReconnect, retryCount), delay);108} else {109console.log(`\nā Max retries (${maxRetries}) reached. Giving up.`);110}111} else {112console.error('\ngRPC error:', err.code, '-', err.message);113}114});115116call.on('end', () => {117console.log('\nStream ended');118});119120// Wait for stream to complete121await new Promise((resolve) => {122call.on('end', resolve);123call.on('error', resolve);124});125126break; // Exit retry loop on success127128} catch (err) {129console.error('Error:', err.message);130break;131}132}133}134135// Parse command line args136const args = process.argv.slice(2);137const coin = args.find(a => a.startsWith('--coin='))?.split('=')[1] || 'BTC';138const levels = parseInt(args.find(a => a.startsWith('--levels='))?.split('=')[1]) || 20;139140console.log('\n' + '='.repeat(60));141console.log('Hyperliquid StreamL2Book Example');142console.log(`Endpoint: ${GRPC_ENDPOINT}`);143console.log('='.repeat(60));144145streamL2Orderbook(coin, levels);146
1#!/usr/bin/env python32"""3StreamL2Book Example - Stream aggregated orderbook 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_l2_example.py --coin BTC --levels 2011"""1213import grpc14import sys15import time16import argparse17from typing import Optional1819try:20import orderbook_pb2 as pb21import orderbook_pb2_grpc as pb_grpc22except ImportError:23print("Error: Proto files not generated. Run:")24print(" python -m grpc_tools.protoc -I../../proto --python_out=. --grpc_python_out=. ../../proto/orderbook.proto")25sys.exit(1)2627# Configuration28GRPC_ENDPOINT = "your-endpoint.hype-mainnet.quiknode.pro:10000"29AUTH_TOKEN = "your-auth-token"303132def stream_l2_orderbook(coin: str, n_levels: int = 20, n_sig_figs: Optional[int] = None, mantissa: Optional[int] = None, auto_reconnect: bool = True):33"""34Stream L2 (aggregated) orderbook updates for a coin.3536Args:37coin: Symbol to stream (e.g., "BTC", "ETH")38n_levels: Number of price levels to display (default 20, max 100)39n_sig_figs: Significance figures for price bucketing (2-5)40mantissa: Mantissa for bucketing (1, 2, or 5)41auto_reconnect: Auto-reconnect on DATA_LOSS errors (default True)42"""43print(f"\n{'='*60}")44print(f"Streaming L2 Orderbook for {coin}")45print(f"Levels: {n_levels}")46print(f"Auto-reconnect: {auto_reconnect}")47print(f"{'='*60}\n")4849retry_count = 050max_retries = 1051base_delay = 25253while retry_count < max_retries:54channel = grpc.secure_channel(55GRPC_ENDPOINT,56grpc.ssl_channel_credentials(),57options=[58('grpc.max_receive_message_length', 100 * 1024 * 1024),59('grpc.keepalive_time_ms', 30000),60]61)62stub = pb_grpc.OrderBookStreamingStub(channel)6364# Build request65request = pb.L2BookRequest(66coin=coin,67n_levels=n_levels68)69if n_sig_figs is not None:70request.n_sig_figs = n_sig_figs71if mantissa is not None:72request.mantissa = mantissa7374msg_count = 07576try:77if retry_count > 0:78print(f"\nš Reconnecting (attempt {retry_count + 1}/{max_retries})...")79else:80print(f"Connecting to {GRPC_ENDPOINT}...")8182for update in stub.StreamL2Book(request, metadata=[('x-token', AUTH_TOKEN)]):83msg_count += 18485if msg_count == 1:86print(f"ā First L2 update received!\n")87retry_count = 0 # Reset retry count on successful connection8889# Display the L2 orderbook90print(f"\n{'ā'*60}")91print(f"Block: {update.block_number} | Time: {update.time} | Coin: {update.coin}")92print(f"{'ā'*60}")9394# Show asks (sorted highest to lowest for display)95if update.asks:96print("\n ASKS:")97for level in reversed(list(update.asks[:10])): # Top 10 asks98print(f" {level.px:>12} | {level.sz:>12} | ({level.n} orders)")99100# Show spread101if update.bids and update.asks:102best_bid = float(update.bids[0].px) if update.bids else 0103best_ask = float(update.asks[0].px) if update.asks else 0104if best_bid and best_ask:105spread = best_ask - best_bid106spread_bps = (spread / best_bid) * 10000 if best_bid > 0 else 0107print(f"\n {'ā'*44}")108print(f" SPREAD: {spread:.2f} ({spread_bps:.2f} bps)")109print(f" {'ā'*44}")110111# Show bids112if update.bids:113print("\n BIDS:")114for level in update.bids[:10]: # Top 10 bids115print(f" {level.px:>12} | {level.sz:>12} | ({level.n} orders)")116117print(f"\n Messages received: {msg_count}")118119except grpc.RpcError as e:120if e.code() == grpc.StatusCode.DATA_LOSS and auto_reconnect:121print(f"\nā ļø Server reinitialized: {e.details()}")122retry_count += 1123if retry_count < max_retries:124delay = base_delay * (2 ** (retry_count - 1)) # Exponential backoff125print(f"ā³ Waiting {delay}s before reconnecting...")126time.sleep(delay)127channel.close()128continue129else:130print(f"\nā Max retries ({max_retries}) reached. Giving up.")131break132else:133print(f"\ngRPC error: {e.code()} - {e.details()}")134break135except KeyboardInterrupt:136print("\nStopping L2 stream...")137break138finally:139channel.close()140141# If we get here without error, break the retry loop142break143144145def main():146parser = argparse.ArgumentParser(description='Stream Hyperliquid L2 orderbook data via gRPC')147parser.add_argument('--coin', default='BTC', help='Coin symbol to stream')148parser.add_argument('--levels', type=int, default=20, help='Number of price levels (default: 20, max: 100)')149parser.add_argument('--sig-figs', type=int, default=None, help='Significance figures for bucketing (2-5)')150parser.add_argument('--mantissa', type=int, default=None, help='Mantissa for bucketing (1, 2, or 5)')151152args = parser.parse_args()153154print(f"\n{'='*60}")155print("Hyperliquid StreamL2Book Example")156print(f"Endpoint: {GRPC_ENDPOINT}")157print(f"{'='*60}")158159try:160stream_l2_orderbook(args.coin, n_levels=args.levels, n_sig_figs=args.sig_figs, mantissa=args.mantissa)161except Exception as e:162print(f"\nError: {e}")163import traceback164traceback.print_exc()165sys.exit(1)166167168if __name__ == "__main__":169main()170
1#!/usr/bin/env python32"""3StreamL2Book Example - Stream aggregated orderbook 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_l2_example.py --coin BTC --levels 2011"""1213import grpc14import sys15import time16import argparse17from typing import Optional1819try:20import orderbook_pb2 as pb21import orderbook_pb2_grpc as pb_grpc22except ImportError:23print("Error: Proto files not generated. Run:")24print(" python -m grpc_tools.protoc -I../../proto --python_out=. --grpc_python_out=. ../../proto/orderbook.proto")25sys.exit(1)2627# Configuration28GRPC_ENDPOINT = "your-endpoint.hype-mainnet.quiknode.pro:10000"29AUTH_TOKEN = "your-auth-token"303132def stream_l2_orderbook(coin: str, n_levels: int = 20, n_sig_figs: Optional[int] = None, mantissa: Optional[int] = None, auto_reconnect: bool = True):33"""34Stream L2 (aggregated) orderbook updates for a coin.3536Args:37coin: Symbol to stream (e.g., "BTC", "ETH")38n_levels: Number of price levels to display (default 20, max 100)39n_sig_figs: Significance figures for price bucketing (2-5)40mantissa: Mantissa for bucketing (1, 2, or 5)41auto_reconnect: Auto-reconnect on DATA_LOSS errors (default True)42"""43print(f"\n{'='*60}")44print(f"Streaming L2 Orderbook for {coin}")45print(f"Levels: {n_levels}")46print(f"Auto-reconnect: {auto_reconnect}")47print(f"{'='*60}\n")4849retry_count = 050max_retries = 1051base_delay = 25253while retry_count < max_retries:54channel = grpc.secure_channel(55GRPC_ENDPOINT,56grpc.ssl_channel_credentials(),57options=[58('grpc.max_receive_message_length', 100 * 1024 * 1024),59('grpc.keepalive_time_ms', 30000),60]61)62stub = pb_grpc.OrderBookStreamingStub(channel)6364# Build request65request = pb.L2BookRequest(66coin=coin,67n_levels=n_levels68)69if n_sig_figs is not None:70request.n_sig_figs = n_sig_figs71if mantissa is not None:72request.mantissa = mantissa7374msg_count = 07576try:77if retry_count > 0:78print(f"\nš Reconnecting (attempt {retry_count + 1}/{max_retries})...")79else:80print(f"Connecting to {GRPC_ENDPOINT}...")8182for update in stub.StreamL2Book(request, metadata=[('x-token', AUTH_TOKEN)]):83msg_count += 18485if msg_count == 1:86print(f"ā First L2 update received!\n")87retry_count = 0 # Reset retry count on successful connection8889# Display the L2 orderbook90print(f"\n{'ā'*60}")91print(f"Block: {update.block_number} | Time: {update.time} | Coin: {update.coin}")92print(f"{'ā'*60}")9394# Show asks (sorted highest to lowest for display)95if update.asks:96print("\n ASKS:")97for level in reversed(list(update.asks[:10])): # Top 10 asks98print(f" {level.px:>12} | {level.sz:>12} | ({level.n} orders)")99100# Show spread101if update.bids and update.asks:102best_bid = float(update.bids[0].px) if update.bids else 0103best_ask = float(update.asks[0].px) if update.asks else 0104if best_bid and best_ask:105spread = best_ask - best_bid106spread_bps = (spread / best_bid) * 10000 if best_bid > 0 else 0107print(f"\n {'ā'*44}")108print(f" SPREAD: {spread:.2f} ({spread_bps:.2f} bps)")109print(f" {'ā'*44}")110111# Show bids112if update.bids:113print("\n BIDS:")114for level in update.bids[:10]: # Top 10 bids115print(f" {level.px:>12} | {level.sz:>12} | ({level.n} orders)")116117print(f"\n Messages received: {msg_count}")118119except grpc.RpcError as e:120if e.code() == grpc.StatusCode.DATA_LOSS and auto_reconnect:121print(f"\nā ļø Server reinitialized: {e.details()}")122retry_count += 1123if retry_count < max_retries:124delay = base_delay * (2 ** (retry_count - 1)) # Exponential backoff125print(f"ā³ Waiting {delay}s before reconnecting...")126time.sleep(delay)127channel.close()128continue129else:130print(f"\nā Max retries ({max_retries}) reached. Giving up.")131break132else:133print(f"\ngRPC error: {e.code()} - {e.details()}")134break135except KeyboardInterrupt:136print("\nStopping L2 stream...")137break138finally:139channel.close()140141# If we get here without error, break the retry loop142break143144145def main():146parser = argparse.ArgumentParser(description='Stream Hyperliquid L2 orderbook data via gRPC')147parser.add_argument('--coin', default='BTC', help='Coin symbol to stream')148parser.add_argument('--levels', type=int, default=20, help='Number of price levels (default: 20, max: 100)')149parser.add_argument('--sig-figs', type=int, default=None, help='Significance figures for bucketing (2-5)')150parser.add_argument('--mantissa', type=int, default=None, help='Mantissa for bucketing (1, 2, or 5)')151152args = parser.parse_args()153154print(f"\n{'='*60}")155print("Hyperliquid StreamL2Book Example")156print(f"Endpoint: {GRPC_ENDPOINT}")157print(f"{'='*60}")158159try:160stream_l2_orderbook(args.coin, n_levels=args.levels, n_sig_figs=args.sig_figs, mantissa=args.mantissa)161except Exception as e:162print(f"\nError: {e}")163import traceback164traceback.print_exc()165sys.exit(1)166167168if __name__ == "__main__":169main()170
Don't have an account yet?
Create your Quicknode endpoint in seconds and start building
Get started for free