or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

authenticated-client.mdindex.mdorder-book.mdpublic-client.mdwebsocket-client.md

order-book.mddocs/

0

# Order Book Management

1

2

The OrderBook class provides automated maintenance of real-time order book state by processing WebSocket messages from Coinbase Pro's feed. It maintains accurate bid/ask data and handles the complexity of order book updates, making it easy to access current market depth.

3

4

## Capabilities

5

6

### Order Book Initialization

7

8

Create an OrderBook instance to track real-time order book state for a specific product.

9

10

```python { .api }

11

class OrderBook:

12

def __init__(self, product_id: str = 'BTC-USD', log_to = None):

13

"""

14

Initialize order book for real-time tracking of a specific product.

15

16

Parameters:

17

- product_id (str): Product to track (e.g., 'BTC-USD', 'ETH-USD')

18

- log_to: Optional file-like object for logging order book messages

19

"""

20

```

21

22

**Usage Example:**

23

```python

24

import cbpro

25

26

# Create order book for BTC-USD

27

order_book = cbpro.OrderBook(product_id='BTC-USD')

28

29

# With logging to file

30

with open('orderbook.log', 'wb') as log_file:

31

order_book = cbpro.OrderBook(product_id='BTC-USD', log_to=log_file)

32

```

33

34

### Message Processing

35

36

Process WebSocket messages to maintain accurate order book state with automatic synchronization.

37

38

```python { .api }

39

def process_message(self, message: dict):

40

"""

41

Process a WebSocket message to update the order book state.

42

43

Handles different message types:

44

- 'open': Add new order to book

45

- 'done': Remove completed order from book

46

- 'match': Update order sizes after trade execution

47

- 'change': Update order size changes

48

49

Parameters:

50

- message (dict): WebSocket message from Coinbase Pro feed

51

52

Automatically handles:

53

- Message sequence validation

54

- Order book resyncing on sequence gaps

55

- Initial book loading from REST API

56

"""

57

58

def reset_book(self):

59

"""

60

Reset and reload the order book from Coinbase Pro REST API.

61

62

Automatically called when:

63

- First message is processed

64

- Message sequence gaps are detected

65

- Manual reset is needed

66

"""

67

68

def on_sequence_gap(self, gap_start: int, gap_end: int):

69

"""

70

Called when a sequence gap is detected in WebSocket messages.

71

72

Override this method to customize gap handling behavior. Default

73

implementation calls reset_book() to resync.

74

75

Parameters:

76

- gap_start (int): Last processed sequence number

77

- gap_end (int): Next received sequence number

78

"""

79

```

80

81

**Usage Example:**

82

```python

83

# Integration with WebSocket client

84

class OrderBookWebsocket(cbpro.WebsocketClient):

85

def __init__(self, product_id):

86

super().__init__(

87

products=[product_id],

88

channels=['full'], # Full channel for complete order book updates

89

should_print=False

90

)

91

self.order_book = cbpro.OrderBook(product_id=product_id)

92

93

def on_message(self, msg):

94

# Process each message through order book

95

self.order_book.process_message(msg)

96

97

# Start tracking BTC-USD order book

98

ws_client = OrderBookWebsocket('BTC-USD')

99

ws_client.start()

100

101

# Access order book data

102

time.sleep(5) # Let it initialize

103

current_book = ws_client.order_book.get_current_book()

104

print(f"Bids: {len(current_book['bids'])}")

105

print(f"Asks: {len(current_book['asks'])}")

106

```

107

108

### Order Book Data Access

109

110

Retrieve current order book state and market data with convenient access methods.

111

112

```python { .api }

113

def get_current_book(self) -> dict:

114

"""

115

Get complete current order book state.

116

117

Returns:

118

dict: Complete order book containing:

119

- sequence: Current message sequence number

120

- bids: List of [price, size, order_id] for buy orders

121

- asks: List of [price, size, order_id] for sell orders

122

"""

123

124

def get_ask(self):

125

"""

126

Get the best (lowest) ask price.

127

128

Returns:

129

Decimal: Best ask price, or None if no asks available

130

"""

131

132

def get_bid(self):

133

"""

134

Get the best (highest) bid price.

135

136

Returns:

137

Decimal: Best bid price, or None if no bids available

138

"""

139

140

def get_asks(self, price) -> list:

141

"""

142

Get all ask orders at a specific price level.

143

144

Parameters:

145

- price: Price level to query

146

147

Returns:

148

list: List of orders at that price level, or None if no orders

149

"""

150

151

def get_bids(self, price) -> list:

152

"""

153

Get all bid orders at a specific price level.

154

155

Parameters:

156

- price: Price level to query

157

158

Returns:

159

list: List of orders at that price level, or None if no orders

160

"""

161

162

def get_current_ticker(self) -> dict:

163

"""

164

Get the most recent trade information.

165

166

Returns:

167

dict: Last trade data from 'match' messages, or None if no trades yet

168

"""

169

```

