StreamL2BookDiff gRPC Method
Please note that this method is metered based on data consumption at 0.0165 MB = 10 API credits.
Parâmetros
coins
repeated string
A carregar...
n_levels
uint32
A carregar...
n_sig_figs
uint32
A carregar...
mantissa
uint64
A carregar...
skip_initial_snapshot
bool
A carregar...
Devoluções
stream
stream<L2BookDiffUpdate>
A carregar...
tempo
uint64
A carregar...
altura
uint64
A carregar...
snapshot
bool
A carregar...
diffs
array<L2CoinDiff>
A carregar...
Pedido
1// StreamL2BookDiff Example - Stream incremental L2 price-level changes via gRPC2package main34importar (5"contexto"6"flag"7"fmt"8"io"9"registo"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 streamL2BookDiff(coins []string, nLevels uint32) error {31fmt.Println(strings.Repeat("=", 60))32fmt.Printf("Streaming L2 Book Diffs for %s\n", strings.Join(coins, ", "))33fmt.Printf("Levels: %d\n", nLevels)34fmt.Println("Auto-reconnect: true")35fmt.Println(strings.Repeat("=", 60) + "\n")3637retryCount := 03839// Track last seq per coin for gap detection40lastSeq := make(map[string]uint64)4142for retryCount < maxRetries {43creds := credentials.NewClientTLSFromCert(nil, "")44conn, err := grpc.Dial(grpcEndpoint,45grpc.WithTransportCredentials(creds),46grpc.WithDefaultCallOptions(grpc.MaxCallRecvMsgSize(100*1024*1024)))47if err != nil {48return fmt.Errorf("failed to connect: %w", err)49}5051client := pb.NewOrderBookStreamingClient(conn)52ctx := metadata.AppendToOutgoingContext(context.Background(), "x-token", authToken)5354request := &pb.L2BookDiffRequest{55Coins: coins,56NLevels: nLevels,57}5859if retryCount > 0 {60fmt.Printf("\n🔄 Reconnecting (attempt %d/%d)...\n", retryCount+1, maxRetries)61} else {62fmt.Printf("Connecting to %s...\n", grpcEndpoint)63}6465stream, err := client.StreamL2BookDiff(ctx, request)66if err != nil {67conn.Close()68return fmt.Errorf("failed to start stream: %w", err)69}7071msgCount := 072shouldRetry := false7374for {75update, err := stream.Recv()76if err == io.EOF {77break78}79if err != nil {80st, ok := status.FromError(err)81if ok && st.Code() == codes.DataLoss {82fmt.Printf("\n⚠️ Server reinitialized: %s\n", st.Message())83retryCount++84if retryCount < maxRetries {85delay := baseDelay * time.Duration(math.Pow(2, float64(retryCount-1)))86fmt.Printf("⏳ Waiting %v before reconnecting...\n", delay)87time.Sleep(delay)88shouldRetry = true89break90} else {91fmt.Printf("\n❌ Max retries (%d) reached. Giving up.\n", maxRetries)92conn.Close()93return nil94}95}96conn.Close()97return fmt.Errorf("stream error: %w", err)98}99100msgCount++101if msgCount == 1 {102fmt.Println("✓ First L2 diff update received!\n")103retryCount = 0 // Reset on success104}105106// Display diff update107fmt.Println("\n" + strings.Repeat("─", 60))108snapshotLabel := ""109if update.Snapshot {110snapshotLabel = " | SNAPSHOT"111}112fmt.Printf("Block: %d | Time: %d%s\n", update.Height, update.Time, snapshotLabel)113fmt.Println(strings.Repeat("─", 60))114115for _, diff := range update.Diffs {116// Check seq continuity per coin117if prev, ok := lastSeq[diff.Coin]; ok && diff.PrevSeq != 0 && diff.PrevSeq != prev {118fmt.Printf(" ⚠️ Sequence gap for %s: expected prev_seq %d, got %d\n", diff.Coin, prev, diff.PrevSeq)119}120lastSeq[diff.Coin] = diff.Seq121122label := "DIFF"123if diff.Snapshot {124label = "SNAPSHOT"125}126fmt.Printf("\n %s [%s] seq: %d (prev: %d)\n", diff.Coin, label, diff.Seq, diff.PrevSeq)127128// Changed ask levels (sz "0" means level removed)129for _, level := range diff.Asks {130action := "SET"131if level.Sz == "0" {132action = "REMOVE"133}134fmt.Printf(" ASK %-6s %12s | %12s\n", action, level.Px, level.Sz)135}136137// Changed bid levels138for _, level := range diff.Bids {139action := "SET"140if level.Sz == "0" {141action = "REMOVE"142}143fmt.Printf(" BID %-6s %12s | %12s\n", action, level.Px, level.Sz)144}145}146147fmt.Printf("\n Messages received: %d\n", msgCount)148}149150conn.Close()151152if !shouldRetry {153break154}155}156157return nil158}159160func main() {161coinsFlag := flag.String("coins", "BTC", "Comma-separated coin symbols to stream (e.g., BTC,ETH)")162levels := flag.Uint("levels", 20, "Maximum price levels per side")163164flag.Parse()165166coins := strings.Split(*coinsFlag, ",")167168fmt.Println("\n" + strings.Repeat("=", 60))169fmt.Println("Hyperliquid StreamL2BookDiff Example")170fmt.Printf("Endpoint: %s\n", grpcEndpoint)171fmt.Println(strings.Repeat("=", 60))172173if err := streamL2BookDiff(coins, uint32(*levels)); err != nil {174log.Fatal(err)175}176}177
1// StreamL2BookDiff Example - Stream incremental L2 price-level changes via gRPC2package main34importar (5"contexto"6"flag"7"fmt"8"io"9"registo"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 streamL2BookDiff(coins []string, nLevels uint32) error {31fmt.Println(strings.Repeat("=", 60))32fmt.Printf("Streaming L2 Book Diffs for %s\n", strings.Join(coins, ", "))33fmt.Printf("Levels: %d\n", nLevels)34fmt.Println("Auto-reconnect: true")35fmt.Println(strings.Repeat("=", 60) + "\n")3637retryCount := 03839// Track last seq per coin for gap detection40lastSeq := make(map[string]uint64)4142for retryCount < maxRetries {43creds := credentials.NewClientTLSFromCert(nil, "")44conn, err := grpc.Dial(grpcEndpoint,45grpc.WithTransportCredentials(creds),46grpc.WithDefaultCallOptions(grpc.MaxCallRecvMsgSize(100*1024*1024)))47if err != nil {48return fmt.Errorf("failed to connect: %w", err)49}5051client := pb.NewOrderBookStreamingClient(conn)52ctx := metadata.AppendToOutgoingContext(context.Background(), "x-token", authToken)5354request := &pb.L2BookDiffRequest{55Coins: coins,56NLevels: nLevels,57}5859if retryCount > 0 {60fmt.Printf("\n🔄 Reconnecting (attempt %d/%d)...\n", retryCount+1, maxRetries)61} else {62fmt.Printf("Connecting to %s...\n", grpcEndpoint)63}6465stream, err := client.StreamL2BookDiff(ctx, request)66if err != nil {67conn.Close()68return fmt.Errorf("failed to start stream: %w", err)69}7071msgCount := 072shouldRetry := false7374for {75update, err := stream.Recv()76if err == io.EOF {77break78}79if err != nil {80st, ok := status.FromError(err)81if ok && st.Code() == codes.DataLoss {82fmt.Printf("\n⚠️ Server reinitialized: %s\n", st.Message())83retryCount++84if retryCount < maxRetries {85delay := baseDelay * time.Duration(math.Pow(2, float64(retryCount-1)))86fmt.Printf("⏳ Waiting %v before reconnecting...\n", delay)87time.Sleep(delay)88shouldRetry = true89break90} else {91fmt.Printf("\n❌ Max retries (%d) reached. Giving up.\n", maxRetries)92conn.Close()93return nil94}95}96conn.Close()97return fmt.Errorf("stream error: %w", err)98}99100msgCount++101if msgCount == 1 {102fmt.Println("✓ First L2 diff update received!\n")103retryCount = 0 // Reset on success104}105106// Display diff update107fmt.Println("\n" + strings.Repeat("─", 60))108snapshotLabel := ""109if update.Snapshot {110snapshotLabel = " | SNAPSHOT"111}112fmt.Printf("Block: %d | Time: %d%s\n", update.Height, update.Time, snapshotLabel)113fmt.Println(strings.Repeat("─", 60))114115for _, diff := range update.Diffs {116// Check seq continuity per coin117if prev, ok := lastSeq[diff.Coin]; ok && diff.PrevSeq != 0 && diff.PrevSeq != prev {118fmt.Printf(" ⚠️ Sequence gap for %s: expected prev_seq %d, got %d\n", diff.Coin, prev, diff.PrevSeq)119}120lastSeq[diff.Coin] = diff.Seq121122label := "DIFF"123if diff.Snapshot {124label = "SNAPSHOT"125}126fmt.Printf("\n %s [%s] seq: %d (prev: %d)\n", diff.Coin, label, diff.Seq, diff.PrevSeq)127128// Changed ask levels (sz "0" means level removed)129for _, level := range diff.Asks {130action := "SET"131if level.Sz == "0" {132action = "REMOVE"133}134fmt.Printf(" ASK %-6s %12s | %12s\n", action, level.Px, level.Sz)135}136137// Changed bid levels138for _, level := range diff.Bids {139action := "SET"140if level.Sz == "0" {141action = "REMOVE"142}143fmt.Printf(" BID %-6s %12s | %12s\n", action, level.Px, level.Sz)144}145}146147fmt.Printf("\n Messages received: %d\n", msgCount)148}149150conn.Close()151152if !shouldRetry {153break154}155}156157return nil158}159160func main() {161coinsFlag := flag.String("coins", "BTC", "Comma-separated coin symbols to stream (e.g., BTC,ETH)")162levels := flag.Uint("levels", 20, "Maximum price levels per side")163164flag.Parse()165166coins := strings.Split(*coinsFlag, ",")167168fmt.Println("\n" + strings.Repeat("=", 60))169fmt.Println("Hyperliquid StreamL2BookDiff Example")170fmt.Printf("Endpoint: %s\n", grpcEndpoint)171fmt.Println(strings.Repeat("=", 60))172173if err := streamL2BookDiff(coins, uint32(*levels)); err != nil {174log.Fatal(err)175}176}177
1// StreamL2BookDiff Example - Stream incremental L2 price-level changes 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 incremental L2 book diffs28async function streamL2BookDiff(coins, nLevels = 20, autoReconnect = true, retryCount = 0) {29console.log('='.repeat(60));30console.log(`Streaming L2 Book Diffs for ${coins.length > 0 ? coins.join(', ') : 'all coins'}`);31console.log(`Levels: ${nLevels}`);32console.log(`Auto-reconnect: ${autoReconnect}`);33console.log('='.repeat(60) + '\n');3435const maxRetries = 10;36const baseDelay = 2000;3738// Track last seq per coin for gap detection39const lastSeq = {};4041while (retryCount < maxRetries) {42const client = createClient();43const metadata = new grpc.Metadata();44metadata.add('x-token', AUTH_TOKEN);4546const request = {47coins: coins,48n_levels: nLevels49};5051try {52if (retryCount > 0) {53console.log(`\n🔄 Reconnecting (attempt ${retryCount + 1}/${maxRetries})...`);54} else {55console.log(`Connecting to ${GRPC_ENDPOINT}...`);56}5758let msgCount = 0;59const call = client.StreamL2BookDiff(request, metadata);6061call.on('data', (update) => {62msgCount++;6364if (msgCount === 1) {65console.log('✓ First L2 diff update received!\n');66retryCount = 0; // Reset on success67}6869console.log('\n' + '─'.repeat(60));70console.log(`Block: ${update.height} | Time: ${update.time}${update.snapshot ? ' | SNAPSHOT' : ''}`);71console.log('─'.repeat(60));7273(update.diffs || []).forEach(diff => {74// Check seq continuity per coin75if (lastSeq[diff.coin] !== undefined && diff.prev_seq && String(diff.prev_seq) !== String(lastSeq[diff.coin])) {76console.log(` ⚠️ Sequence gap for ${diff.coin}: expected prev_seq ${lastSeq[diff.coin]}, got ${diff.prev_seq}`);77}78lastSeq[diff.coin] = diff.seq;7980const label = diff.snapshot ? 'SNAPSHOT' : 'DIFF';81console.log(`\n ${diff.coin} [${label}] seq: ${diff.seq} (prev: ${diff.prev_seq})`);8283// Changed ask levels (sz "0" means level removed)84(diff.asks || []).forEach(level => {85const action = level.sz === '0' ? 'REMOVE' : 'SET';86console.log(` ASK ${action.padEnd(6)} ${level.px.padStart(12)} | ${level.sz.padStart(12)}`);87});8889// Changed bid levels90(diff.bids || []).forEach(level => {91const action = level.sz === '0' ? 'REMOVE' : 'SET';92console.log(` BID ${action.padEnd(6)} ${level.px.padStart(12)} | ${level.sz.padStart(12)}`);93});94});9596console.log(`\n Messages received: ${msgCount}`);97});9899call.on('error', (err) => {100if (err.code === grpc.status.DATA_LOSS && autoReconnect) {101console.log(`\n⚠️ Server reinitialized: ${err.message}`);102retryCount++;103if (retryCount < maxRetries) {104const delay = baseDelay * Math.pow(2, retryCount - 1);105console.log(`⏳ Waiting ${delay / 1000}s before reconnecting...`);106setTimeout(() => streamL2BookDiff(coins, nLevels, autoReconnect, retryCount), delay);107} else {108console.log(`\n❌ Max retries (${maxRetries}) reached. Giving up.`);109}110} else {111console.error('\ngRPC error:', err.code, '-', err.message);112}113});114115call.on('end', () => {116console.log('\nStream ended');117});118119// Wait for stream to complete120await new Promise((resolve) => {121call.on('end', resolve);122call.on('error', resolve);123});124125break; // Exit retry loop on success126127} catch (err) {128console.error('Error:', err.message);129break;130}131}132}133134// Parse command line args135const args = process.argv.slice(2);136const coins = (args.find(a => a.startsWith('--coins='))?.split('=')[1] || 'BTC').split(',');137const levels = parseInt(args.find(a => a.startsWith('--levels='))?.split('=')[1]) || 20;138139console.log('\n' + '='.repeat(60));140console.log('Hyperliquid StreamL2BookDiff Example');141console.log(`Endpoint: ${GRPC_ENDPOINT}`);142console.log('='.repeat(60));143144streamL2BookDiff(coins, levels);145
1// StreamL2BookDiff Example - Stream incremental L2 price-level changes 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 incremental L2 book diffs28async function streamL2BookDiff(coins, nLevels = 20, autoReconnect = true, retryCount = 0) {29console.log('='.repeat(60));30console.log(`Streaming L2 Book Diffs for ${coins.length > 0 ? coins.join(', ') : 'all coins'}`);31console.log(`Levels: ${nLevels}`);32console.log(`Auto-reconnect: ${autoReconnect}`);33console.log('='.repeat(60) + '\n');3435const maxRetries = 10;36const baseDelay = 2000;3738// Track last seq per coin for gap detection39const lastSeq = {};4041while (retryCount < maxRetries) {42const client = createClient();43const metadata = new grpc.Metadata();44metadata.add('x-token', AUTH_TOKEN);4546const request = {47coins: coins,48n_levels: nLevels49};5051try {52if (retryCount > 0) {53console.log(`\n🔄 Reconnecting (attempt ${retryCount + 1}/${maxRetries})...`);54} else {55console.log(`Connecting to ${GRPC_ENDPOINT}...`);56}5758let msgCount = 0;59const call = client.StreamL2BookDiff(request, metadata);6061call.on('data', (update) => {62msgCount++;6364if (msgCount === 1) {65console.log('✓ First L2 diff update received!\n');66retryCount = 0; // Reset on success67}6869console.log('\n' + '─'.repeat(60));70console.log(`Block: ${update.height} | Time: ${update.time}${update.snapshot ? ' | SNAPSHOT' : ''}`);71console.log('─'.repeat(60));7273(update.diffs || []).forEach(diff => {74// Check seq continuity per coin75if (lastSeq[diff.coin] !== undefined && diff.prev_seq && String(diff.prev_seq) !== String(lastSeq[diff.coin])) {76console.log(` ⚠️ Sequence gap for ${diff.coin}: expected prev_seq ${lastSeq[diff.coin]}, got ${diff.prev_seq}`);77}78lastSeq[diff.coin] = diff.seq;7980const label = diff.snapshot ? 'SNAPSHOT' : 'DIFF';81console.log(`\n ${diff.coin} [${label}] seq: ${diff.seq} (prev: ${diff.prev_seq})`);8283// Changed ask levels (sz "0" means level removed)84(diff.asks || []).forEach(level => {85const action = level.sz === '0' ? 'REMOVE' : 'SET';86console.log(` ASK ${action.padEnd(6)} ${level.px.padStart(12)} | ${level.sz.padStart(12)}`);87});8889// Changed bid levels90(diff.bids || []).forEach(level => {91const action = level.sz === '0' ? 'REMOVE' : 'SET';92console.log(` BID ${action.padEnd(6)} ${level.px.padStart(12)} | ${level.sz.padStart(12)}`);93});94});9596console.log(`\n Messages received: ${msgCount}`);97});9899call.on('error', (err) => {100if (err.code === grpc.status.DATA_LOSS && autoReconnect) {101console.log(`\n⚠️ Server reinitialized: ${err.message}`);102retryCount++;103if (retryCount < maxRetries) {104const delay = baseDelay * Math.pow(2, retryCount - 1);105console.log(`⏳ Waiting ${delay / 1000}s before reconnecting...`);106setTimeout(() => streamL2BookDiff(coins, nLevels, autoReconnect, retryCount), delay);107} else {108console.log(`\n❌ Max retries (${maxRetries}) reached. Giving up.`);109}110} else {111console.error('\ngRPC error:', err.code, '-', err.message);112}113});114115call.on('end', () => {116console.log('\nStream ended');117});118119// Wait for stream to complete120await new Promise((resolve) => {121call.on('end', resolve);122call.on('error', resolve);123});124125break; // Exit retry loop on success126127} catch (err) {128console.error('Error:', err.message);129break;130}131}132}133134// Parse command line args135const args = process.argv.slice(2);136const coins = (args.find(a => a.startsWith('--coins='))?.split('=')[1] || 'BTC').split(',');137const levels = parseInt(args.find(a => a.startsWith('--levels='))?.split('=')[1]) || 20;138139console.log('\n' + '='.repeat(60));140console.log('Hyperliquid StreamL2BookDiff Example');141console.log(`Endpoint: ${GRPC_ENDPOINT}`);142console.log('='.repeat(60));143144streamL2BookDiff(coins, levels);145
1#!/usr/bin/env python32"""3StreamL2BookDiff Example - Stream incremental L2 price-level changes 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_diff_example.py --coins BTC,ETH --levels 2011"""1213import grpc14import sys15importar hora16import 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_book_diff(coins: list, n_levels: int = 20, n_sig_figs: Optional[int] = None, mantissa: Optional[int] = None, skip_initial_snapshot: bool = False, auto_reconnect: bool = True):33"""34Stream incremental L2 price-level changes for one or more coins.3536Args:37coins: Symbols to stream (e.g., ["BTC", "ETH"]). Empty list means all coins38n_levels: Maximum number of price levels tracked per side (default 20, max 100)39n_sig_figs: Significance figures for price bucketing (2-5)40mantissa: Mantissa for bucketing (1, 2, or 5)41skip_initial_snapshot: Skip the initial per-coin snapshot (default False)42auto_reconnect: Auto-reconnect on DATA_LOSS errors (default True)43"""44print(f"\n{'='*60}")45print(f"Streaming L2 Book Diffs for {', '.join(coins) if coins else 'all coins'}")46print(f"Levels: {n_levels}")47print(f"Auto-reconnect: {auto_reconnect}")48print(f"{'='*60}\n")4950retry_count = 051max_retries = 1052base_delay = 25354# Track last seq per coin for gap detection55last_seq = {}5657while retry_count < max_retries:58channel = grpc.secure_channel(59GRPC_ENDPOINT,60grpc.ssl_channel_credentials(),61options=[62('grpc.max_receive_message_length', 100 * 1024 * 1024),63('grpc.keepalive_time_ms', 30000),64]65)66stub = pb_grpc.OrderBookStreamingStub(channel)6768# Build request69request = pb.L2BookDiffRequest(70coins=coins,71n_levels=n_levels,72skip_initial_snapshot=skip_initial_snapshot73)74if n_sig_figs is not None:75request.n_sig_figs = n_sig_figs76if mantissa is not None:77request.mantissa = mantissa7879msg_count = 08081try:82if retry_count > 0:83print(f"\n🔄 Reconnecting (attempt {retry_count + 1}/{max_retries})...")84else:85print(f"Connecting to {GRPC_ENDPOINT}...")8687for update in stub.StreamL2BookDiff(request, metadata=[('x-token', AUTH_TOKEN)]):88msg_count += 18990if msg_count == 1:91print(f"✓ First L2 diff update received!\n")92retry_count = 0 # Reset retry count on successful connection9394# Display the diff update95print(f"\n{'─'*60}")96snapshot_label = " | SNAPSHOT" if update.snapshot else ""97print(f"Block: {update.height} | Time: {update.time}{snapshot_label}")98print(f"{'─'*60}")99100for diff in update.diffs:101# Check seq continuity per coin102if diff.coin in last_seq and diff.prev_seq and diff.prev_seq != last_seq[diff.coin]:103print(f" ⚠️ Sequence gap for {diff.coin}: expected prev_seq {last_seq[diff.coin]}, got {diff.prev_seq}")104last_seq[diff.coin] = diff.seq105106label = "SNAPSHOT" if diff.snapshot else "DIFF"107print(f"\n {diff.coin} [{label}] seq: {diff.seq} (prev: {diff.prev_seq})")108109# Changed ask levels (sz "0" means level removed)110for level in diff.asks:111action = "REMOVE" if level.sz == "0" else "SET"112print(f" ASK {action:<6} {level.px:>12} | {level.sz:>12}")113114# Changed bid levels115for level in diff.bids:116action = "REMOVE" if level.sz == "0" else "SET"117print(f" BID {action:<6} {level.px:>12} | {level.sz:>12}")118119print(f"\n Messages received: {msg_count}")120121except grpc.RpcError as e:122if e.code() == grpc.StatusCode.DATA_LOSS and auto_reconnect:123print(f"\n⚠️ Server reinitialized: {e.details()}")124retry_count += 1125if retry_count < max_retries:126delay = base_delay * (2 ** (retry_count - 1)) # Exponential backoff127print(f"⏳ Waiting {delay}s before reconnecting...")128time.sleep(delay)129channel.close()130continue131else:132print(f"\n❌ Max retries ({max_retries}) reached. Giving up.")133break134else:135print(f"\ngRPC error: {e.code()} - {e.details()}")136break137except KeyboardInterrupt:138print("\nStopping L2 diff stream...")139break140finally:141channel.close()142143# If we get here without error, break the retry loop144break145146147def main():148parser = argparse.ArgumentParser(description='Stream Hyperliquid incremental L2 order book diffs via gRPC')149parser.add_argument('--coins', default='BTC', help='Comma-separated coin symbols to stream (e.g., BTC,ETH)')150parser.add_argument('--levels', type=int, default=20, help='Maximum price levels per side (default: 20, max: 100)')151parser.add_argument('--sig-figs', type=int, default=None, help='Significance figures for bucketing (2-5)')152parser.add_argument('--mantissa', type=int, default=None, help='Mantissa for bucketing (1, 2, or 5)')153parser.add_argument('--skip-snapshot', action='store_true', help='Skip the initial per-coin snapshot')154155args = parser.parse_args()156coins = [c.strip() for c in args.coins.split(',') if c.strip()]157158print(f"\n{'='*60}")159print("Hyperliquid StreamL2BookDiff Example")160print(f"Endpoint: {GRPC_ENDPOINT}")161print(f"{'='*60}")162163try:164stream_l2_book_diff(coins, n_levels=args.levels, n_sig_figs=args.sig_figs, mantissa=args.mantissa, skip_initial_snapshot=args.skip_snapshot)165except Exception as e:166print(f"\nError: {e}")167import traceback168traceback.print_exc()169sys.exit(1)170171172if __name__ == "__main__":173main()174
1#!/usr/bin/env python32"""3StreamL2BookDiff Example - Stream incremental L2 price-level changes 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_diff_example.py --coins BTC,ETH --levels 2011"""1213import grpc14import sys15importar hora16import 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_book_diff(coins: list, n_levels: int = 20, n_sig_figs: Optional[int] = None, mantissa: Optional[int] = None, skip_initial_snapshot: bool = False, auto_reconnect: bool = True):33"""34Stream incremental L2 price-level changes for one or more coins.3536Args:37coins: Symbols to stream (e.g., ["BTC", "ETH"]). Empty list means all coins38n_levels: Maximum number of price levels tracked per side (default 20, max 100)39n_sig_figs: Significance figures for price bucketing (2-5)40mantissa: Mantissa for bucketing (1, 2, or 5)41skip_initial_snapshot: Skip the initial per-coin snapshot (default False)42auto_reconnect: Auto-reconnect on DATA_LOSS errors (default True)43"""44print(f"\n{'='*60}")45print(f"Streaming L2 Book Diffs for {', '.join(coins) if coins else 'all coins'}")46print(f"Levels: {n_levels}")47print(f"Auto-reconnect: {auto_reconnect}")48print(f"{'='*60}\n")4950retry_count = 051max_retries = 1052base_delay = 25354# Track last seq per coin for gap detection55last_seq = {}5657while retry_count < max_retries:58channel = grpc.secure_channel(59GRPC_ENDPOINT,60grpc.ssl_channel_credentials(),61options=[62('grpc.max_receive_message_length', 100 * 1024 * 1024),63('grpc.keepalive_time_ms', 30000),64]65)66stub = pb_grpc.OrderBookStreamingStub(channel)6768# Build request69request = pb.L2BookDiffRequest(70coins=coins,71n_levels=n_levels,72skip_initial_snapshot=skip_initial_snapshot73)74if n_sig_figs is not None:75request.n_sig_figs = n_sig_figs76if mantissa is not None:77request.mantissa = mantissa7879msg_count = 08081try:82if retry_count > 0:83print(f"\n🔄 Reconnecting (attempt {retry_count + 1}/{max_retries})...")84else:85print(f"Connecting to {GRPC_ENDPOINT}...")8687for update in stub.StreamL2BookDiff(request, metadata=[('x-token', AUTH_TOKEN)]):88msg_count += 18990if msg_count == 1:91print(f"✓ First L2 diff update received!\n")92retry_count = 0 # Reset retry count on successful connection9394# Display the diff update95print(f"\n{'─'*60}")96snapshot_label = " | SNAPSHOT" if update.snapshot else ""97print(f"Block: {update.height} | Time: {update.time}{snapshot_label}")98print(f"{'─'*60}")99100for diff in update.diffs:101# Check seq continuity per coin102if diff.coin in last_seq and diff.prev_seq and diff.prev_seq != last_seq[diff.coin]:103print(f" ⚠️ Sequence gap for {diff.coin}: expected prev_seq {last_seq[diff.coin]}, got {diff.prev_seq}")104last_seq[diff.coin] = diff.seq105106label = "SNAPSHOT" if diff.snapshot else "DIFF"107print(f"\n {diff.coin} [{label}] seq: {diff.seq} (prev: {diff.prev_seq})")108109# Changed ask levels (sz "0" means level removed)110for level in diff.asks:111action = "REMOVE" if level.sz == "0" else "SET"112print(f" ASK {action:<6} {level.px:>12} | {level.sz:>12}")113114# Changed bid levels115for level in diff.bids:116action = "REMOVE" if level.sz == "0" else "SET"117print(f" BID {action:<6} {level.px:>12} | {level.sz:>12}")118119print(f"\n Messages received: {msg_count}")120121except grpc.RpcError as e:122if e.code() == grpc.StatusCode.DATA_LOSS and auto_reconnect:123print(f"\n⚠️ Server reinitialized: {e.details()}")124retry_count += 1125if retry_count < max_retries:126delay = base_delay * (2 ** (retry_count - 1)) # Exponential backoff127print(f"⏳ Waiting {delay}s before reconnecting...")128time.sleep(delay)129channel.close()130continue131else:132print(f"\n❌ Max retries ({max_retries}) reached. Giving up.")133break134else:135print(f"\ngRPC error: {e.code()} - {e.details()}")136break137except KeyboardInterrupt:138print("\nStopping L2 diff stream...")139break140finally:141channel.close()142143# If we get here without error, break the retry loop144break145146147def main():148parser = argparse.ArgumentParser(description='Stream Hyperliquid incremental L2 order book diffs via gRPC')149parser.add_argument('--coins', default='BTC', help='Comma-separated coin symbols to stream (e.g., BTC,ETH)')150parser.add_argument('--levels', type=int, default=20, help='Maximum price levels per side (default: 20, max: 100)')151parser.add_argument('--sig-figs', type=int, default=None, help='Significance figures for bucketing (2-5)')152parser.add_argument('--mantissa', type=int, default=None, help='Mantissa for bucketing (1, 2, or 5)')153parser.add_argument('--skip-snapshot', action='store_true', help='Skip the initial per-coin snapshot')154155args = parser.parse_args()156coins = [c.strip() for c in args.coins.split(',') if c.strip()]157158print(f"\n{'='*60}")159print("Hyperliquid StreamL2BookDiff Example")160print(f"Endpoint: {GRPC_ENDPOINT}")161print(f"{'='*60}")162163try:164stream_l2_book_diff(coins, n_levels=args.levels, n_sig_figs=args.sig_figs, mantissa=args.mantissa, skip_initial_snapshot=args.skip_snapshot)165except Exception as e:166print(f"\nError: {e}")167import traceback168traceback.print_exc()169sys.exit(1)170171172if __name__ == "__main__":173main()174
Ainda não tem uma conta?
Crie o seu ponto de extremidade Quicknode em segundos e comece a desenvolver
Comece gratuitamente