Skip to main content

StreamL4BookUpdates gRPC Method

Loading...

Updated on
Jun 10, 2026

StreamL4BookUpdates gRPC Method

Please note that this method is metered based on data consumption at 0.0165 MB = 10 API credits.

Parameters

coins
repeated string
Loading...

Returns

stream
stream<L4BookUpdatesUpdate>
Loading...
time
uint64
Loading...
height
uint64
Loading...
snapshot
bool
Loading...
diffs
array<L4OrderDiff>
Loading...
Request
1
// StreamL4BookUpdates Example - Stream typed per-order book updates 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
type localOrder struct {
31
coin string
32
user string
33
side string
34
px string
35
sz string
36
}
37
38
func streamL4BookUpdates(coins []string) error {
39
fmt.Println(strings.Repeat("=", 60))
40
fmt.Printf("Streaming L4 Book Updates for %s\n", strings.Join(coins, ", "))
41
fmt.Println("Auto-reconnect: true")
42
fmt.Println(strings.Repeat("=", 60) + "\n")
43
44
retryCount := 0
45
46
// Simple local order map keyed by oid
47
orders := make(map[uint64]localOrder)
48
49
for retryCount < maxRetries {
50
creds := credentials.NewClientTLSFromCert(nil, "")
51
conn, err := grpc.Dial(grpcEndpoint,
52
grpc.WithTransportCredentials(creds),
53
grpc.WithDefaultCallOptions(grpc.MaxCallRecvMsgSize(100*1024*1024)))
54
if err != nil {
55
return fmt.Errorf("failed to connect: %w", err)
56
}
57
58
client := pb.NewOrderBookStreamingClient(conn)
59
ctx := metadata.AppendToOutgoingContext(context.Background(), "x-token", authToken)
60
61
request := &pb.L4BookUpdatesRequest{
62
Coins: coins,
63
}
64
65
if retryCount > 0 {
66
fmt.Printf("\nšŸ”„ Reconnecting (attempt %d/%d)...\n", retryCount+1, maxRetries)
67
} else {
68
fmt.Printf("Connecting to %s...\n", grpcEndpoint)
69
}
70
71
stream, err := client.StreamL4BookUpdates(ctx, request)
72
if err != nil {
73
conn.Close()
74
return fmt.Errorf("failed to start stream: %w", err)
75
}
76
77
msgCount := 0
78
shouldRetry := false
79
80
for {
81
update, err := stream.Recv()
82
if err == io.EOF {
83
break
84
}
85
if err != nil {
86
st, ok := status.FromError(err)
87
if ok && st.Code() == codes.DataLoss {
88
fmt.Printf("\nāš ļø Server reinitialized: %s\n", st.Message())
89
retryCount++
90
if retryCount < maxRetries {
91
delay := baseDelay * time.Duration(math.Pow(2, float64(retryCount-1)))
92
fmt.Printf("ā³ Waiting %v before reconnecting...\n", delay)
93
time.Sleep(delay)
94
shouldRetry = true
95
break
96
} else {
97
fmt.Printf("\nāŒ Max retries (%d) reached. Giving up.\n", maxRetries)
98
conn.Close()
99
return nil
100
}
101
}
102
conn.Close()
103
return fmt.Errorf("stream error: %w", err)
104
}
105
106
msgCount++
107
if msgCount == 1 {
108
fmt.Println("āœ“ First L4 update received!\n")
109
retryCount = 0 // Reset on success
110
}
111
112
if update.Snapshot {
113
// Full reset snapshot: rebuild local state
114
orders = make(map[uint64]localOrder)
115
}
116
117
// Display update
118
fmt.Println("\n" + strings.Repeat("─", 60))
119
snapshotLabel := ""
120
if update.Snapshot {
121
snapshotLabel = " | SNAPSHOT"
122
}
123
fmt.Printf("Block: %d | Time: %d%s | Diffs: %d\n", update.Height, update.Time, snapshotLabel, len(update.Diffs))
124
fmt.Println(strings.Repeat("─", 60))
125
126
for _, diff := range update.Diffs {
127
side := "ASK"
128
if diff.Side == "B" {
129
side = "BID"
130
}
131
132
switch diff.DiffType {
133
case pb.L4OrderDiffType_L4_ORDER_DIFF_TYPE_NEW:
134
orders[diff.Oid] = localOrder{coin: diff.Coin, user: diff.User, side: diff.Side, px: diff.Px, sz: diff.Sz}
135
if !update.Snapshot {
136
fmt.Printf(" NEW %s oid: %d | %s %s x %s | %s\n", diff.Coin, diff.Oid, side, diff.Px, diff.Sz, diff.User)
137
}
138
case pb.L4OrderDiffType_L4_ORDER_DIFF_TYPE_UPDATE:
139
if existing, ok := orders[diff.Oid]; ok {
140
existing.sz = diff.Sz
141
orders[diff.Oid] = existing
142
}
143
fmt.Printf(" UPDATE %s oid: %d | %s %s x %s | %s\n", diff.Coin, diff.Oid, side, diff.Px, diff.Sz, diff.User)
144
case pb.L4OrderDiffType_L4_ORDER_DIFF_TYPE_REMOVE:
145
delete(orders, diff.Oid)
146
fmt.Printf(" REMOVE %s oid: %d | %s %s | %s\n", diff.Coin, diff.Oid, side, diff.Px, diff.User)
147
}
148
}
149
150
fmt.Printf("\n Resting orders tracked: %d | Messages received: %d\n", len(orders), msgCount)
151
}
152
153
conn.Close()
154
155
if !shouldRetry {
156
break
157
}
158
}
159
160
return nil
161
}
162
163
func main() {
164
coinsFlag := flag.String("coins", "BTC", "Comma-separated coin symbols to stream (e.g., BTC,ETH)")
165
166
flag.Parse()
167
168
coins := strings.Split(*coinsFlag, ",")
169
170
fmt.Println("\n" + strings.Repeat("=", 60))
171
fmt.Println("Hyperliquid StreamL4BookUpdates Example")
172
fmt.Printf("Endpoint: %s\n", grpcEndpoint)
173
fmt.Println(strings.Repeat("=", 60))
174
175
if err := streamL4BookUpdates(coins); err != nil {
176
log.Fatal(err)
177
}
178
}
179
1
// StreamL4BookUpdates Example - Stream typed per-order book updates 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
type localOrder struct {
31
coin string
32
user string
33
side string
34
px string
35
sz string
36
}
37
38
func streamL4BookUpdates(coins []string) error {
39
fmt.Println(strings.Repeat("=", 60))
40
fmt.Printf("Streaming L4 Book Updates for %s\n", strings.Join(coins, ", "))
41
fmt.Println("Auto-reconnect: true")
42
fmt.Println(strings.Repeat("=", 60) + "\n")
43
44
retryCount := 0
45
46
// Simple local order map keyed by oid
47
orders := make(map[uint64]localOrder)
48
49
for retryCount < maxRetries {
50
creds := credentials.NewClientTLSFromCert(nil, "")
51
conn, err := grpc.Dial(grpcEndpoint,
52
grpc.WithTransportCredentials(creds),
53
grpc.WithDefaultCallOptions(grpc.MaxCallRecvMsgSize(100*1024*1024)))
54
if err != nil {
55
return fmt.Errorf("failed to connect: %w", err)
56
}
57
58
client := pb.NewOrderBookStreamingClient(conn)
59
ctx := metadata.AppendToOutgoingContext(context.Background(), "x-token", authToken)
60
61
request := &pb.L4BookUpdatesRequest{
62
Coins: coins,
63
}
64
65
if retryCount > 0 {
66
fmt.Printf("\nšŸ”„ Reconnecting (attempt %d/%d)...\n", retryCount+1, maxRetries)
67
} else {
68
fmt.Printf("Connecting to %s...\n", grpcEndpoint)
69
}
70
71
stream, err := client.StreamL4BookUpdates(ctx, request)
72
if err != nil {
73
conn.Close()
74
return fmt.Errorf("failed to start stream: %w", err)
75
}
76
77
msgCount := 0
78
shouldRetry := false
79
80
for {
81
update, err := stream.Recv()
82
if err == io.EOF {
83
break
84
}
85
if err != nil {
86
st, ok := status.FromError(err)
87
if ok && st.Code() == codes.DataLoss {
88
fmt.Printf("\nāš ļø Server reinitialized: %s\n", st.Message())
89
retryCount++
90
if retryCount < maxRetries {
91
delay := baseDelay * time.Duration(math.Pow(2, float64(retryCount-1)))
92
fmt.Printf("ā³ Waiting %v before reconnecting...\n", delay)
93
time.Sleep(delay)
94
shouldRetry = true
95
break
96
} else {
97
fmt.Printf("\nāŒ Max retries (%d) reached. Giving up.\n", maxRetries)
98
conn.Close()
99
return nil
100
}
101
}
102
conn.Close()
103
return fmt.Errorf("stream error: %w", err)
104
}
105
106
msgCount++
107
if msgCount == 1 {
108
fmt.Println("āœ“ First L4 update received!\n")
109
retryCount = 0 // Reset on success
110
}
111
112
if update.Snapshot {
113
// Full reset snapshot: rebuild local state
114
orders = make(map[uint64]localOrder)
115
}
116
117
// Display update
118
fmt.Println("\n" + strings.Repeat("─", 60))
119
snapshotLabel := ""
120
if update.Snapshot {
121
snapshotLabel = " | SNAPSHOT"
122
}
123
fmt.Printf("Block: %d | Time: %d%s | Diffs: %d\n", update.Height, update.Time, snapshotLabel, len(update.Diffs))
124
fmt.Println(strings.Repeat("─", 60))
125
126
for _, diff := range update.Diffs {
127
side := "ASK"
128
if diff.Side == "B" {
129
side = "BID"
130
}
131
132
switch diff.DiffType {
133
case pb.L4OrderDiffType_L4_ORDER_DIFF_TYPE_NEW:
134
orders[diff.Oid] = localOrder{coin: diff.Coin, user: diff.User, side: diff.Side, px: diff.Px, sz: diff.Sz}
135
if !update.Snapshot {
136
fmt.Printf(" NEW %s oid: %d | %s %s x %s | %s\n", diff.Coin, diff.Oid, side, diff.Px, diff.Sz, diff.User)
137
}
138
case pb.L4OrderDiffType_L4_ORDER_DIFF_TYPE_UPDATE:
139
if existing, ok := orders[diff.Oid]; ok {
140
existing.sz = diff.Sz
141
orders[diff.Oid] = existing
142
}
143
fmt.Printf(" UPDATE %s oid: %d | %s %s x %s | %s\n", diff.Coin, diff.Oid, side, diff.Px, diff.Sz, diff.User)
144
case pb.L4OrderDiffType_L4_ORDER_DIFF_TYPE_REMOVE:
145
delete(orders, diff.Oid)
146
fmt.Printf(" REMOVE %s oid: %d | %s %s | %s\n", diff.Coin, diff.Oid, side, diff.Px, diff.User)
147
}
148
}
149
150
fmt.Printf("\n Resting orders tracked: %d | Messages received: %d\n", len(orders), msgCount)
151
}
152
153
conn.Close()
154
155
if !shouldRetry {
156
break
157
}
158
}
159
160
return nil
161
}
162
163
func main() {
164
coinsFlag := flag.String("coins", "BTC", "Comma-separated coin symbols to stream (e.g., BTC,ETH)")
165
166
flag.Parse()
167
168
coins := strings.Split(*coinsFlag, ",")
169
170
fmt.Println("\n" + strings.Repeat("=", 60))
171
fmt.Println("Hyperliquid StreamL4BookUpdates Example")
172
fmt.Printf("Endpoint: %s\n", grpcEndpoint)
173
fmt.Println(strings.Repeat("=", 60))
174
175
if err := streamL4BookUpdates(coins); err != nil {
176
log.Fatal(err)
177
}
178
}
179
Don't have an account yet?
Create your Quicknode endpoint in seconds and start building
Get started for free