StreamL4BookUpdates gRPC Method
이 방식은 데이터 사용량을 기준으로 0.0165 MB당 10 API 크레딧으로 과금된다는 점에 유의하시기 바랍니다.
매개변수
동전
반복되는 문자열
로딩 중...
반품
스트림
stream<L4BookUpdatesUpdate>
로딩 중...
시간
uint64
로딩 중...
높이
uint64
로딩 중...
스냅샷
bool
로딩 중...
diffs
array<L4OrderDiff>
로딩 중...
요청
1// StreamL4BookUpdates Example - Stream typed per-order book updates via gRPC2패키지 main34import (5"문맥"6"깃발"7"fmt"8"io"9"로그"10"수학"11"문자열"12"시간"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.초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.PrintlnPrintln ("자동 재연결: 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 {55return fmt.Errorf("연결 실패: %w", err)56}5758클라이언트 := pb.NewOrderBookStreamingClient)(conn)59ctx := 메타데이터.AppendToOutgoingContext(컨텍스트.배경(), "x-token", authToken)6061request := &pb.L4BookUpdatesRequest{62동전: 동전,63}6465if retryCount > 0 {66fmt.Printf("\n🔄 재연결 중 (시도 %d/%d)...\n", retryCount+1, maxRetries)67} else {68fmt.Printf("%s에 연결 중...\n", printf("%s에 연결 중...\n",)69}7071stream, err := client.StreamL4BookUpdates(ctx, request)72만약 err != nil {73conn.닫기()74return fmt.Errorf("스트림 시작에 실패했습니다: %w", err)75}7677msgCount := 078shouldRetry := false7980~을 위해 {81업데이트, 오류 := 스트림.Recv()82만약 err == io.EOF {83중단84}85만약 err != nil {86st, 알겠습니다 := 상태.FromError(err)87만약 ok && st.코드() == 코드.DataLoss {88fmt.Printf("\n⚠️ 서버 재초기화됨: %s\n", st.메시지())89retryCount++90if retryCount < maxRetries {91지연 := 기본지연 * time.Duration(수학.Pow(2, float64(retryCount-1)))92fmt.Printf("⏳ 재연결하기 전 %v 동안 대기 중...\n", delay)93시간.수면(지연)94shouldRetry = true95중단96} else {97fmt.Printf("\n❌ 최대 재시도 횟수(%d)에 도달했습니다. 중단합니다.\n", maxRetries)98conn.닫기()99return nil100}101}102conn.닫기()103return fmt.Errorf("스트림 오류: %w", err)104}105106msgCount++107if msgCount == 1 {108fmt.Println("✓ First L4 update received!\n")109retryCount = 0 // 성공 시 재설정110}111112만약 업데이트.스냅샷 {113// Full reset snapshot: rebuild local state114orders = make(map[uint64]localOrder)115}116117// Display update118fmt.PrintlnPrintln ("\n" + strings.반복("─", 60))119snapshotLabel := ""120만약 업데이트.스냅샷 {121스냅샷 레이블 = " | SNAPSHOT"122}123fmt.Printf("블록: %d | 시간: %d%s | 차이점: %d\n", update.높이, 업데이트.시간, 스냅샷 레이블, len(update.차이점))124fmt.PrintlnPrintln문자열.반복("─", 60))125126for _, diff := range update.Diffs {127side := "ASK"128만약 diff.측면 == "B" {129측면 = "BID"130}131132전환 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}159160return nil161}162163func main() {164coinsFlag := flag.String("coins", "BTC", "Comma-separated coin symbols to stream (e.g., BTC,ETH)")165166깃발.구문 분석()167168동전 := 문자열.Split(*coinsFlag, ",")169170fmt.PrintlnPrintln ("\n" + strings.반복("=", 60))171fmt.Println("Hyperliquid StreamL4BookUpdates Example")172fmt.Printf("엔드포인트: %s\n", grpcEndpoint)173fmt.PrintlnPrintln문자열.반복("=", 60))174175if err := streamL4BookUpdates(coins); err != nil {176로그.치명적(err)177}178}179
1// StreamL4BookUpdates Example - Stream typed per-order book updates via gRPC2패키지 main34import (5"문맥"6"깃발"7"fmt"8"io"9"로그"10"수학"11"문자열"12"시간"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.초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.PrintlnPrintln ("자동 재연결: 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 {55return fmt.Errorf("연결 실패: %w", err)56}5758클라이언트 := pb.NewOrderBookStreamingClient)(conn)59ctx := 메타데이터.AppendToOutgoingContext(컨텍스트.배경(), "x-token", authToken)6061request := &pb.L4BookUpdatesRequest{62동전: 동전,63}6465if retryCount > 0 {66fmt.Printf("\n🔄 재연결 중 (시도 %d/%d)...\n", retryCount+1, maxRetries)67} else {68fmt.Printf("%s에 연결 중...\n", printf("%s에 연결 중...\n",)69}7071stream, err := client.StreamL4BookUpdates(ctx, request)72만약 err != nil {73conn.닫기()74return fmt.Errorf("스트림 시작에 실패했습니다: %w", err)75}7677msgCount := 078shouldRetry := false7980~을 위해 {81업데이트, 오류 := 스트림.Recv()82만약 err == io.EOF {83중단84}85만약 err != nil {86st, 알겠습니다 := 상태.FromError(err)87만약 ok && st.코드() == 코드.DataLoss {88fmt.Printf("\n⚠️ 서버 재초기화됨: %s\n", st.메시지())89retryCount++90if retryCount < maxRetries {91지연 := 기본지연 * time.Duration(수학.Pow(2, float64(retryCount-1)))92fmt.Printf("⏳ 재연결하기 전 %v 동안 대기 중...\n", delay)93시간.수면(지연)94shouldRetry = true95중단96} else {97fmt.Printf("\n❌ 최대 재시도 횟수(%d)에 도달했습니다. 중단합니다.\n", maxRetries)98conn.닫기()99return nil100}101}102conn.닫기()103return fmt.Errorf("스트림 오류: %w", err)104}105106msgCount++107if msgCount == 1 {108fmt.Println("✓ First L4 update received!\n")109retryCount = 0 // 성공 시 재설정110}111112만약 업데이트.스냅샷 {113// Full reset snapshot: rebuild local state114orders = make(map[uint64]localOrder)115}116117// Display update118fmt.PrintlnPrintln ("\n" + strings.반복("─", 60))119snapshotLabel := ""120만약 업데이트.스냅샷 {121스냅샷 레이블 = " | SNAPSHOT"122}123fmt.Printf("블록: %d | 시간: %d%s | 차이점: %d\n", update.높이, 업데이트.시간, 스냅샷 레이블, len(update.차이점))124fmt.PrintlnPrintln문자열.반복("─", 60))125126for _, diff := range update.Diffs {127side := "ASK"128만약 diff.측면 == "B" {129측면 = "BID"130}131132전환 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}159160return nil161}162163func main() {164coinsFlag := flag.String("coins", "BTC", "Comma-separated coin symbols to stream (e.g., BTC,ETH)")165166깃발.구문 분석()167168동전 := 문자열.Split(*coinsFlag, ",")169170fmt.PrintlnPrintln ("\n" + strings.반복("=", 60))171fmt.Println("Hyperliquid StreamL4BookUpdates Example")172fmt.Printf("엔드포인트: %s\n", grpcEndpoint)173fmt.PrintlnPrintln문자열.반복("=", 60))174175if err := streamL4BookUpdates(coins); err != nil {176로그.치명적(err)177}178}179
1// StreamL4BookUpdates Example - Stream typed per-order book updates 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 패키지 정의 = protoLoader.loadSync(PROTO_PATH, {11keepCase: true,12longs: String,13열거형: String,14기본값: true,15oneofs: true16});17const proto = grpc.loadPackageDefinition(packageDefinition).hyperliquid;1819함수 createClient() {20돌아가기 new proto.OrderBookStreaming((21GRPC_ENDPOINT,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콘솔.log('='.반복(60));30console.log(`Streaming L4 Book Updates for ${coins.length > 0 ? coins.join(', ') : 'all coins'}`);31console.log(`Auto-reconnect: ${autoReconnect}`);32콘솔.log('='.반복(60) + '\n');3334const maxRetries = 10;35const baseDelay = 2000;3637// Simple local order map keyed by oid38const orders = new Map();3940while (retryCount < maxRetries) {41const client = createClient();42const 메타데이터 = new grpc.메타데이터();43메타데이터.추가('x-token', AUTH_TOKEN);4445const request = {46동전: 동전47};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);5859호출.on('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콘솔.log('\n' + '─'.반복(60));73console.log(`Block: ${update.height} | Time: ${update.time}${update.snapshot ? ' | SNAPSHOT' : ''} | Diffs: ${(update.diffs || []).length}`);74콘솔.log('─'.반복(60));7576(업데이트.차이점 || []).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});100101호출.on('error', (err) => {102만약 (err.코드 === grpc.상태.DATA_LOSS && autoReconnect) {103console.log(`\n⚠️ Server reinitialized: ${err.message}`);104retryCount++;105if (retryCount < maxRetries) {106const delay = 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.메시지);114}115});116117호출.on('end', () => {118콘솔.log('\n스트림 종료됨');119});120121// Wait for stream to complete122기다리다 새로운 약속((결심) => {123호출.on('end', 해결);124호출.on('error', 해결);125});126127break; // 성공 시 재시도 루프 종료128129} catch (err) {130콘솔.오류('오류:', err.message);131break;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콘솔.log('\n' + '='.반복(60));141console.log('Hyperliquid StreamL4BookUpdates Example');142console.log(`Endpoint: ${GRPC_ENDPOINT}`);143콘솔.log('='.반복(60));144145streamL4BookUpdates(coins);146
1// StreamL4BookUpdates Example - Stream typed per-order book updates 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 패키지 정의 = protoLoader.loadSync(PROTO_PATH, {11keepCase: true,12longs: String,13열거형: String,14기본값: true,15oneofs: true16});17const proto = grpc.loadPackageDefinition(packageDefinition).hyperliquid;1819함수 createClient() {20돌아가기 new proto.OrderBookStreaming((21GRPC_ENDPOINT,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콘솔.log('='.반복(60));30console.log(`Streaming L4 Book Updates for ${coins.length > 0 ? coins.join(', ') : 'all coins'}`);31console.log(`Auto-reconnect: ${autoReconnect}`);32콘솔.log('='.반복(60) + '\n');3334const maxRetries = 10;35const baseDelay = 2000;3637// Simple local order map keyed by oid38const orders = new Map();3940while (retryCount < maxRetries) {41const client = createClient();42const 메타데이터 = new grpc.메타데이터();43메타데이터.추가('x-token', AUTH_TOKEN);4445const request = {46동전: 동전47};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);5859호출.on('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콘솔.log('\n' + '─'.반복(60));73console.log(`Block: ${update.height} | Time: ${update.time}${update.snapshot ? ' | SNAPSHOT' : ''} | Diffs: ${(update.diffs || []).length}`);74콘솔.log('─'.반복(60));7576(업데이트.차이점 || []).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});100101호출.on('error', (err) => {102만약 (err.코드 === grpc.상태.DATA_LOSS && autoReconnect) {103console.log(`\n⚠️ Server reinitialized: ${err.message}`);104retryCount++;105if (retryCount < maxRetries) {106const delay = 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.메시지);114}115});116117호출.on('end', () => {118콘솔.log('\n스트림 종료됨');119});120121// Wait for stream to complete122기다리다 새로운 약속((결심) => {123호출.on('end', 해결);124호출.on('error', 해결);125});126127break; // 성공 시 재시도 루프 종료128129} catch (err) {130콘솔.오류('오류:', err.message);131break;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콘솔.log('\n' + '='.반복(60));141console.log('Hyperliquid StreamL4BookUpdates Example');142console.log(`Endpoint: ${GRPC_ENDPOINT}`);143콘솔.log('='.반복(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 -m grpc_tools.protoc -I../../proto --python_out=. --grpc_python_out=. ../../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 -m grpc_tools.protoc -I../../proto --python_out=. --grpc_python_out=. ../../proto/orderbook.proto")24sys.exit(1)2526# 구성27GRPC_ENDPOINT = "your-endpoint.hype-mainnet.quiknode.pro:10000"28AUTH_TOKEN = "your-auth-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_ENDPOINT,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.OrderBookStreamingStub(채널)6162# 빌드 요청63request = pb.L4BookUpdatesRequest(coins=coins)6465msg_count = 06667시도해 보세요:68if retry_count > 0:69인쇄(f"\n🔄 다시 연결 중 (시도 중 {재시도 횟수 + 1}/{최대 재시도 횟수})...")70else:71인쇄(f"연결 중 {GRPC_ENDPOINT}...")7273for update in stub.StreamL4BookUpdates(request, metadata=[('x-token', AUTH_TOKEN)]):74msg_count += 17576if 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# 업데이트 내용 표시85인쇄(f"\n{'─'*60}")86snapshot_label = " | SNAPSHOT" if update.스냅샷 else ""87print(f"Block: {update.height} | Time: {update.time}{snapshot_label} | Diffs: {len(update.diffs)}")88인쇄(f"{'─'*60}")8990~을 위해 diff in update.diffs:91측면 = "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.StatusCode.DATA_LOSS 및 auto_reconnect:109print(f"\n⚠️ Server reinitialized: {e.details()}")110retry_count += 1111if retry_count < max_retries:112지연 = 기본_지연 * (2 ** (retry_count - 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 = parser.parse_args()138동전 = [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 트레이스백150트레이스백.print_exc()151sys.exit(1)152153154if __name__ == "__main__":155main()156
1#!/usr/bin/env python32"""3StreamL4BookUpdates Example - Stream typed per-order book updates via gRPC45설정:6pip install grpcio grpcio-tools protobuf zstandard7python -m grpc_tools.protoc -I../../proto --python_out=. --grpc_python_out=. ../../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 -m grpc_tools.protoc -I../../proto --python_out=. --grpc_python_out=. ../../proto/orderbook.proto")24sys.exit(1)2526# 구성27GRPC_ENDPOINT = "your-endpoint.hype-mainnet.quiknode.pro:10000"28AUTH_TOKEN = "your-auth-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_ENDPOINT,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.OrderBookStreamingStub(채널)6162# 빌드 요청63request = pb.L4BookUpdatesRequest(coins=coins)6465msg_count = 06667시도해 보세요:68if retry_count > 0:69인쇄(f"\n🔄 다시 연결 중 (시도 중 {재시도 횟수 + 1}/{최대 재시도 횟수})...")70else:71인쇄(f"연결 중 {GRPC_ENDPOINT}...")7273for update in stub.StreamL4BookUpdates(request, metadata=[('x-token', AUTH_TOKEN)]):74msg_count += 17576if 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# 업데이트 내용 표시85인쇄(f"\n{'─'*60}")86snapshot_label = " | SNAPSHOT" if update.스냅샷 else ""87print(f"Block: {update.height} | Time: {update.time}{snapshot_label} | Diffs: {len(update.diffs)}")88인쇄(f"{'─'*60}")8990~을 위해 diff in update.diffs:91측면 = "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.StatusCode.DATA_LOSS 및 auto_reconnect:109print(f"\n⚠️ Server reinitialized: {e.details()}")110retry_count += 1111if retry_count < max_retries:112지연 = 기본_지연 * (2 ** (retry_count - 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 = parser.parse_args()138동전 = [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 트레이스백150트레이스백.print_exc()151sys.exit(1)152153154if __name__ == "__main__":155main()156