Skip to main content

Setting Up Go for OrderBook (L2/L4)

Updated on
Feb 24, 2026

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.

Related Guides

For core streaming API (StreamData, StreamBlocks, Ping), see Go Setup Guide

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 runtime
  • protoc-gen-go - Protocol buffer Go generator
  • protoc-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 definitions
  • proto/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)
}
}
StreamL4Book Example

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!

Share this doc