Overview
This guide demonstrates how to stream real-time orderbook data from Hyperliquid using gRPC in Go. You'll learn to:
- Stream L2 orderbook (aggregated price levels with depths)
- Stream L4 orderbook (individual orders with full details)
- Generate Go code from Protocol Buffer definitions
- Handle reconnections and errors gracefully
The orderbook streaming API uses a separate proto file (orderbook.proto) from the core streaming API, providing specialized methods optimized for market data.
For core streaming API (StreamData, StreamBlocks, Ping), see Go Setup Guide
Access to /hypercore (JSON-RPC and WebSocket) and HyperCore gRPC streaming requires a Quicknode Build plan or higher. View pricing to upgrade.
Prerequisites
1. Install Go
Go 1.21 or later is required. Follow the official installation guide or use a package manager:
# macOS
brew install go
# Verify installation
go version
2. Install Protocol Buffers Compiler
The protoc compiler is required to generate Go code from .proto files:
# macOS
brew install protobuf
# Linux (Ubuntu/Debian)
apt-get install -y protobuf-compiler
# Verify installation
protoc --version
3. Get Quicknode Credentials
Sign up at Quicknode and create a Hyperliquid endpoint. Your credentials consist of:
From HTTP Provider URL:
https://example-endpoint.hype-mainnet.quiknode.pro/abc123def456/
Extract:
- Endpoint:
example-endpoint.hype-mainnet.quiknode.pro:10000(note port 10000) - Token:
abc123def456
Project Setup
Step 1: Create Project Structure
mkdir -p hyperliquid-orderbook-go/{proto,examples}
cd hyperliquid-orderbook-go
Step 2: Initialize Go Module
go mod init hyperliquid-orderbook-example
Step 3: Install Dependencies
go get google.golang.org/grpc@latest
go get google.golang.org/protobuf/cmd/protoc-gen-go@latest
go get google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest
This installs:
google.golang.org/grpc- gRPC runtimeprotoc-gen-go- Protocol buffer Go generatorprotoc-gen-go-grpc- gRPC Go generator
Step 4: Download Proto File
Download the orderbook proto definition:
curl -o proto/orderbook.proto \
https://raw.githubusercontent.com/quiknode-labs/hypercore-grpc-examples/main/proto/orderbook.proto
Step 5: Add Go Package Option
Edit proto/orderbook.proto and add the go_package option after the package declaration:
syntax = "proto3";
package hyperliquid;
option go_package = "hyperliquid-orderbook-example/proto";
service OrderBookStreaming {
// ... rest of the proto file
}
Step 6: Generate Go Code
Generate Go code from the proto file:
protoc --go_out=. --go_opt=paths=source_relative \
--go-grpc_out=. --go-grpc_opt=paths=source_relative \
proto/orderbook.proto
This creates:
proto/orderbook.pb.go- Message definitionsproto/orderbook_grpc.pb.go- gRPC service client
Verify the files were generated:
ls -la proto/
# Should show: orderbook.proto, orderbook.pb.go, orderbook_grpc.pb.go
Implementation
StreamL2Book Example
Create examples/stream_l2.go:
package main
import (
"context"
"flag"
"fmt"
"io"
"log"
"math"
"strings"
"time"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/credentials"
"google.golang.org/grpc/metadata"
"google.golang.org/grpc/status"
pb "hyperliquid-orderbook-example/proto"
)
const (
maxRetries = 10
baseDelay = 2 * time.Second
)
func streamL2Orderbook(grpcEndpoint, authToken, coin string, nLevels uint32) error {
fmt.Println(strings.Repeat("=", 60))
fmt.Printf("Streaming L2 Orderbook for %s\n", coin)
fmt.Printf("Levels: %d\n", nLevels)
fmt.Println(strings.Repeat("=", 60) + "\n")
retryCount := 0
for retryCount < maxRetries {
// Create TLS credentials
creds := credentials.NewClientTLSFromCert(nil, "")
conn, err := grpc.Dial(grpcEndpoint,
grpc.WithTransportCredentials(creds),
grpc.WithDefaultCallOptions(grpc.MaxCallRecvMsgSize(100*1024*1024)))
if err != nil {
return fmt.Errorf("failed to connect: %w", err)
}
// Create client and add auth token to context
client := pb.NewOrderBookStreamingClient(conn)
ctx := metadata.AppendToOutgoingContext(context.Background(), "x-token", authToken)
// Create request
request := &pb.L2BookRequest{
Coin: coin,
NLevels: nLevels,
}
if retryCount > 0 {
fmt.Printf("\n🔄 Reconnecting (attempt %d/%d)...\n", retryCount+1, maxRetries)
} else {
fmt.Printf("Connecting to %s...\n", grpcEndpoint)
}
stream, err := client.StreamL2Book(ctx, request)
if err != nil {
conn.Close()
return fmt.Errorf("failed to start stream: %w", err)
}
msgCount := 0
shouldRetry := false
for {
update, err := stream.Recv()
if err == io.EOF {
break
}
if err != nil {
st, ok := status.FromError(err)
if ok && st.Code() == codes.DataLoss {
fmt.Printf("\n⚠️ Server reinitialized: %s\n", st.Message())
retryCount++
if retryCount < maxRetries {
delay := baseDelay * time.Duration(math.Pow(2, float64(retryCount-1)))
fmt.Printf("⏳ Waiting %v before reconnecting...\n", delay)
time.Sleep(delay)
shouldRetry = true
break
}
}
conn.Close()
return fmt.Errorf("stream error: %w", err)
}
msgCount++
if msgCount == 1 {
fmt.Println("✓ First L2 update received!\n")
retryCount = 0 // Reset on success
}
// Display orderbook
fmt.Println("\n" + strings.Repeat("─", 60))
fmt.Printf("Block: %d | Time: %d | Coin: %s\n", update.BlockNumber, update.Time, update.Coin)
fmt.Println(strings.Repeat("─", 60))
// Display asks (reversed for proper display)
if len(update.Asks) > 0 {
fmt.Println("\n ASKS:")
askCount := len(update.Asks)
if askCount > 10 {
askCount = 10
}
for i := askCount - 1; i >= 0; i-- {
level := update.Asks[i]
fmt.Printf(" %12s | %12s | (%d orders)\n", level.Px, level.Sz, level.N)
}
}
// Display spread
if len(update.Bids) > 0 && len(update.Asks) > 0 {
fmt.Println("\n " + strings.Repeat("─", 44))
fmt.Printf(" SPREAD: (best bid: %s, best ask: %s)\n", update.Bids[0].Px, update.Asks[0].Px)
fmt.Println(" " + strings.Repeat("─", 44))
}
// Display bids
if len(update.Bids) > 0 {
fmt.Println("\n BIDS:")
bidCount := len(update.Bids)
if bidCount > 10 {
bidCount = 10
}
for i := 0; i < bidCount; i++ {
level := update.Bids[i]
fmt.Printf(" %12s | %12s | (%d orders)\n", level.Px, level.Sz, level.N)
}
}
fmt.Printf("\n Messages received: %d\n", msgCount)
}
conn.Close()
if !shouldRetry {
break
}
}
return nil
}
func main() {
endpoint := flag.String("endpoint", "your-endpoint.hype-mainnet.quiknode.pro:10000", "gRPC endpoint")
token := flag.String("token", "your-auth-token", "Authentication token")
coin := flag.String("coin", "BTC", "Coin symbol to stream")
levels := flag.Uint("levels", 20, "Number of price levels")
flag.Parse()
fmt.Println("\n" + strings.Repeat("=", 60))
fmt.Println("Hyperliquid StreamL2Book Example")
fmt.Printf("Endpoint: %s\n", *endpoint)
fmt.Println(strings.Repeat("=", 60))
if err := streamL2Orderbook(*endpoint, *token, *coin, uint32(*levels)); err != nil {
log.Fatal(err)
}
}
For individual order streaming with full details, see the StreamL4Book method documentation which includes complete code examples.
Build and Run
Build the Example
# Tidy dependencies
go mod tidy
# Build L2 example
cd examples
go build -o stream_l2 stream_l2.go
Run StreamL2Book
Stream aggregated orderbook with configurable depth:
./stream_l2 \
-endpoint="your-endpoint.hype-mainnet.quiknode.pro:10000" \
-token="your-auth-token" \
-coin="BTC" \
-levels=20
Options:
-endpoint- gRPC endpoint with port 10000-token- Authentication token from Quicknode-coin- Trading pair (BTC, ETH, SOL, etc.)-levels- Number of price levels (max 100)
Output:
============================================================
Streaming L2 Orderbook for BTC
Levels: 20
============================================================
Connecting to your-endpoint.hype-mainnet.quiknode.pro:10000...
✓ First L2 update received!
────────────────────────────────────────────────────────────
Block: 903697656 | Time: 1771865026011 | Coin: BTC
────────────────────────────────────────────────────────────
ASKS:
65577 | 0.00017 | (1 orders)
65576 | 0.00017 | (1 orders)
65575 | 1.14631 | (3 orders)
...
────────────────────────────────────────
SPREAD: (best bid: 65558, best ask: 65564)
────────────────────────────────────────
BIDS:
65558 | 25.0365 | (48 orders)
65557 | 18.9296 | (11 orders)
...
Messages received: 1
Understanding the Output:
- Block/Time - Blockchain block number and timestamp
- Asks - Sell orders (displayed high to low)
- Spread - Difference between best bid and ask
- Bids - Buy orders (displayed high to low)
- Order Count - Number of individual orders at each price level
We ❤️ Feedback!
If you have any feedback or questions about this documentation, let us know. We'd love to hear from you!