Skip to main content

StreamL4Book gRPC Method

Loading...

Updated on
Feb 24, 2026

StreamL4Book gRPC Method

Parameters

coin
string
Loading...

Returns

stream
stream<L4BookUpdate>
Loading...
snapshot
L4BookSnapshot
Loading...
diff
L4BookDiff
Loading...
Request
1
// StreamL4Book Example - Stream individual order data via gRPC
2
package main
3
4
import (
5
"context"
6
"encoding/json"
7
"flag"
8
"fmt"
9
"io"
10
"log"
11
"math"
12
"strings"
13
"time"
14
15
"google.golang.org/grpc"
16
"google.golang.org/grpc/codes"
17
"google.golang.org/grpc/credentials"
18
"google.golang.org/grpc/metadata"
19
"google.golang.org/grpc/status"
20
21
pb "hyperliquid-orderbook-example/proto"
22
)
23
24
const (
25
grpcEndpoint = "your-endpoint.hype-mainnet.quiknode.pro:10000"
26
authToken = "your-auth-token"
27
maxRetries = 10
28
baseDelay = 2 * time.Second
29
)
30
31
func streamL4Orderbook(coin string, maxMessages int) error {
32
fmt.Println(strings.Repeat("=", 60))
33
fmt.Printf("Streaming L4 Orderbook for %s\n", coin)
34
fmt.Println("Auto-reconnect: true")
35
fmt.Println(strings.Repeat("=", 60) + "\n")
36
37
retryCount := 0
38
totalMsgCount := 0
39
40
for retryCount < maxRetries {
41
creds := credentials.NewClientTLSFromCert(nil, "")
42
conn, err := grpc.Dial(grpcEndpoint,
43
grpc.WithTransportCredentials(creds),
44
grpc.WithDefaultCallOptions(grpc.MaxCallRecvMsgSize(100*1024*1024)))
45
if err != nil {
46
return fmt.Errorf("failed to connect: %w", err)
47
}
48
49
client := pb.NewOrderBookStreamingClient(conn)
50
ctx := metadata.AppendToOutgoingContext(context.Background(), "x-token", authToken)
51
52
request := &pb.L4BookRequest{
53
Coin: coin,
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.StreamL4Book(ctx, request)
63
if err != nil {
64
conn.Close()
65
return fmt.Errorf("failed to start stream: %w", err)
66
}
67
68
snapshotReceived := false
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
totalMsgCount++
98
99
if snapshot := update.GetSnapshot(); snapshot != nil {
100
snapshotReceived = true
101
retryCount = 0 // Reset on success
102
103
fmt.Println("\n✓ L4 Snapshot Received!")
104
fmt.Println(strings.Repeat("─", 60))
105
fmt.Printf("Coin: %s\n", snapshot.Coin)
106
fmt.Printf("Height: %d\n", snapshot.Height)
107
fmt.Printf("Time: %d\n", snapshot.Time)
108
fmt.Printf("Bids: %d orders\n", len(snapshot.Bids))
109
fmt.Printf("Asks: %d orders\n", len(snapshot.Asks))
110
fmt.Println(strings.Repeat("─", 60))
111
112
// Sample bids
113
if len(snapshot.Bids) > 0 {
114
fmt.Println("\nSample Bids (first 5):")
115
bidCount := len(snapshot.Bids)
116
if bidCount > 5 {
117
bidCount = 5
118
}
119
for i := 0; i < bidCount; i++ {
120
order := snapshot.Bids[i]
121
userShort := order.User
122
if len(userShort) > 10 {
123
userShort = userShort[:10] + "..."
124
}
125
fmt.Printf(" OID: %d | Price: %s | Size: %s | User: %s\n",
126
order.Oid, order.LimitPx, order.Sz, userShort)
127
}
128
}
129
130
// Sample asks
131
if len(snapshot.Asks) > 0 {
132
fmt.Println("\nSample Asks (first 5):")
133
askCount := len(snapshot.Asks)
134
if askCount > 5 {
135
askCount = 5
136
}
137
for i := 0; i < askCount; i++ {
138
order := snapshot.Asks[i]
139
userShort := order.User
140
if len(userShort) > 10 {
141
userShort = userShort[:10] + "..."
142
}
143
fmt.Printf(" OID: %d | Price: %s | Size: %s | User: %s\n",
144
order.Oid, order.LimitPx, order.Sz, userShort)
145
}
146
}
147
148
} else if diff := update.GetDiff(); diff != nil {
149
if !snapshotReceived {
150
fmt.Println("\n⚠ Received diff before snapshot")
151
}
152
153
var diffData map[string]interface{}
154
if err := json.Unmarshal([]byte(diff.Data), &diffData); err == nil {
155
orderStatuses := []interface{}{}
156
bookDiffs := []interface{}{}
157
158
if os, ok := diffData["order_statuses"].([]interface{}); ok {
159
orderStatuses = os
160
}
161
if bd, ok := diffData["book_diffs"].([]interface{}); ok {
162
bookDiffs = bd
163
}
164
165
fmt.Printf("\n[Block %d] L4 Diff:\n", diff.Height)
166
fmt.Printf(" Time: %d\n", diff.Time)
167
fmt.Printf(" Order Statuses: %d\n", len(orderStatuses))
168
fmt.Printf(" Book Diffs: %d\n", len(bookDiffs))
169
170
if len(bookDiffs) > 0 && len(bookDiffs) <= 5 {
171
pretty, _ := json.MarshalIndent(bookDiffs, " ", " ")
172
fmt.Printf(" Diffs: %s\n", pretty)
173
}
174
}
175
}
176
177
if maxMessages > 0 && totalMsgCount >= maxMessages {
178
fmt.Printf("\nReached max messages (%d), stopping...\n", maxMessages)
179
conn.Close()
180
return nil
181
}
182
}
183
184
conn.Close()
185
186
if !shouldRetry {
187
break
188
}
189
}
190
191
return nil
192
}
193
194
func main() {
195
coin := flag.String("coin", "BTC", "Coin symbol to stream")
196
maxMessages := flag.Int("max-messages", 0, "Maximum messages (0 = unlimited)")
197
198
flag.Parse()
199
200
fmt.Println("\n" + strings.Repeat("=", 60))
201
fmt.Println("Hyperliquid StreamL4Book Example")
202
fmt.Printf("Endpoint: %s\n", grpcEndpoint)
203
fmt.Println(strings.Repeat("=", 60))
204
205
if err := streamL4Orderbook(*coin, *maxMessages); err != nil {
206
log.Fatal(err)
207
}
208
}
209
1
// StreamL4Book Example - Stream individual order data via gRPC
2
package main
3
4
import (
5
"context"
6
"encoding/json"
7
"flag"
8
"fmt"
9
"io"
10
"log"
11
"math"
12
"strings"
13
"time"
14
15
"google.golang.org/grpc"
16
"google.golang.org/grpc/codes"
17
"google.golang.org/grpc/credentials"
18
"google.golang.org/grpc/metadata"
19
"google.golang.org/grpc/status"
20
21
pb "hyperliquid-orderbook-example/proto"
22
)
23
24
const (
25
grpcEndpoint = "your-endpoint.hype-mainnet.quiknode.pro:10000"
26
authToken = "your-auth-token"
27
maxRetries = 10
28
baseDelay = 2 * time.Second
29
)
30
31
func streamL4Orderbook(coin string, maxMessages int) error {
32
fmt.Println(strings.Repeat("=", 60))
33
fmt.Printf("Streaming L4 Orderbook for %s\n", coin)
34
fmt.Println("Auto-reconnect: true")
35
fmt.Println(strings.Repeat("=", 60) + "\n")
36
37
retryCount := 0
38
totalMsgCount := 0
39
40
for retryCount < maxRetries {
41
creds := credentials.NewClientTLSFromCert(nil, "")
42
conn, err := grpc.Dial(grpcEndpoint,
43
grpc.WithTransportCredentials(creds),
44
grpc.WithDefaultCallOptions(grpc.MaxCallRecvMsgSize(100*1024*1024)))
45
if err != nil {
46
return fmt.Errorf("failed to connect: %w", err)
47
}
48
49
client := pb.NewOrderBookStreamingClient(conn)
50
ctx := metadata.AppendToOutgoingContext(context.Background(), "x-token", authToken)
51
52
request := &pb.L4BookRequest{
53
Coin: coin,
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.StreamL4Book(ctx, request)
63
if err != nil {
64
conn.Close()
65
return fmt.Errorf("failed to start stream: %w", err)
66
}
67
68
snapshotReceived := false
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
totalMsgCount++
98
99
if snapshot := update.GetSnapshot(); snapshot != nil {
100
snapshotReceived = true
101
retryCount = 0 // Reset on success
102
103
fmt.Println("\n✓ L4 Snapshot Received!")
104
fmt.Println(strings.Repeat("─", 60))
105
fmt.Printf("Coin: %s\n", snapshot.Coin)
106
fmt.Printf("Height: %d\n", snapshot.Height)
107
fmt.Printf("Time: %d\n", snapshot.Time)
108
fmt.Printf("Bids: %d orders\n", len(snapshot.Bids))
109
fmt.Printf("Asks: %d orders\n", len(snapshot.Asks))
110
fmt.Println(strings.Repeat("─", 60))
111
112
// Sample bids
113
if len(snapshot.Bids) > 0 {
114
fmt.Println("\nSample Bids (first 5):")
115
bidCount := len(snapshot.Bids)
116
if bidCount > 5 {
117
bidCount = 5
118
}
119
for i := 0; i < bidCount; i++ {
120
order := snapshot.Bids[i]
121
userShort := order.User
122
if len(userShort) > 10 {
123
userShort = userShort[:10] + "..."
124
}
125
fmt.Printf(" OID: %d | Price: %s | Size: %s | User: %s\n",
126
order.Oid, order.LimitPx, order.Sz, userShort)
127
}
128
}
129
130
// Sample asks
131
if len(snapshot.Asks) > 0 {
132
fmt.Println("\nSample Asks (first 5):")
133
askCount := len(snapshot.Asks)
134
if askCount > 5 {
135
askCount = 5
136
}
137
for i := 0; i < askCount; i++ {
138
order := snapshot.Asks[i]
139
userShort := order.User
140
if len(userShort) > 10 {
141
userShort = userShort[:10] + "..."
142
}
143
fmt.Printf(" OID: %d | Price: %s | Size: %s | User: %s\n",
144
order.Oid, order.LimitPx, order.Sz, userShort)
145
}
146
}
147
148
} else if diff := update.GetDiff(); diff != nil {
149
if !snapshotReceived {
150
fmt.Println("\n⚠ Received diff before snapshot")
151
}
152
153
var diffData map[string]interface{}
154
if err := json.Unmarshal([]byte(diff.Data), &diffData); err == nil {
155
orderStatuses := []interface{}{}
156
bookDiffs := []interface{}{}
157
158
if os, ok := diffData["order_statuses"].([]interface{}); ok {
159
orderStatuses = os
160
}
161
if bd, ok := diffData["book_diffs"].([]interface{}); ok {
162
bookDiffs = bd
163
}
164
165
fmt.Printf("\n[Block %d] L4 Diff:\n", diff.Height)
166
fmt.Printf(" Time: %d\n", diff.Time)
167
fmt.Printf(" Order Statuses: %d\n", len(orderStatuses))
168
fmt.Printf(" Book Diffs: %d\n", len(bookDiffs))
169
170
if len(bookDiffs) > 0 && len(bookDiffs) <= 5 {
171
pretty, _ := json.MarshalIndent(bookDiffs, " ", " ")
172
fmt.Printf(" Diffs: %s\n", pretty)
173
}
174
}
175
}
176
177
if maxMessages > 0 && totalMsgCount >= maxMessages {
178
fmt.Printf("\nReached max messages (%d), stopping...\n", maxMessages)
179
conn.Close()
180
return nil
181
}
182
}
183
184
conn.Close()
185
186
if !shouldRetry {
187
break
188
}
189
}
190
191
return nil
192
}
193
194
func main() {
195
coin := flag.String("coin", "BTC", "Coin symbol to stream")
196
maxMessages := flag.Int("max-messages", 0, "Maximum messages (0 = unlimited)")
197
198
flag.Parse()
199
200
fmt.Println("\n" + strings.Repeat("=", 60))
201
fmt.Println("Hyperliquid StreamL4Book Example")
202
fmt.Printf("Endpoint: %s\n", grpcEndpoint)
203
fmt.Println(strings.Repeat("=", 60))
204
205
if err := streamL4Orderbook(*coin, *maxMessages); err != nil {
206
log.Fatal(err)
207
}
208
}
209
Don't have an account yet?
Create your Quicknode endpoint in seconds and start building
Get started for free