Skip to main content

StreamL2Book gRPC Method

Loading...

Updated on
Feb 24, 2026

StreamL2Book gRPC Method

Parameters

coin
string
Loading...
n_levels
uint32
Loading...
n_sig_figs
uint32
Loading...
mantissa
uint64
Loading...

Returns

stream
stream<L2BookUpdate>
Loading...
coin
string
Loading...
block_timestamp
uint64
Loading...
block_number
uint64
Loading...
bids
array<L2Level>
Loading...
asks
array<L2Level>
Loading...
Request
1
// StreamL2Book Example - Stream aggregated orderbook data via gRPC
2
package main
3
4
import (
5
"context"
6
"flag"
7
"fmt"
8
"io"
9
"log"
10
"math"
11
"strings"
12
"time"
13
14
"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"
19
20
pb "hyperliquid-orderbook-example/proto"
21
)
22
23
const (
24
grpcEndpoint = "your-endpoint.hype-mainnet.quiknode.pro:10000"
25
authToken = "your-auth-token"
26
maxRetries = 10
27
baseDelay = 2 * time.Second
28
)
29
30
func streamL2Orderbook(coin string, nLevels uint32) error {
31
fmt.Println(strings.Repeat("=", 60))
32
fmt.Printf("Streaming L2 Orderbook for %s\n", coin)
33
fmt.Printf("Levels: %d\n", nLevels)
34
fmt.Println("Auto-reconnect: true")
35
fmt.Println(strings.Repeat("=", 60) + "\n")
36
37
retryCount := 0
38
39
for retryCount < maxRetries {
40
creds := credentials.NewClientTLSFromCert(nil, "")
41
conn, err := grpc.Dial(grpcEndpoint,
42
grpc.WithTransportCredentials(creds),
43
grpc.WithDefaultCallOptions(grpc.MaxCallRecvMsgSize(100*1024*1024)))
44
if err != nil {
45
return fmt.Errorf("failed to connect: %w", err)
46
}
47
48
client := pb.NewOrderBookStreamingClient(conn)
49
ctx := metadata.AppendToOutgoingContext(context.Background(), "x-token", authToken)
50
51
request := &pb.L2BookRequest{
52
Coin: coin,
53
NLevels: nLevels,
54
}
55
56
if retryCount > 0 {
57
fmt.Printf("\nšŸ”„ Reconnecting (attempt %d/%d)...\n", retryCount+1, maxRetries)
58
} else {
59
fmt.Printf("Connecting to %s...\n", grpcEndpoint)
60
}
61
62
stream, err := client.StreamL2Book(ctx, request)
63
if err != nil {
64
conn.Close()
65
return fmt.Errorf("failed to start stream: %w", err)
66
}
67
68
msgCount := 0
69
shouldRetry := false
70
71
for {
72
update, err := stream.Recv()
73
if err == io.EOF {
74
break
75
}
76
if err != nil {
77
st, ok := status.FromError(err)
78
if ok && st.Code() == codes.DataLoss {
79
fmt.Printf("\nāš ļø Server reinitialized: %s\n", st.Message())
80
retryCount++
81
if retryCount < maxRetries {
82
delay := baseDelay * time.Duration(math.Pow(2, float64(retryCount-1)))
83
fmt.Printf("ā³ Waiting %v before reconnecting...\n", delay)
84
time.Sleep(delay)
85
shouldRetry = true
86
break
87
} else {
88
fmt.Printf("\nāŒ Max retries (%d) reached. Giving up.\n", maxRetries)
89
conn.Close()
90
return nil
91
}
92
}
93
conn.Close()
94
return fmt.Errorf("stream error: %w", err)
95
}
96
97
msgCount++
98
if msgCount == 1 {
99
fmt.Println("āœ“ First L2 update received!\n")
100
retryCount = 0 // Reset on success
101
}
102
103
// Display orderbook
104
fmt.Println("\n" + strings.Repeat("─", 60))
105
fmt.Printf("Block: %d | Time: %d | Coin: %s\n", update.BlockNumber, update.Time, update.Coin)
106
fmt.Println(strings.Repeat("─", 60))
107
108
// Display asks (reversed)
109
if len(update.Asks) > 0 {
110
fmt.Println("\n ASKS:")
111
askCount := len(update.Asks)
112
if askCount > 10 {
113
askCount = 10
114
}
115
for i := askCount - 1; i >= 0; i-- {
116
level := update.Asks[i]
117
fmt.Printf(" %12s | %12s | (%d orders)\n", level.Px, level.Sz, level.N)
118
}
119
}
120
121
// Display spread
122
if len(update.Bids) > 0 && len(update.Asks) > 0 {
123
fmt.Println("\n " + strings.Repeat("─", 44))
124
fmt.Printf(" SPREAD: (best bid: %s, best ask: %s)\n", update.Bids[0].Px, update.Asks[0].Px)
125
fmt.Println(" " + strings.Repeat("─", 44))
126
}
127
128
// Display bids
129
if len(update.Bids) > 0 {
130
fmt.Println("\n BIDS:")
131
bidCount := len(update.Bids)
132
if bidCount > 10 {
133
bidCount = 10
134
}
135
for i := 0; i < bidCount; i++ {
136
level := update.Bids[i]
137
fmt.Printf(" %12s | %12s | (%d orders)\n", level.Px, level.Sz, level.N)
138
}
139
}
140
141
fmt.Printf("\n Messages received: %d\n", msgCount)
142
}
143
144
conn.Close()
145
146
if !shouldRetry {
147
break
148
}
149
}
150
151
return nil
152
}
153
154
func main() {
155
coin := flag.String("coin", "BTC", "Coin symbol to stream")
156
levels := flag.Uint("levels", 20, "Number of price levels")
157
158
flag.Parse()
159
160
fmt.Println("\n" + strings.Repeat("=", 60))
161
fmt.Println("Hyperliquid StreamL2Book Example")
162
fmt.Printf("Endpoint: %s\n", grpcEndpoint)
163
fmt.Println(strings.Repeat("=", 60))
164
165
if err := streamL2Orderbook(*coin, uint32(*levels)); err != nil {
166
log.Fatal(err)
167
}
168
}
169
1
// StreamL2Book Example - Stream aggregated orderbook data via gRPC
2
package main
3
4
import (
5
"context"
6
"flag"
7
"fmt"
8
"io"
9
"log"
10
"math"
11
"strings"
12
"time"
13
14
"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"
19
20
pb "hyperliquid-orderbook-example/proto"
21
)
22
23
const (
24
grpcEndpoint = "your-endpoint.hype-mainnet.quiknode.pro:10000"
25
authToken = "your-auth-token"
26
maxRetries = 10
27
baseDelay = 2 * time.Second
28
)
29
30
func streamL2Orderbook(coin string, nLevels uint32) error {
31
fmt.Println(strings.Repeat("=", 60))
32
fmt.Printf("Streaming L2 Orderbook for %s\n", coin)
33
fmt.Printf("Levels: %d\n", nLevels)
34
fmt.Println("Auto-reconnect: true")
35
fmt.Println(strings.Repeat("=", 60) + "\n")
36
37
retryCount := 0
38
39
for retryCount < maxRetries {
40
creds := credentials.NewClientTLSFromCert(nil, "")
41
conn, err := grpc.Dial(grpcEndpoint,
42
grpc.WithTransportCredentials(creds),
43
grpc.WithDefaultCallOptions(grpc.MaxCallRecvMsgSize(100*1024*1024)))
44
if err != nil {
45
return fmt.Errorf("failed to connect: %w", err)
46
}
47
48
client := pb.NewOrderBookStreamingClient(conn)
49
ctx := metadata.AppendToOutgoingContext(context.Background(), "x-token", authToken)
50
51
request := &pb.L2BookRequest{
52
Coin: coin,
53
NLevels: nLevels,
54
}
55
56
if retryCount > 0 {
57
fmt.Printf("\nšŸ”„ Reconnecting (attempt %d/%d)...\n", retryCount+1, maxRetries)
58
} else {
59
fmt.Printf("Connecting to %s...\n", grpcEndpoint)
60
}
61
62
stream, err := client.StreamL2Book(ctx, request)
63
if err != nil {
64
conn.Close()
65
return fmt.Errorf("failed to start stream: %w", err)
66
}
67
68
msgCount := 0
69
shouldRetry := false
70
71
for {
72
update, err := stream.Recv()
73
if err == io.EOF {
74
break
75
}
76
if err != nil {
77
st, ok := status.FromError(err)
78
if ok && st.Code() == codes.DataLoss {
79
fmt.Printf("\nāš ļø Server reinitialized: %s\n", st.Message())
80
retryCount++
81
if retryCount < maxRetries {
82
delay := baseDelay * time.Duration(math.Pow(2, float64(retryCount-1)))
83
fmt.Printf("ā³ Waiting %v before reconnecting...\n", delay)
84
time.Sleep(delay)
85
shouldRetry = true
86
break
87
} else {
88
fmt.Printf("\nāŒ Max retries (%d) reached. Giving up.\n", maxRetries)
89
conn.Close()
90
return nil
91
}
92
}
93
conn.Close()
94
return fmt.Errorf("stream error: %w", err)
95
}
96
97
msgCount++
98
if msgCount == 1 {
99
fmt.Println("āœ“ First L2 update received!\n")
100
retryCount = 0 // Reset on success
101
}
102
103
// Display orderbook
104
fmt.Println("\n" + strings.Repeat("─", 60))
105
fmt.Printf("Block: %d | Time: %d | Coin: %s\n", update.BlockNumber, update.Time, update.Coin)
106
fmt.Println(strings.Repeat("─", 60))
107
108
// Display asks (reversed)
109
if len(update.Asks) > 0 {
110
fmt.Println("\n ASKS:")
111
askCount := len(update.Asks)
112
if askCount > 10 {
113
askCount = 10
114
}
115
for i := askCount - 1; i >= 0; i-- {
116
level := update.Asks[i]
117
fmt.Printf(" %12s | %12s | (%d orders)\n", level.Px, level.Sz, level.N)
118
}
119
}
120
121
// Display spread
122
if len(update.Bids) > 0 && len(update.Asks) > 0 {
123
fmt.Println("\n " + strings.Repeat("─", 44))
124
fmt.Printf(" SPREAD: (best bid: %s, best ask: %s)\n", update.Bids[0].Px, update.Asks[0].Px)
125
fmt.Println(" " + strings.Repeat("─", 44))
126
}
127
128
// Display bids
129
if len(update.Bids) > 0 {
130
fmt.Println("\n BIDS:")
131
bidCount := len(update.Bids)
132
if bidCount > 10 {
133
bidCount = 10
134
}
135
for i := 0; i < bidCount; i++ {
136
level := update.Bids[i]
137
fmt.Printf(" %12s | %12s | (%d orders)\n", level.Px, level.Sz, level.N)
138
}
139
}
140
141
fmt.Printf("\n Messages received: %d\n", msgCount)
142
}
143
144
conn.Close()
145
146
if !shouldRetry {
147
break
148
}
149
}
150
151
return nil
152
}
153
154
func main() {
155
coin := flag.String("coin", "BTC", "Coin symbol to stream")
156
levels := flag.Uint("levels", 20, "Number of price levels")
157
158
flag.Parse()
159
160
fmt.Println("\n" + strings.Repeat("=", 60))
161
fmt.Println("Hyperliquid StreamL2Book Example")
162
fmt.Printf("Endpoint: %s\n", grpcEndpoint)
163
fmt.Println(strings.Repeat("=", 60))
164
165
if err := streamL2Orderbook(*coin, uint32(*levels)); err != nil {
166
log.Fatal(err)
167
}
168
}
169
Don't have an account yet?
Create your Quicknode endpoint in seconds and start building
Get started for free