StreamTpslUpdates gRPC Method
请注意,此方法的计费是根据数据消耗量计算的,按 0.0165 MB = 10 个 API 积分 进行计费。
参数
coins
repeated string
正在加载...
退货
流
stream<TpslUpdatesUpdate>
正在加载...
时间
uint64
正在加载...
高度
uint64
正在加载...
snapshot
bool
正在加载...
diffs
array<TpslOrderDiff>
正在加载...
请求
1// StreamTpslUpdates Example - Stream TP/SL trigger-order 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)2930func streamTpslUpdates(coins []string) error {31fmt.PrintlnPrintln字符串.重复("=", 60))32fmt.Printf("Streaming TP/SL Updates for %s\n", strings.Join(coins, ", "))33fmt.Println("自动重连:true")34fmt.PrintlnPrintln字符串.重复("=", 60) + "\n")3536retryCount := 03738for retryCount < maxRetries {39creds := 凭证.NewClientTLSFromCert(nil, "")40conn, err := grpc.Dial(grpcEndpoint,41grpc.WithTransportCredentials(creds),42grpc.WithDefaultCallOptions(grpc.MaxCallRecvMsgSize(100*1024*1024)))43如果 err != nil {44返回 fmt.Errorf("连接失败:%w", err)45}4647client := pb.NewOrderBookStreamingClient(conn)48ctx := 元数据.AppendToOutgoingContext(context.背景(), “x-token”, authToken)4950request := &pb.TpslUpdatesRequest{51Coins: coins,52}5354if retryCount > 0 {55fmt.Printf("\n🔄 正在重新连接(尝试 %d/%d)...\n", retryCount+1, maxRetries)56} else {57fmt.Printf("正在连接到 %s...\n", grpcEndpoint)58}5960stream, err := client.StreamTpslUpdates(ctx, request)61如果 err != nil {62conn.关闭()63返回 fmt.Errorf("启动流失败:%w", err)64}6566msgCount := 067shouldRetry := false6869用于 {70更新, err := 流.Recv()71如果 err == io.EOF {72换行73}74如果 err != nil {75st, 好的 := 状态.FromError(err)76如果 好的 && st.代码() == 代码.DataLoss {77fmt.Printf("\n⚠️ 服务器已重新初始化:%s\n", st.消息())78retryCount++79if retryCount < maxRetries {80延迟 := baseDelay * time.Duration(数学.Pow(2, float64(重试次数-1)))81fmt.Printf("⏳ 等待 %v 后再重新连接……\n", 延迟)82时间.睡眠(延迟)83shouldRetry = true84换行85} else {86fmt.Printf("\n❌ 达到最大重试次数 (%d)。放弃。\n", maxRetries)87conn.关闭()88返回 nil89}90}91conn.关闭()92返回 fmt.Errorf("流错误:%w", err)93}9495msgCount++96如果 msgCount == 1 {97fmt.Println("✓ First TP/SL update received!\n")98retryCount = 0 // 成功后重置99}100101// Display update102fmt.Println("\n" + 字符串.重复("─", 60))103snapshotLabel := ""104if update.Snapshot {105snapshotLabel = " | SNAPSHOT"106}107fmt.Printf("Block: %d | Time: %d%s | Diffs: %d\n", update.Height, update.Time, snapshotLabel, len(update.Diffs))108fmt.PrintlnPrintln字符串.重复("─", 60))109110for _, diff := range update.Diffs {111side := "ASK"112if diff.Side == "B" {113side = "BID"114}115sz := diff.Sz116if diff.IsPositionTpsl {117sz = "position-sized"118}119120switch diff.DiffType {121case pb.TpslDiffType_TPSL_DIFF_TYPE_ADD:122fmt.Printf(" ADD %s oid: %d | %s | %s sz: %s\n", diff.Coin, diff.Oid, diff.OrderType, side, sz)123fmt.Printf(" trigger: %s | limit px: %s | reduce-only: %t\n", diff.TriggerCondition, diff.LimitPx, diff.ReduceOnly)124case pb.TpslDiffType_TPSL_DIFF_TYPE_REMOVE:125fmt.Printf(" REMOVE %s oid: %d | %s | reason: %s\n", diff.Coin, diff.Oid, diff.OrderType, diff.Reason)126}127}128129fmt.Printf("\n 已接收消息:%d\n", msgCount)130}131132conn.关闭()133134如果 !shouldRetry {135换行136}137}138139返回 nil140}141142func main() {143coinsFlag := flag.String("coins", "ETH", "Comma-separated coin symbols to stream (e.g., ETH,BTC)")144145flag.解析()146147coins := strings.Split(*coinsFlag, ",")148149fmt.Println("\n" + 字符串.重复("=", 60))150fmt.Println("Hyperliquid StreamTpslUpdates Example")151fmt.Printf("Endpoint:%s\n", grpcEndpoint)152fmt.PrintlnPrintln字符串.重复("=", 60))153154if err := streamTpslUpdates(coins); err != nil {155log.致命(err)156}157}158
1// StreamTpslUpdates Example - Stream TP/SL trigger-order 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)2930func streamTpslUpdates(coins []string) error {31fmt.PrintlnPrintln字符串.重复("=", 60))32fmt.Printf("Streaming TP/SL Updates for %s\n", strings.Join(coins, ", "))33fmt.Println("自动重连:true")34fmt.PrintlnPrintln字符串.重复("=", 60) + "\n")3536retryCount := 03738for retryCount < maxRetries {39creds := 凭证.NewClientTLSFromCert(nil, "")40conn, err := grpc.Dial(grpcEndpoint,41grpc.WithTransportCredentials(creds),42grpc.WithDefaultCallOptions(grpc.MaxCallRecvMsgSize(100*1024*1024)))43如果 err != nil {44返回 fmt.Errorf("连接失败:%w", err)45}4647client := pb.NewOrderBookStreamingClient(conn)48ctx := 元数据.AppendToOutgoingContext(context.背景(), “x-token”, authToken)4950request := &pb.TpslUpdatesRequest{51Coins: coins,52}5354if retryCount > 0 {55fmt.Printf("\n🔄 正在重新连接(尝试 %d/%d)...\n", retryCount+1, maxRetries)56} else {57fmt.Printf("正在连接到 %s...\n", grpcEndpoint)58}5960stream, err := client.StreamTpslUpdates(ctx, request)61如果 err != nil {62conn.关闭()63返回 fmt.Errorf("启动流失败:%w", err)64}6566msgCount := 067shouldRetry := false6869用于 {70更新, err := 流.Recv()71如果 err == io.EOF {72换行73}74如果 err != nil {75st, 好的 := 状态.FromError(err)76如果 好的 && st.代码() == 代码.DataLoss {77fmt.Printf("\n⚠️ 服务器已重新初始化:%s\n", st.消息())78retryCount++79if retryCount < maxRetries {80延迟 := baseDelay * time.Duration(数学.Pow(2, float64(重试次数-1)))81fmt.Printf("⏳ 等待 %v 后再重新连接……\n", 延迟)82时间.睡眠(延迟)83shouldRetry = true84换行85} else {86fmt.Printf("\n❌ 达到最大重试次数 (%d)。放弃。\n", maxRetries)87conn.关闭()88返回 nil89}90}91conn.关闭()92返回 fmt.Errorf("流错误:%w", err)93}9495msgCount++96如果 msgCount == 1 {97fmt.Println("✓ First TP/SL update received!\n")98retryCount = 0 // 成功后重置99}100101// Display update102fmt.Println("\n" + 字符串.重复("─", 60))103snapshotLabel := ""104if update.Snapshot {105snapshotLabel = " | SNAPSHOT"106}107fmt.Printf("Block: %d | Time: %d%s | Diffs: %d\n", update.Height, update.Time, snapshotLabel, len(update.Diffs))108fmt.PrintlnPrintln字符串.重复("─", 60))109110for _, diff := range update.Diffs {111side := "ASK"112if diff.Side == "B" {113side = "BID"114}115sz := diff.Sz116if diff.IsPositionTpsl {117sz = "position-sized"118}119120switch diff.DiffType {121case pb.TpslDiffType_TPSL_DIFF_TYPE_ADD:122fmt.Printf(" ADD %s oid: %d | %s | %s sz: %s\n", diff.Coin, diff.Oid, diff.OrderType, side, sz)123fmt.Printf(" trigger: %s | limit px: %s | reduce-only: %t\n", diff.TriggerCondition, diff.LimitPx, diff.ReduceOnly)124case pb.TpslDiffType_TPSL_DIFF_TYPE_REMOVE:125fmt.Printf(" REMOVE %s oid: %d | %s | reason: %s\n", diff.Coin, diff.Oid, diff.OrderType, diff.Reason)126}127}128129fmt.Printf("\n 已接收消息:%d\n", msgCount)130}131132conn.关闭()133134如果 !shouldRetry {135换行136}137}138139返回 nil140}141142func main() {143coinsFlag := flag.String("coins", "ETH", "Comma-separated coin symbols to stream (e.g., ETH,BTC)")144145flag.解析()146147coins := strings.Split(*coinsFlag, ",")148149fmt.Println("\n" + 字符串.重复("=", 60))150fmt.Println("Hyperliquid StreamTpslUpdates Example")151fmt.Printf("Endpoint:%s\n", grpcEndpoint)152fmt.PrintlnPrintln字符串.重复("=", 60))153154if err := streamTpslUpdates(coins); err != nil {155log.致命(err)156}157}158
1// StreamTpslUpdates Example - Stream TP/SL trigger-order 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 TP/SL trigger-order updates28async function streamTpslUpdates(coins, autoReconnect = true, retryCount = 0) {29控制台.日志('='.重复(60));30console.log(`Streaming TP/SL Updates for ${coins.length > 0 ? coins.join(', ') : 'all perp coins'}`);31console.log(`Auto-reconnect: ${autoReconnect}`);32控制台.日志('='.重复(60) + '\n');3334const maxRetries = 10;35const baseDelay = 2000;3637while (retryCount < maxRetries) {38const 客户端 = createClient();39const 元数据 = new grpc.元数据();40元数据.添加('x-token', AUTH_TOKEN);4142const 请求 = {43coins: coins44};4546试一试 {47如果 (retryCount > 0) {48console.log(`\n🔄 Reconnecting (attempt ${retryCount + 1}/${maxRetries})...`);49} else {50console.log(`Connecting to ${GRPC_ENDPOINT}...`);51}5253let msgCount = 0;54const call = client.StreamTpslUpdates(request, metadata);5556call。在('data', (更新) => {57msgCount++;5859如果 (msgCount === 1) {60console.log('✓ First TP/SL update received!\n');61retryCount = 0; // 成功后重置62}6364控制台.日志('\n' + '─'.重复(60));65console.log(`Block: ${update.height} | Time: ${update.time}${update.snapshot ? ' | SNAPSHOT' : ''} | Diffs: ${(update.diffs || []).length}`);66控制台.日志('─'.重复(60));6768(update.diffs || []).forEach(diff => {69const side = diff.side === 'B' ? 'BID' : 'ASK';70const sz = diff.is_position_tpsl ? 'position-sized' : diff.sz;7172if (diff.diff_type === 'TPSL_DIFF_TYPE_ADD') {73console.log(` ADD ${diff.coin} oid: ${diff.oid} | ${diff.order_type} | ${side} sz: ${sz}`);74console.log(` trigger: ${diff.trigger_condition} | limit px: ${diff.limit_px} | reduce-only: ${diff.reduce_only}`);75} else if (diff.diff_type === 'TPSL_DIFF_TYPE_REMOVE') {76console.log(` REMOVE ${diff.coin} oid: ${diff.oid} | ${diff.order_type} | reason: ${diff.reason}`);77}78});7980console.log(`\n Messages received: ${msgCount}`);81});8283call。在('error', (err) => {84如果 (err.代码 === grpc.状态.数据丢失 && 自动重连) {85console.log(`\n⚠️ Server reinitialized: ${err.message}`);86retryCount++;87if (retryCount < maxRetries) {88const 延迟 = baseDelay * Math.pow(2, retryCount - 1);89console.log(`⏳ Waiting ${delay / 1000}s before reconnecting...`);90setTimeout(() => streamTpslUpdates(coins, autoReconnect, retryCount), delay);91} else {92console.log(`\n❌ Max retries (${maxRetries}) reached. Giving up.`);93}94} else {95控制台.错误('\ngRPC 错误:', err.code, '-', err.message);96}97});9899call。在('end', () => {100控制台.日志('\n流已结束');101});102103// Wait for stream to complete104等待 new Promise((决心) => {105call。在('end', resolve);106call。在('error', resolve);107});108109换行; // 成功后退出重试循环110111} catch (err) {112控制台.错误('错误:', err.message);113换行;114}115}116}117118// Parse command line args119const args = process.argv.slice(2);120const coins = (args.find(a => a.startsWith('--coins='))?.split('=')[1] || 'ETH').split(',');121122控制台.日志('\n' + '='.重复(60));123console.log('Hyperliquid StreamTpslUpdates Example');124console.log(`Endpoint: ${GRPC_ENDPOINT}`);125控制台.日志('='.重复(60));126127streamTpslUpdates(coins);128
1// StreamTpslUpdates Example - Stream TP/SL trigger-order 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 TP/SL trigger-order updates28async function streamTpslUpdates(coins, autoReconnect = true, retryCount = 0) {29控制台.日志('='.重复(60));30console.log(`Streaming TP/SL Updates for ${coins.length > 0 ? coins.join(', ') : 'all perp coins'}`);31console.log(`Auto-reconnect: ${autoReconnect}`);32控制台.日志('='.重复(60) + '\n');3334const maxRetries = 10;35const baseDelay = 2000;3637while (retryCount < maxRetries) {38const 客户端 = createClient();39const 元数据 = new grpc.元数据();40元数据.添加('x-token', AUTH_TOKEN);4142const 请求 = {43coins: coins44};4546试一试 {47如果 (retryCount > 0) {48console.log(`\n🔄 Reconnecting (attempt ${retryCount + 1}/${maxRetries})...`);49} else {50console.log(`Connecting to ${GRPC_ENDPOINT}...`);51}5253let msgCount = 0;54const call = client.StreamTpslUpdates(request, metadata);5556call。在('data', (更新) => {57msgCount++;5859如果 (msgCount === 1) {60console.log('✓ First TP/SL update received!\n');61retryCount = 0; // 成功后重置62}6364控制台.日志('\n' + '─'.重复(60));65console.log(`Block: ${update.height} | Time: ${update.time}${update.snapshot ? ' | SNAPSHOT' : ''} | Diffs: ${(update.diffs || []).length}`);66控制台.日志('─'.重复(60));6768(update.diffs || []).forEach(diff => {69const side = diff.side === 'B' ? 'BID' : 'ASK';70const sz = diff.is_position_tpsl ? 'position-sized' : diff.sz;7172if (diff.diff_type === 'TPSL_DIFF_TYPE_ADD') {73console.log(` ADD ${diff.coin} oid: ${diff.oid} | ${diff.order_type} | ${side} sz: ${sz}`);74console.log(` trigger: ${diff.trigger_condition} | limit px: ${diff.limit_px} | reduce-only: ${diff.reduce_only}`);75} else if (diff.diff_type === 'TPSL_DIFF_TYPE_REMOVE') {76console.log(` REMOVE ${diff.coin} oid: ${diff.oid} | ${diff.order_type} | reason: ${diff.reason}`);77}78});7980console.log(`\n Messages received: ${msgCount}`);81});8283call。在('error', (err) => {84如果 (err.代码 === grpc.状态.数据丢失 && 自动重连) {85console.log(`\n⚠️ Server reinitialized: ${err.message}`);86retryCount++;87if (retryCount < maxRetries) {88const 延迟 = baseDelay * Math.pow(2, retryCount - 1);89console.log(`⏳ Waiting ${delay / 1000}s before reconnecting...`);90setTimeout(() => streamTpslUpdates(coins, autoReconnect, retryCount), delay);91} else {92console.log(`\n❌ Max retries (${maxRetries}) reached. Giving up.`);93}94} else {95控制台.错误('\ngRPC 错误:', err.code, '-', err.message);96}97});9899call。在('end', () => {100控制台.日志('\n流已结束');101});102103// Wait for stream to complete104等待 new Promise((决心) => {105call。在('end', resolve);106call。在('error', resolve);107});108109换行; // 成功后退出重试循环110111} catch (err) {112控制台.错误('错误:', err.message);113换行;114}115}116}117118// Parse command line args119const args = process.argv.slice(2);120const coins = (args.find(a => a.startsWith('--coins='))?.split('=')[1] || 'ETH').split(',');121122控制台.日志('\n' + '='.重复(60));123console.log('Hyperliquid StreamTpslUpdates Example');124console.log(`Endpoint: ${GRPC_ENDPOINT}`);125控制台.日志('='.重复(60));126127streamTpslUpdates(coins);128
1#!/usr/bin/env python32"""3StreamTpslUpdates Example - Stream TP/SL trigger-order updates via gRPC45设置:6pip install grpcio grpcio-tools protobuf zstandard7python -mgrpc.protoc -I../../proto --python_out=.grpc. ../../proto/orderbook.proto89用法:10python stream_tpsl_example.py --coins ETH,BTC11"""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_tpsl_updates(coins: list, auto_reconnect: bool = True):32"""33Stream TP/SL trigger-order updates for one or more coins.3435参数:36coins: Symbols to stream (e.g., ["ETH", "BTC"]). Empty list means all perp coins37auto_reconnect:在发生 DATA_LOSS 错误时自动重新连接(默认值为 True)38"""39打印(f"\n{'='*60}")40print(f"Streaming TP/SL Updates for {', '.join(coins) if coins else 'all perp coins'}")41print(f"Auto-reconnect: {auto_reconnect}")42打印(f"{'='*60}\n")4344retry_count = 045max_retries = 1046base_delay = 24748while retry_count < max_retries:49频道 = grpc.secure_channel(50GRPC,51grpc.ssl_channel_credentials(),52选项=[53(grpc.max_receive_message_length', 100 * 1024 * 1024),54(grpc.keepalive_time_ms', 30000),55]56)57占位符 = pb_grpc.订单流存根(channel)5859# 构建请求60request = pb.TpslUpdatesRequest(coins=coins)6162msg_count = 06364试一试:65如果 retry_count > 0:66打印(f"\n🔄 正在重新连接(尝试 {重试次数 + 1}/{最大重试次数})...")67else:68打印(f"正在连接到 {GRPC}...")6970for update in stub.StreamTpslUpdates(request, metadata=[('x-token', AUTH_TOKEN)]):71msg_count += 17273如果 msg_count == 1:74print(f"✓ First TP/SL update received!\n")75retry_count = 0 # 连接成功后重置重试计数7677# Display the update78打印(f"\n{'─'*60}")79snapshot_label = " | SNAPSHOT" if update.snapshot else ""80print(f"Block: {update.height} | Time: {update.time}{snapshot_label} | Diffs: {len(update.diffs)}")81打印(f"{'─'*60}")8283for diff in update.diffs:84side = "BID" if diff.side == "B" else "ASK"85sz = "position-sized" if diff.is_position_tpsl else diff.sz8687if diff.diff_type == pb.TPSL_DIFF_TYPE_ADD:88print(f" ADD {diff.coin} oid: {diff.oid} | {diff.order_type} | {side} sz: {sz}")89print(f" trigger: {diff.trigger_condition} | limit px: {diff.limit_px} | reduce-only: {diff.reduce_only}")90elif diff.diff_type == pb.TPSL_DIFF_TYPE_REMOVE:91print(f" REMOVE {diff.coin} oid: {diff.oid} | {diff.order_type} | reason: {diff.reason}")9293print(f"\n Messages received: {msg_count}")9495除了 grpc。RpcError 作为 e:96如果 e.代码() == grpc.状态码.DATA_LOSS 和 auto_reconnect:97print(f"\n⚠️ Server reinitialized: {e.details()}")98retry_count += 199if retry_count < max_retries:100延迟 = base_delay * (2 ** (重试次数 - 1)) # 指数退避101打印(f"⏳ 正在等待 {延迟}秒后重新连接...")102时间.sleep(延迟)103频道.关闭()104继续105else:106打印(f"\n❌ 最大重试次数 ({max_retries}) 已达到。放弃。")107换行108else:109print(f"\ngRPC error: {e.code()} - {e.details()}")110换行111除了 KeyboardInterrupt:112print("\nStopping TP/SL stream...")113换行114最后:115频道.关闭()116117# 如果在此处未出现错误,则退出重试循环118换行119120121def main():122parser = argparse.ArgumentParser(description='Stream Hyperliquid TP/SL trigger-order updates via gRPC')123parser.add_argument('--coins', default='ETH', help='Comma-separated coin symbols to stream (e.g., ETH,BTC)')124125args = 解析器.parse_args()126coins = [c.strip() for c in args.coins.split(',') if c.strip()]127128打印(f"\n{'='*60}")129print("Hyperliquid StreamTpslUpdates Example")130print(f"Endpoint: {GRPC_ENDPOINT}")131打印(f"{'='*60}")132133试一试:134stream_tpsl_updates(coins)135除 异常 作为 e:136print(f"\nError: {e}")137import traceback138回溯.print_exc()139sys.exit(1)140141142如果 __name__ == "__main__":143主()144
1#!/usr/bin/env python32"""3StreamTpslUpdates Example - Stream TP/SL trigger-order updates via gRPC45设置:6pip install grpcio grpcio-tools protobuf zstandard7python -mgrpc.protoc -I../../proto --python_out=.grpc. ../../proto/orderbook.proto89用法:10python stream_tpsl_example.py --coins ETH,BTC11"""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_tpsl_updates(coins: list, auto_reconnect: bool = True):32"""33Stream TP/SL trigger-order updates for one or more coins.3435参数:36coins: Symbols to stream (e.g., ["ETH", "BTC"]). Empty list means all perp coins37auto_reconnect:在发生 DATA_LOSS 错误时自动重新连接(默认值为 True)38"""39打印(f"\n{'='*60}")40print(f"Streaming TP/SL Updates for {', '.join(coins) if coins else 'all perp coins'}")41print(f"Auto-reconnect: {auto_reconnect}")42打印(f"{'='*60}\n")4344retry_count = 045max_retries = 1046base_delay = 24748while retry_count < max_retries:49频道 = grpc.secure_channel(50GRPC,51grpc.ssl_channel_credentials(),52选项=[53(grpc.max_receive_message_length', 100 * 1024 * 1024),54(grpc.keepalive_time_ms', 30000),55]56)57占位符 = pb_grpc.订单流存根(channel)5859# 构建请求60request = pb.TpslUpdatesRequest(coins=coins)6162msg_count = 06364试一试:65如果 retry_count > 0:66打印(f"\n🔄 正在重新连接(尝试 {重试次数 + 1}/{最大重试次数})...")67else:68打印(f"正在连接到 {GRPC}...")6970for update in stub.StreamTpslUpdates(request, metadata=[('x-token', AUTH_TOKEN)]):71msg_count += 17273如果 msg_count == 1:74print(f"✓ First TP/SL update received!\n")75retry_count = 0 # 连接成功后重置重试计数7677# Display the update78打印(f"\n{'─'*60}")79snapshot_label = " | SNAPSHOT" if update.snapshot else ""80print(f"Block: {update.height} | Time: {update.time}{snapshot_label} | Diffs: {len(update.diffs)}")81打印(f"{'─'*60}")8283for diff in update.diffs:84side = "BID" if diff.side == "B" else "ASK"85sz = "position-sized" if diff.is_position_tpsl else diff.sz8687if diff.diff_type == pb.TPSL_DIFF_TYPE_ADD:88print(f" ADD {diff.coin} oid: {diff.oid} | {diff.order_type} | {side} sz: {sz}")89print(f" trigger: {diff.trigger_condition} | limit px: {diff.limit_px} | reduce-only: {diff.reduce_only}")90elif diff.diff_type == pb.TPSL_DIFF_TYPE_REMOVE:91print(f" REMOVE {diff.coin} oid: {diff.oid} | {diff.order_type} | reason: {diff.reason}")9293print(f"\n Messages received: {msg_count}")9495除了 grpc。RpcError 作为 e:96如果 e.代码() == grpc.状态码.DATA_LOSS 和 auto_reconnect:97print(f"\n⚠️ Server reinitialized: {e.details()}")98retry_count += 199if retry_count < max_retries:100延迟 = base_delay * (2 ** (重试次数 - 1)) # 指数退避101打印(f"⏳ 正在等待 {延迟}秒后重新连接...")102时间.sleep(延迟)103频道.关闭()104继续105else:106打印(f"\n❌ 最大重试次数 ({max_retries}) 已达到。放弃。")107换行108else:109print(f"\ngRPC error: {e.code()} - {e.details()}")110换行111除了 KeyboardInterrupt:112print("\nStopping TP/SL stream...")113换行114最后:115频道.关闭()116117# 如果在此处未出现错误,则退出重试循环118换行119120121def main():122parser = argparse.ArgumentParser(description='Stream Hyperliquid TP/SL trigger-order updates via gRPC')123parser.add_argument('--coins', default='ETH', help='Comma-separated coin symbols to stream (e.g., ETH,BTC)')124125args = 解析器.parse_args()126coins = [c.strip() for c in args.coins.split(',') if c.strip()]127128打印(f"\n{'='*60}")129print("Hyperliquid StreamTpslUpdates Example")130print(f"Endpoint: {GRPC_ENDPOINT}")131打印(f"{'='*60}")132133试一试:134stream_tpsl_updates(coins)135除 异常 作为 e:136print(f"\nError: {e}")137import traceback138回溯.print_exc()139sys.exit(1)140141142如果 __name__ == "__main__":143主()144