C SDK
Order Book
Market data access and real-time order book updates with the C SDK
Order Book
This guide covers accessing order book data and subscribing to real-time market updates using the LX C SDK.
Data Structures
Order Book Level
struct lxdex_level {
double price; // Price level
double size; // Total size at this level
int order_count; // Number of orders at this level
};Order Book
struct lxdex_orderbook {
const char *symbol; // Trading pair
struct lxdex_level *bids; // Bid levels (highest first)
size_t bid_count; // Number of bid levels
struct lxdex_level *asks; // Ask levels (lowest first)
size_t ask_count; // Number of ask levels
uint64_t sequence; // Sequence number
uint64_t timestamp; // Timestamp (milliseconds)
};Getting Order Book Snapshot
Basic Snapshot
#include <lxdex.h>
#include <stdio.h>
int get_orderbook(struct lxdex_client *client) {
struct lxdex_orderbook book;
int err = lxdex_get_orderbook(client, "BTC-USD", 10, &book);
if (err != LXDEX_OK) {
fprintf(stderr, "Failed to get order book: %s\n", lxdex_strerror(err));
return err;
}
printf("Order Book: %s\n", book.symbol);
printf("Sequence: %llu\n", book.sequence);
printf("\nAsks (lowest first):\n");
for (size_t i = 0; i < book.ask_count; i++) {
printf(" %.2f: %.8f (%d orders)\n",
book.asks[i].price,
book.asks[i].size,
book.asks[i].order_count);
}
printf("\nBids (highest first):\n");
for (size_t i = 0; i < book.bid_count; i++) {
printf(" %.2f: %.8f (%d orders)\n",
book.bids[i].price,
book.bids[i].size,
book.bids[i].order_count);
}
// Free memory allocated by the library
lxdex_orderbook_free(&book);
return LXDEX_OK;
}Full Depth
int get_full_depth(struct lxdex_client *client, const char *symbol) {
struct lxdex_orderbook book;
// Pass 0 for full depth
int err = lxdex_get_orderbook(client, symbol, 0, &book);
if (err == LXDEX_OK) {
printf("Full depth: %zu bids, %zu asks\n",
book.bid_count, book.ask_count);
lxdex_orderbook_free(&book);
}
return err;
}Best Bid/Ask
int get_bbo(struct lxdex_client *client, const char *symbol) {
struct lxdex_orderbook book;
// Request depth of 1 for just BBO
int err = lxdex_get_orderbook(client, symbol, 1, &book);
if (err == LXDEX_OK && book.bid_count > 0 && book.ask_count > 0) {
double best_bid = book.bids[0].price;
double best_ask = book.asks[0].price;
double spread = best_ask - best_bid;
double mid = (best_bid + best_ask) / 2.0;
printf("BBO for %s:\n", symbol);
printf(" Best Bid: %.2f (%.8f)\n", best_bid, book.bids[0].size);
printf(" Best Ask: %.2f (%.8f)\n", best_ask, book.asks[0].size);
printf(" Spread: %.2f (%.4f%%)\n", spread, (spread / mid) * 100);
lxdex_orderbook_free(&book);
}
return err;
}Subscribing to Updates
Real-Time Order Book
#include <lxdex.h>
#include <stdio.h>
#include <signal.h>
static volatile int running = 1;
void signal_handler(int sig) {
(void)sig;
running = 0;
}
void on_orderbook_update(const struct lxdex_orderbook *book, void *user_data) {
(void)user_data;
if (book->bid_count > 0 && book->ask_count > 0) {
double spread = book->asks[0].price - book->bids[0].price;
printf("[%llu] %s: %.2f / %.2f (spread: %.2f)\n",
book->sequence,
book->symbol,
book->bids[0].price,
book->asks[0].price,
spread);
}
}
int subscribe_orderbook(struct lxdex_client *client) {
signal(SIGINT, signal_handler);
struct lxdex_subscription *sub = lxdex_subscribe_orderbook(
client,
"BTC-USD",
10, // depth
on_orderbook_update,
NULL // user_data
);
if (!sub) {
fprintf(stderr, "Subscription failed\n");
return -1;
}
printf("Subscribed to BTC-USD order book\n");
while (running) {
lxdex_client_poll(client, 100);
}
lxdex_unsubscribe(sub);
return 0;
}Multiple Symbols
struct symbol_data {
const char *symbol;
double last_bid;
double last_ask;
};
void on_multi_symbol_update(const struct lxdex_orderbook *book, void *user_data) {
struct symbol_data *data = user_data;
if (book->bid_count > 0) {
data->last_bid = book->bids[0].price;
}
if (book->ask_count > 0) {
data->last_ask = book->asks[0].price;
}
printf("[%s] %.2f / %.2f\n", data->symbol, data->last_bid, data->last_ask);
}
int subscribe_multiple_symbols(struct lxdex_client *client) {
const char *symbols[] = {"BTC-USD", "ETH-USD", "SOL-USD"};
const int num_symbols = 3;
struct symbol_data data[3];
struct lxdex_subscription *subs[3];
for (int i = 0; i < num_symbols; i++) {
data[i].symbol = symbols[i];
data[i].last_bid = 0;
data[i].last_ask = 0;
subs[i] = lxdex_subscribe_orderbook(
client,
symbols[i],
5,
on_multi_symbol_update,
&data[i]
);
if (!subs[i]) {
fprintf(stderr, "Failed to subscribe to %s\n", symbols[i]);
}
}
while (running) {
lxdex_client_poll(client, 100);
}
for (int i = 0; i < num_symbols; i++) {
if (subs[i]) {
lxdex_unsubscribe(subs[i]);
}
}
return 0;
}Trade Stream
Trade Structure
struct lxdex_trade {
uint64_t trade_id; // Unique trade ID
const char *symbol; // Trading pair
double price; // Trade price
double size; // Trade size
enum lxdex_side side; // Aggressor side
uint64_t timestamp; // Timestamp (milliseconds)
};Subscribe to Trades
void on_trade(const struct lxdex_trade *trade, void *user_data) {
(void)user_data;
const char *side = trade->side == LXDEX_SIDE_BUY ? "BUY" : "SELL";
printf("[TRADE] %s %s %.8f @ %.2f\n",
side, trade->symbol, trade->size, trade->price);
}
int subscribe_trades(struct lxdex_client *client, const char *symbol) {
struct lxdex_subscription *sub = lxdex_subscribe_trades(
client,
symbol,
on_trade,
NULL
);
if (!sub) {
return -1;
}
while (running) {
lxdex_client_poll(client, 100);
}
lxdex_unsubscribe(sub);
return 0;
}Ticker Data
Ticker Structure
struct lxdex_ticker {
const char *symbol;
double last_price;
double bid_price;
double ask_price;
double high_24h;
double low_24h;
double volume_24h;
double price_change_24h;
double price_change_pct_24h;
uint64_t timestamp;
};Get Ticker
int get_ticker(struct lxdex_client *client, const char *symbol) {
struct lxdex_ticker ticker;
int err = lxdex_get_ticker(client, symbol, &ticker);
if (err == LXDEX_OK) {
printf("Ticker: %s\n", ticker.symbol);
printf(" Last: %.2f\n", ticker.last_price);
printf(" Bid/Ask: %.2f / %.2f\n", ticker.bid_price, ticker.ask_price);
printf(" 24h High/Low: %.2f / %.2f\n", ticker.high_24h, ticker.low_24h);
printf(" 24h Volume: %.2f\n", ticker.volume_24h);
printf(" 24h Change: %.2f (%.2f%%)\n",
ticker.price_change_24h, ticker.price_change_pct_24h);
}
return err;
}Subscribe to Ticker
void on_ticker(const struct lxdex_ticker *ticker, void *user_data) {
(void)user_data;
printf("[TICKER] %s: %.2f (%.2f%%)\n",
ticker->symbol, ticker->last_price, ticker->price_change_pct_24h);
}
int subscribe_ticker(struct lxdex_client *client, const char *symbol) {
struct lxdex_subscription *sub = lxdex_subscribe_ticker(
client,
symbol,
on_ticker,
NULL
);
if (!sub) {
return -1;
}
while (running) {
lxdex_client_poll(client, 100);
}
lxdex_unsubscribe(sub);
return 0;
}Incremental Updates
Order Book Delta Callback
enum lxdex_delta_type {
LXDEX_DELTA_ADD = 0, // New level
LXDEX_DELTA_UPDATE = 1, // Level changed
LXDEX_DELTA_DELETE = 2 // Level removed
};
struct lxdex_orderbook_delta {
enum lxdex_delta_type type;
enum lxdex_side side;
double price;
double size;
uint64_t sequence;
};
void on_orderbook_delta(const struct lxdex_orderbook_delta *delta, void *user_data) {
const char *type_str;
switch (delta->type) {
case LXDEX_DELTA_ADD: type_str = "ADD"; break;
case LXDEX_DELTA_UPDATE: type_str = "UPD"; break;
case LXDEX_DELTA_DELETE: type_str = "DEL"; break;
default: type_str = "???"; break;
}
const char *side_str = delta->side == LXDEX_SIDE_BUY ? "BID" : "ASK";
printf("[%s] %s %.2f: %.8f\n", type_str, side_str, delta->price, delta->size);
}Subscribe to Deltas
int subscribe_orderbook_deltas(struct lxdex_client *client, const char *symbol) {
struct lxdex_subscription *sub = lxdex_subscribe_orderbook_deltas(
client,
symbol,
on_orderbook_delta,
NULL
);
if (!sub) {
return -1;
}
while (running) {
lxdex_client_poll(client, 100);
}
lxdex_unsubscribe(sub);
return 0;
}Maintaining Local Order Book
Local Book Structure
#include <stdlib.h>
#include <string.h>
#define MAX_LEVELS 1000
struct local_orderbook {
char symbol[32];
struct lxdex_level bids[MAX_LEVELS];
size_t bid_count;
struct lxdex_level asks[MAX_LEVELS];
size_t ask_count;
uint64_t sequence;
};
void local_book_init(struct local_orderbook *book, const char *symbol) {
memset(book, 0, sizeof(*book));
strncpy(book->symbol, symbol, sizeof(book->symbol) - 1);
}
void local_book_apply_snapshot(struct local_orderbook *book,
const struct lxdex_orderbook *snapshot) {
book->bid_count = snapshot->bid_count < MAX_LEVELS ?
snapshot->bid_count : MAX_LEVELS;
book->ask_count = snapshot->ask_count < MAX_LEVELS ?
snapshot->ask_count : MAX_LEVELS;
memcpy(book->bids, snapshot->bids,
book->bid_count * sizeof(struct lxdex_level));
memcpy(book->asks, snapshot->asks,
book->ask_count * sizeof(struct lxdex_level));
book->sequence = snapshot->sequence;
}Apply Delta Updates
static int find_level(struct lxdex_level *levels, size_t count,
double price, int is_bid) {
for (size_t i = 0; i < count; i++) {
if (levels[i].price == price) {
return (int)i;
}
// For bids, prices are descending; for asks, ascending
if (is_bid && levels[i].price < price) return -(int)(i + 1);
if (!is_bid && levels[i].price > price) return -(int)(i + 1);
}
return -(int)(count + 1);
}
static void insert_level(struct lxdex_level *levels, size_t *count,
int pos, const struct lxdex_level *level) {
if (*count >= MAX_LEVELS) return;
// Shift levels down
memmove(&levels[pos + 1], &levels[pos],
(*count - pos) * sizeof(struct lxdex_level));
levels[pos] = *level;
(*count)++;
}
static void remove_level(struct lxdex_level *levels, size_t *count, int pos) {
if (pos >= (int)*count) return;
memmove(&levels[pos], &levels[pos + 1],
(*count - pos - 1) * sizeof(struct lxdex_level));
(*count)--;
}
void local_book_apply_delta(struct local_orderbook *book,
const struct lxdex_orderbook_delta *delta) {
// Skip old updates
if (delta->sequence <= book->sequence) {
return;
}
struct lxdex_level *levels;
size_t *count;
int is_bid = delta->side == LXDEX_SIDE_BUY;
if (is_bid) {
levels = book->bids;
count = &book->bid_count;
} else {
levels = book->asks;
count = &book->ask_count;
}
int idx = find_level(levels, *count, delta->price, is_bid);
switch (delta->type) {
case LXDEX_DELTA_ADD:
if (idx < 0) {
struct lxdex_level level = {
.price = delta->price,
.size = delta->size,
.order_count = 1
};
insert_level(levels, count, -idx - 1, &level);
}
break;
case LXDEX_DELTA_UPDATE:
if (idx >= 0) {
levels[idx].size = delta->size;
}
break;
case LXDEX_DELTA_DELETE:
if (idx >= 0) {
remove_level(levels, count, idx);
}
break;
}
book->sequence = delta->sequence;
}Using Local Book
struct local_orderbook g_book;
void on_snapshot(const struct lxdex_orderbook *snapshot, void *user_data) {
struct local_orderbook *book = user_data;
local_book_apply_snapshot(book, snapshot);
printf("Snapshot applied (seq %llu)\n", book->sequence);
}
void on_delta(const struct lxdex_orderbook_delta *delta, void *user_data) {
struct local_orderbook *book = user_data;
local_book_apply_delta(book, delta);
}
int maintain_local_book(struct lxdex_client *client, const char *symbol) {
local_book_init(&g_book, symbol);
// Get initial snapshot
struct lxdex_orderbook snapshot;
if (lxdex_get_orderbook(client, symbol, 0, &snapshot) == LXDEX_OK) {
local_book_apply_snapshot(&g_book, &snapshot);
lxdex_orderbook_free(&snapshot);
}
// Subscribe to deltas
struct lxdex_subscription *sub = lxdex_subscribe_orderbook_deltas(
client, symbol, on_delta, &g_book);
while (running) {
lxdex_client_poll(client, 100);
// Use local book
if (g_book.bid_count > 0 && g_book.ask_count > 0) {
printf("Local BBO: %.2f / %.2f\n",
g_book.bids[0].price, g_book.asks[0].price);
}
}
lxdex_unsubscribe(sub);
return 0;
}Performance Considerations
Pre-allocated Buffers
// Pre-allocate buffer for order book data
#define BOOK_BUFFER_SIZE (sizeof(struct lxdex_level) * 2000)
static char book_buffer[BOOK_BUFFER_SIZE];
int get_orderbook_preallocated(struct lxdex_client *client,
const char *symbol,
struct lxdex_orderbook *book) {
// Use pre-allocated buffer
return lxdex_get_orderbook_with_buffer(
client, symbol, 100, book,
book_buffer, BOOK_BUFFER_SIZE
);
// No need to call lxdex_orderbook_free()
}Callback Performance
// Keep callbacks fast - move processing to separate thread
#include <pthread.h>
struct ring_buffer {
struct lxdex_orderbook_delta deltas[10000];
size_t head;
size_t tail;
pthread_mutex_t mutex;
pthread_cond_t cond;
};
void fast_callback(const struct lxdex_orderbook_delta *delta, void *user_data) {
struct ring_buffer *rb = user_data;
// Quick copy to ring buffer
pthread_mutex_lock(&rb->mutex);
rb->deltas[rb->head] = *delta;
rb->head = (rb->head + 1) % 10000;
pthread_cond_signal(&rb->cond);
pthread_mutex_unlock(&rb->mutex);
}
void *processing_thread(void *arg) {
struct ring_buffer *rb = arg;
while (running) {
pthread_mutex_lock(&rb->mutex);
while (rb->head == rb->tail && running) {
pthread_cond_wait(&rb->cond, &rb->mutex);
}
if (rb->head != rb->tail) {
struct lxdex_orderbook_delta delta = rb->deltas[rb->tail];
rb->tail = (rb->tail + 1) % 10000;
pthread_mutex_unlock(&rb->mutex);
// Process delta (can take time)
process_delta(&delta);
} else {
pthread_mutex_unlock(&rb->mutex);
}
}
return NULL;
}Complete Example
#include <lxdex.h>
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
static volatile int running = 1;
void signal_handler(int sig) {
(void)sig;
running = 0;
}
void on_book(const struct lxdex_orderbook *book, void *user_data) {
(void)user_data;
if (book->bid_count > 0 && book->ask_count > 0) {
double spread = book->asks[0].price - book->bids[0].price;
double mid = (book->bids[0].price + book->asks[0].price) / 2.0;
printf("%s: %.2f / %.2f (spread: %.4f%%)\n",
book->symbol,
book->bids[0].price,
book->asks[0].price,
(spread / mid) * 100);
}
}
void on_trade(const struct lxdex_trade *trade, void *user_data) {
(void)user_data;
printf("[TRADE] %s %.8f @ %.2f\n",
trade->side == LXDEX_SIDE_BUY ? "BUY" : "SELL",
trade->size, trade->price);
}
int main(int argc, char *argv[]) {
const char *symbol = argc > 1 ? argv[1] : "BTC-USD";
signal(SIGINT, signal_handler);
signal(SIGTERM, signal_handler);
if (lxdex_init() != LXDEX_OK) {
return 1;
}
struct lxdex_config config = {
.url = "wss://api.lux.network/ws"
};
struct lxdex_client *client = lxdex_client_new(&config);
if (!client || lxdex_client_connect(client) != LXDEX_OK) {
fprintf(stderr, "Connection failed\n");
lxdex_shutdown();
return 1;
}
printf("Connected. Streaming %s...\n", symbol);
// Subscribe to order book and trades
struct lxdex_subscription *book_sub = lxdex_subscribe_orderbook(
client, symbol, 5, on_book, NULL);
struct lxdex_subscription *trade_sub = lxdex_subscribe_trades(
client, symbol, on_trade, NULL);
if (!book_sub || !trade_sub) {
fprintf(stderr, "Subscription failed\n");
} else {
while (running) {
lxdex_client_poll(client, 100);
}
}
printf("\nShutting down...\n");
if (book_sub) lxdex_unsubscribe(book_sub);
if (trade_sub) lxdex_unsubscribe(trade_sub);
lxdex_client_disconnect(client);
lxdex_client_free(client);
lxdex_shutdown();
return 0;
}