StreamBboBook gRPC Method
이 방식은 데이터 사용량을 기준으로 0.0165 MB당 10 API 크레딧으로 과금된다는 점에 유의하시기 바랍니다.
매개변수
동전
반복되는 문자열
로딩 중...
반품
스트림
stream<BboBookUpdate>
로딩 중...
동전
문자열
로딩 중...
시간
uint64
로딩 중...
block_number
uint64
로딩 중...
bid
L2Level
로딩 중...
ask
L2Level
로딩 중...
요청
1// StreamBboBook Example - Stream top-of-book best bid/ask changes 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)2930func streamBboBook(coins []string) error {31fmt.PrintlnPrintln문자열.반복("=", 60))32fmt.Printf("Streaming BBO Book for %s\n", strings.Join(coins, ", "))33fmt.PrintlnPrintln ("자동 재연결: 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 {44return fmt.Errorf("연결 실패: %w", err)45}4647클라이언트 := pb.NewOrderBookStreamingClient)(conn)48ctx := 메타데이터.AppendToOutgoingContext(컨텍스트.배경(), "x-token", authToken)4950request := &pb.BboBookRequest{51동전: 동전,52}5354if retryCount > 0 {55fmt.Printf("\n🔄 재연결 중 (시도 %d/%d)...\n", retryCount+1, maxRetries)56} else {57fmt.Printf("%s에 연결 중...\n", printf("%s에 연결 중...\n",)58}5960stream, err := client.StreamBboBook(ctx, request)61만약 err != nil {62conn.닫기()63return fmt.Errorf("스트림 시작에 실패했습니다: %w", err)64}6566msgCount := 067shouldRetry := false6869~을 위해 {70업데이트, 오류 := 스트림.Recv()71만약 err == io.EOF {72중단73}74만약 err != nil {75st, 알겠습니다 := 상태.FromError(err)76만약 ok && st.코드() == 코드.DataLoss {77fmt.Printf("\n⚠️ 서버 재초기화됨: %s\n", st.메시지())78retryCount++79if retryCount < maxRetries {80지연 := 기본지연 * time.Duration(수학.Pow(2, float64(retryCount-1)))81fmt.Printf("⏳ 재연결하기 전 %v 동안 대기 중...\n", delay)82시간.수면(지연)83shouldRetry = true84중단85} else {86fmt.Printf("\n❌ 최대 재시도 횟수(%d)에 도달했습니다. 중단합니다.\n", maxRetries)87conn.닫기()88return nil89}90}91conn.닫기()92return fmt.Errorf("스트림 오류: %w", err)93}9495msgCount++96if msgCount == 1 {97fmt.Println("✓ First BBO update received!\n")98retryCount = 0 // 성공 시 재설정99}100101// Display BBO update102fmt.PrintlnPrintln ("\n" + strings.반복("─", 60))103fmt.Printf("Block: %d | Time: %d | Coin: %s\n", update.BlockNumber, update.Time, update.Coin)104fmt.PrintlnPrintln문자열.반복("─", 60))105106// Display best ask107if update.Ask != nil {108fmt.Printf(" BEST ASK: %12s | %12s | (%d orders)\n", update.Ask.Px, update.Ask.Sz, update.Ask.N)109} else {110fmt.Println(" BEST ASK: (none)")111}112113// Display best bid114if update.Bid != nil {115fmt.Printf(" BEST BID: %12s | %12s | (%d orders)\n", update.Bid.Px, update.Bid.Sz, update.Bid.N)116} else {117fmt.Println(" BEST BID: (none)")118}119120// Display spread121if update.Bid != nil && update.Ask != nil {122fmt.Printf(" SPREAD: (best bid: %s, best ask: %s)\n", update.Bid.Px, update.Ask.Px)123}124125fmt.Printf("\n 수신된 메시지: %d\n", msgCount)126}127128conn.닫기()129130만약 !shouldRetry {131중단132}133}134135return nil136}137138func main() {139coinsFlag := flag.String("coins", "BTC", "Comma-separated coin symbols to stream (e.g., BTC,ETH)")140141깃발.구문 분석()142143동전 := 문자열.Split(*coinsFlag, ",")144145fmt.PrintlnPrintln ("\n" + strings.반복("=", 60))146fmt.Println("Hyperliquid StreamBboBook Example")147fmt.Printf("엔드포인트: %s\n", grpcEndpoint)148fmt.PrintlnPrintln문자열.반복("=", 60))149150if err := streamBboBook(coins); err != nil {151로그.치명적(err)152}153}154
1// StreamBboBook Example - Stream top-of-book best bid/ask changes 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)2930func streamBboBook(coins []string) error {31fmt.PrintlnPrintln문자열.반복("=", 60))32fmt.Printf("Streaming BBO Book for %s\n", strings.Join(coins, ", "))33fmt.PrintlnPrintln ("자동 재연결: 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 {44return fmt.Errorf("연결 실패: %w", err)45}4647클라이언트 := pb.NewOrderBookStreamingClient)(conn)48ctx := 메타데이터.AppendToOutgoingContext(컨텍스트.배경(), "x-token", authToken)4950request := &pb.BboBookRequest{51동전: 동전,52}5354if retryCount > 0 {55fmt.Printf("\n🔄 재연결 중 (시도 %d/%d)...\n", retryCount+1, maxRetries)56} else {57fmt.Printf("%s에 연결 중...\n", printf("%s에 연결 중...\n",)58}5960stream, err := client.StreamBboBook(ctx, request)61만약 err != nil {62conn.닫기()63return fmt.Errorf("스트림 시작에 실패했습니다: %w", err)64}6566msgCount := 067shouldRetry := false6869~을 위해 {70업데이트, 오류 := 스트림.Recv()71만약 err == io.EOF {72중단73}74만약 err != nil {75st, 알겠습니다 := 상태.FromError(err)76만약 ok && st.코드() == 코드.DataLoss {77fmt.Printf("\n⚠️ 서버 재초기화됨: %s\n", st.메시지())78retryCount++79if retryCount < maxRetries {80지연 := 기본지연 * time.Duration(수학.Pow(2, float64(retryCount-1)))81fmt.Printf("⏳ 재연결하기 전 %v 동안 대기 중...\n", delay)82시간.수면(지연)83shouldRetry = true84중단85} else {86fmt.Printf("\n❌ 최대 재시도 횟수(%d)에 도달했습니다. 중단합니다.\n", maxRetries)87conn.닫기()88return nil89}90}91conn.닫기()92return fmt.Errorf("스트림 오류: %w", err)93}9495msgCount++96if msgCount == 1 {97fmt.Println("✓ First BBO update received!\n")98retryCount = 0 // 성공 시 재설정99}100101// Display BBO update102fmt.PrintlnPrintln ("\n" + strings.반복("─", 60))103fmt.Printf("Block: %d | Time: %d | Coin: %s\n", update.BlockNumber, update.Time, update.Coin)104fmt.PrintlnPrintln문자열.반복("─", 60))105106// Display best ask107if update.Ask != nil {108fmt.Printf(" BEST ASK: %12s | %12s | (%d orders)\n", update.Ask.Px, update.Ask.Sz, update.Ask.N)109} else {110fmt.Println(" BEST ASK: (none)")111}112113// Display best bid114if update.Bid != nil {115fmt.Printf(" BEST BID: %12s | %12s | (%d orders)\n", update.Bid.Px, update.Bid.Sz, update.Bid.N)116} else {117fmt.Println(" BEST BID: (none)")118}119120// Display spread121if update.Bid != nil && update.Ask != nil {122fmt.Printf(" SPREAD: (best bid: %s, best ask: %s)\n", update.Bid.Px, update.Ask.Px)123}124125fmt.Printf("\n 수신된 메시지: %d\n", msgCount)126}127128conn.닫기()129130만약 !shouldRetry {131중단132}133}134135return nil136}137138func main() {139coinsFlag := flag.String("coins", "BTC", "Comma-separated coin symbols to stream (e.g., BTC,ETH)")140141깃발.구문 분석()142143동전 := 문자열.Split(*coinsFlag, ",")144145fmt.PrintlnPrintln ("\n" + strings.반복("=", 60))146fmt.Println("Hyperliquid StreamBboBook Example")147fmt.Printf("엔드포인트: %s\n", grpcEndpoint)148fmt.PrintlnPrintln문자열.반복("=", 60))149150if err := streamBboBook(coins); err != nil {151로그.치명적(err)152}153}154
1// StreamBboBook Example - Stream top-of-book best bid/ask changes via gRPC2const grpc = require('@grpc/grpc-js');3const protoLoader = require('@grpc/proto-loader');4const path = require('path');56const GRPC_ENDPOINT = 'your-endpoint.hype-mainnet.quiknode.pro:10000';7const AUTH_TOKEN = 'your-auth-token';8const PROTO_PATH = path.join(__dirname, 'proto', 'orderbook.proto');910const 패키지 정의 = 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 best bid/offer updates28async function streamBboBook(coins, autoReconnect = true, retryCount = 0) {29콘솔.log('='.반복(60));30console.log(`Streaming BBO Book for ${coins.length > 0 ? coins.join(', ') : 'all coins'}`);31console.log(`Auto-reconnect: ${autoReconnect}`);32콘솔.log('='.반복(60) + '\n');3334const maxRetries = 10;35const baseDelay = 2000;3637while (retryCount < maxRetries) {38const client = createClient();39const 메타데이터 = new grpc.메타데이터();40메타데이터.추가('x-token', AUTH_TOKEN);4142const request = {43동전: 동전44};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.StreamBboBook(request, metadata);5556호출.on('data', (업데이트) => {57msgCount++;5859만약 (msgCount === 1) {60console.log('✓ First BBO update received!\n');61retryCount = 0; // 성공 시 재설정62}6364콘솔.log('\n' + '─'.반복(60));65console.log(`Block: ${update.block_number} | Time: ${update.time} | Coin: ${update.coin}`);66콘솔.log('─'.반복(60));6768// Display best ask69if (update.ask) {70console.log(` BEST ASK: ${update.ask.px.padStart(12)} | ${update.ask.sz.padStart(12)} | (${update.ask.n} orders)`);71} else {72console.log(' BEST ASK: (none)');73}7475// Display best bid76if (update.bid) {77console.log(` BEST BID: ${update.bid.px.padStart(12)} | ${update.bid.sz.padStart(12)} | (${update.bid.n} orders)`);78} else {79console.log(' BEST BID: (none)');80}8182// Display spread83if (update.bid && update.ask) {84const bestBid = parseFloat(update.bid.px);85const bestAsk = parseFloat(update.ask.px);86const spread = bestAsk - bestBid;87const spreadBps = (spread / bestBid) * 10000;88console.log(` SPREAD: ${spread.toFixed(2)} (${spreadBps.toFixed(2)} bps)`);89}9091console.log(`\n Messages received: ${msgCount}`);92});9394호출.on('error', (err) => {95만약 (err.코드 === grpc.상태.DATA_LOSS && autoReconnect) {96console.log(`\n⚠️ Server reinitialized: ${err.message}`);97retryCount++;98if (retryCount < maxRetries) {99const delay = baseDelay * Math.pow(2, retryCount - 1);100console.log(`⏳ Waiting ${delay / 1000}s before reconnecting...`);101setTimeout(() => streamBboBook(coins, autoReconnect, retryCount), delay);102} else {103console.log(`\n❌ Max retries (${maxRetries}) reached. Giving up.`);104}105} else {106콘솔.오류('\ngRPC 오류:', err.code, '-', err.메시지);107}108});109110호출.on('end', () => {111콘솔.log('\n스트림 종료됨');112});113114// Wait for stream to complete115기다리다 새로운 약속((결심) => {116호출.on('end', 해결);117호출.on('error', 해결);118});119120break; // 성공 시 재시도 루프 종료121122} catch (err) {123콘솔.오류('오류:', err.message);124break;125}126}127}128129// Parse command line args130const args = process.argv.slice(2);131const coins = (args.find(a => a.startsWith('--coins='))?.split('=')[1] || 'BTC').split(',');132133콘솔.log('\n' + '='.반복(60));134console.log('Hyperliquid StreamBboBook Example');135console.log(`Endpoint: ${GRPC_ENDPOINT}`);136콘솔.log('='.반복(60));137138streamBboBook(coins);139
1// StreamBboBook Example - Stream top-of-book best bid/ask changes via gRPC2const grpc = require('@grpc/grpc-js');3const protoLoader = require('@grpc/proto-loader');4const path = require('path');56const GRPC_ENDPOINT = 'your-endpoint.hype-mainnet.quiknode.pro:10000';7const AUTH_TOKEN = 'your-auth-token';8const PROTO_PATH = path.join(__dirname, 'proto', 'orderbook.proto');910const 패키지 정의 = 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 best bid/offer updates28async function streamBboBook(coins, autoReconnect = true, retryCount = 0) {29콘솔.log('='.반복(60));30console.log(`Streaming BBO Book for ${coins.length > 0 ? coins.join(', ') : 'all coins'}`);31console.log(`Auto-reconnect: ${autoReconnect}`);32콘솔.log('='.반복(60) + '\n');3334const maxRetries = 10;35const baseDelay = 2000;3637while (retryCount < maxRetries) {38const client = createClient();39const 메타데이터 = new grpc.메타데이터();40메타데이터.추가('x-token', AUTH_TOKEN);4142const request = {43동전: 동전44};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.StreamBboBook(request, metadata);5556호출.on('data', (업데이트) => {57msgCount++;5859만약 (msgCount === 1) {60console.log('✓ First BBO update received!\n');61retryCount = 0; // 성공 시 재설정62}6364콘솔.log('\n' + '─'.반복(60));65console.log(`Block: ${update.block_number} | Time: ${update.time} | Coin: ${update.coin}`);66콘솔.log('─'.반복(60));6768// Display best ask69if (update.ask) {70console.log(` BEST ASK: ${update.ask.px.padStart(12)} | ${update.ask.sz.padStart(12)} | (${update.ask.n} orders)`);71} else {72console.log(' BEST ASK: (none)');73}7475// Display best bid76if (update.bid) {77console.log(` BEST BID: ${update.bid.px.padStart(12)} | ${update.bid.sz.padStart(12)} | (${update.bid.n} orders)`);78} else {79console.log(' BEST BID: (none)');80}8182// Display spread83if (update.bid && update.ask) {84const bestBid = parseFloat(update.bid.px);85const bestAsk = parseFloat(update.ask.px);86const spread = bestAsk - bestBid;87const spreadBps = (spread / bestBid) * 10000;88console.log(` SPREAD: ${spread.toFixed(2)} (${spreadBps.toFixed(2)} bps)`);89}9091console.log(`\n Messages received: ${msgCount}`);92});9394호출.on('error', (err) => {95만약 (err.코드 === grpc.상태.DATA_LOSS && autoReconnect) {96console.log(`\n⚠️ Server reinitialized: ${err.message}`);97retryCount++;98if (retryCount < maxRetries) {99const delay = baseDelay * Math.pow(2, retryCount - 1);100console.log(`⏳ Waiting ${delay / 1000}s before reconnecting...`);101setTimeout(() => streamBboBook(coins, autoReconnect, retryCount), delay);102} else {103console.log(`\n❌ Max retries (${maxRetries}) reached. Giving up.`);104}105} else {106콘솔.오류('\ngRPC 오류:', err.code, '-', err.메시지);107}108});109110호출.on('end', () => {111콘솔.log('\n스트림 종료됨');112});113114// Wait for stream to complete115기다리다 새로운 약속((결심) => {116호출.on('end', 해결);117호출.on('error', 해결);118});119120break; // 성공 시 재시도 루프 종료121122} catch (err) {123콘솔.오류('오류:', err.message);124break;125}126}127}128129// Parse command line args130const args = process.argv.slice(2);131const coins = (args.find(a => a.startsWith('--coins='))?.split('=')[1] || 'BTC').split(',');132133콘솔.log('\n' + '='.반복(60));134console.log('Hyperliquid StreamBboBook Example');135console.log(`Endpoint: ${GRPC_ENDPOINT}`);136콘솔.log('='.반복(60));137138streamBboBook(coins);139
1#!/usr/bin/env python32"""3StreamBboBook Example - Stream top-of-book best bid/ask changes 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_bbo_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_bbo_book(coins: list, auto_reconnect: bool = True):32"""33Stream best bid/offer (top-of-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 BBO Book for {', '.join(coins) if coins else 'all 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_ENDPOINT,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.OrderBookStreamingStub(채널)5859# 빌드 요청60request = pb.BboBookRequest(coins=coins)6162msg_count = 06364시도해 보세요:65if retry_count > 0:66인쇄(f"\n🔄 다시 연결 중 (시도 중 {재시도 횟수 + 1}/{최대 재시도 횟수})...")67else:68인쇄(f"연결 중 {GRPC_ENDPOINT}...")6970for update in stub.StreamBboBook(request, metadata=[('x-token', AUTH_TOKEN)]):71msg_count += 17273if msg_count == 1:74print(f"✓ First BBO update received!\n")75retry_count = 0 # 연결에 성공하면 재시도 횟수를 초기화합니다7677# Display the BBO update78인쇄(f"\n{'─'*60}")79print(f"Block: {update.block_number} | Time: {update.time} | Coin: {update.coin}")80인쇄(f"{'─'*60}")8182# Show best ask83if update.HasField('ask'):84print(f" BEST ASK: {update.ask.px:>12} | {update.ask.sz:>12} | ({update.ask.n} orders)")85else:86print(" BEST ASK: (none)")8788# Show best bid89if update.HasField('bid'):90print(f" BEST BID: {update.bid.px:>12} | {update.bid.sz:>12} | ({update.bid.n} orders)")91else:92print(" BEST BID: (none)")9394# Show spread95if update.HasField('bid') and update.HasField('ask'):96best_bid = float(update.bid.px)97best_ask = float(update.ask.px)98spread = best_ask - best_bid99spread_bps = (spread / best_bid) * 10000 if best_bid > 0 else 0100print(f" SPREAD: {spread:.2f} ({spread_bps:.2f} bps)")101102print(f"\n Messages received: {msg_count}")103104단, grpc.RpcError 를 e:105만약 e.코드() == grpc.StatusCode.DATA_LOSS 및 auto_reconnect:106print(f"\n⚠️ Server reinitialized: {e.details()}")107retry_count += 1108if retry_count < max_retries:109지연 = 기본_지연 * (2 ** (retry_count - 1)) # 지수적 백오프110인쇄(f"⏳ 대기 중 {지연}초 후 재연결됩니다...")111시간.sleep(지연)112채널.닫기()113계속114else:115인쇄(f"\n❌ 최대 재시도 횟수 ({max_retries})에 도달했습니다. 중단합니다.")116중단117else:118print(f"\ngRPC error: {e.code()} - {e.details()}")119중단120단, KeyboardInterrupt:121print("\nStopping BBO stream...")122중단123드디어:124채널.닫기()125126# 오류 없이 여기까지 도달했다면, 재시도 루프를 종료한다127중단128129130def main():131parser = argparse.ArgumentParser(description='Stream Hyperliquid best bid/offer data via gRPC')132parser.add_argument('--coins', default='BTC', help='Comma-separated coin symbols to stream (e.g., BTC,ETH)')133134args = parser.parse_args()135동전 = [c.strip() for c in args.coins.split(',') if c.strip()]136137인쇄(f"\n{'='*60}")138print("Hyperliquid StreamBboBook Example")139print(f"Endpoint: {GRPC_ENDPOINT}")140인쇄(f"{'='*60}")141142시도해 보세요:143stream_bbo_book(coins)144단, 예외 ~처럼 e:145print(f"\nError: {e}")146import 트레이스백147트레이스백.print_exc()148sys.exit(1)149150151if __name__ == "__main__":152main()153
1#!/usr/bin/env python32"""3StreamBboBook Example - Stream top-of-book best bid/ask changes 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_bbo_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_bbo_book(coins: list, auto_reconnect: bool = True):32"""33Stream best bid/offer (top-of-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 BBO Book for {', '.join(coins) if coins else 'all 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_ENDPOINT,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.OrderBookStreamingStub(채널)5859# 빌드 요청60request = pb.BboBookRequest(coins=coins)6162msg_count = 06364시도해 보세요:65if retry_count > 0:66인쇄(f"\n🔄 다시 연결 중 (시도 중 {재시도 횟수 + 1}/{최대 재시도 횟수})...")67else:68인쇄(f"연결 중 {GRPC_ENDPOINT}...")6970for update in stub.StreamBboBook(request, metadata=[('x-token', AUTH_TOKEN)]):71msg_count += 17273if msg_count == 1:74print(f"✓ First BBO update received!\n")75retry_count = 0 # 연결에 성공하면 재시도 횟수를 초기화합니다7677# Display the BBO update78인쇄(f"\n{'─'*60}")79print(f"Block: {update.block_number} | Time: {update.time} | Coin: {update.coin}")80인쇄(f"{'─'*60}")8182# Show best ask83if update.HasField('ask'):84print(f" BEST ASK: {update.ask.px:>12} | {update.ask.sz:>12} | ({update.ask.n} orders)")85else:86print(" BEST ASK: (none)")8788# Show best bid89if update.HasField('bid'):90print(f" BEST BID: {update.bid.px:>12} | {update.bid.sz:>12} | ({update.bid.n} orders)")91else:92print(" BEST BID: (none)")9394# Show spread95if update.HasField('bid') and update.HasField('ask'):96best_bid = float(update.bid.px)97best_ask = float(update.ask.px)98spread = best_ask - best_bid99spread_bps = (spread / best_bid) * 10000 if best_bid > 0 else 0100print(f" SPREAD: {spread:.2f} ({spread_bps:.2f} bps)")101102print(f"\n Messages received: {msg_count}")103104단, grpc.RpcError 를 e:105만약 e.코드() == grpc.StatusCode.DATA_LOSS 및 auto_reconnect:106print(f"\n⚠️ Server reinitialized: {e.details()}")107retry_count += 1108if retry_count < max_retries:109지연 = 기본_지연 * (2 ** (retry_count - 1)) # 지수적 백오프110인쇄(f"⏳ 대기 중 {지연}초 후 재연결됩니다...")111시간.sleep(지연)112채널.닫기()113계속114else:115인쇄(f"\n❌ 최대 재시도 횟수 ({max_retries})에 도달했습니다. 중단합니다.")116중단117else:118print(f"\ngRPC error: {e.code()} - {e.details()}")119중단120단, KeyboardInterrupt:121print("\nStopping BBO stream...")122중단123드디어:124채널.닫기()125126# 오류 없이 여기까지 도달했다면, 재시도 루프를 종료한다127중단128129130def main():131parser = argparse.ArgumentParser(description='Stream Hyperliquid best bid/offer data via gRPC')132parser.add_argument('--coins', default='BTC', help='Comma-separated coin symbols to stream (e.g., BTC,ETH)')133134args = parser.parse_args()135동전 = [c.strip() for c in args.coins.split(',') if c.strip()]136137인쇄(f"\n{'='*60}")138print("Hyperliquid StreamBboBook Example")139print(f"Endpoint: {GRPC_ENDPOINT}")140인쇄(f"{'='*60}")141142시도해 보세요:143stream_bbo_book(coins)144단, 예외 ~처럼 e:145print(f"\nError: {e}")146import 트레이스백147트레이스백.print_exc()148sys.exit(1)149150151if __name__ == "__main__":152main()153