StreamL2BookgRPC
请注意,此方法的计费是根据数据消耗量计算的,按 0.0165 MB = 10 个 API 积分 进行计费。
参数
硬币
字符串
正在加载...
n_levels
uint32
正在加载...
n_sig_figs
uint32
正在加载...
尾数
uint64
正在加载...
退货
流
stream<L2BookUpdate>
正在加载...
硬币
字符串
正在加载...
时间
uint64
正在加载...
区块号
uint64
正在加载...
出价
array<L2Level>
正在加载...
询问
array<L2Level>
正在加载...
请求
1// StreamL2Book Example - Stream aggregated orderbook data 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)2930func streamL2Orderbook(币种 字符串, nLevels uint32) error {31fmt.PrintlnPrintln字符串.重复("=", 60))32fmt.Printf("%s 的 L2 订单簿流式传输\n", coin)33fmt.Printf("水位:%d\n", nLevels)34fmt.Println("自动重连:true")35fmt.PrintlnPrintln字符串.重复("=", 60) + "\n")3637retryCount := 03839for retryCount < maxRetries {40creds := 凭证.NewClientTLSFromCert(nil, "")41conn, err := grpc.Dial(grpcEndpoint,42grpc.WithTransportCredentials(creds),43grpc.WithDefaultCallOptions(grpc.MaxCallRecvMsgSize(100*1024*1024)))44如果 err != nil {45返回 fmt.Errorf("连接失败:%w", err)46}4748client := pb.NewOrderBookStreamingClient(conn)49ctx := 元数据.AppendToOutgoingContext(context.背景(), “x-token”, authToken)5051request := &pb.L2BookRequest{52硬币: 硬币,53NLevels: nLevels,54}5556if retryCount > 0 {57fmt.Printf("\n🔄 正在重新连接(尝试 %d/%d)...\n", retryCount+1, maxRetries)58} else {59fmt.Printf("正在连接到 %s...\n", grpcEndpoint)60}6162流, err := client.StreamL2Book(ctx, request)63如果 err != nil {64conn.关闭()65返回 fmt.Errorf("启动流失败:%w", err)66}6768msgCount := 069shouldRetry := false7071用于 {72更新, err := 流.Recv()73如果 err == io.EOF {74换行75}76如果 err != nil {77st, 好的 := 状态.FromError(err)78如果 好的 && st.代码() == 代码.DataLoss {79fmt.Printf("\n⚠️ 服务器已重新初始化:%s\n", st.消息())80retryCount++81if retryCount < maxRetries {82延迟 := baseDelay * time.Duration(数学.Pow(2, float64(重试次数-1)))83fmt.Printf("⏳ 等待 %v 后再重新连接……\n", 延迟)84时间.睡眠(延迟)85shouldRetry = true86换行87} else {88fmt.Printf("\n❌ 达到最大重试次数 (%d)。放弃。\n", maxRetries)89conn.关闭()90返回 nil91}92}93conn.关闭()94返回 fmt.Errorf("流错误:%w", err)95}9697msgCount++98如果 msgCount == 1 {99fmt.Println("✓ 已收到首次 L2 更新!\n")100retryCount = 0 // 成功后重置101}102103// Display orderbook104fmt.Println("\n" + 字符串.重复("─", 60))105fmt.Printf("方块:%d | 时间:%d | 金币:%s\n", update.区块号, 更新.Time, 更新.硬币)106fmt.PrintlnPrintln字符串.重复("─", 60))107108// Display asks (reversed)109如果 len(update.询问) > 0 {110fmt.Println("\n ASKS:")111askCount := len(update.询问)112如果 askCount > 10 {113askCount = 10114}115for i := askCount - 1; i >= 0; i-- {116level := 更新.查询[i]117fmt.Printf(" %12s | %12s | (%d 笔订单)\n", level.Px, level.Sz, level.N)118}119}120121// Display spread122如果 len(update.出价) > 0 && len(update.询问) > 0 {123fmt.Println("\n " + 字符串.重复("─", 44))124fmt.Printf(" 点差:(最佳买价:%s,最佳卖价:%s)\n", update.买价[0].Px, 更新.询问[0].Px)125fmt.Println(" " + 字符串.重复("─", 44))126}127128// Display bids129如果 len(update.出价) > 0 {130fmt.Println("\n 买入价:")131bidCount := len(update.买单)132如果 bidCount > 10 {133bidCount = 10134}135for i := 0; i < bidCount; i++ {136level := 更新.买单[i]137fmt.Printf(" %12s | %12s | (%d 笔订单)\n", level.Px, level.Sz, level.N)138}139}140141fmt.Printf("\n 已接收消息:%d\n", msgCount)142}143144conn.关闭()145146如果 !shouldRetry {147换行148}149}150151返回 nil152}153154func main() {155coin := 标志.字符串("coin", "BTC", "要直播的币种代码")156级别 := 标志.Uint("levels", 20, "价格层级数")157158flag.解析()159160fmt.Println("\n" + 字符串.重复("=", 60))161fmt.Println("Hyperliquid 示例")162fmt.Printf("Endpoint:%s\n", grpcEndpoint)163fmt.PrintlnPrintln字符串.重复("=", 60))164165if err := streamL2Orderbook(*coin, uint32(*levels)); err != nil {166log.致命(err)167}168}169
1// StreamL2Book Example - Stream aggregated orderbook data 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)2930func streamL2Orderbook(币种 字符串, nLevels uint32) error {31fmt.PrintlnPrintln字符串.重复("=", 60))32fmt.Printf("%s 的 L2 订单簿流式传输\n", coin)33fmt.Printf("水位:%d\n", nLevels)34fmt.Println("自动重连:true")35fmt.PrintlnPrintln字符串.重复("=", 60) + "\n")3637retryCount := 03839for retryCount < maxRetries {40creds := 凭证.NewClientTLSFromCert(nil, "")41conn, err := grpc.Dial(grpcEndpoint,42grpc.WithTransportCredentials(creds),43grpc.WithDefaultCallOptions(grpc.MaxCallRecvMsgSize(100*1024*1024)))44如果 err != nil {45返回 fmt.Errorf("连接失败:%w", err)46}4748client := pb.NewOrderBookStreamingClient(conn)49ctx := 元数据.AppendToOutgoingContext(context.背景(), “x-token”, authToken)5051request := &pb.L2BookRequest{52硬币: 硬币,53NLevels: nLevels,54}5556if retryCount > 0 {57fmt.Printf("\n🔄 正在重新连接(尝试 %d/%d)...\n", retryCount+1, maxRetries)58} else {59fmt.Printf("正在连接到 %s...\n", grpcEndpoint)60}6162流, err := client.StreamL2Book(ctx, request)63如果 err != nil {64conn.关闭()65返回 fmt.Errorf("启动流失败:%w", err)66}6768msgCount := 069shouldRetry := false7071用于 {72更新, err := 流.Recv()73如果 err == io.EOF {74换行75}76如果 err != nil {77st, 好的 := 状态.FromError(err)78如果 好的 && st.代码() == 代码.DataLoss {79fmt.Printf("\n⚠️ 服务器已重新初始化:%s\n", st.消息())80retryCount++81if retryCount < maxRetries {82延迟 := baseDelay * time.Duration(数学.Pow(2, float64(重试次数-1)))83fmt.Printf("⏳ 等待 %v 后再重新连接……\n", 延迟)84时间.睡眠(延迟)85shouldRetry = true86换行87} else {88fmt.Printf("\n❌ 达到最大重试次数 (%d)。放弃。\n", maxRetries)89conn.关闭()90返回 nil91}92}93conn.关闭()94返回 fmt.Errorf("流错误:%w", err)95}9697msgCount++98如果 msgCount == 1 {99fmt.Println("✓ 已收到首次 L2 更新!\n")100retryCount = 0 // 成功后重置101}102103// Display orderbook104fmt.Println("\n" + 字符串.重复("─", 60))105fmt.Printf("方块:%d | 时间:%d | 金币:%s\n", update.区块号, 更新.Time, 更新.硬币)106fmt.PrintlnPrintln字符串.重复("─", 60))107108// Display asks (reversed)109如果 len(update.询问) > 0 {110fmt.Println("\n ASKS:")111askCount := len(update.询问)112如果 askCount > 10 {113askCount = 10114}115for i := askCount - 1; i >= 0; i-- {116level := 更新.查询[i]117fmt.Printf(" %12s | %12s | (%d 笔订单)\n", level.Px, level.Sz, level.N)118}119}120121// Display spread122如果 len(update.出价) > 0 && len(update.询问) > 0 {123fmt.Println("\n " + 字符串.重复("─", 44))124fmt.Printf(" 点差:(最佳买价:%s,最佳卖价:%s)\n", update.买价[0].Px, 更新.询问[0].Px)125fmt.Println(" " + 字符串.重复("─", 44))126}127128// Display bids129如果 len(update.出价) > 0 {130fmt.Println("\n 买入价:")131bidCount := len(update.买单)132如果 bidCount > 10 {133bidCount = 10134}135for i := 0; i < bidCount; i++ {136level := 更新.买单[i]137fmt.Printf(" %12s | %12s | (%d 笔订单)\n", level.Px, level.Sz, level.N)138}139}140141fmt.Printf("\n 已接收消息:%d\n", msgCount)142}143144conn.关闭()145146如果 !shouldRetry {147换行148}149}150151返回 nil152}153154func main() {155coin := 标志.字符串("coin", "BTC", "要直播的币种代码")156级别 := 标志.Uint("levels", 20, "价格层级数")157158flag.解析()159160fmt.Println("\n" + 字符串.重复("=", 60))161fmt.Println("Hyperliquid 示例")162fmt.Printf("Endpoint:%s\n", grpcEndpoint)163fmt.PrintlnPrintln字符串.重复("=", 60))164165if err := streamL2Orderbook(*coin, uint32(*levels)); err != nil {166log.致命(err)167}168}169
1// StreamL2Book Example - Stream aggregated orderbook data 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 L2 (aggregated) orderbook28async 函数 streamL2Orderbook(币种, nLevels = 20, autoReconnect = true, 重试次数 = 0) {29控制台.日志('='.重复(60));30console.log(`Streaming L2 Orderbook for ${coin}`);31console.log(`Levels: ${nLevels}`);32console.log(`Auto-reconnect: ${autoReconnect}`);33控制台.日志('='.重复(60) + '\n');3435const maxRetries = 10;36const baseDelay = 2000;3738while (retryCount < maxRetries) {39const 客户端 = createClient();40const 元数据 = new grpc.元数据();41元数据.添加('x-token', AUTH_TOKEN);4243const 请求 = {44硬币: 硬币,45n_levels: nLevels46};4748试一试 {49如果 (retryCount > 0) {50console.log(`\n🔄 Reconnecting (attempt ${retryCount + 1}/${maxRetries})...`);51} else {52console.log(`Connecting to ${GRPC_ENDPOINT}...`);53}5455let msgCount = 0;56const 调用 = 客户端.StreamL2Book(请求, 元数据);5758call。在('data', (更新) => {59msgCount++;6061如果 (msgCount === 1) {62控制台.日志('✓ 已收到首次 L2 更新!\n');63retryCount = 0; // 成功后重置64}6566控制台.日志('\n' + '─'.重复(60));67console.log(`Block: ${update.block_number} | Time: ${update.time} | Coin: ${update.coin}`);68控制台.日志('─'.重复(60));6970// Display asks (reversed for display)71如果 (更新.询问 && 更新.询问.长度 > 0) {72控制台.日志('\n ASKS:');73更新.询问.切片(0, 10).reverse().forEach(level => {74console.log(` ${level.px.padStart(12)} | ${level.sz.padStart(12)} | (${level.n} orders)`);75});76}7778// Display spread79如果 (更新.出价 && 更新.出价.长度 > 0 && update.询问 && 更新.询问.长度 > 0) {80const bestBid = parseFloat(update.bids[0].px);81const bestAsk = parseFloat(update.询问[0].px);82const spread = bestAsk - 最佳买价;83const spreadBps = (spread / bestBid) * 10000;84控制台.日志('\n ' + '─'.重复(44));85console.log(` SPREAD: ${spread.toFixed(2)} (${spreadBps.toFixed(2)} bps)`);86控制台.日志(' ' + '─'.重复(44));87}8889// Display bids90如果 (更新.出价 && 更新.出价.长度 > 0) {91控制台.日志('\n BIDS:');92更新.出价.切片(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。在('error', (err) => {101如果 (err.代码 === grpc.状态.数据丢失 && 自动重连) {102console.log(`\n⚠️ Server reinitialized: ${err.message}`);103retryCount++;104if (retryCount < maxRetries) {105const 延迟 = baseDelay * Math.pow(2, retryCount - 1);106console.log(`⏳ Waiting ${delay / 1000}s before reconnecting...`);107setTimeout(() => streamL2Orderbook(币种, nLevels, autoReconnect, 重试次数), 延迟);108} else {109console.log(`\n❌ Max retries (${maxRetries}) reached. Giving up.`);110}111} else {112控制台.错误('\ngRPC 错误:', err.code, '-', err.message);113}114});115116call。在('end', () => {117控制台.日志('\n流已结束');118});119120// Wait for stream to complete121等待 new Promise((决心) => {122call。在('end', resolve);123call。在('error', resolve);124});125126换行; // 成功后退出重试循环127128} catch (err) {129控制台.错误('错误:', err.message);130换行;131}132}133}134135// Parse command line args136const args = process.argv.slice(2);137const 硬币 = args.find(a => a.startsWith('--coin='))?.split('=')[1] || 'BTC';138const 等级 = parseInt(args.find(a => a.startsWith('--levels='))?.split('=')[1]) || 20;139140控制台.日志('\n' + '='.重复(60));141控制台.日志(Hyperliquid 示例');142console.log(`Endpoint: ${GRPC_ENDPOINT}`);143控制台.日志('='.重复(60));144145streamL2Orderbook(币种, 级别);146
1// StreamL2Book Example - Stream aggregated orderbook data 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 L2 (aggregated) orderbook28async 函数 streamL2Orderbook(币种, nLevels = 20, autoReconnect = true, 重试次数 = 0) {29控制台.日志('='.重复(60));30console.log(`Streaming L2 Orderbook for ${coin}`);31console.log(`Levels: ${nLevels}`);32console.log(`Auto-reconnect: ${autoReconnect}`);33控制台.日志('='.重复(60) + '\n');3435const maxRetries = 10;36const baseDelay = 2000;3738while (retryCount < maxRetries) {39const 客户端 = createClient();40const 元数据 = new grpc.元数据();41元数据.添加('x-token', AUTH_TOKEN);4243const 请求 = {44硬币: 硬币,45n_levels: nLevels46};4748试一试 {49如果 (retryCount > 0) {50console.log(`\n🔄 Reconnecting (attempt ${retryCount + 1}/${maxRetries})...`);51} else {52console.log(`Connecting to ${GRPC_ENDPOINT}...`);53}5455let msgCount = 0;56const 调用 = 客户端.StreamL2Book(请求, 元数据);5758call。在('data', (更新) => {59msgCount++;6061如果 (msgCount === 1) {62控制台.日志('✓ 已收到首次 L2 更新!\n');63retryCount = 0; // 成功后重置64}6566控制台.日志('\n' + '─'.重复(60));67console.log(`Block: ${update.block_number} | Time: ${update.time} | Coin: ${update.coin}`);68控制台.日志('─'.重复(60));6970// Display asks (reversed for display)71如果 (更新.询问 && 更新.询问.长度 > 0) {72控制台.日志('\n ASKS:');73更新.询问.切片(0, 10).reverse().forEach(level => {74console.log(` ${level.px.padStart(12)} | ${level.sz.padStart(12)} | (${level.n} orders)`);75});76}7778// Display spread79如果 (更新.出价 && 更新.出价.长度 > 0 && update.询问 && 更新.询问.长度 > 0) {80const bestBid = parseFloat(update.bids[0].px);81const bestAsk = parseFloat(update.询问[0].px);82const spread = bestAsk - 最佳买价;83const spreadBps = (spread / bestBid) * 10000;84控制台.日志('\n ' + '─'.重复(44));85console.log(` SPREAD: ${spread.toFixed(2)} (${spreadBps.toFixed(2)} bps)`);86控制台.日志(' ' + '─'.重复(44));87}8889// Display bids90如果 (更新.出价 && 更新.出价.长度 > 0) {91控制台.日志('\n BIDS:');92更新.出价.切片(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。在('error', (err) => {101如果 (err.代码 === grpc.状态.数据丢失 && 自动重连) {102console.log(`\n⚠️ Server reinitialized: ${err.message}`);103retryCount++;104if (retryCount < maxRetries) {105const 延迟 = baseDelay * Math.pow(2, retryCount - 1);106console.log(`⏳ Waiting ${delay / 1000}s before reconnecting...`);107setTimeout(() => streamL2Orderbook(币种, nLevels, autoReconnect, 重试次数), 延迟);108} else {109console.log(`\n❌ Max retries (${maxRetries}) reached. Giving up.`);110}111} else {112控制台.错误('\ngRPC 错误:', err.code, '-', err.message);113}114});115116call。在('end', () => {117控制台.日志('\n流已结束');118});119120// Wait for stream to complete121等待 new Promise((决心) => {122call。在('end', resolve);123call。在('error', resolve);124});125126换行; // 成功后退出重试循环127128} catch (err) {129控制台.错误('错误:', err.message);130换行;131}132}133}134135// Parse command line args136const args = process.argv.slice(2);137const 硬币 = args.find(a => a.startsWith('--coin='))?.split('=')[1] || 'BTC';138const 等级 = parseInt(args.find(a => a.startsWith('--levels='))?.split('=')[1]) || 20;139140控制台.日志('\n' + '='.重复(60));141控制台.日志(Hyperliquid 示例');142console.log(`Endpoint: ${GRPC_ENDPOINT}`);143控制台.日志('='.重复(60));144145streamL2Orderbook(币种, 级别);146
1#!/usr/bin/env python32"""3StreamL2Book 示例——通过gRPC流式传输聚合的订单簿数据45设置:6pip install grpcio grpcio-tools protobuf zstandard7python -mgrpc.protoc -I../../proto --python_out=.grpc. ../../proto/orderbook.proto89用法:10python stream_l2_example.py --coin BTC --levels 2011"""1213import grpc14import sys15import 时间16import argparse17来自 输入 import Optional1819试一试:20import orderbook_pb2 作为 pb21import orderbook_pb2_grpc 为 pb_grpc22except ImportError:23打印("错误:未生成 Proto 文件。请运行:")24打印(" python -mgrpc.protoc -I../../proto --python_out=.grpc. ../../proto/orderbook.proto")25sys.exit(1)2627# 配置28GRPC= "endpoint.mainnet.quiknode.pro:10000"29AUTH_TOKEN = "您的认证令牌"303132def stream_l2_orderbook(币种: str, n_levels: int = 20, n_sig_figs: 可选[int] = None, 尾数: 可选[int] = None, auto_reconnect: bool = True):33"""34流式传输某币种的 L2(聚合)订单簿更新。3536参数:37币种:要直播的币种符号(例如,“BTC”、“ETH”)38n_levels:要显示的价格级别数量(默认值为 20,最大值为 100)39n_sig_figs:价格分档的有效数字(2-5)40尾数:用于分桶的尾数(1、2 或 5)41auto_reconnect:在发生 DATA_LOSS 错误时自动重新连接(默认值为 True)42"""43打印(f"\n{'='*60}")44打印(f"流式传输 L2 订单簿,用于 {币种}")45print(f"Levels: {n_levels}")46print(f"Auto-reconnect: {auto_reconnect}")47打印(f"{'='*60}\n")4849retry_count = 050max_retries = 1051base_delay = 25253while retry_count < max_retries:54频道 = grpc.secure_channel(55GRPC,56grpc.ssl_channel_credentials(),57选项=[58(grpc.max_receive_message_length', 100 * 1024 * 1024),59(grpc.keepalive_time_ms', 30000),60]61)62占位符 = pb_grpc.订单流存根(channel)6364# 构建请求65请求 = pb.L2BookRequest(66硬币=硬币,67n_levels=n_levels68)69如果 n_sig_figs 为 不 None时:70请求.n_sig_figs = n_sig_figs71如果 尾数 是 不 None:72请求.尾数 = 尾数7374msg_count = 07576试一试:77如果 retry_count > 0:78打印(f"\n🔄 正在重新连接(尝试 {重试次数 + 1}/{最大重试次数})...")79else:80打印(f"正在连接到 {GRPC}...")8182用于 更新 在 存根中。StreamL2Book(请求, 元数据=[('x-token', AUTH_TOKEN)]):83msg_count += 18485如果 msg_count == 1:86打印(f"✓ 收到首个 L2 更新!\n")87retry_count = 0 # 连接成功后重置重试计数8889# 显示 L2 订单簿90打印(f"\n{'─'*60}")91print(f"Block: {update.block_number} | Time: {update.time} | Coin: {update.coin}")92打印(f"{'─'*60}")9394# 显示问题(按得分从高到低排序显示)95如果 更新.询问:96打印("\n ASKS:")97用于 级别 在 反向(列表(更新.询问[:10])): # 十大请求98print(f" {level.px:>12} | {level.sz:>12} | ({level.n} orders)")99100# 显示点差101如果 更新.出价 和 update.询价:102best_bid = float(update.出价[0].px) 如果 更新.出价 else 0103best_ask = float(更新.询问[0].px) 如果 更新.询问 else 0104如果 best_bid 且 best_ask:105spread = 最佳卖价 - 最佳买价106spread_bps = (点差 / 最佳买价) * 10000 如果 best_bid > 0 else 0107打印(f"\n {'─'*44}")108print(f" SPREAD: {spread:.2f} ({spread_bps:.2f} bps)")109打印(f" {'─'*44}")110111# 显示出价112如果 更新.出价:113打印("\n BIDS:")114用于 级别 在 update中。出价[:10]: # 前10名出价115print(f" {level.px:>12} | {level.sz:>12} | ({level.n} orders)")116117print(f"\n Messages received: {msg_count}")118119除了 grpc。RpcError 作为 e:120如果 e.代码() == grpc.状态码.DATA_LOSS 和 auto_reconnect:121print(f"\n⚠️ Server reinitialized: {e.details()}")122retry_count += 1123if retry_count < max_retries:124延迟 = base_delay * (2 ** (重试次数 - 1)) # 指数退避125打印(f"⏳ 正在等待 {延迟}秒后重新连接...")126时间.sleep(延迟)127频道.关闭()128继续129else:130打印(f"\n❌ 最大重试次数 ({max_retries}) 已达到。放弃。")131换行132else:133print(f"\ngRPC error: {e.code()} - {e.details()}")134换行135除了 KeyboardInterrupt:136打印("\n停止 L2 流...")137换行138最后:139频道.关闭()140141# 如果在此处未出现错误,则退出重试循环142换行143144145def main():146解析器 = argparse.ArgumentParser(描述='通过 gRPC 流式传输Hyperliquid 订单簿数据gRPC)147解析器.add_argument('--coin', 默认='BTC', 帮助='要直播的币种代码')148解析器.add_argument('--levels', type=int, 默认值=20, 帮助='价格层级数量(默认:20,最大:100)')149解析器.add_argument('--sig-figs', type=int, 默认值=None, 帮助='分桶法中的有效数字(2-5)')150解析器.add_argument('--mantissa', type=int, 默认值=None, 帮助='分桶小数部分(1、2 或 5)')151152args = 解析器.parse_args()153154打印(f"\n{'='*60}")155打印(Hyperliquid 示例”)156print(f"Endpoint: {GRPC_ENDPOINT}")157打印(f"{'='*60}")158159试一试:160stream_l2_orderbook(参数.coin, n_levels=args.levels, n_sig_figs=args.sig_figs, 尾数=args.尾数)161除 异常 作为 e:162print(f"\nError: {e}")163import traceback164回溯.print_exc()165sys.exit(1)166167168如果 __name__ == "__main__":169主()170
1#!/usr/bin/env python32"""3StreamL2Book 示例——通过gRPC流式传输聚合的订单簿数据45设置:6pip install grpcio grpcio-tools protobuf zstandard7python -mgrpc.protoc -I../../proto --python_out=.grpc. ../../proto/orderbook.proto89用法:10python stream_l2_example.py --coin BTC --levels 2011"""1213import grpc14import sys15import 时间16import argparse17来自 输入 import Optional1819试一试:20import orderbook_pb2 作为 pb21import orderbook_pb2_grpc 为 pb_grpc22except ImportError:23打印("错误:未生成 Proto 文件。请运行:")24打印(" python -mgrpc.protoc -I../../proto --python_out=.grpc. ../../proto/orderbook.proto")25sys.exit(1)2627# 配置28GRPC= "endpoint.mainnet.quiknode.pro:10000"29AUTH_TOKEN = "您的认证令牌"303132def stream_l2_orderbook(币种: str, n_levels: int = 20, n_sig_figs: 可选[int] = None, 尾数: 可选[int] = None, auto_reconnect: bool = True):33"""34流式传输某币种的 L2(聚合)订单簿更新。3536参数:37币种:要直播的币种符号(例如,“BTC”、“ETH”)38n_levels:要显示的价格级别数量(默认值为 20,最大值为 100)39n_sig_figs:价格分档的有效数字(2-5)40尾数:用于分桶的尾数(1、2 或 5)41auto_reconnect:在发生 DATA_LOSS 错误时自动重新连接(默认值为 True)42"""43打印(f"\n{'='*60}")44打印(f"流式传输 L2 订单簿,用于 {币种}")45print(f"Levels: {n_levels}")46print(f"Auto-reconnect: {auto_reconnect}")47打印(f"{'='*60}\n")4849retry_count = 050max_retries = 1051base_delay = 25253while retry_count < max_retries:54频道 = grpc.secure_channel(55GRPC,56grpc.ssl_channel_credentials(),57选项=[58(grpc.max_receive_message_length', 100 * 1024 * 1024),59(grpc.keepalive_time_ms', 30000),60]61)62占位符 = pb_grpc.订单流存根(channel)6364# 构建请求65请求 = pb.L2BookRequest(66硬币=硬币,67n_levels=n_levels68)69如果 n_sig_figs 为 不 None时:70请求.n_sig_figs = n_sig_figs71如果 尾数 是 不 None:72请求.尾数 = 尾数7374msg_count = 07576试一试:77如果 retry_count > 0:78打印(f"\n🔄 正在重新连接(尝试 {重试次数 + 1}/{最大重试次数})...")79else:80打印(f"正在连接到 {GRPC}...")8182用于 更新 在 存根中。StreamL2Book(请求, 元数据=[('x-token', AUTH_TOKEN)]):83msg_count += 18485如果 msg_count == 1:86打印(f"✓ 收到首个 L2 更新!\n")87retry_count = 0 # 连接成功后重置重试计数8889# 显示 L2 订单簿90打印(f"\n{'─'*60}")91print(f"Block: {update.block_number} | Time: {update.time} | Coin: {update.coin}")92打印(f"{'─'*60}")9394# 显示问题(按得分从高到低排序显示)95如果 更新.询问:96打印("\n ASKS:")97用于 级别 在 反向(列表(更新.询问[:10])): # 十大请求98print(f" {level.px:>12} | {level.sz:>12} | ({level.n} orders)")99100# 显示点差101如果 更新.出价 和 update.询价:102best_bid = float(update.出价[0].px) 如果 更新.出价 else 0103best_ask = float(更新.询问[0].px) 如果 更新.询问 else 0104如果 best_bid 且 best_ask:105spread = 最佳卖价 - 最佳买价106spread_bps = (点差 / 最佳买价) * 10000 如果 best_bid > 0 else 0107打印(f"\n {'─'*44}")108print(f" SPREAD: {spread:.2f} ({spread_bps:.2f} bps)")109打印(f" {'─'*44}")110111# 显示出价112如果 更新.出价:113打印("\n BIDS:")114用于 级别 在 update中。出价[:10]: # 前10名出价115print(f" {level.px:>12} | {level.sz:>12} | ({level.n} orders)")116117print(f"\n Messages received: {msg_count}")118119除了 grpc。RpcError 作为 e:120如果 e.代码() == grpc.状态码.DATA_LOSS 和 auto_reconnect:121print(f"\n⚠️ Server reinitialized: {e.details()}")122retry_count += 1123if retry_count < max_retries:124延迟 = base_delay * (2 ** (重试次数 - 1)) # 指数退避125打印(f"⏳ 正在等待 {延迟}秒后重新连接...")126时间.sleep(延迟)127频道.关闭()128继续129else:130打印(f"\n❌ 最大重试次数 ({max_retries}) 已达到。放弃。")131换行132else:133print(f"\ngRPC error: {e.code()} - {e.details()}")134换行135除了 KeyboardInterrupt:136打印("\n停止 L2 流...")137换行138最后:139频道.关闭()140141# 如果在此处未出现错误,则退出重试循环142换行143144145def main():146解析器 = argparse.ArgumentParser(描述='通过 gRPC 流式传输Hyperliquid 订单簿数据gRPC)147解析器.add_argument('--coin', 默认='BTC', 帮助='要直播的币种代码')148解析器.add_argument('--levels', type=int, 默认值=20, 帮助='价格层级数量(默认:20,最大:100)')149解析器.add_argument('--sig-figs', type=int, 默认值=None, 帮助='分桶法中的有效数字(2-5)')150解析器.add_argument('--mantissa', type=int, 默认值=None, 帮助='分桶小数部分(1、2 或 5)')151152args = 解析器.parse_args()153154打印(f"\n{'='*60}")155打印(Hyperliquid 示例”)156print(f"Endpoint: {GRPC_ENDPOINT}")157打印(f"{'='*60}")158159试一试:160stream_l2_orderbook(参数.coin, n_levels=args.levels, n_sig_figs=args.sig_figs, 尾数=args.尾数)161除 异常 作为 e:162print(f"\nError: {e}")163import traceback164回溯.print_exc()165sys.exit(1)166167168如果 __name__ == "__main__":169主()170