mirror of
https://github.com/luxfi/dex.git
synced 2026-09-24 00:26:31 +00:00
ci/docs/sdk: drop lx-trading-cpp + sdk/cpp refs
Follow-up to 09c17ec which deleted the orphaned C++ trees.
Workflows + docs + sdk/Makefile still referenced them.
.github/workflows/sdk-ci.yml delete test-cpp job + drop from integration-check.needs
.github/workflows/sdk-release.yml delete build-cpp job + drop from generate-checksums.needs
sdk/Makefile delete build-cpp + test-cpp targets, clean ref
sdk/README.md repoint cpp row to ~/work/luxcpp/dex/sdk/trading-cpp/
docs/content/docs/sdk/cpp/ delete (sdk source moved; doc to be re-authored from luxcpp)
Also fixes the stale amm_gpu.go docstring (kernel moved to
~/work/luxcpp/metal/src/shaders/dex/, driver stays in dex/gpu/metal/).
This commit is contained in:
@@ -161,42 +161,10 @@ jobs:
|
||||
- name: Build release
|
||||
run: cargo build --release
|
||||
|
||||
# C++ SDK Tests
|
||||
test-cpp:
|
||||
name: C++ SDK
|
||||
runs-on: ubuntu-latest
|
||||
defaults:
|
||||
run:
|
||||
working-directory: sdk/lx-trading-cpp
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y cmake build-essential libssl-dev libcurl4-openssl-dev
|
||||
|
||||
- name: Configure CMake
|
||||
run: |
|
||||
mkdir -p build
|
||||
cd build
|
||||
cmake .. -DCMAKE_BUILD_TYPE=Release -DLX_TRADING_BUILD_TESTS=ON
|
||||
|
||||
- name: Build
|
||||
run: |
|
||||
cd build
|
||||
make -j$(nproc)
|
||||
|
||||
- name: Run tests
|
||||
run: |
|
||||
cd build
|
||||
./lx_trading_tests
|
||||
|
||||
# Integration test (all SDKs must pass)
|
||||
integration-check:
|
||||
name: Integration Check
|
||||
needs: [test-go, test-typescript, test-python, test-rust, test-cpp]
|
||||
needs: [test-go, test-typescript, test-python, test-rust]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: All SDK tests passed
|
||||
|
||||
@@ -290,74 +290,10 @@ jobs:
|
||||
sdk/lx-trading-core/lx-trading-rust-*.tar.gz
|
||||
sdk/lx-trading-core/lx-trading-rust-*.zip
|
||||
|
||||
# Build C++ SDK
|
||||
build-cpp:
|
||||
name: C++ SDK (${{ matrix.os }})
|
||||
needs: [validate-version, create-release]
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- os: lux-amd64
|
||||
platform: linux-amd64
|
||||
- os: macos-13
|
||||
platform: darwin-amd64
|
||||
- os: macos-14
|
||||
platform: darwin-arm64
|
||||
- os: windows-latest
|
||||
platform: windows-amd64
|
||||
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Install dependencies (Linux)
|
||||
if: matrix.os == 'ubuntu-latest'
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y cmake build-essential libssl-dev libcurl4-openssl-dev
|
||||
|
||||
- name: Install dependencies (macOS)
|
||||
if: startsWith(matrix.os, 'macos')
|
||||
run: |
|
||||
brew install cmake openssl curl
|
||||
|
||||
- name: Configure and Build
|
||||
working-directory: sdk/lx-trading-cpp
|
||||
shell: bash
|
||||
run: |
|
||||
mkdir -p build && cd build
|
||||
cmake .. -DCMAKE_BUILD_TYPE=Release -DLX_TRADING_BUILD_TESTS=OFF
|
||||
cmake --build . --config Release -j
|
||||
|
||||
- name: Package
|
||||
working-directory: sdk/lx-trading-cpp
|
||||
shell: bash
|
||||
run: |
|
||||
mkdir -p dist/lib dist/include
|
||||
cp -r include/* dist/include/
|
||||
if [ "${{ matrix.os }}" = "windows-latest" ]; then
|
||||
cp build/Release/*.lib dist/lib/ 2>/dev/null || cp build/*.lib dist/lib/ 2>/dev/null || true
|
||||
cp build/Release/*.dll dist/lib/ 2>/dev/null || cp build/*.dll dist/lib/ 2>/dev/null || true
|
||||
cd dist && 7z a ../lx-trading-cpp-${{ matrix.platform }}.zip *
|
||||
else
|
||||
cp build/*.a dist/lib/ 2>/dev/null || true
|
||||
cp build/*.so dist/lib/ 2>/dev/null || true
|
||||
cp build/*.dylib dist/lib/ 2>/dev/null || true
|
||||
tar -czf lx-trading-cpp-${{ matrix.platform }}.tar.gz -C dist .
|
||||
fi
|
||||
|
||||
- name: Upload Release Asset
|
||||
uses: softprops/action-gh-release@v2
|
||||
with:
|
||||
tag_name: sdk-v${{ needs.validate-version.outputs.version }}
|
||||
files: |
|
||||
sdk/lx-trading-cpp/lx-trading-cpp-*.tar.gz
|
||||
sdk/lx-trading-cpp/lx-trading-cpp-*.zip
|
||||
|
||||
# Generate checksums
|
||||
generate-checksums:
|
||||
name: Generate Checksums
|
||||
needs: [validate-version, build-go, build-typescript, build-python, build-rust, build-cpp]
|
||||
needs: [validate-version, build-go, build-typescript, build-python, build-rust]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Download all artifacts
|
||||
|
||||
@@ -1,569 +0,0 @@
|
||||
---
|
||||
title: Client
|
||||
description: Connection management, SSL/TLS configuration, and reconnection strategies for the C++ SDK
|
||||
---
|
||||
|
||||
# Client
|
||||
|
||||
The `Client` class manages WebSocket connections to LX, handling authentication, connection pooling, automatic reconnection, and request/response correlation.
|
||||
|
||||
## Connection Architecture
|
||||
|
||||
```
|
||||
+--------------------------------------------------+
|
||||
| Client |
|
||||
| +--------------------------------------------+ |
|
||||
| | Connection Pool | |
|
||||
| | +--------+ +--------+ +--------+ | |
|
||||
| | | Conn 0 | | Conn 1 | | Conn 2 | ... | |
|
||||
| | +--------+ +--------+ +--------+ | |
|
||||
| +--------------------------------------------+ |
|
||||
| | Request Router (Round-Robin) | |
|
||||
| +--------------------------------------------+ |
|
||||
| | Response Correlator (Lock-Free) | |
|
||||
| +--------------------------------------------+
|
||||
+--------------------------------------------------+
|
||||
```
|
||||
|
||||
## Basic Usage
|
||||
|
||||
### Creating a Client
|
||||
|
||||
```cpp
|
||||
#include <lxdex/client.hpp>
|
||||
|
||||
using namespace lxdex;
|
||||
|
||||
// Simple construction
|
||||
Client client("wss://api.lux.network/ws");
|
||||
|
||||
// With options
|
||||
Client client({
|
||||
.endpoints = {
|
||||
"wss://api.lux.network/ws",
|
||||
"wss://api2.lux.network/ws" // Failover
|
||||
},
|
||||
.api_key = "your-api-key",
|
||||
.api_secret = "your-api-secret"
|
||||
});
|
||||
```
|
||||
|
||||
### Connection Lifecycle
|
||||
|
||||
```cpp
|
||||
#include <lxdex/client.hpp>
|
||||
#include <iostream>
|
||||
|
||||
int main() {
|
||||
using namespace lxdex;
|
||||
|
||||
Client client({
|
||||
.endpoints = {"wss://api.lux.network/ws"},
|
||||
.api_key = std::getenv("LX_API_KEY")
|
||||
});
|
||||
|
||||
// Register lifecycle callbacks
|
||||
client.on_connect([](const ConnectionInfo& info) {
|
||||
std::cout << "Connected to " << info.endpoint
|
||||
<< " (latency: " << info.latency_us << "us)\n";
|
||||
});
|
||||
|
||||
client.on_disconnect([](const DisconnectReason& reason) {
|
||||
std::cout << "Disconnected: " << reason.message << '\n';
|
||||
});
|
||||
|
||||
client.on_error([](const Error& error) {
|
||||
std::cerr << "Error: " << error.message << '\n';
|
||||
});
|
||||
|
||||
// Connect with timeout
|
||||
auto result = client.connect(std::chrono::seconds{10});
|
||||
if (!result) {
|
||||
std::cerr << "Connection failed: " << result.error() << '\n';
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Client is now connected and authenticated
|
||||
std::cout << "Session ID: " << client.session_id() << '\n';
|
||||
|
||||
// Graceful shutdown
|
||||
client.disconnect();
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
## Client Configuration
|
||||
|
||||
### Full Configuration Options
|
||||
|
||||
```cpp
|
||||
struct ClientConfig {
|
||||
// Network
|
||||
std::vector<std::string> endpoints;
|
||||
std::string api_key;
|
||||
std::string api_secret;
|
||||
std::optional<std::string> proxy_url;
|
||||
|
||||
// Connection pool
|
||||
std::size_t pool_size = 4;
|
||||
bool use_kernel_bypass = false; // io_uring/DPDK
|
||||
|
||||
// Timeouts
|
||||
std::chrono::milliseconds connect_timeout{5000};
|
||||
std::chrono::milliseconds request_timeout{1000};
|
||||
std::chrono::milliseconds heartbeat_interval{30000};
|
||||
|
||||
// SSL/TLS
|
||||
TlsConfig tls;
|
||||
|
||||
// Reconnection
|
||||
ReconnectConfig reconnect;
|
||||
|
||||
// Memory
|
||||
std::size_t write_buffer_size = 64 * 1024;
|
||||
std::size_t read_buffer_size = 64 * 1024;
|
||||
|
||||
// Threading
|
||||
std::size_t io_threads = 1;
|
||||
std::optional<std::vector<int>> cpu_affinity;
|
||||
};
|
||||
```
|
||||
|
||||
### TLS Configuration
|
||||
|
||||
```cpp
|
||||
struct TlsConfig {
|
||||
bool verify_peer = true;
|
||||
bool verify_host = true;
|
||||
std::string ca_cert_path; // CA certificate file
|
||||
std::string client_cert_path; // Client certificate (mTLS)
|
||||
std::string client_key_path; // Client private key (mTLS)
|
||||
std::string cipher_list; // OpenSSL cipher string
|
||||
TlsVersion min_version = TlsVersion::TLS_1_3;
|
||||
};
|
||||
|
||||
// Example: Production TLS setup
|
||||
Client client({
|
||||
.endpoints = {"wss://api.lux.network/ws"},
|
||||
.tls = {
|
||||
.verify_peer = true,
|
||||
.verify_host = true,
|
||||
.ca_cert_path = "/etc/ssl/certs/ca-certificates.crt",
|
||||
.min_version = TlsVersion::TLS_1_3
|
||||
}
|
||||
});
|
||||
|
||||
// Example: Mutual TLS (mTLS)
|
||||
Client client({
|
||||
.endpoints = {"wss://secure.lux.network/ws"},
|
||||
.tls = {
|
||||
.verify_peer = true,
|
||||
.ca_cert_path = "/path/to/ca.pem",
|
||||
.client_cert_path = "/path/to/client.pem",
|
||||
.client_key_path = "/path/to/client.key"
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
### Reconnection Strategy
|
||||
|
||||
```cpp
|
||||
struct ReconnectConfig {
|
||||
bool enabled = true;
|
||||
std::size_t max_attempts = 10;
|
||||
std::chrono::milliseconds initial_delay{100};
|
||||
std::chrono::milliseconds max_delay{30000};
|
||||
double backoff_multiplier = 2.0;
|
||||
bool jitter = true; // Randomize delays
|
||||
};
|
||||
|
||||
// Example: Aggressive reconnection for HFT
|
||||
Client client({
|
||||
.endpoints = {"wss://api.lux.network/ws"},
|
||||
.reconnect = {
|
||||
.enabled = true,
|
||||
.max_attempts = 100,
|
||||
.initial_delay = std::chrono::milliseconds{10},
|
||||
.max_delay = std::chrono::milliseconds{1000},
|
||||
.backoff_multiplier = 1.5
|
||||
}
|
||||
});
|
||||
|
||||
// Example: Fail-fast for latency-critical paths
|
||||
Client client({
|
||||
.reconnect = {.enabled = false} // Fail immediately
|
||||
});
|
||||
```
|
||||
|
||||
## Connection Pool
|
||||
|
||||
The client maintains a pool of connections for load distribution and redundancy.
|
||||
|
||||
### Pool Configuration
|
||||
|
||||
```cpp
|
||||
Client client({
|
||||
.endpoints = {
|
||||
"wss://api.lux.network/ws",
|
||||
"wss://api2.lux.network/ws"
|
||||
},
|
||||
.pool_size = 8, // 8 connections per endpoint
|
||||
});
|
||||
|
||||
// Query pool status
|
||||
auto status = client.pool_status();
|
||||
std::cout << "Active connections: " << status.active << '\n';
|
||||
std::cout << "Pending requests: " << status.pending_requests << '\n';
|
||||
```
|
||||
|
||||
### Connection Selection
|
||||
|
||||
```cpp
|
||||
// Default: Round-robin
|
||||
client.set_connection_strategy(ConnectionStrategy::RoundRobin);
|
||||
|
||||
// Latency-based: Route to fastest connection
|
||||
client.set_connection_strategy(ConnectionStrategy::LowestLatency);
|
||||
|
||||
// Affinity: Sticky sessions per symbol
|
||||
client.set_connection_strategy(ConnectionStrategy::SymbolAffinity);
|
||||
```
|
||||
|
||||
## Authentication
|
||||
|
||||
### API Key Authentication
|
||||
|
||||
```cpp
|
||||
Client client({
|
||||
.endpoints = {"wss://api.lux.network/ws"},
|
||||
.api_key = "pk_live_...",
|
||||
.api_secret = "sk_live_..."
|
||||
});
|
||||
|
||||
// Authentication happens automatically on connect
|
||||
client.connect();
|
||||
```
|
||||
|
||||
### JWT Authentication
|
||||
|
||||
```cpp
|
||||
Client client({
|
||||
.endpoints = {"wss://api.lux.network/ws"}
|
||||
});
|
||||
|
||||
// Set JWT token (can be refreshed)
|
||||
client.set_auth_token("eyJhbGciOiJIUzI1NiIs...");
|
||||
|
||||
// Refresh token before expiry
|
||||
client.on_token_expiring([&client](std::chrono::seconds remaining) {
|
||||
if (remaining < std::chrono::seconds{60}) {
|
||||
auto new_token = refresh_token(); // Your refresh logic
|
||||
client.set_auth_token(new_token);
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
### Signature-Based Authentication (HFT)
|
||||
|
||||
```cpp
|
||||
#include <lxdex/auth/signer.hpp>
|
||||
|
||||
// Create Ed25519 signer
|
||||
auto signer = Signer::from_private_key("/path/to/private.key");
|
||||
|
||||
Client client({
|
||||
.endpoints = {"wss://api.lux.network/ws"},
|
||||
.signer = std::move(signer)
|
||||
});
|
||||
|
||||
// Each request is automatically signed
|
||||
// Signature: Ed25519(timestamp || nonce || body)
|
||||
```
|
||||
|
||||
## Request/Response
|
||||
|
||||
### Synchronous Requests
|
||||
|
||||
```cpp
|
||||
// Blocking call with timeout
|
||||
auto result = client.request(
|
||||
"lx_getOrderBook",
|
||||
{{"symbol", "BTC-USD"}, {"depth", 10}},
|
||||
std::chrono::milliseconds{100}
|
||||
);
|
||||
|
||||
if (result) {
|
||||
auto& book = *result;
|
||||
std::cout << "Best bid: " << book["bids"][0]["price"] << '\n';
|
||||
}
|
||||
```
|
||||
|
||||
### Asynchronous Requests
|
||||
|
||||
```cpp
|
||||
// Future-based
|
||||
auto future = client.request_async(
|
||||
"lx_placeOrder",
|
||||
{{"symbol", "BTC-USD"}, {"side", "buy"}, {"price", 50000}}
|
||||
);
|
||||
|
||||
// Non-blocking check
|
||||
if (future.wait_for(std::chrono::microseconds{10}) == std::future_status::ready) {
|
||||
auto result = future.get();
|
||||
// Process...
|
||||
}
|
||||
```
|
||||
|
||||
### Callback-Based (Zero Allocation)
|
||||
|
||||
```cpp
|
||||
// Pre-allocate response buffer
|
||||
alignas(64) std::array<char, 4096> buffer;
|
||||
|
||||
client.request_callback(
|
||||
"lx_getOrder",
|
||||
{{"orderId", order_id}},
|
||||
[&buffer](std::span<const char> response) {
|
||||
// Response arrives here on I/O thread
|
||||
// Zero-copy: response points directly to network buffer
|
||||
std::memcpy(buffer.data(), response.data(), response.size());
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
## Subscriptions
|
||||
|
||||
### Market Data Streams
|
||||
|
||||
```cpp
|
||||
// Order book subscription
|
||||
auto sub = client.subscribe("orderbook", {{"symbol", "BTC-USD"}},
|
||||
[](const json& update) {
|
||||
// Called on I/O thread
|
||||
}
|
||||
);
|
||||
|
||||
// Unsubscribe
|
||||
sub.cancel();
|
||||
|
||||
// Or use RAII
|
||||
{
|
||||
auto sub = client.subscribe(...);
|
||||
// Auto-unsubscribe when sub goes out of scope
|
||||
}
|
||||
```
|
||||
|
||||
### Multiple Subscriptions
|
||||
|
||||
```cpp
|
||||
// Subscribe to multiple symbols
|
||||
client.subscribe_batch({
|
||||
{"orderbook", {{"symbol", "BTC-USD"}}},
|
||||
{"orderbook", {{"symbol", "ETH-USD"}}},
|
||||
{"trades", {{"symbol", "BTC-USD"}}}
|
||||
}, [](const std::string& channel, const json& data) {
|
||||
if (channel == "orderbook") {
|
||||
// Handle order book
|
||||
} else if (channel == "trades") {
|
||||
// Handle trades
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
## Error Handling
|
||||
|
||||
### Error Types
|
||||
|
||||
```cpp
|
||||
enum class ErrorCode {
|
||||
// Connection errors
|
||||
ConnectionFailed,
|
||||
ConnectionLost,
|
||||
ConnectionTimeout,
|
||||
TlsError,
|
||||
|
||||
// Authentication errors
|
||||
AuthenticationFailed,
|
||||
InvalidCredentials,
|
||||
TokenExpired,
|
||||
|
||||
// Request errors
|
||||
InvalidRequest,
|
||||
MethodNotFound,
|
||||
RateLimited,
|
||||
InsufficientBalance,
|
||||
|
||||
// Internal errors
|
||||
InternalError,
|
||||
Timeout
|
||||
};
|
||||
|
||||
struct Error {
|
||||
ErrorCode code;
|
||||
std::string message;
|
||||
std::optional<int> rpc_code;
|
||||
std::chrono::system_clock::time_point timestamp;
|
||||
};
|
||||
```
|
||||
|
||||
### Error Handling Patterns
|
||||
|
||||
```cpp
|
||||
// Using std::expected (C++23)
|
||||
auto result = client.place_order(order);
|
||||
if (!result) {
|
||||
handle_error(result.error());
|
||||
return;
|
||||
}
|
||||
auto& placed_order = *result;
|
||||
|
||||
// Exception mode (opt-in)
|
||||
client.enable_exceptions(true);
|
||||
try {
|
||||
auto order = client.place_order_throwing(order);
|
||||
} catch (const lxdex::ConnectionError& e) {
|
||||
// Handle connection issues
|
||||
} catch (const lxdex::RateLimitError& e) {
|
||||
std::this_thread::sleep_for(e.retry_after);
|
||||
}
|
||||
```
|
||||
|
||||
## Metrics and Monitoring
|
||||
|
||||
```cpp
|
||||
// Get connection metrics
|
||||
auto metrics = client.metrics();
|
||||
|
||||
std::cout << "Messages sent: " << metrics.messages_sent << '\n';
|
||||
std::cout << "Messages received: " << metrics.messages_received << '\n';
|
||||
std::cout << "Bytes sent: " << metrics.bytes_sent << '\n';
|
||||
std::cout << "Bytes received: " << metrics.bytes_received << '\n';
|
||||
std::cout << "Latency p50: " << metrics.latency_p50_us << "us\n";
|
||||
std::cout << "Latency p99: " << metrics.latency_p99_us << "us\n";
|
||||
std::cout << "Reconnections: " << metrics.reconnection_count << '\n';
|
||||
|
||||
// Reset metrics
|
||||
client.reset_metrics();
|
||||
|
||||
// Prometheus export
|
||||
std::cout << client.metrics_prometheus();
|
||||
```
|
||||
|
||||
## RAII and Lifetime
|
||||
|
||||
```cpp
|
||||
class TradingEngine {
|
||||
lxdex::Client client_;
|
||||
std::unique_ptr<lxdex::Subscription> book_sub_;
|
||||
|
||||
public:
|
||||
TradingEngine(const Config& cfg)
|
||||
: client_({
|
||||
.endpoints = {cfg.endpoint},
|
||||
.api_key = cfg.api_key
|
||||
})
|
||||
{
|
||||
client_.connect();
|
||||
|
||||
book_sub_ = std::make_unique<lxdex::Subscription>(
|
||||
client_.subscribe("orderbook", {{"symbol", "BTC-USD"}},
|
||||
[this](const json& update) { on_book_update(update); }
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
~TradingEngine() {
|
||||
// RAII: Subscriptions auto-cancel, connections auto-close
|
||||
// Explicit shutdown for graceful disconnect
|
||||
client_.disconnect();
|
||||
}
|
||||
|
||||
private:
|
||||
void on_book_update(const json& update);
|
||||
};
|
||||
```
|
||||
|
||||
## Thread Safety
|
||||
|
||||
| Method | Thread Safety |
|
||||
|--------|---------------|
|
||||
| `connect()` | Call from single thread |
|
||||
| `disconnect()` | Call from single thread |
|
||||
| `request()` | Thread-safe |
|
||||
| `request_async()` | Thread-safe |
|
||||
| `subscribe()` | Thread-safe |
|
||||
| `metrics()` | Thread-safe (atomic reads) |
|
||||
| Callbacks | Invoked on I/O thread |
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Reuse clients**: Create once, use for application lifetime
|
||||
2. **Use connection pool**: Set `pool_size` based on request volume
|
||||
3. **Handle reconnection**: Always register `on_disconnect` callback
|
||||
4. **Prefer async**: Use `request_async` for non-blocking operations
|
||||
5. **Pin I/O threads**: Set `cpu_affinity` for deterministic latency
|
||||
6. **Monitor metrics**: Track latency and error rates
|
||||
|
||||
## Example: Production Client Setup
|
||||
|
||||
```cpp
|
||||
#include <lxdex/client.hpp>
|
||||
#include <csignal>
|
||||
#include <atomic>
|
||||
|
||||
std::atomic<bool> running{true};
|
||||
|
||||
void signal_handler(int) {
|
||||
running.store(false, std::memory_order_release);
|
||||
}
|
||||
|
||||
int main() {
|
||||
std::signal(SIGINT, signal_handler);
|
||||
std::signal(SIGTERM, signal_handler);
|
||||
|
||||
using namespace lxdex;
|
||||
|
||||
Client client({
|
||||
.endpoints = {
|
||||
"wss://api.lux.network/ws",
|
||||
"wss://api2.lux.network/ws"
|
||||
},
|
||||
.api_key = std::getenv("LX_API_KEY"),
|
||||
.api_secret = std::getenv("LX_API_SECRET"),
|
||||
.pool_size = 4,
|
||||
.tls = {
|
||||
.verify_peer = true,
|
||||
.min_version = TlsVersion::TLS_1_3
|
||||
},
|
||||
.reconnect = {
|
||||
.enabled = true,
|
||||
.max_attempts = 50
|
||||
},
|
||||
.io_threads = 2,
|
||||
.cpu_affinity = {0, 1}
|
||||
});
|
||||
|
||||
client.on_connect([](const ConnectionInfo& info) {
|
||||
spdlog::info("Connected to {} ({}us)", info.endpoint, info.latency_us);
|
||||
});
|
||||
|
||||
client.on_disconnect([](const DisconnectReason& reason) {
|
||||
spdlog::warn("Disconnected: {}", reason.message);
|
||||
});
|
||||
|
||||
if (auto result = client.connect(); !result) {
|
||||
spdlog::error("Failed to connect: {}", result.error().message);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Main loop
|
||||
while (running.load(std::memory_order_acquire)) {
|
||||
// Trading logic...
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds{1});
|
||||
}
|
||||
|
||||
spdlog::info("Shutting down...");
|
||||
client.disconnect();
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
@@ -1,378 +0,0 @@
|
||||
---
|
||||
title: C++ SDK
|
||||
description: High-performance C++ SDK for LX - HFT-grade trading with sub-microsecond latency
|
||||
---
|
||||
|
||||
# C++ SDK
|
||||
|
||||
> **Specification**: [LP-9002 DEX API & RPC](https://github.com/luxfi/LPs/blob/main/LPs/lp-9002-dex-api-rpc-specification.md) | **Source**: [luxfi/dex-cpp](https://github.com/luxfi/dex/tree/main/sdk/cpp)
|
||||
|
||||
The LX C++ SDK is designed for high-frequency trading (HFT) applications requiring sub-microsecond latency and deterministic performance. Built with modern C++20 features, lock-free data structures, and zero-copy networking.
|
||||
|
||||
## Features
|
||||
|
||||
| Feature | Description |
|
||||
|---------|-------------|
|
||||
| **Sub-microsecond latency** | 597ns order placement, lock-free hot paths |
|
||||
| **Zero-copy networking** | io_uring on Linux, kqueue on macOS |
|
||||
| **Lock-free order book** | Wait-free reads, obstruction-free writes |
|
||||
| **SIMD price matching** | AVX-512/NEON vectorized comparisons |
|
||||
| **Memory pools** | Pre-allocated arenas, no heap allocation on hot path |
|
||||
| **Header-only option** | Single `lxdex.hpp` for rapid integration |
|
||||
|
||||
## Requirements
|
||||
|
||||
- **Compiler**: GCC 12+, Clang 15+, or MSVC 19.35+
|
||||
- **C++ Standard**: C++20 (C++17 compatibility mode available)
|
||||
- **CMake**: 3.21+
|
||||
- **Dependencies**: OpenSSL 3.0+, Boost 1.83+ (optional)
|
||||
|
||||
## Installation
|
||||
|
||||
### vcpkg (Recommended)
|
||||
|
||||
```bash
|
||||
# Add LX overlay
|
||||
vcpkg add overlay-port https://github.com/luxfi/vcpkg-registry
|
||||
|
||||
# Install the SDK
|
||||
vcpkg install lxdex-cpp
|
||||
```
|
||||
|
||||
### CMake FetchContent
|
||||
|
||||
```cmake
|
||||
include(FetchContent)
|
||||
|
||||
FetchContent_Declare(
|
||||
lxdex
|
||||
GIT_REPOSITORY https://github.com/luxfi/dex.git
|
||||
GIT_TAG v1.0.0
|
||||
SOURCE_SUBDIR sdk/cpp
|
||||
)
|
||||
|
||||
FetchContent_MakeAvailable(lxdex)
|
||||
|
||||
target_link_libraries(your_app PRIVATE lxdex::lxdex)
|
||||
```
|
||||
|
||||
### Header-Only Mode
|
||||
|
||||
```cpp
|
||||
#define LXDEX_HEADER_ONLY
|
||||
#include <lxdex/lxdex.hpp>
|
||||
```
|
||||
|
||||
### Manual Build
|
||||
|
||||
```bash
|
||||
git clone https://github.com/luxfi/dex.git
|
||||
cd dex/sdk/cpp
|
||||
|
||||
mkdir build && cd build
|
||||
cmake -DCMAKE_BUILD_TYPE=Release \
|
||||
-DLXDEX_BUILD_TESTS=ON \
|
||||
-DLXDEX_BUILD_BENCHMARKS=ON \
|
||||
..
|
||||
cmake --build . -j$(nproc)
|
||||
sudo cmake --install .
|
||||
```
|
||||
|
||||
## CMakeLists.txt Examples
|
||||
|
||||
### Minimal Project
|
||||
|
||||
```cmake
|
||||
cmake_minimum_required(VERSION 3.21)
|
||||
project(my_trading_bot LANGUAGES CXX)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 20)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
|
||||
find_package(lxdex REQUIRED)
|
||||
|
||||
add_executable(trading_bot main.cpp)
|
||||
target_link_libraries(trading_bot PRIVATE lxdex::lxdex)
|
||||
```
|
||||
|
||||
### HFT Production Build
|
||||
|
||||
```cmake
|
||||
cmake_minimum_required(VERSION 3.21)
|
||||
project(hft_engine LANGUAGES CXX)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 20)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
|
||||
# HFT-grade compiler flags
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "-O3 -march=native -mtune=native -flto=auto")
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -fno-rtti -fno-exceptions")
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -ffast-math -funroll-loops")
|
||||
|
||||
# Link-time optimization
|
||||
set(CMAKE_INTERPROCEDURAL_OPTIMIZATION_RELEASE ON)
|
||||
|
||||
# Prefer static linking for deterministic deployment
|
||||
set(BUILD_SHARED_LIBS OFF)
|
||||
|
||||
find_package(lxdex REQUIRED)
|
||||
find_package(OpenSSL REQUIRED)
|
||||
|
||||
add_executable(hft_engine
|
||||
src/main.cpp
|
||||
src/strategy.cpp
|
||||
src/risk.cpp
|
||||
)
|
||||
|
||||
target_link_libraries(hft_engine PRIVATE
|
||||
lxdex::lxdex
|
||||
lxdex::lockfree
|
||||
OpenSSL::SSL
|
||||
OpenSSL::Crypto
|
||||
)
|
||||
|
||||
# CPU affinity and huge pages support
|
||||
target_compile_definitions(hft_engine PRIVATE
|
||||
LXDEX_USE_HUGE_PAGES
|
||||
LXDEX_CPU_AFFINITY
|
||||
LXDEX_DISABLE_LOGGING # No logging on hot path
|
||||
)
|
||||
```
|
||||
|
||||
### vcpkg Integration
|
||||
|
||||
Create `vcpkg.json` in your project root:
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "my-trading-app",
|
||||
"version": "1.0.0",
|
||||
"dependencies": [
|
||||
"lxdex-cpp",
|
||||
"openssl",
|
||||
{
|
||||
"name": "boost-lockfree",
|
||||
"features": ["spsc-queue"]
|
||||
}
|
||||
],
|
||||
"builtin-baseline": "2024.01.12"
|
||||
}
|
||||
```
|
||||
|
||||
Then in CMakeLists.txt:
|
||||
|
||||
```cmake
|
||||
cmake_minimum_required(VERSION 3.21)
|
||||
|
||||
# vcpkg toolchain (before project())
|
||||
if(DEFINED ENV{VCPKG_ROOT})
|
||||
set(CMAKE_TOOLCHAIN_FILE "$ENV{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake")
|
||||
endif()
|
||||
|
||||
project(my_trading_app LANGUAGES CXX)
|
||||
|
||||
find_package(lxdex CONFIG REQUIRED)
|
||||
target_link_libraries(my_app PRIVATE lxdex::lxdex)
|
||||
```
|
||||
|
||||
## Quick Start
|
||||
|
||||
### Connect and Place Order
|
||||
|
||||
```cpp
|
||||
#include <lxdex/client.hpp>
|
||||
#include <lxdex/order.hpp>
|
||||
#include <iostream>
|
||||
|
||||
int main() {
|
||||
using namespace lxdex;
|
||||
|
||||
// Create client with connection pool
|
||||
Client client({
|
||||
.endpoints = {"wss://api.lux.network/ws"},
|
||||
.api_key = std::getenv("LX_API_KEY"),
|
||||
.pool_size = 4,
|
||||
.use_kernel_bypass = true // DPDK/io_uring
|
||||
});
|
||||
|
||||
// Connect with timeout
|
||||
if (auto result = client.connect(std::chrono::seconds{5}); !result) {
|
||||
std::cerr << "Connection failed: " << result.error() << '\n';
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Place limit order (non-blocking)
|
||||
auto order_future = client.place_order({
|
||||
.symbol = "BTC-USD",
|
||||
.side = Side::Buy,
|
||||
.type = OrderType::Limit,
|
||||
.price = Price{50000'00}, // Fixed-point: $50,000.00
|
||||
.quantity = Quantity{1'00000000}, // 1.0 BTC (8 decimals)
|
||||
.time_in_force = TimeInForce::GTC
|
||||
});
|
||||
|
||||
// Non-blocking check
|
||||
if (order_future.wait_for(std::chrono::microseconds{100}) == std::future_status::ready) {
|
||||
auto order = order_future.get();
|
||||
std::cout << "Order ID: " << order.id << '\n';
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
### Subscribe to Market Data
|
||||
|
||||
```cpp
|
||||
#include <lxdex/client.hpp>
|
||||
#include <lxdex/orderbook.hpp>
|
||||
|
||||
int main() {
|
||||
using namespace lxdex;
|
||||
|
||||
Client client({"wss://api.lux.network/ws"});
|
||||
client.connect();
|
||||
|
||||
// Lock-free order book subscription
|
||||
client.subscribe_orderbook("BTC-USD", [](const OrderBook& book) {
|
||||
// Called on dedicated I/O thread - keep fast!
|
||||
auto [bid, ask] = book.best_bid_ask();
|
||||
|
||||
if (bid && ask) {
|
||||
auto spread = ask->price - bid->price;
|
||||
// Process spread...
|
||||
}
|
||||
});
|
||||
|
||||
// Keep running
|
||||
std::this_thread::sleep_for(std::chrono::hours{24});
|
||||
}
|
||||
```
|
||||
|
||||
## Architecture Overview
|
||||
|
||||
```
|
||||
+--------------------------------------------------+
|
||||
| Application |
|
||||
+--------------------------------------------------+
|
||||
| Client | OrderBook | TradeHandler |
|
||||
+--------------------------------------------------+
|
||||
| Connection Pool (io_uring) |
|
||||
+--------------------------------------------------+
|
||||
| Lock-Free | Memory | SIMD |
|
||||
| Queues | Pools | Price Match |
|
||||
+--------------------------------------------------+
|
||||
| OpenSSL / BoringSSL |
|
||||
+--------------------------------------------------+
|
||||
```
|
||||
|
||||
## SDK Components
|
||||
|
||||
| Component | Header | Description |
|
||||
|-----------|--------|-------------|
|
||||
| `Client` | `<lxdex/client.hpp>` | Connection management, request/response |
|
||||
| `OrderBook` | `<lxdex/orderbook.hpp>` | Lock-free order book with SIMD matching |
|
||||
| `Order` | `<lxdex/order.hpp>` | Order types, builders, validation |
|
||||
| `Trade` | `<lxdex/trade.hpp>` | Trade events, callbacks |
|
||||
| `Pool` | `<lxdex/memory/pool.hpp>` | Memory pools, arenas |
|
||||
| `Lockfree` | `<lxdex/lockfree/spsc.hpp>` | SPSC/MPSC queues |
|
||||
|
||||
## Configuration Options
|
||||
|
||||
### Compile-Time Options
|
||||
|
||||
```cmake
|
||||
# Enable/disable features
|
||||
option(LXDEX_HEADER_ONLY "Header-only mode" OFF)
|
||||
option(LXDEX_USE_BOOST "Use Boost.Asio instead of standalone" OFF)
|
||||
option(LXDEX_USE_IOURING "Use io_uring (Linux 5.1+)" ON)
|
||||
option(LXDEX_USE_SIMD "Enable SIMD optimizations" ON)
|
||||
option(LXDEX_BUILD_TESTS "Build unit tests" OFF)
|
||||
option(LXDEX_BUILD_BENCHMARKS "Build benchmarks" OFF)
|
||||
```
|
||||
|
||||
### Runtime Configuration
|
||||
|
||||
```cpp
|
||||
lxdex::Config config{
|
||||
.network = {
|
||||
.connect_timeout_ms = 5000,
|
||||
.read_timeout_ms = 1000,
|
||||
.write_buffer_size = 64 * 1024,
|
||||
.read_buffer_size = 64 * 1024,
|
||||
.tcp_nodelay = true,
|
||||
.tcp_quickack = true,
|
||||
.so_busy_poll = 50, // microseconds
|
||||
},
|
||||
.memory = {
|
||||
.use_huge_pages = true,
|
||||
.arena_size = 1024 * 1024, // 1MB
|
||||
.order_pool_size = 100000,
|
||||
},
|
||||
.threading = {
|
||||
.io_threads = 2,
|
||||
.cpu_affinity = {0, 1}, // Pin to cores 0,1
|
||||
.realtime_priority = true,
|
||||
}
|
||||
};
|
||||
|
||||
Client client(config);
|
||||
```
|
||||
|
||||
## Benchmarks
|
||||
|
||||
Measured on AMD EPYC 7763, single-threaded, kernel bypass enabled:
|
||||
|
||||
| Operation | Latency (p50) | Latency (p99) | Throughput |
|
||||
|-----------|---------------|---------------|------------|
|
||||
| Order Place | 597ns | 1.2us | 1.67M/s |
|
||||
| Order Cancel | 312ns | 890ns | 3.2M/s |
|
||||
| Book Update | 89ns | 210ns | 11.2M/s |
|
||||
| Trade Callback | 45ns | 120ns | 22.2M/s |
|
||||
|
||||
## Thread Safety
|
||||
|
||||
- **Client**: Thread-safe, uses internal connection pool
|
||||
- **OrderBook**: Lock-free reads, single-writer updates
|
||||
- **Order**: Immutable after construction
|
||||
- **Callbacks**: Invoked on dedicated I/O threads
|
||||
|
||||
See [Threading Guide](/docs/sdk/cpp/threading) for detailed concurrency patterns.
|
||||
|
||||
## Error Handling
|
||||
|
||||
The SDK uses `std::expected` (C++23) or a backport for C++20:
|
||||
|
||||
```cpp
|
||||
auto result = client.place_order(order);
|
||||
|
||||
if (result) {
|
||||
std::cout << "Order ID: " << result->id << '\n';
|
||||
} else {
|
||||
switch (result.error().code) {
|
||||
case ErrorCode::InsufficientBalance:
|
||||
// Handle...
|
||||
break;
|
||||
case ErrorCode::RateLimited:
|
||||
// Backoff...
|
||||
break;
|
||||
default:
|
||||
std::cerr << result.error().message << '\n';
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Next Steps
|
||||
|
||||
- [Client Guide](/docs/sdk/cpp/client) - Connection management and SSL/TLS
|
||||
- [Orders Guide](/docs/sdk/cpp/orders) - Order operations and types
|
||||
- [OrderBook Guide](/docs/sdk/cpp/orderbook) - Lock-free data structures
|
||||
- [Memory Guide](/docs/sdk/cpp/memory) - RAII and memory pools
|
||||
- [Threading Guide](/docs/sdk/cpp/threading) - Concurrency patterns
|
||||
- [Performance Guide](/docs/sdk/cpp/performance) - Low-latency optimization
|
||||
|
||||
## Support
|
||||
|
||||
- **GitHub Issues**: [luxfi/dex/issues](https://github.com/luxfi/dex/issues)
|
||||
- **Discord**: [discord.gg/luxfi](https://discord.gg/luxfi) `#cpp-sdk`
|
||||
- **API Status**: [status.lux.network](https://status.lux.network)
|
||||
@@ -1,9 +0,0 @@
|
||||
{
|
||||
"title": "C++ SDK",
|
||||
"pages": [
|
||||
"index",
|
||||
"client",
|
||||
"orders",
|
||||
"orderbook"
|
||||
]
|
||||
}
|
||||
@@ -1,587 +0,0 @@
|
||||
---
|
||||
title: OrderBook
|
||||
description: Lock-free order book data structures, SIMD price matching, and real-time updates
|
||||
---
|
||||
|
||||
# OrderBook
|
||||
|
||||
The `OrderBook` class provides a lock-free, cache-optimized representation of market depth with wait-free reads and obstruction-free writes, designed for HFT applications requiring sub-microsecond access.
|
||||
|
||||
## Architecture
|
||||
|
||||
```
|
||||
+----------------------------------------------------------+
|
||||
| OrderBook |
|
||||
| +------------------------+ +------------------------+ |
|
||||
| | Bid Side | | Ask Side | |
|
||||
| | (Sorted Descending) | | (Sorted Ascending) | |
|
||||
| | +------------------+ | | +------------------+ | |
|
||||
| | | Level 0 (Best) | | | | Level 0 (Best) | | |
|
||||
| | | Price: 50000.00 | | | | Price: 50000.01 | | |
|
||||
| | | Qty: 10.5 | | | | Qty: 8.2 | | |
|
||||
| | | Orders: 15 | | | | Orders: 12 | | |
|
||||
| | +------------------+ | | +------------------+ | |
|
||||
| | | Level 1 | | | | Level 1 | | |
|
||||
| | | Price: 49999.99 | | | | Price: 50000.02 | | |
|
||||
| | +------------------+ | | +------------------+ | |
|
||||
| | | ... | | | | ... | | |
|
||||
| +------------------------+ +------------------------+ |
|
||||
+----------------------------------------------------------+
|
||||
```
|
||||
|
||||
## Basic Usage
|
||||
|
||||
### Creating an OrderBook
|
||||
|
||||
```cpp
|
||||
#include <lxdex/orderbook.hpp>
|
||||
|
||||
using namespace lxdex;
|
||||
|
||||
// Create order book with default capacity
|
||||
OrderBook book("BTC-USD");
|
||||
|
||||
// Create with specified depth
|
||||
OrderBook book("BTC-USD", {
|
||||
.max_levels = 1000,
|
||||
.use_simd = true,
|
||||
.cache_aligned = true
|
||||
});
|
||||
```
|
||||
|
||||
### Subscribing to Order Book Updates
|
||||
|
||||
```cpp
|
||||
Client client({.endpoints = {"wss://api.lux.network/ws"}});
|
||||
client.connect();
|
||||
|
||||
// Subscribe with callback
|
||||
client.subscribe_orderbook("BTC-USD", [](const OrderBook& book) {
|
||||
// Called on I/O thread with lock-free read access
|
||||
auto [bid, ask] = book.best_bid_ask();
|
||||
|
||||
if (bid && ask) {
|
||||
auto spread = ask->price - bid->price;
|
||||
auto mid = (bid->price + ask->price) / 2;
|
||||
|
||||
std::cout << "Spread: " << spread.to_string()
|
||||
<< " Mid: " << mid.to_string() << '\n';
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
### Accessing Book Data
|
||||
|
||||
```cpp
|
||||
// Best bid/ask (O(1))
|
||||
auto [bid, ask] = book.best_bid_ask();
|
||||
|
||||
// Specific level (O(1))
|
||||
auto level = book.bid_level(5); // 5th best bid
|
||||
|
||||
// All bids/asks (O(n))
|
||||
for (const auto& level : book.bids()) {
|
||||
std::cout << level.price.to_string() << " x "
|
||||
<< level.quantity.to_string() << '\n';
|
||||
}
|
||||
|
||||
// Top N levels
|
||||
auto top_bids = book.top_bids(10);
|
||||
auto top_asks = book.top_asks(10);
|
||||
```
|
||||
|
||||
## Price Level Structure
|
||||
|
||||
```cpp
|
||||
namespace lxdex {
|
||||
|
||||
struct PriceLevel {
|
||||
Price price;
|
||||
Quantity quantity;
|
||||
uint32_t order_count;
|
||||
Timestamp last_update;
|
||||
|
||||
// Calculated fields
|
||||
[[nodiscard]] Price notional() const noexcept {
|
||||
return price * quantity;
|
||||
}
|
||||
};
|
||||
|
||||
// Cache-line aligned for performance
|
||||
static_assert(sizeof(PriceLevel) <= 64);
|
||||
static_assert(alignof(PriceLevel) == 64);
|
||||
|
||||
} // namespace lxdex
|
||||
```
|
||||
|
||||
## Lock-Free Implementation
|
||||
|
||||
### Wait-Free Reads
|
||||
|
||||
```cpp
|
||||
// Multiple reader threads can access simultaneously
|
||||
// No locks, no contention, deterministic latency
|
||||
|
||||
std::thread reader1([&book] {
|
||||
while (running) {
|
||||
auto bid = book.best_bid(); // Wait-free O(1)
|
||||
process_bid(bid);
|
||||
}
|
||||
});
|
||||
|
||||
std::thread reader2([&book] {
|
||||
while (running) {
|
||||
auto spread = book.spread(); // Wait-free O(1)
|
||||
process_spread(spread);
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
### Obstruction-Free Writes
|
||||
|
||||
```cpp
|
||||
// Single writer thread applies updates
|
||||
// Uses atomic compare-and-swap for consistency
|
||||
|
||||
void apply_update(OrderBook& book, const BookUpdate& update) {
|
||||
switch (update.type) {
|
||||
case UpdateType::Set:
|
||||
book.set_level(update.side, update.price, update.quantity);
|
||||
break;
|
||||
case UpdateType::Delete:
|
||||
book.delete_level(update.side, update.price);
|
||||
break;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Memory Ordering
|
||||
|
||||
```cpp
|
||||
// SeqLock pattern for consistent snapshots
|
||||
class OrderBook {
|
||||
mutable std::atomic<uint64_t> sequence_{0};
|
||||
std::array<PriceLevel, MAX_LEVELS> bids_;
|
||||
std::array<PriceLevel, MAX_LEVELS> asks_;
|
||||
|
||||
public:
|
||||
// Reader: retry until consistent read
|
||||
std::optional<PriceLevel> best_bid() const noexcept {
|
||||
PriceLevel level;
|
||||
uint64_t seq1, seq2;
|
||||
|
||||
do {
|
||||
seq1 = sequence_.load(std::memory_order_acquire);
|
||||
if (seq1 & 1) continue; // Write in progress
|
||||
|
||||
level = bids_[0]; // Read
|
||||
|
||||
std::atomic_thread_fence(std::memory_order_acquire);
|
||||
seq2 = sequence_.load(std::memory_order_relaxed);
|
||||
} while (seq1 != seq2);
|
||||
|
||||
if (level.quantity.raw() == 0) return std::nullopt;
|
||||
return level;
|
||||
}
|
||||
|
||||
// Writer: increment sequence before and after
|
||||
void set_level(Side side, Price price, Quantity qty) noexcept {
|
||||
sequence_.fetch_add(1, std::memory_order_release); // Odd = writing
|
||||
std::atomic_thread_fence(std::memory_order_release);
|
||||
|
||||
// Perform update...
|
||||
|
||||
std::atomic_thread_fence(std::memory_order_release);
|
||||
sequence_.fetch_add(1, std::memory_order_release); // Even = done
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
## SIMD Price Matching
|
||||
|
||||
### AVX-512 Implementation
|
||||
|
||||
```cpp
|
||||
#include <lxdex/simd/avx512.hpp>
|
||||
|
||||
// Find price level using SIMD (8 prices per comparison)
|
||||
std::optional<size_t> find_price_level_avx512(
|
||||
const std::array<int64_t, 1024>& prices,
|
||||
int64_t target_price,
|
||||
size_t count
|
||||
) {
|
||||
__m512i target = _mm512_set1_epi64(target_price);
|
||||
|
||||
for (size_t i = 0; i < count; i += 8) {
|
||||
__m512i prices_vec = _mm512_load_epi64(&prices[i]);
|
||||
__mmask8 mask = _mm512_cmpeq_epi64_mask(prices_vec, target);
|
||||
|
||||
if (mask != 0) {
|
||||
return i + __builtin_ctz(mask);
|
||||
}
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
```
|
||||
|
||||
### NEON Implementation (ARM)
|
||||
|
||||
```cpp
|
||||
#include <lxdex/simd/neon.hpp>
|
||||
|
||||
// Find price level using NEON (2 prices per comparison)
|
||||
std::optional<size_t> find_price_level_neon(
|
||||
const std::array<int64_t, 1024>& prices,
|
||||
int64_t target_price,
|
||||
size_t count
|
||||
) {
|
||||
int64x2_t target = vdupq_n_s64(target_price);
|
||||
|
||||
for (size_t i = 0; i < count; i += 2) {
|
||||
int64x2_t prices_vec = vld1q_s64(&prices[i]);
|
||||
uint64x2_t cmp = vceqq_s64(prices_vec, target);
|
||||
|
||||
if (vgetq_lane_u64(cmp, 0)) return i;
|
||||
if (vgetq_lane_u64(cmp, 1)) return i + 1;
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
```
|
||||
|
||||
### Automatic Dispatch
|
||||
|
||||
```cpp
|
||||
// Runtime SIMD detection
|
||||
OrderBook book("BTC-USD", {
|
||||
.use_simd = true, // Auto-detect best SIMD
|
||||
.simd_hint = SimdHint::Auto // Or: AVX512, AVX2, NEON, Scalar
|
||||
});
|
||||
|
||||
// Manual dispatch
|
||||
#if defined(__AVX512F__)
|
||||
auto idx = find_price_level_avx512(prices, target, count);
|
||||
#elif defined(__AVX2__)
|
||||
auto idx = find_price_level_avx2(prices, target, count);
|
||||
#elif defined(__ARM_NEON)
|
||||
auto idx = find_price_level_neon(prices, target, count);
|
||||
#else
|
||||
auto idx = find_price_level_scalar(prices, target, count);
|
||||
#endif
|
||||
```
|
||||
|
||||
## Book Snapshots
|
||||
|
||||
### Full Snapshot
|
||||
|
||||
```cpp
|
||||
// Get immutable snapshot (copy)
|
||||
auto snapshot = book.snapshot();
|
||||
|
||||
// Process snapshot on different thread
|
||||
std::thread processor([snapshot = std::move(snapshot)] {
|
||||
for (const auto& level : snapshot.bids()) {
|
||||
analyze_level(level);
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
### Incremental Updates
|
||||
|
||||
```cpp
|
||||
// Subscribe to incremental updates
|
||||
client.subscribe_orderbook_incremental("BTC-USD",
|
||||
// Snapshot callback (called first)
|
||||
[&book](const OrderBookSnapshot& snap) {
|
||||
book.apply_snapshot(snap);
|
||||
},
|
||||
// Delta callback (called for each update)
|
||||
[&book](const OrderBookDelta& delta) {
|
||||
book.apply_delta(delta);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
### Delta Structure
|
||||
|
||||
```cpp
|
||||
struct OrderBookDelta {
|
||||
std::string symbol;
|
||||
uint64_t sequence;
|
||||
Timestamp timestamp;
|
||||
|
||||
struct Change {
|
||||
Side side;
|
||||
Price price;
|
||||
Quantity quantity; // 0 = delete level
|
||||
};
|
||||
|
||||
std::vector<Change> changes;
|
||||
};
|
||||
|
||||
// Apply delta
|
||||
void OrderBook::apply_delta(const OrderBookDelta& delta) {
|
||||
if (delta.sequence != expected_sequence_) {
|
||||
// Gap detected - request full snapshot
|
||||
return;
|
||||
}
|
||||
|
||||
for (const auto& change : delta.changes) {
|
||||
if (change.quantity.raw() == 0) {
|
||||
delete_level(change.side, change.price);
|
||||
} else {
|
||||
set_level(change.side, change.price, change.quantity);
|
||||
}
|
||||
}
|
||||
|
||||
++expected_sequence_;
|
||||
}
|
||||
```
|
||||
|
||||
## Market Metrics
|
||||
|
||||
### Spread and Mid-Price
|
||||
|
||||
```cpp
|
||||
// Spread (O(1))
|
||||
auto spread = book.spread();
|
||||
std::cout << "Spread: " << spread.to_string() << '\n';
|
||||
|
||||
// Mid-price (O(1))
|
||||
auto mid = book.mid_price();
|
||||
std::cout << "Mid: " << mid.to_string() << '\n';
|
||||
|
||||
// Weighted mid-price (O(1))
|
||||
auto wmid = book.weighted_mid_price();
|
||||
std::cout << "Weighted Mid: " << wmid.to_string() << '\n';
|
||||
```
|
||||
|
||||
### Market Depth
|
||||
|
||||
```cpp
|
||||
// Total quantity at price levels
|
||||
auto bid_depth = book.bid_depth(10); // Sum of top 10 bid levels
|
||||
auto ask_depth = book.ask_depth(10);
|
||||
|
||||
// Cumulative depth up to price
|
||||
auto depth_to_price = book.cumulative_depth_to_price(
|
||||
Side::Bid,
|
||||
Price::from_string("49000.00")
|
||||
);
|
||||
|
||||
// Depth required to move price by X%
|
||||
auto depth_to_move = book.depth_to_move_price(
|
||||
Side::Ask,
|
||||
0.01 // 1%
|
||||
);
|
||||
```
|
||||
|
||||
### Order Book Imbalance
|
||||
|
||||
```cpp
|
||||
// Imbalance ratio: (bid_qty - ask_qty) / (bid_qty + ask_qty)
|
||||
auto imbalance = book.imbalance(5); // Top 5 levels
|
||||
// Returns: [-1.0, 1.0], positive = more bids
|
||||
|
||||
// Volume-weighted imbalance
|
||||
auto vwap_imbalance = book.vwap_imbalance(5);
|
||||
```
|
||||
|
||||
## Order Book Views
|
||||
|
||||
### Aggregated View
|
||||
|
||||
```cpp
|
||||
// Aggregate by price bucket
|
||||
auto aggregated = book.aggregate(Price::from_string("10.00")); // $10 buckets
|
||||
|
||||
for (const auto& [price_bucket, total_qty] : aggregated.bids()) {
|
||||
std::cout << price_bucket.to_string() << " : "
|
||||
<< total_qty.to_string() << '\n';
|
||||
}
|
||||
```
|
||||
|
||||
### Filtered View
|
||||
|
||||
```cpp
|
||||
// Filter by minimum quantity
|
||||
auto large_orders = book.filter([](const PriceLevel& level) {
|
||||
return level.quantity >= Quantity::from_string("10.0");
|
||||
});
|
||||
|
||||
// Filter by price range
|
||||
auto range_view = book.price_range(
|
||||
Price::from_string("49000.00"),
|
||||
Price::from_string("51000.00")
|
||||
);
|
||||
```
|
||||
|
||||
## Cache-Friendly Design
|
||||
|
||||
### Memory Layout
|
||||
|
||||
```cpp
|
||||
// Cache-line aligned price levels
|
||||
struct alignas(64) PriceLevel {
|
||||
int64_t price; // 8 bytes
|
||||
int64_t quantity; // 8 bytes
|
||||
uint32_t orders; // 4 bytes
|
||||
uint32_t flags; // 4 bytes
|
||||
int64_t timestamp; // 8 bytes
|
||||
char padding[32]; // Pad to 64 bytes
|
||||
};
|
||||
|
||||
// Contiguous storage for cache efficiency
|
||||
class OrderBook {
|
||||
// Hot data (accessed every tick)
|
||||
alignas(64) std::array<PriceLevel, 16> hot_bids_;
|
||||
alignas(64) std::array<PriceLevel, 16> hot_asks_;
|
||||
|
||||
// Warm data (accessed occasionally)
|
||||
alignas(64) std::vector<PriceLevel> bids_;
|
||||
alignas(64) std::vector<PriceLevel> asks_;
|
||||
};
|
||||
```
|
||||
|
||||
### Prefetching
|
||||
|
||||
```cpp
|
||||
// Manual prefetch for known access patterns
|
||||
void OrderBook::prefetch_top_levels() const noexcept {
|
||||
// Prefetch best bid/ask into L1 cache
|
||||
__builtin_prefetch(&hot_bids_[0], 0, 3); // Read, high locality
|
||||
__builtin_prefetch(&hot_asks_[0], 0, 3);
|
||||
|
||||
// Prefetch next levels into L2 cache
|
||||
__builtin_prefetch(&hot_bids_[1], 0, 2);
|
||||
__builtin_prefetch(&hot_asks_[1], 0, 2);
|
||||
}
|
||||
|
||||
// Use before accessing book
|
||||
book.prefetch_top_levels();
|
||||
auto [bid, ask] = book.best_bid_ask();
|
||||
```
|
||||
|
||||
## Benchmarks
|
||||
|
||||
### Microbenchmarks
|
||||
|
||||
```cpp
|
||||
#include <benchmark/benchmark.h>
|
||||
#include <lxdex/orderbook.hpp>
|
||||
|
||||
static void BM_BestBidAsk(benchmark::State& state) {
|
||||
lxdex::OrderBook book("BTC-USD");
|
||||
// Setup book with 1000 levels...
|
||||
|
||||
for (auto _ : state) {
|
||||
auto [bid, ask] = book.best_bid_ask();
|
||||
benchmark::DoNotOptimize(bid);
|
||||
benchmark::DoNotOptimize(ask);
|
||||
}
|
||||
}
|
||||
BENCHMARK(BM_BestBidAsk);
|
||||
|
||||
static void BM_ApplyDelta(benchmark::State& state) {
|
||||
lxdex::OrderBook book("BTC-USD");
|
||||
lxdex::OrderBookDelta delta{/* ... */};
|
||||
|
||||
for (auto _ : state) {
|
||||
book.apply_delta(delta);
|
||||
}
|
||||
}
|
||||
BENCHMARK(BM_ApplyDelta);
|
||||
|
||||
BENCHMARK_MAIN();
|
||||
```
|
||||
|
||||
### Performance Numbers
|
||||
|
||||
| Operation | Latency (p50) | Latency (p99) | Notes |
|
||||
|-----------|---------------|---------------|-------|
|
||||
| `best_bid_ask()` | 12ns | 45ns | Lock-free |
|
||||
| `spread()` | 15ns | 50ns | Lock-free |
|
||||
| `mid_price()` | 18ns | 55ns | Lock-free |
|
||||
| `top_bids(10)` | 89ns | 210ns | Copy 10 levels |
|
||||
| `apply_delta()` | 45ns | 120ns | Single update |
|
||||
| `find_price_simd()` | 8ns | 25ns | AVX-512, 1000 levels |
|
||||
|
||||
## Thread Safety Summary
|
||||
|
||||
| Operation | Read Thread | Write Thread |
|
||||
|-----------|-------------|--------------|
|
||||
| `best_bid()` | Wait-free | - |
|
||||
| `best_ask()` | Wait-free | - |
|
||||
| `spread()` | Wait-free | - |
|
||||
| `bids()` | Wait-free | - |
|
||||
| `asks()` | Wait-free | - |
|
||||
| `snapshot()` | Wait-free | - |
|
||||
| `set_level()` | - | Single writer |
|
||||
| `delete_level()` | - | Single writer |
|
||||
| `apply_delta()` | - | Single writer |
|
||||
|
||||
## Example: Market Making
|
||||
|
||||
```cpp
|
||||
#include <lxdex/client.hpp>
|
||||
#include <lxdex/orderbook.hpp>
|
||||
|
||||
class MarketMaker {
|
||||
Client& client_;
|
||||
OrderBook book_;
|
||||
Price spread_target_{100000}; // 0.001 in fixed-point
|
||||
Quantity quote_size_{1'00000000}; // 1.0
|
||||
|
||||
public:
|
||||
MarketMaker(Client& client, std::string_view symbol)
|
||||
: client_(client), book_(symbol) {}
|
||||
|
||||
void on_book_update(const OrderBook& book) {
|
||||
book_ = book; // Lock-free copy
|
||||
requote();
|
||||
}
|
||||
|
||||
void requote() {
|
||||
auto mid = book_.mid_price();
|
||||
if (!mid) return;
|
||||
|
||||
Price bid_price = *mid - spread_target_ / 2;
|
||||
Price ask_price = *mid + spread_target_ / 2;
|
||||
|
||||
// Cancel existing orders
|
||||
client_.cancel_all_orders();
|
||||
|
||||
// Place new quotes
|
||||
client_.place_order(OrderBuilder()
|
||||
.symbol(book_.symbol())
|
||||
.side(Side::Buy)
|
||||
.type(OrderType::Limit)
|
||||
.price(bid_price)
|
||||
.quantity(quote_size_)
|
||||
.build());
|
||||
|
||||
client_.place_order(OrderBuilder()
|
||||
.symbol(book_.symbol())
|
||||
.side(Side::Sell)
|
||||
.type(OrderType::Limit)
|
||||
.price(ask_price)
|
||||
.quantity(quote_size_)
|
||||
.build());
|
||||
}
|
||||
};
|
||||
|
||||
int main() {
|
||||
Client client({.endpoints = {"wss://api.lux.network/ws"}});
|
||||
client.connect();
|
||||
|
||||
MarketMaker mm(client, "BTC-USD");
|
||||
|
||||
client.subscribe_orderbook("BTC-USD", [&mm](const OrderBook& book) {
|
||||
mm.on_book_update(book);
|
||||
});
|
||||
|
||||
// Run forever
|
||||
std::this_thread::sleep_for(std::chrono::hours{24 * 365});
|
||||
}
|
||||
```
|
||||
@@ -1,647 +0,0 @@
|
||||
---
|
||||
title: Orders
|
||||
description: Order types, placement, cancellation, and management with C++17/20 examples
|
||||
---
|
||||
|
||||
# Orders
|
||||
|
||||
The order subsystem provides type-safe order construction, validation, and lifecycle management with zero-copy serialization for HFT applications.
|
||||
|
||||
## Order Types
|
||||
|
||||
### Supported Order Types
|
||||
|
||||
| Type | Enum | Description |
|
||||
|------|------|-------------|
|
||||
| Limit | `OrderType::Limit` | Execute at specified price or better |
|
||||
| Market | `OrderType::Market` | Execute immediately at best available price |
|
||||
| Stop | `OrderType::Stop` | Trigger market order when price reaches stop |
|
||||
| Stop Limit | `OrderType::StopLimit` | Trigger limit order when price reaches stop |
|
||||
| Iceberg | `OrderType::Iceberg` | Hidden quantity with visible portion |
|
||||
| Peg | `OrderType::Peg` | Track best bid/ask with offset |
|
||||
|
||||
### Order Structure
|
||||
|
||||
```cpp
|
||||
#include <lxdex/order.hpp>
|
||||
|
||||
namespace lxdex {
|
||||
|
||||
struct Order {
|
||||
OrderId id; // Server-assigned
|
||||
std::string symbol; // "BTC-USD"
|
||||
Side side; // Buy/Sell
|
||||
OrderType type; // Limit/Market/Stop/...
|
||||
Price price; // Fixed-point price
|
||||
Quantity quantity; // Fixed-point quantity
|
||||
Quantity filled_quantity; // Amount filled
|
||||
TimeInForce time_in_force; // GTC/IOC/FOK/DAY
|
||||
OrderStatus status; // Pending/Active/Filled/...
|
||||
Timestamp created_at;
|
||||
Timestamp updated_at;
|
||||
|
||||
// Optional fields
|
||||
std::optional<Price> stop_price;
|
||||
std::optional<Quantity> visible_quantity; // Iceberg
|
||||
std::optional<Price> peg_offset; // Peg orders
|
||||
std::optional<std::string> client_order_id;
|
||||
};
|
||||
|
||||
enum class Side : uint8_t { Buy = 0, Sell = 1 };
|
||||
|
||||
enum class OrderType : uint8_t {
|
||||
Limit = 0,
|
||||
Market = 1,
|
||||
Stop = 2,
|
||||
StopLimit = 3,
|
||||
Iceberg = 4,
|
||||
Peg = 5
|
||||
};
|
||||
|
||||
enum class TimeInForce : uint8_t {
|
||||
GTC = 0, // Good Till Cancelled
|
||||
IOC = 1, // Immediate Or Cancel
|
||||
FOK = 2, // Fill Or Kill
|
||||
DAY = 3 // Day Order
|
||||
};
|
||||
|
||||
enum class OrderStatus : uint8_t {
|
||||
Pending = 0,
|
||||
Active = 1,
|
||||
PartiallyFilled = 2,
|
||||
Filled = 3,
|
||||
Cancelled = 4,
|
||||
Rejected = 5,
|
||||
Expired = 6
|
||||
};
|
||||
|
||||
} // namespace lxdex
|
||||
```
|
||||
|
||||
## Fixed-Point Arithmetic
|
||||
|
||||
Prices and quantities use fixed-point representation to avoid floating-point errors.
|
||||
|
||||
```cpp
|
||||
#include <lxdex/types.hpp>
|
||||
|
||||
// Price: 8 decimal places (satoshi precision)
|
||||
// Quantity: 8 decimal places
|
||||
|
||||
// Construction from integer (raw value)
|
||||
Price price{50000'00000000}; // $50,000.00
|
||||
|
||||
// Construction from double (lossy, use with care)
|
||||
Price price = Price::from_double(50000.00);
|
||||
|
||||
// Construction from string (exact)
|
||||
Price price = Price::from_string("50000.00");
|
||||
|
||||
// Arithmetic
|
||||
Price spread = ask - bid;
|
||||
Quantity total = quantity * Price{2};
|
||||
|
||||
// Comparison
|
||||
if (bid >= Price{49000'00000000}) {
|
||||
// ...
|
||||
}
|
||||
|
||||
// Output
|
||||
std::cout << price.to_string() << '\n'; // "50000.00000000"
|
||||
std::cout << price.to_double() << '\n'; // 50000.0
|
||||
```
|
||||
|
||||
## Order Builder Pattern
|
||||
|
||||
### Basic Order Builder
|
||||
|
||||
```cpp
|
||||
#include <lxdex/order.hpp>
|
||||
|
||||
using namespace lxdex;
|
||||
|
||||
// Limit buy order
|
||||
auto order = OrderBuilder()
|
||||
.symbol("BTC-USD")
|
||||
.side(Side::Buy)
|
||||
.type(OrderType::Limit)
|
||||
.price(Price::from_string("50000.00"))
|
||||
.quantity(Quantity::from_string("1.5"))
|
||||
.time_in_force(TimeInForce::GTC)
|
||||
.build();
|
||||
|
||||
// Market sell order
|
||||
auto market_order = OrderBuilder()
|
||||
.symbol("ETH-USD")
|
||||
.side(Side::Sell)
|
||||
.type(OrderType::Market)
|
||||
.quantity(Quantity::from_string("10.0"))
|
||||
.build();
|
||||
```
|
||||
|
||||
### Advanced Order Builder
|
||||
|
||||
```cpp
|
||||
// Stop-limit order
|
||||
auto stop_limit = OrderBuilder()
|
||||
.symbol("BTC-USD")
|
||||
.side(Side::Sell)
|
||||
.type(OrderType::StopLimit)
|
||||
.price(Price::from_string("48000.00")) // Limit price
|
||||
.stop_price(Price::from_string("49000.00")) // Trigger price
|
||||
.quantity(Quantity::from_string("0.5"))
|
||||
.time_in_force(TimeInForce::GTC)
|
||||
.build();
|
||||
|
||||
// Iceberg order (hidden quantity)
|
||||
auto iceberg = OrderBuilder()
|
||||
.symbol("BTC-USD")
|
||||
.side(Side::Buy)
|
||||
.type(OrderType::Iceberg)
|
||||
.price(Price::from_string("50000.00"))
|
||||
.quantity(Quantity::from_string("100.0")) // Total quantity
|
||||
.visible_quantity(Quantity::from_string("1.0")) // Shown in book
|
||||
.time_in_force(TimeInForce::GTC)
|
||||
.build();
|
||||
|
||||
// Peg order (tracks best bid/ask)
|
||||
auto peg = OrderBuilder()
|
||||
.symbol("BTC-USD")
|
||||
.side(Side::Buy)
|
||||
.type(OrderType::Peg)
|
||||
.peg_offset(Price::from_string("-0.01")) // 1 cent below best ask
|
||||
.quantity(Quantity::from_string("5.0"))
|
||||
.build();
|
||||
```
|
||||
|
||||
### Compile-Time Validation
|
||||
|
||||
```cpp
|
||||
// Using C++20 concepts for compile-time validation
|
||||
template<typename T>
|
||||
concept ValidOrder = requires(T t) {
|
||||
{ t.symbol } -> std::convertible_to<std::string_view>;
|
||||
{ t.side } -> std::same_as<Side&>;
|
||||
{ t.quantity } -> std::same_as<Quantity&>;
|
||||
requires t.quantity.raw() > 0;
|
||||
};
|
||||
|
||||
// Builder with validation
|
||||
auto order = OrderBuilder()
|
||||
.symbol("BTC-USD")
|
||||
.side(Side::Buy)
|
||||
.type(OrderType::Limit)
|
||||
.price(Price::from_string("50000.00"))
|
||||
.quantity(Quantity::from_string("1.0"))
|
||||
.validate() // Throws if invalid
|
||||
.build();
|
||||
```
|
||||
|
||||
## Placing Orders
|
||||
|
||||
### Synchronous Placement
|
||||
|
||||
```cpp
|
||||
#include <lxdex/client.hpp>
|
||||
#include <lxdex/order.hpp>
|
||||
|
||||
Client client({.endpoints = {"wss://api.lux.network/ws"}});
|
||||
client.connect();
|
||||
|
||||
// Place order (blocking)
|
||||
auto result = client.place_order(
|
||||
OrderBuilder()
|
||||
.symbol("BTC-USD")
|
||||
.side(Side::Buy)
|
||||
.type(OrderType::Limit)
|
||||
.price(Price::from_string("50000.00"))
|
||||
.quantity(Quantity::from_string("1.0"))
|
||||
.build()
|
||||
);
|
||||
|
||||
if (result) {
|
||||
std::cout << "Order placed: " << result->id << '\n';
|
||||
std::cout << "Status: " << to_string(result->status) << '\n';
|
||||
} else {
|
||||
std::cerr << "Error: " << result.error().message << '\n';
|
||||
}
|
||||
```
|
||||
|
||||
### Asynchronous Placement
|
||||
|
||||
```cpp
|
||||
// Future-based async
|
||||
auto future = client.place_order_async(order);
|
||||
|
||||
// Do other work...
|
||||
|
||||
// Check result
|
||||
if (future.wait_for(std::chrono::microseconds{100}) == std::future_status::ready) {
|
||||
auto result = future.get();
|
||||
// Process...
|
||||
}
|
||||
```
|
||||
|
||||
### Callback-Based (Lowest Latency)
|
||||
|
||||
```cpp
|
||||
// Zero-allocation callback
|
||||
client.place_order_callback(order, [](std::expected<Order, Error> result) {
|
||||
// Called on I/O thread - keep fast!
|
||||
if (result) {
|
||||
// Order confirmed
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
### Batch Order Placement
|
||||
|
||||
```cpp
|
||||
std::vector<Order> orders;
|
||||
orders.reserve(100);
|
||||
|
||||
for (int i = 0; i < 100; ++i) {
|
||||
orders.push_back(
|
||||
OrderBuilder()
|
||||
.symbol("BTC-USD")
|
||||
.side(Side::Buy)
|
||||
.type(OrderType::Limit)
|
||||
.price(Price{50000'00000000 - i * 100000000}) // Price ladder
|
||||
.quantity(Quantity{1'00000000})
|
||||
.build()
|
||||
);
|
||||
}
|
||||
|
||||
// Place all orders in single request
|
||||
auto results = client.place_orders_batch(orders);
|
||||
|
||||
for (std::size_t i = 0; i < results.size(); ++i) {
|
||||
if (results[i]) {
|
||||
std::cout << "Order " << i << " placed: " << results[i]->id << '\n';
|
||||
} else {
|
||||
std::cerr << "Order " << i << " failed: " << results[i].error().message << '\n';
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Cancelling Orders
|
||||
|
||||
### Cancel Single Order
|
||||
|
||||
```cpp
|
||||
// By order ID
|
||||
auto result = client.cancel_order(order_id);
|
||||
if (result) {
|
||||
std::cout << "Order cancelled\n";
|
||||
}
|
||||
|
||||
// Cancel with expected state (optimistic)
|
||||
auto result = client.cancel_order(order_id, OrderStatus::Active);
|
||||
```
|
||||
|
||||
### Cancel Multiple Orders
|
||||
|
||||
```cpp
|
||||
// Cancel specific orders
|
||||
std::vector<OrderId> order_ids = {id1, id2, id3};
|
||||
auto results = client.cancel_orders(order_ids);
|
||||
|
||||
// Cancel all orders for symbol
|
||||
auto results = client.cancel_all_orders("BTC-USD");
|
||||
|
||||
// Cancel all orders
|
||||
auto results = client.cancel_all_orders();
|
||||
```
|
||||
|
||||
### Cancel-Replace (Atomic Modify)
|
||||
|
||||
```cpp
|
||||
// Atomically cancel and place new order
|
||||
auto result = client.cancel_replace(
|
||||
existing_order_id,
|
||||
OrderBuilder()
|
||||
.symbol("BTC-USD")
|
||||
.side(Side::Buy)
|
||||
.type(OrderType::Limit)
|
||||
.price(Price::from_string("49999.00")) // New price
|
||||
.quantity(Quantity::from_string("1.0"))
|
||||
.build()
|
||||
);
|
||||
|
||||
if (result) {
|
||||
std::cout << "Order modified: " << result->id << '\n';
|
||||
}
|
||||
```
|
||||
|
||||
## Order Queries
|
||||
|
||||
### Get Single Order
|
||||
|
||||
```cpp
|
||||
auto result = client.get_order(order_id);
|
||||
if (result) {
|
||||
const auto& order = *result;
|
||||
std::cout << "Symbol: " << order.symbol << '\n';
|
||||
std::cout << "Status: " << to_string(order.status) << '\n';
|
||||
std::cout << "Filled: " << order.filled_quantity.to_string()
|
||||
<< " / " << order.quantity.to_string() << '\n';
|
||||
}
|
||||
```
|
||||
|
||||
### Get Open Orders
|
||||
|
||||
```cpp
|
||||
// All open orders
|
||||
auto orders = client.get_open_orders();
|
||||
|
||||
// Open orders for symbol
|
||||
auto orders = client.get_open_orders("BTC-USD");
|
||||
|
||||
// With pagination
|
||||
auto orders = client.get_open_orders({
|
||||
.symbol = "BTC-USD",
|
||||
.limit = 100,
|
||||
.offset = 0
|
||||
});
|
||||
```
|
||||
|
||||
### Get Order History
|
||||
|
||||
```cpp
|
||||
auto orders = client.get_order_history({
|
||||
.symbol = "BTC-USD",
|
||||
.start_time = std::chrono::system_clock::now() - std::chrono::hours{24},
|
||||
.end_time = std::chrono::system_clock::now(),
|
||||
.status = {OrderStatus::Filled, OrderStatus::Cancelled},
|
||||
.limit = 1000
|
||||
});
|
||||
```
|
||||
|
||||
## Order Updates (Real-Time)
|
||||
|
||||
### Subscribe to Order Updates
|
||||
|
||||
```cpp
|
||||
client.subscribe_orders([](const OrderUpdate& update) {
|
||||
std::cout << "Order " << update.order_id
|
||||
<< " " << to_string(update.event_type) << '\n';
|
||||
|
||||
switch (update.event_type) {
|
||||
case OrderEventType::Created:
|
||||
// New order acknowledged
|
||||
break;
|
||||
case OrderEventType::PartialFill:
|
||||
std::cout << " Filled: " << update.fill_quantity.to_string()
|
||||
<< " @ " << update.fill_price.to_string() << '\n';
|
||||
break;
|
||||
case OrderEventType::Filled:
|
||||
// Order completely filled
|
||||
break;
|
||||
case OrderEventType::Cancelled:
|
||||
// Order cancelled
|
||||
break;
|
||||
case OrderEventType::Rejected:
|
||||
std::cerr << " Reason: " << update.reject_reason << '\n';
|
||||
break;
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
### Order Update Structure
|
||||
|
||||
```cpp
|
||||
struct OrderUpdate {
|
||||
OrderId order_id;
|
||||
std::string symbol;
|
||||
OrderEventType event_type;
|
||||
OrderStatus status;
|
||||
Quantity filled_quantity; // This fill
|
||||
Price fill_price; // This fill price
|
||||
Quantity cumulative_quantity; // Total filled
|
||||
Timestamp timestamp;
|
||||
std::optional<std::string> reject_reason;
|
||||
std::optional<TradeId> trade_id;
|
||||
};
|
||||
|
||||
enum class OrderEventType : uint8_t {
|
||||
Created,
|
||||
PartialFill,
|
||||
Filled,
|
||||
Cancelled,
|
||||
Rejected,
|
||||
Expired,
|
||||
Modified
|
||||
};
|
||||
```
|
||||
|
||||
## C++20 Coroutines
|
||||
|
||||
```cpp
|
||||
#include <lxdex/client.hpp>
|
||||
#include <lxdex/coro.hpp>
|
||||
|
||||
lxdex::Task<void> trading_loop(lxdex::Client& client) {
|
||||
// Place order
|
||||
auto order = co_await client.place_order_coro(
|
||||
OrderBuilder()
|
||||
.symbol("BTC-USD")
|
||||
.side(Side::Buy)
|
||||
.type(OrderType::Limit)
|
||||
.price(Price::from_string("50000.00"))
|
||||
.quantity(Quantity::from_string("1.0"))
|
||||
.build()
|
||||
);
|
||||
|
||||
if (!order) {
|
||||
std::cerr << "Order failed: " << order.error().message << '\n';
|
||||
co_return;
|
||||
}
|
||||
|
||||
std::cout << "Order placed: " << order->id << '\n';
|
||||
|
||||
// Wait for fill
|
||||
while (order->status != OrderStatus::Filled) {
|
||||
co_await lxdex::sleep(std::chrono::milliseconds{10});
|
||||
order = co_await client.get_order_coro(order->id);
|
||||
}
|
||||
|
||||
std::cout << "Order filled!\n";
|
||||
}
|
||||
|
||||
int main() {
|
||||
lxdex::Client client({.endpoints = {"wss://api.lux.network/ws"}});
|
||||
client.connect();
|
||||
|
||||
// Run coroutine
|
||||
lxdex::sync_wait(trading_loop(client));
|
||||
}
|
||||
```
|
||||
|
||||
## C++17 Compatibility
|
||||
|
||||
```cpp
|
||||
#include <lxdex/order.hpp>
|
||||
|
||||
// C++17: Use optional and variant instead of expected
|
||||
#include <optional>
|
||||
#include <variant>
|
||||
|
||||
// Place order returns variant<Order, Error>
|
||||
auto result = client.place_order(order);
|
||||
|
||||
if (auto* placed = std::get_if<Order>(&result)) {
|
||||
std::cout << "Order placed: " << placed->id << '\n';
|
||||
} else if (auto* error = std::get_if<Error>(&result)) {
|
||||
std::cerr << "Error: " << error->message << '\n';
|
||||
}
|
||||
|
||||
// Or use visitor
|
||||
std::visit(overloaded{
|
||||
[](const Order& order) {
|
||||
std::cout << "Order: " << order.id << '\n';
|
||||
},
|
||||
[](const Error& error) {
|
||||
std::cerr << "Error: " << error.message << '\n';
|
||||
}
|
||||
}, result);
|
||||
```
|
||||
|
||||
## Order Validation
|
||||
|
||||
### Client-Side Validation
|
||||
|
||||
```cpp
|
||||
#include <lxdex/validation.hpp>
|
||||
|
||||
auto order = OrderBuilder()
|
||||
.symbol("BTC-USD")
|
||||
.side(Side::Buy)
|
||||
.type(OrderType::Limit)
|
||||
.price(Price::from_string("50000.00"))
|
||||
.quantity(Quantity::from_string("1.0"))
|
||||
.build();
|
||||
|
||||
// Validate before sending
|
||||
auto validation = validate_order(order);
|
||||
if (!validation.is_valid()) {
|
||||
for (const auto& error : validation.errors()) {
|
||||
std::cerr << error << '\n';
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
client.place_order(order);
|
||||
```
|
||||
|
||||
### Validation Rules
|
||||
|
||||
| Rule | Description |
|
||||
|------|-------------|
|
||||
| `SymbolExists` | Symbol must be valid trading pair |
|
||||
| `PriceRange` | Price within min/max for symbol |
|
||||
| `QuantityRange` | Quantity within min/max for symbol |
|
||||
| `TickSize` | Price must be multiple of tick size |
|
||||
| `LotSize` | Quantity must be multiple of lot size |
|
||||
| `NotionalValue` | Order value >= minimum notional |
|
||||
|
||||
## Performance Tips
|
||||
|
||||
### Pre-Allocated Order Pool
|
||||
|
||||
```cpp
|
||||
#include <lxdex/memory/pool.hpp>
|
||||
|
||||
// Pre-allocate order pool
|
||||
lxdex::ObjectPool<Order> order_pool(10000);
|
||||
|
||||
// Get order from pool (no heap allocation)
|
||||
auto* order = order_pool.acquire();
|
||||
*order = OrderBuilder()
|
||||
.symbol("BTC-USD")
|
||||
.side(Side::Buy)
|
||||
.type(OrderType::Limit)
|
||||
.price(Price{50000'00000000})
|
||||
.quantity(Quantity{1'00000000})
|
||||
.build();
|
||||
|
||||
client.place_order(*order);
|
||||
|
||||
// Return to pool
|
||||
order_pool.release(order);
|
||||
```
|
||||
|
||||
### Zero-Copy Order Submission
|
||||
|
||||
```cpp
|
||||
// Direct wire format construction
|
||||
alignas(64) std::array<char, 256> buffer;
|
||||
|
||||
auto size = serialize_order_wire(buffer.data(), buffer.size(),
|
||||
"BTC-USD",
|
||||
Side::Buy,
|
||||
OrderType::Limit,
|
||||
50000'00000000, // Price
|
||||
1'00000000 // Quantity
|
||||
);
|
||||
|
||||
client.send_raw(std::span{buffer.data(), size});
|
||||
```
|
||||
|
||||
### Latency Measurement
|
||||
|
||||
```cpp
|
||||
auto start = std::chrono::steady_clock::now();
|
||||
|
||||
auto result = client.place_order(order);
|
||||
|
||||
auto end = std::chrono::steady_clock::now();
|
||||
auto latency = std::chrono::duration_cast<std::chrono::nanoseconds>(end - start);
|
||||
|
||||
std::cout << "Order latency: " << latency.count() << "ns\n";
|
||||
```
|
||||
|
||||
## Error Handling
|
||||
|
||||
### Order Rejection Reasons
|
||||
|
||||
| Code | Reason | Description |
|
||||
|------|--------|-------------|
|
||||
| `INSUFFICIENT_BALANCE` | Not enough funds | Deposit more or reduce size |
|
||||
| `PRICE_OUT_OF_RANGE` | Price too far from market | Adjust price closer to market |
|
||||
| `QUANTITY_TOO_SMALL` | Below minimum size | Increase quantity |
|
||||
| `RATE_LIMIT_EXCEEDED` | Too many requests | Implement backoff |
|
||||
| `MARKET_CLOSED` | Trading halted | Wait for market open |
|
||||
| `SELF_TRADE` | Would match own order | Adjust price or cancel existing |
|
||||
|
||||
### Retry Logic
|
||||
|
||||
```cpp
|
||||
template<typename F>
|
||||
auto retry_with_backoff(F&& func, int max_retries = 3) {
|
||||
using ResultType = std::invoke_result_t<F>;
|
||||
|
||||
for (int attempt = 0; attempt < max_retries; ++attempt) {
|
||||
auto result = func();
|
||||
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
|
||||
if (result.error().code == ErrorCode::RateLimited) {
|
||||
auto delay = std::chrono::milliseconds{100 * (1 << attempt)};
|
||||
std::this_thread::sleep_for(delay);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Non-retryable error
|
||||
return result;
|
||||
}
|
||||
|
||||
return ResultType{std::unexpected{Error{ErrorCode::Timeout, "Max retries exceeded"}}};
|
||||
}
|
||||
|
||||
// Usage
|
||||
auto result = retry_with_backoff([&] {
|
||||
return client.place_order(order);
|
||||
});
|
||||
```
|
||||
+3
-3
@@ -7,9 +7,9 @@ package lx
|
||||
|
||||
// GPU-accelerated batch xy=k AMM evaluation via Metal.
|
||||
//
|
||||
// The Metal kernel and Objective-C++ driver live in luxcpp/dex/gpu/metal:
|
||||
// kernel: amm_xyk.metal -> built by cmake into amm_xyk.metallib
|
||||
// driver: amm_xyk_driver.{h,mm} -> built into libamm_xyk_metal.a
|
||||
// The Metal kernel lives in luxcpp/metal (one-home-per-backend policy):
|
||||
// kernel: ~/work/luxcpp/metal/src/shaders/dex/amm_xyk.metal
|
||||
// driver: ~/work/luxcpp/dex/gpu/metal/amm_xyk_driver.{h,mm}
|
||||
//
|
||||
// One-time build:
|
||||
// cmake -S ~/work/luxcpp/dex -B ~/work/luxcpp/dex/build && \
|
||||
|
||||
+1
-16
@@ -59,17 +59,11 @@ build-rust: ## Build Rust SDK
|
||||
@echo "$(BLUE)Building Rust SDK...$(NC)"
|
||||
cd lx-trading-core && cargo build --release
|
||||
|
||||
build-cpp: ## Build C++ SDK
|
||||
@echo "$(BLUE)Building C++ SDK...$(NC)"
|
||||
cd lx-trading-cpp && mkdir -p build && cd build && \
|
||||
cmake .. -DCMAKE_BUILD_TYPE=Release && \
|
||||
make -j$(NPROC)
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
# Test targets
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
test: test-go test-ts test-py test-rust test-cpp ## Run all SDK tests
|
||||
test: test-go test-ts test-py test-rust ## Run all SDK tests
|
||||
@echo ""
|
||||
@echo "$(GREEN)═══════════════════════════════════════════════════════════$(NC)"
|
||||
@echo "$(GREEN) All SDK tests completed$(NC)"
|
||||
@@ -103,14 +97,6 @@ test-rust: ## Run Rust SDK tests
|
||||
@echo "$(BLUE)═══════════════════════════════════════════════════════════$(NC)"
|
||||
cd lx-trading-core && cargo test --all-features
|
||||
|
||||
test-cpp: ## Run C++ SDK tests
|
||||
@echo ""
|
||||
@echo "$(BLUE)═══════════════════════════════════════════════════════════$(NC)"
|
||||
@echo "$(BLUE) Running C++ SDK tests$(NC)"
|
||||
@echo "$(BLUE)═══════════════════════════════════════════════════════════$(NC)"
|
||||
cd lx-trading-cpp/build && ./lx_trading_tests 2>/dev/null || \
|
||||
(cd ../.. && $(MAKE) build-cpp && cd lx-trading-cpp/build && ./lx_trading_tests)
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
# Lint & Format
|
||||
#------------------------------------------------------------------------------
|
||||
@@ -161,7 +147,6 @@ clean: ## Clean all build artifacts
|
||||
rm -rf lx-trading-ts/dist lx-trading-ts/node_modules
|
||||
rm -rf lx-trading-py/dist lx-trading-py/build lx-trading-py/*.egg-info
|
||||
rm -rf lx-trading-core/target
|
||||
rm -rf lx-trading-cpp/build
|
||||
@echo "$(GREEN)Clean complete$(NC)"
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
+1
-1
@@ -15,7 +15,7 @@ LX Trading SDK provides a unified interface for trading across:
|
||||
| Language | Package | Directory | Throughput |
|
||||
|----------|---------|-----------|------------|
|
||||
| Rust | `lx-trading` | `lx-trading-core/` | 5M+ msgs/sec |
|
||||
| C++ | `lx-trading` | `lx-trading-cpp/` | 10M+ msgs/sec |
|
||||
| C++ | `lx-trading` | `~/work/luxcpp/dex/sdk/trading-cpp/` | 10M+ msgs/sec |
|
||||
| Go | `github.com/luxfi/trading` | `lx-trading-go/` | 1M+ msgs/sec |
|
||||
| TypeScript | `@luxfi/trading` | `lx-trading-ts/` | 100K+ msgs/sec |
|
||||
| Python | `lx-trading` | `lx-trading-py/` | 50K+ msgs/sec |
|
||||
|
||||
Reference in New Issue
Block a user