StreamL4BookUpdates gRPC Method
请注意,此方法的计费是根据数据消耗量计算的,按 0.0165 MB = 10 个 API 积分 进行计费。
参数
coins
repeated string
正在加载...
退货
流
stream<L4BookUpdatesUpdate>
正在加载...
时间
uint64
正在加载...
高度
uint64
正在加载...
snapshot
bool
正在加载...
diffs
array<L4OrderDiff>
正在加载...
请求
1// StreamL4BookUpdates Example - Stream typed per-order book updates via gRPC2包 main34import (5"上下文"6“flag”7"fmt"8"io"9“日志”10"数学"11“字符串”12“时间”1314"google.golang.grpc"15"google.golang.grpc"16"google.golang.grpc"17"google.golang.grpc"18"google.golang.grpc"1920pb "hyperliquid"21)2223const (24grpcEndpoint = "endpoint.mainnet.quiknode.pro:10000"25authToken = "您的认证令牌"26maxRetries = 1027baseDelay = 2 * time.秒28)2930type localOrder struct {31coin string32user string33side string34px string35sz string36}3738func streamL4BookUpdates(coins []string) error {39fmt.PrintlnPrintln字符串.重复("=", 60))40fmt.Printf("Streaming L4 Book Updates for %s\n", strings.Join(coins, ", "))41fmt.Println("自动重连:true")42fmt.PrintlnPrintln字符串.重复("=", 60) + "\n")4344retryCount := 04546// Simple local order map keyed by oid47orders := make(map[uint64]localOrder)4849for retryCount < maxRetries {50creds := 凭证.NewClientTLSFromCert(nil, "")51conn, err := grpc.Dial(grpcEndpoint,52grpc.WithTransportCredentials(creds),53grpc.WithDefaultCallOptions(grpc.MaxCallRecvMsgSize(100*1024*1024)))54如果 err != nil {55返回 fmt.Errorf("连接失败:%w", err)56}5758client := pb.NewOrderBookStreamingClient(conn)59ctx := 元数据.AppendToOutgoingContext(context.背景(), “x-token”, authToken)6061request := &pb.L4BookUpdatesRequest{62Coins: coins,63}6465if retryCount > 0 {66fmt.Printf("\n🔄 正在重新连接(尝试 %d/%d)...\n", retryCount+1, maxRetries)67} else {68fmt.Printf("正在连接到 %s...\n", grpcEndpoint)69}7071stream, err := client.StreamL4BookUpdates(ctx, request)72如果 err != nil {73conn.关闭()74返回 fmt.Errorf("启动流失败:%w", err)75}7677msgCount := 078shouldRetry := false7980用于 {81更新, err := 流.Recv()82如果 err == io.EOF {83换行84}85如果 err != nil {86st, 好的 := 状态.FromError(err)87如果 好的 && st.代码() == 代码.DataLoss {88fmt.Printf("\n⚠️ 服务器已重新初始化:%s\n", st.消息())89retryCount++90if retryCount < maxRetries {91延迟 := baseDelay * time.Duration(数学.Pow(2, float64(重试次数-1)))92fmt.Printf("⏳ 等待 %v 后再重新连接……\n", 延迟)93时间.睡眠(延迟)94shouldRetry = true95换行96} else {97fmt.Printf("\n❌ 达到最大重试次数 (%d)。放弃。\n", maxRetries)98conn.关闭()99返回 nil100}101}102conn.关闭()103返回 fmt.Errorf("流错误:%w", err)104}105106msgCount++107如果 msgCount == 1 {108fmt.Println("✓ First L4 update received!\n")109retryCount = 0 // 成功后重置110}111112if update.Snapshot {113// Full reset snapshot: rebuild local state114orders = make(map[uint64]localOrder)115}116117// Display update118fmt.Println("\n" + 字符串.重复("─", 60))119snapshotLabel := ""120if update.Snapshot {121snapshotLabel = " | SNAPSHOT"122}123fmt.Printf("Block: %d | Time: %d%s | Diffs: %d\n", update.Height, update.Time, snapshotLabel, len(update.Diffs))124fmt.PrintlnPrintln字符串.重复("─", 60))125126for _, diff := range update.Diffs {127side := "ASK"128if diff.Side == "B" {129side = "BID"130}131132switch diff.DiffType {133case pb.L4OrderDiffType_L4_ORDER_DIFF_TYPE_NEW:134orders[diff.Oid] = localOrder{coin: diff.Coin, user: diff.User, side: diff.Side, px: diff.Px, sz: diff.Sz}135if !update.Snapshot {136fmt.Printf(" NEW %s oid: %d | %s %s x %s | %s\n", diff.Coin, diff.Oid, side, diff.Px, diff.Sz, diff.User)137}138case pb.L4OrderDiffType_L4_ORDER_DIFF_TYPE_UPDATE:139if existing, ok := orders[diff.Oid]; ok {140existing.sz = diff.Sz141orders[diff.Oid] = existing142}143fmt.Printf(" UPDATE %s oid: %d | %s %s x %s | %s\n", diff.Coin, diff.Oid, side, diff.Px, diff.Sz, diff.User)144case pb.L4OrderDiffType_L4_ORDER_DIFF_TYPE_REMOVE:145delete(orders, diff.Oid)146fmt.Printf(" REMOVE %s oid: %d | %s %s | %s\n", diff.Coin, diff.Oid, side, diff.Px, diff.User)147}148}149150fmt.Printf("\n Resting orders tracked: %d | Messages received: %d\n", len(orders), msgCount)151}152153conn.关闭()154155如果 !shouldRetry {156换行157}158}159160返回 nil161}162163func main() {164coinsFlag := flag.String("coins", "BTC", "Comma-separated coin symbols to stream (e.g., BTC,ETH)")165166flag.解析()167168coins := strings.Split(*coinsFlag, ",")169170fmt.Println("\n" + 字符串.重复("=", 60))171fmt.Println("Hyperliquid StreamL4BookUpdates Example")172fmt.Printf("Endpoint:%s\n", grpcEndpoint)173fmt.PrintlnPrintln字符串.重复("=", 60))174175if err := streamL4BookUpdates(coins); err != nil {176log.致命(err)177}178}179
1// StreamL4BookUpdates Example - Stream typed per-order book updates via gRPC2包 main34import (5"上下文"6“flag”7"fmt"8"io"9“日志”10"数学"11“字符串”12“时间”1314"google.golang.grpc"15"google.golang.grpc"16"google.golang.grpc"17"google.golang.grpc"18"google.golang.grpc"1920pb "hyperliquid"21)2223const (24grpcEndpoint = "endpoint.mainnet.quiknode.pro:10000"25authToken = "您的认证令牌"26maxRetries = 1027baseDelay = 2 * time.秒28)2930type localOrder struct {31coin string32user string33side string34px string35sz string36}3738func streamL4BookUpdates(coins []string) error {39fmt.PrintlnPrintln字符串.重复("=", 60))40fmt.Printf("Streaming L4 Book Updates for %s\n", strings.Join(coins, ", "))41fmt.Println("自动重连:true")42fmt.PrintlnPrintln字符串.重复("=", 60) + "\n")4344retryCount := 04546// Simple local order map keyed by oid47orders := make(map[uint64]localOrder)4849for retryCount < maxRetries {50creds := 凭证.NewClientTLSFromCert(nil, "")51conn, err := grpc.Dial(grpcEndpoint,52grpc.WithTransportCredentials(creds),53grpc.WithDefaultCallOptions(grpc.MaxCallRecvMsgSize(100*1024*1024)))54如果 err != nil {55返回 fmt.Errorf("连接失败:%w", err)56}5758client := pb.NewOrderBookStreamingClient(conn)59ctx := 元数据.AppendToOutgoingContext(context.背景(), “x-token”, authToken)6061request := &pb.L4BookUpdatesRequest{62Coins: coins,63}6465if retryCount > 0 {66fmt.Printf("\n🔄 正在重新连接(尝试 %d/%d)...\n", retryCount+1, maxRetries)67} else {68fmt.Printf("正在连接到 %s...\n", grpcEndpoint)69}7071stream, err := client.StreamL4BookUpdates(ctx, request)72如果 err != nil {73conn.关闭()74返回 fmt.Errorf("启动流失败:%w", err)75}7677msgCount := 078shouldRetry := false7980用于 {81更新, err := 流.Recv()82如果 err == io.EOF {83换行84}85如果 err != nil {86st, 好的 := 状态.FromError(err)87如果 好的 && st.代码() == 代码.DataLoss {88fmt.Printf("\n⚠️ 服务器已重新初始化:%s\n", st.消息())89retryCount++90if retryCount < maxRetries {91延迟 := baseDelay * time.Duration(数学.Pow(2, float64(重试次数-1)))92fmt.Printf("⏳ 等待 %v 后再重新连接……\n", 延迟)93时间.睡眠(延迟)94shouldRetry = true95换行96} else {97fmt.Printf("\n❌ 达到最大重试次数 (%d)。放弃。\n", maxRetries)98conn.关闭()99返回 nil100}101}102conn.关闭()103返回 fmt.Errorf("流错误:%w", err)104}105106msgCount++107如果 msgCount == 1 {108fmt.Println("✓ First L4 update received!\n")109retryCount = 0 // 成功后重置110}111112if update.Snapshot {113// Full reset snapshot: rebuild local state114orders = make(map[uint64]localOrder)115}116117// Display update118fmt.Println("\n" + 字符串.重复("─", 60))119snapshotLabel := ""120if update.Snapshot {121snapshotLabel = " | SNAPSHOT"122}123fmt.Printf("Block: %d | Time: %d%s | Diffs: %d\n", update.Height, update.Time, snapshotLabel, len(update.Diffs))124fmt.PrintlnPrintln字符串.重复("─", 60))125126for _, diff := range update.Diffs {127side := "ASK"128if diff.Side == "B" {129side = "BID"130}131132switch diff.DiffType {133case pb.L4OrderDiffType_L4_ORDER_DIFF_TYPE_NEW:134orders[diff.Oid] = localOrder{coin: diff.Coin, user: diff.User, side: diff.Side, px: diff.Px, sz: diff.Sz}135if !update.Snapshot {136fmt.Printf(" NEW %s oid: %d | %s %s x %s | %s\n", diff.Coin, diff.Oid, side, diff.Px, diff.Sz, diff.User)137}138case pb.L4OrderDiffType_L4_ORDER_DIFF_TYPE_UPDATE:139if existing, ok := orders[diff.Oid]; ok {140existing.sz = diff.Sz141orders[diff.Oid] = existing142}143fmt.Printf(" UPDATE %s oid: %d | %s %s x %s | %s\n", diff.Coin, diff.Oid, side, diff.Px, diff.Sz, diff.User)144case pb.L4OrderDiffType_L4_ORDER_DIFF_TYPE_REMOVE:145delete(orders, diff.Oid)146fmt.Printf(" REMOVE %s oid: %d | %s %s | %s\n", diff.Coin, diff.Oid, side, diff.Px, diff.User)147}148}149150fmt.Printf("\n Resting orders tracked: %d | Messages received: %d\n", len(orders), msgCount)151}152153conn.关闭()154155如果 !shouldRetry {156换行157}158}159160返回 nil161}162163func main() {164coinsFlag := flag.String("coins", "BTC", "Comma-separated coin symbols to stream (e.g., BTC,ETH)")165166flag.解析()167168coins := strings.Split(*coinsFlag, ",")169170fmt.Println("\n" + 字符串.重复("=", 60))171fmt.Println("Hyperliquid StreamL4BookUpdates Example")172fmt.Printf("Endpoint:%s\n", grpcEndpoint)173fmt.PrintlnPrintln字符串.重复("=", 60))174175if err := streamL4BookUpdates(coins); err != nil {176log.致命(err)177}178}179
1// StreamL4BookUpdates Example - Stream typed per-order book updates via gRPC2const grpc = require(grpc);3const protoLoader = require(grpc);4const 路径 = require('path');56const GRPC = endpoint.mainnet.quiknode.pro:10000';7const AUTH_TOKEN = '您的认证令牌';8const PROTO_PATH = 路径.join(__dirname, 'proto', 'orderbook.proto');910const 包定义 = protoLoader.loadSync(PROTO_PATH, {11keepCase: true,12多头: 字符串,13枚举: 字符串,14默认值: true,15oneofs: true16});17const proto = grpc.loadPackageDefinition(packageDefinition).hyperliquid;1819函数 createClient() {20返回 new proto.OrderBookStreaming(21GRPC,22grpc.凭据.createSsl(),23{ 'grpc.max_receive_message_length': 100 * 1024 * 1024 }24);25}2627// Stream typed per-order L4 book updates28async function streamL4BookUpdates(coins, autoReconnect = true, retryCount = 0) {29控制台.日志('='.重复(60));30console.log(`Streaming L4 Book Updates for ${coins.length > 0 ? coins.join(', ') : 'all coins'}`);31console.log(`Auto-reconnect: ${autoReconnect}`);32控制台.日志('='.重复(60) + '\n');3334const maxRetries = 10;35const baseDelay = 2000;3637// Simple local order map keyed by oid38const orders = new Map();3940while (retryCount < maxRetries) {41const 客户端 = createClient();42const 元数据 = new grpc.元数据();43元数据.添加('x-token', AUTH_TOKEN);4445const 请求 = {46coins: coins47};4849试一试 {50如果 (retryCount > 0) {51console.log(`\n🔄 Reconnecting (attempt ${retryCount + 1}/${maxRetries})...`);52} else {53console.log(`Connecting to ${GRPC_ENDPOINT}...`);54}5556let msgCount = 0;57const call = client.StreamL4BookUpdates(request, metadata);5859call。在('data', (更新) => {60msgCount++;6162如果 (msgCount === 1) {63console.log('✓ First L4 update received!\n');64retryCount = 0; // 成功后重置65}6667if (update.snapshot) {68// Full reset snapshot: rebuild local state69orders.clear();70}7172控制台.日志('\n' + '─'.重复(60));73console.log(`Block: ${update.height} | Time: ${update.time}${update.snapshot ? ' | SNAPSHOT' : ''} | Diffs: ${(update.diffs || []).length}`);74控制台.日志('─'.重复(60));7576(update.diffs || []).forEach(diff => {77switch (diff.diff_type) {78case 'L4_ORDER_DIFF_TYPE_NEW':79orders.set(diff.oid, { coin: diff.coin, user: diff.user, side: diff.side, px: diff.px, sz: diff.sz });80if (!update.snapshot) {81console.log(` NEW ${diff.coin} oid: ${diff.oid} | ${diff.side === 'B' ? 'BID' : 'ASK'} ${diff.px} x ${diff.sz} | ${diff.user}`);82}83break;84case 'L4_ORDER_DIFF_TYPE_UPDATE':85const existing = orders.get(diff.oid);86if (existing) {87existing.sz = diff.sz;88}89console.log(` UPDATE ${diff.coin} oid: ${diff.oid} | ${diff.side === 'B' ? 'BID' : 'ASK'} ${diff.px} x ${diff.sz} | ${diff.user}`);90break;91case 'L4_ORDER_DIFF_TYPE_REMOVE':92orders.delete(diff.oid);93console.log(` REMOVE ${diff.coin} oid: ${diff.oid} | ${diff.side === 'B' ? 'BID' : 'ASK'} ${diff.px} | ${diff.user}`);94break;95}96});9798console.log(`\n Resting orders tracked: ${orders.size} | Messages received: ${msgCount}`);99});100101call。在('error', (err) => {102如果 (err.代码 === grpc.状态.数据丢失 && 自动重连) {103console.log(`\n⚠️ Server reinitialized: ${err.message}`);104retryCount++;105if (retryCount < maxRetries) {106const 延迟 = baseDelay * Math.pow(2, retryCount - 1);107console.log(`⏳ Waiting ${delay / 1000}s before reconnecting...`);108setTimeout(() => streamL4BookUpdates(coins, autoReconnect, retryCount), delay);109} else {110console.log(`\n❌ Max retries (${maxRetries}) reached. Giving up.`);111}112} else {113控制台.错误('\ngRPC 错误:', err.code, '-', err.message);114}115});116117call。在('end', () => {118控制台.日志('\n流已结束');119});120121// Wait for stream to complete122等待 new Promise((决心) => {123call。在('end', resolve);124call。在('error', resolve);125});126127换行; // 成功后退出重试循环128129} catch (err) {130控制台.错误('错误:', err.message);131换行;132}133}134}135136// Parse command line args137const args = process.argv.slice(2);138const coins = (args.find(a => a.startsWith('--coins='))?.split('=')[1] || 'BTC').split(',');139140控制台.日志('\n' + '='.重复(60));141console.log('Hyperliquid StreamL4BookUpdates Example');142console.log(`Endpoint: ${GRPC_ENDPOINT}`);143控制台.日志('='.重复(60));144145streamL4BookUpdates(coins);146
1// StreamL4BookUpdates Example - Stream typed per-order book updates via gRPC2const grpc = require(grpc);3const protoLoader = require(grpc);4const 路径 = require('path');56const GRPC = endpoint.mainnet.quiknode.pro:10000';7const AUTH_TOKEN = '您的认证令牌';8const PROTO_PATH = 路径.join(__dirname, 'proto', 'orderbook.proto');910const 包定义 = protoLoader.loadSync(PROTO_PATH, {11keepCase: true,12多头: 字符串,13枚举: 字符串,14默认值: true,15oneofs: true16});17const proto = grpc.loadPackageDefinition(packageDefinition).hyperliquid;1819函数 createClient() {20返回 new proto.OrderBookStreaming(21GRPC,22grpc.凭据.createSsl(),23{ 'grpc.max_receive_message_length': 100 * 1024 * 1024 }24);25}2627// Stream typed per-order L4 book updates28async function streamL4BookUpdates(coins, autoReconnect = true, retryCount = 0) {29控制台.日志('='.重复(60));30console.log(`Streaming L4 Book Updates for ${coins.length > 0 ? coins.join(', ') : 'all coins'}`);31console.log(`Auto-reconnect: ${autoReconnect}`);32控制台.日志('='.重复(60) + '\n');3334const maxRetries = 10;35const baseDelay = 2000;3637// Simple local order map keyed by oid38const orders = new Map();3940while (retryCount < maxRetries) {41const 客户端 = createClient();42const 元数据 = new grpc.元数据();43元数据.添加('x-token', AUTH_TOKEN);4445const 请求 = {46coins: coins47};4849试一试 {50如果 (retryCount > 0) {51console.log(`\n🔄 Reconnecting (attempt ${retryCount + 1}/${maxRetries})...`);52} else {53console.log(`Connecting to ${GRPC_ENDPOINT}...`);54}5556let msgCount = 0;57const call = client.StreamL4BookUpdates(request, metadata);5859call。在('data', (更新) => {60msgCount++;6162如果 (msgCount === 1) {63console.log('✓ First L4 update received!\n');64retryCount = 0; // 成功后重置65}6667if (update.snapshot) {68// Full reset snapshot: rebuild local state69orders.clear();70}7172控制台.日志('\n' + '─'.重复(60));73console.log(`Block: ${update.height} | Time: ${update.time}${update.snapshot ? ' | SNAPSHOT' : ''} | Diffs: ${(update.diffs || []).length}`);74控制台.日志('─'.重复(60));7576(update.diffs || []).forEach(diff => {77switch (diff.diff_type) {78case 'L4_ORDER_DIFF_TYPE_NEW':79orders.set(diff.oid, { coin: diff.coin, user: diff.user, side: diff.side, px: diff.px, sz: diff.sz });80if (!update.snapshot) {81console.log(` NEW ${diff.coin} oid: ${diff.oid} | ${diff.side === 'B' ? 'BID' : 'ASK'} ${diff.px} x ${diff.sz} | ${diff.user}`);82}83break;84case 'L4_ORDER_DIFF_TYPE_UPDATE':85const existing = orders.get(diff.oid);86if (existing) {87existing.sz = diff.sz;88}89console.log(` UPDATE ${diff.coin} oid: ${diff.oid} | ${diff.side === 'B' ? 'BID' : 'ASK'} ${diff.px} x ${diff.sz} | ${diff.user}`);90break;91case 'L4_ORDER_DIFF_TYPE_REMOVE':92orders.delete(diff.oid);93console.log(` REMOVE ${diff.coin} oid: ${diff.oid} | ${diff.side === 'B' ? 'BID' : 'ASK'} ${diff.px} | ${diff.user}`);94break;95}96});9798console.log(`\n Resting orders tracked: ${orders.size} | Messages received: ${msgCount}`);99});100101call。在('error', (err) => {102如果 (err.代码 === grpc.状态.数据丢失 && 自动重连) {103console.log(`\n⚠️ Server reinitialized: ${err.message}`);104retryCount++;105if (retryCount < maxRetries) {106const 延迟 = baseDelay * Math.pow(2, retryCount - 1);107console.log(`⏳ Waiting ${delay / 1000}s before reconnecting...`);108setTimeout(() => streamL4BookUpdates(coins, autoReconnect, retryCount), delay);109} else {110console.log(`\n❌ Max retries (${maxRetries}) reached. Giving up.`);111}112} else {113控制台.错误('\ngRPC 错误:', err.code, '-', err.message);114}115});116117call。在('end', () => {118控制台.日志('\n流已结束');119});120121// Wait for stream to complete122等待 new Promise((决心) => {123call。在('end', resolve);124call。在('error', resolve);125});126127换行; // 成功后退出重试循环128129} catch (err) {130控制台.错误('错误:', err.message);131换行;132}133}134}135136// Parse command line args137const args = process.argv.slice(2);138const coins = (args.find(a => a.startsWith('--coins='))?.split('=')[1] || 'BTC').split(',');139140控制台.日志('\n' + '='.重复(60));141console.log('Hyperliquid StreamL4BookUpdates Example');142console.log(`Endpoint: ${GRPC_ENDPOINT}`);143控制台.日志('='.重复(60));144145streamL4BookUpdates(coins);146
1#!/usr/bin/env python32"""3StreamL4BookUpdates Example - Stream typed per-order book updates via gRPC45设置:6pip install grpcio grpcio-tools protobuf zstandard7python -mgrpc.protoc -I../../proto --python_out=.grpc. ../../proto/orderbook.proto89用法:10python stream_l4_updates_example.py --coins BTC,ETH11"""1213import grpc14import sys15import 时间16import argparse1718试一试:19import orderbook_pb2 作为 pb20import orderbook_pb2_grpc 为 pb_grpc21except ImportError:22打印("错误:未生成 Proto 文件。请运行:")23打印(" python -mgrpc.protoc -I../../proto --python_out=.grpc. ../../proto/orderbook.proto")24sys.exit(1)2526# 配置27GRPC= "endpoint.mainnet.quiknode.pro:10000"28AUTH_TOKEN = "您的认证令牌"293031def stream_l4_book_updates(coins: list, auto_reconnect: bool = True):32"""33Stream typed per-order L4 book updates for one or more coins.3435参数:36coins: Symbols to stream (e.g., ["BTC", "ETH"]). Empty list means all coins37auto_reconnect:在发生 DATA_LOSS 错误时自动重新连接(默认值为 True)38"""39打印(f"\n{'='*60}")40print(f"Streaming L4 Book Updates for {', '.join(coins) if coins else 'all coins'}")41print(f"Auto-reconnect: {auto_reconnect}")42打印(f"{'='*60}\n")4344retry_count = 045max_retries = 1046base_delay = 24748# Simple local order map keyed by oid49orders = {}5051while retry_count < max_retries:52频道 = grpc.secure_channel(53GRPC,54grpc.ssl_channel_credentials(),55选项=[56(grpc.max_receive_message_length', 100 * 1024 * 1024),57(grpc.keepalive_time_ms', 30000),58]59)60占位符 = pb_grpc.订单流存根(channel)6162# 构建请求63request = pb.L4BookUpdatesRequest(coins=coins)6465msg_count = 06667试一试:68如果 retry_count > 0:69打印(f"\n🔄 正在重新连接(尝试 {重试次数 + 1}/{最大重试次数})...")70else:71打印(f"正在连接到 {GRPC}...")7273for update in stub.StreamL4BookUpdates(request, metadata=[('x-token', AUTH_TOKEN)]):74msg_count += 17576如果 msg_count == 1:77print(f"✓ First L4 update received!\n")78retry_count = 0 # 连接成功后重置重试计数7980if update.snapshot:81# Full reset snapshot: rebuild local state82orders.clear()8384# Display the update85打印(f"\n{'─'*60}")86snapshot_label = " | SNAPSHOT" if update.snapshot else ""87print(f"Block: {update.height} | Time: {update.time}{snapshot_label} | Diffs: {len(update.diffs)}")88打印(f"{'─'*60}")8990for diff in update.diffs:91side = "BID" if diff.side == "B" else "ASK"9293if diff.diff_type == pb.L4_ORDER_DIFF_TYPE_NEW:94orders[diff.oid] = {"coin": diff.coin, "user": diff.user, "side": diff.side, "px": diff.px, "sz": diff.sz}95if not update.snapshot:96print(f" NEW {diff.coin} oid: {diff.oid} | {side} {diff.px} x {diff.sz} | {diff.user}")97elif diff.diff_type == pb.L4_ORDER_DIFF_TYPE_UPDATE:98if diff.oid in orders:99orders[diff.oid]["sz"] = diff.sz100print(f" UPDATE {diff.coin} oid: {diff.oid} | {side} {diff.px} x {diff.sz} | {diff.user}")101elif diff.diff_type == pb.L4_ORDER_DIFF_TYPE_REMOVE:102orders.pop(diff.oid, None)103print(f" REMOVE {diff.coin} oid: {diff.oid} | {side} {diff.px} | {diff.user}")104105print(f"\n Resting orders tracked: {len(orders)} | Messages received: {msg_count}")106107除了 grpc。RpcError 作为 e:108如果 e.代码() == grpc.状态码.DATA_LOSS 和 auto_reconnect:109print(f"\n⚠️ Server reinitialized: {e.details()}")110retry_count += 1111if retry_count < max_retries:112延迟 = base_delay * (2 ** (重试次数 - 1)) # 指数退避113打印(f"⏳ 正在等待 {延迟}秒后重新连接...")114时间.sleep(延迟)115频道.关闭()116继续117else:118打印(f"\n❌ 最大重试次数 ({max_retries}) 已达到。放弃。")119换行120else:121print(f"\ngRPC error: {e.code()} - {e.details()}")122换行123除了 KeyboardInterrupt:124print("\nStopping L4 updates stream...")125换行126最后:127频道.关闭()128129# 如果在此处未出现错误,则退出重试循环130换行131132133def main():134parser = argparse.ArgumentParser(description='Stream Hyperliquid typed per-order L4 book updates via gRPC')135parser.add_argument('--coins', default='BTC', help='Comma-separated coin symbols to stream (e.g., BTC,ETH)')136137args = 解析器.parse_args()138coins = [c.strip() for c in args.coins.split(',') if c.strip()]139140打印(f"\n{'='*60}")141print("Hyperliquid StreamL4BookUpdates Example")142print(f"Endpoint: {GRPC_ENDPOINT}")143打印(f"{'='*60}")144145试一试:146stream_l4_book_updates(coins)147除 异常 作为 e:148print(f"\nError: {e}")149import traceback150回溯.print_exc()151sys.exit(1)152153154如果 __name__ == "__main__":155主()156
1#!/usr/bin/env python32"""3StreamL4BookUpdates Example - Stream typed per-order book updates via gRPC45设置:6pip install grpcio grpcio-tools protobuf zstandard7python -mgrpc.protoc -I../../proto --python_out=.grpc. ../../proto/orderbook.proto89用法:10python stream_l4_updates_example.py --coins BTC,ETH11"""1213import grpc14import sys15import 时间16import argparse1718试一试:19import orderbook_pb2 作为 pb20import orderbook_pb2_grpc 为 pb_grpc21except ImportError:22打印("错误:未生成 Proto 文件。请运行:")23打印(" python -mgrpc.protoc -I../../proto --python_out=.grpc. ../../proto/orderbook.proto")24sys.exit(1)2526# 配置27GRPC= "endpoint.mainnet.quiknode.pro:10000"28AUTH_TOKEN = "您的认证令牌"293031def stream_l4_book_updates(coins: list, auto_reconnect: bool = True):32"""33Stream typed per-order L4 book updates for one or more coins.3435参数:36coins: Symbols to stream (e.g., ["BTC", "ETH"]). Empty list means all coins37auto_reconnect:在发生 DATA_LOSS 错误时自动重新连接(默认值为 True)38"""39打印(f"\n{'='*60}")40print(f"Streaming L4 Book Updates for {', '.join(coins) if coins else 'all coins'}")41print(f"Auto-reconnect: {auto_reconnect}")42打印(f"{'='*60}\n")4344retry_count = 045max_retries = 1046base_delay = 24748# Simple local order map keyed by oid49orders = {}5051while retry_count < max_retries:52频道 = grpc.secure_channel(53GRPC,54grpc.ssl_channel_credentials(),55选项=[56(grpc.max_receive_message_length', 100 * 1024 * 1024),57(grpc.keepalive_time_ms', 30000),58]59)60占位符 = pb_grpc.订单流存根(channel)6162# 构建请求63request = pb.L4BookUpdatesRequest(coins=coins)6465msg_count = 06667试一试:68如果 retry_count > 0:69打印(f"\n🔄 正在重新连接(尝试 {重试次数 + 1}/{最大重试次数})...")70else:71打印(f"正在连接到 {GRPC}...")7273for update in stub.StreamL4BookUpdates(request, metadata=[('x-token', AUTH_TOKEN)]):74msg_count += 17576如果 msg_count == 1:77print(f"✓ First L4 update received!\n")78retry_count = 0 # 连接成功后重置重试计数7980if update.snapshot:81# Full reset snapshot: rebuild local state82orders.clear()8384# Display the update85打印(f"\n{'─'*60}")86snapshot_label = " | SNAPSHOT" if update.snapshot else ""87print(f"Block: {update.height} | Time: {update.time}{snapshot_label} | Diffs: {len(update.diffs)}")88打印(f"{'─'*60}")8990for diff in update.diffs:91side = "BID" if diff.side == "B" else "ASK"9293if diff.diff_type == pb.L4_ORDER_DIFF_TYPE_NEW:94orders[diff.oid] = {"coin": diff.coin, "user": diff.user, "side": diff.side, "px": diff.px, "sz": diff.sz}95if not update.snapshot:96print(f" NEW {diff.coin} oid: {diff.oid} | {side} {diff.px} x {diff.sz} | {diff.user}")97elif diff.diff_type == pb.L4_ORDER_DIFF_TYPE_UPDATE:98if diff.oid in orders:99orders[diff.oid]["sz"] = diff.sz100print(f" UPDATE {diff.coin} oid: {diff.oid} | {side} {diff.px} x {diff.sz} | {diff.user}")101elif diff.diff_type == pb.L4_ORDER_DIFF_TYPE_REMOVE:102orders.pop(diff.oid, None)103print(f" REMOVE {diff.coin} oid: {diff.oid} | {side} {diff.px} | {diff.user}")104105print(f"\n Resting orders tracked: {len(orders)} | Messages received: {msg_count}")106107除了 grpc。RpcError 作为 e:108如果 e.代码() == grpc.状态码.DATA_LOSS 和 auto_reconnect:109print(f"\n⚠️ Server reinitialized: {e.details()}")110retry_count += 1111if retry_count < max_retries:112延迟 = base_delay * (2 ** (重试次数 - 1)) # 指数退避113打印(f"⏳ 正在等待 {延迟}秒后重新连接...")114时间.sleep(延迟)115频道.关闭()116继续117else:118打印(f"\n❌ 最大重试次数 ({max_retries}) 已达到。放弃。")119换行120else:121print(f"\ngRPC error: {e.code()} - {e.details()}")122换行123除了 KeyboardInterrupt:124print("\nStopping L4 updates stream...")125换行126最后:127频道.关闭()128129# 如果在此处未出现错误,则退出重试循环130换行131132133def main():134parser = argparse.ArgumentParser(description='Stream Hyperliquid typed per-order L4 book updates via gRPC')135parser.add_argument('--coins', default='BTC', help='Comma-separated coin symbols to stream (e.g., BTC,ETH)')136137args = 解析器.parse_args()138coins = [c.strip() for c in args.coins.split(',') if c.strip()]139140打印(f"\n{'='*60}")141print("Hyperliquid StreamL4BookUpdates Example")142print(f"Endpoint: {GRPC_ENDPOINT}")143打印(f"{'='*60}")144145试一试:146stream_l4_book_updates(coins)147除 异常 作为 e:148print(f"\nError: {e}")149import traceback150回溯.print_exc()151sys.exit(1)152153154如果 __name__ == "__main__":155主()156