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