170

171

**Usage Example:**

172

```python

173

# Get current market data

174

best_bid = order_book.get_bid()

175

best_ask = order_book.get_ask()

176

177

if best_bid and best_ask:

178

spread = float(best_ask) - float(best_bid)

179

mid_price = (float(best_bid) + float(best_ask)) / 2

180

spread_bps = (spread / mid_price) * 10000

181

182

print(f"Best Bid: ${best_bid}")

183

print(f"Best Ask: ${best_ask}")

184

print(f"Spread: ${spread:.2f} ({spread_bps:.1f} bps)")

185

186

# Get market depth at specific levels

187

bid_orders_at_43000 = order_book.get_bids(43000.00)

188

if bid_orders_at_43000:

189

total_size = sum(float(order['size']) for order in bid_orders_at_43000)

190

print(f"Total size at $43,000 bid: {total_size} BTC")

191

192

# Get complete order book snapshot

193

book_snapshot = order_book.get_current_book()

194

print(f"Order book sequence: {book_snapshot['sequence']}")

195

print(f"Total bid levels: {len(book_snapshot['bids'])}")

196

print(f"Total ask levels: {len(book_snapshot['asks'])}")

197

```

198

199

## Advanced Usage Patterns

200

201

### Bid-Ask Spread Monitor

202

203

Track and log spread changes in real-time with automatic detection of significant moves.

204

205

```python

206

class SpreadMonitor(cbpro.OrderBook):

207

def __init__(self, product_id, spread_threshold=0.01):

208

super().__init__(product_id=product_id)

209

self.spread_threshold = spread_threshold

210

self.last_spread = None

211

self.spread_history = []

212

213

def process_message(self, message):

214

super().process_message(message)

215

216

# Check spread after each update

217

bid = self.get_bid()

218

ask = self.get_ask()

219

220

if bid and ask:

221

current_spread = float(ask) - float(bid)

222

223

# Log significant spread changes

224

if self.last_spread:

225

spread_change = abs(current_spread - self.last_spread)

226

if spread_change > self.spread_threshold:

227

print(f"Spread change: ${self.last_spread:.2f} -> ${current_spread:.2f}")

228

229

self.last_spread = current_spread

230

self.spread_history.append({

231

'timestamp': time.time(),

232

'spread': current_spread,

233

'bid': float(bid),

234

'ask': float(ask)

235

})

236

237

# Keep only last 1000 spreads

238

if len(self.spread_history) > 1000:

239

self.spread_history = self.spread_history[-1000:]

240

241

# Use spread monitor

242

spread_monitor = SpreadMonitor('BTC-USD', spread_threshold=1.0)

243

```

244

245

### Market Depth Analysis

246

247

Analyze order book depth and liquidity at different price levels.

248

249

```python

250

class DepthAnalyzer(cbpro.OrderBook):

251

def __init__(self, product_id):

252

super().__init__(product_id=product_id)

253

self.depth_levels = [0.1, 0.25, 0.5, 1.0, 2.0] # Percentage levels

254

255

def get_market_depth(self):

256

"""Calculate market depth at various percentage levels from best bid/ask."""

257

bid = self.get_bid()

258

ask = self.get_ask()

259

260

if not bid or not ask:

261

return None

262

263

book = self.get_current_book()

264

depth_analysis = {

265

'timestamp': time.time(),

266

'best_bid': float(bid),

267

'best_ask': float(ask),

268

'bid_depth': {},

269

'ask_depth': {}

270

}

271

272

# Analyze bid depth

273

for level_pct in self.depth_levels:

274

price_threshold = float(bid) * (1 - level_pct / 100)

275

total_size = 0

276

total_value = 0

277

278

for bid_level in book['bids']:

279

price, size, _ = bid_level

280

if float(price) >= price_threshold:

281

total_size += float(size)

282

total_value += float(price) * float(size)

283

284

depth_analysis['bid_depth'][f'{level_pct}%'] = {

285

'price_threshold': price_threshold,

286

'total_size': total_size,

287

'total_value': total_value

288

}

289

290

# Analyze ask depth

291

for level_pct in self.depth_levels:

292

price_threshold = float(ask) * (1 + level_pct / 100)

293

total_size = 0

294

total_value = 0

295

296

for ask_level in book['asks']:

297

price, size, _ = ask_level

298

if float(price) <= price_threshold:

299

total_size += float(size)

300

total_value += float(price) * float(size)

301

302

depth_analysis['ask_depth'][f'{level_pct}%'] = {

303

'price_threshold': price_threshold,

304

'total_size': total_size,

305

'total_value': total_value

306

}

307

308

return depth_analysis

309

310

def process_message(self, message):

311

super().process_message(message)

312

313

# Analyze depth every 100 messages

314

if hasattr(self, 'message_count'):

315

self.message_count += 1

316

else:

317

self.message_count = 1

318

319

if self.message_count % 100 == 0:

320

depth = self.get_market_depth()

321

if depth:

322

print(f"Market depth analysis:")

323

print(f" 1% bid depth: {depth['bid_depth']['1.0%']['total_size']:.4f} BTC")

324

print(f" 1% ask depth: {depth['ask_depth']['1.0%']['total_size']:.4f} BTC")

325

326

depth_analyzer = DepthAnalyzer('BTC-USD')

327

```

328

329

### Order Book Console Display

330

331

Real-time console display of order book changes with bid-ask spread monitoring.

332

333

```python

334

class OrderBookConsole(cbpro.OrderBook):

335

def __init__(self, product_id):

336

super().__init__(product_id=product_id)

337

self._bid = None

338

self._ask = None

339

self._bid_depth = None

340

self._ask_depth = None

341

342

def process_message(self, message):

343

if message.get('product_id') == self.product_id:

344

super().process_message(message)

345

346

try:

347

# Calculate current bid-ask spread

348

bid = self.get_bid()

349

ask = self.get_ask()

350

351

if bid and ask:

352

bids = self.get_bids(bid)

353

asks = self.get_asks(ask)

354

355

bid_depth = sum(float(b['size']) for b in bids) if bids else 0

356

ask_depth = sum(float(a['size']) for a in asks) if asks else 0

357

358

# Only print if there are changes

359

if (self._bid != bid or self._ask != ask or

360

self._bid_depth != bid_depth or self._ask_depth != ask_depth):

361

362

self._bid = bid

363

self._ask = ask

364

self._bid_depth = bid_depth

365

self._ask_depth = ask_depth

366

367

spread = float(ask) - float(bid)

368

timestamp = datetime.now().strftime('%H:%M:%S')

369

370

print(f'{timestamp} {self.product_id} '

371

f'bid: {bid_depth:.3f} @ ${float(bid):,.2f} | '

372

f'ask: {ask_depth:.3f} @ ${float(ask):,.2f} | '

373

f'spread: ${spread:.2f}')

374

375

except Exception as e:

376

print(f"Display error: {e}")

377

378

# Usage with WebSocket

379

class ConsoleWebsocket(cbpro.WebsocketClient):

380

def __init__(self, products):

381

super().__init__(

382

products=products,

383

channels=['full'],

384

should_print=False

385

)

386

self.order_books = {}

387

for product in products:

388

self.order_books[product] = OrderBookConsole(product)

389

390

def on_message(self, msg):

391

product_id = msg.get('product_id')

392

if product_id in self.order_books:

393

self.order_books[product_id].process_message(msg)

394

395

# Monitor multiple products

396

console_ws = ConsoleWebsocket(['BTC-USD', 'ETH-USD'])

397

console_ws.start()

398

```

399

400

## Error Handling and Synchronization

401

402

The OrderBook class includes robust error handling:

403

404

- **Sequence Gap Detection**: Automatically detects missing messages and resyncs

405

- **Book Reset**: Reloads complete order book from REST API when needed

406

- **Message Validation**: Handles malformed or out-of-sequence messages

407

- **State Consistency**: Maintains consistent bid/ask ordering using SortedDict

408

409

### Sequence Gap Handling

410

411

```python

412

def on_sequence_gap(self, gap_start, gap_end):

413

"""

414

Called when a sequence gap is detected.

415

Override this method to customize gap handling behavior.

416

417

Parameters:

418

- gap_start: Last processed sequence number

419

- gap_end: Next received sequence number

420

"""

421

print(f"Sequence gap detected: {gap_start} -> {gap_end}")

422

print("Resetting order book...")

423

self.reset_book()

424

```

425

426

## Performance Considerations

427

428

- **Memory Usage**: Order book size grows with market activity; consider periodic resets for long-running applications

429

- **Processing Speed**: Keep message processing fast to avoid falling behind the feed

430

- **Data Structures**: Uses SortedDict for efficient price-level access and maintenance

431

- **Logging**: Optional pickle logging can impact performance; use only when needed

432

- **Channel Selection**: Use 'level2' channel for lighter-weight order book tracking if full depth isn't needed

433

434

The OrderBook class is designed for high-frequency updates and maintains real-time accuracy even during periods of intense trading activity.