Multi-Exchange Trading¶
Set up trading across multiple exchanges with position aggregation and smart routing.
Prerequisites¶
- Multiple exchange connectors configured
- SymbolRegistry with registered exchanges
1. Register Exchanges and Symbols¶
#include "flox/engine/symbol_registry.h"
SymbolRegistry registry;
// Register exchanges
ExchangeId binance = registry.registerExchange("Binance");
ExchangeId bybit = registry.registerExchange("Bybit");
ExchangeId kraken = registry.registerExchange("Kraken");
// Register symbols per exchange
SymbolId btcBinance = registry.registerSymbol(binance, "BTCUSDT");
SymbolId btcBybit = registry.registerSymbol(bybit, "BTCUSDT");
SymbolId btcKraken = registry.registerSymbol(kraken, "XBTUSDT");
// Map equivalent symbols
std::array<SymbolId, 3> btcSymbols = {btcBinance, btcBybit, btcKraken};
registry.mapEquivalentSymbols(btcSymbols);
2. Set Up Clock Synchronization¶
#include "flox/util/sync/exchange_clock_sync.h"
ExchangeClockSync<8> clockSync;
// Record timing samples from API calls
void onApiResponse(ExchangeId ex, int64_t localSend, int64_t serverTime, int64_t localRecv)
{
clockSync.recordSample(ex, localSend, serverTime, localRecv);
}
// Convert exchange timestamp to local time
int64_t localTs = clockSync.toLocalTimeNs(binance, exchangeTimestamp);
// Check sync quality
if (clockSync.hasReliableSync(binance)) {
auto est = clockSync.estimate(binance);
// est.offsetNs, est.latencyNs, est.confidenceNs
}
3. Aggregate Order Books¶
#include "flox/book/composite_book_matrix.h"
CompositeBookMatrix<4> books;
// Subscribe to BookUpdateEvents from all exchanges
// The matrix updates atomically on each event
// Query best prices (thread-safe, lock-free)
auto bestBid = books.bestBid(btcBinance); // Best bid across all exchanges
auto bestAsk = books.bestAsk(btcBinance); // Best ask across all exchanges
if (bestBid.valid && bestAsk.valid) {
int64_t spread = bestAsk.priceRaw - bestBid.priceRaw;
// bestBid.exchange, bestAsk.exchange tell you where
}
// Check for arbitrage
if (books.hasArbitrageOpportunity(btcBinance)) {
// bestBid.priceRaw > bestAsk.priceRaw across different exchanges
}
4. Track Positions¶
#include "flox/position/aggregated_position_tracker.h"
AggregatedPositionTracker<8> positions;
// Update on fills (writer thread)
positions.onFill(binance, btcBinance,
Quantity::fromDouble(1.0).raw(), // Buy 1 BTC
Price::fromDouble(50000.0).raw()); // @ $50,000
positions.onFill(bybit, btcBybit,
Quantity::fromDouble(-0.3).raw(), // Sell 0.3 BTC
Price::fromDouble(50100.0).raw()); // @ $50,100
// Query positions (reader thread, lock-free)
auto binancePos = positions.position(binance, btcBinance);
auto totalPos = positions.totalPosition(btcBinance); // Aggregated across exchanges
Price currentPrice = Price::fromDouble(50200.0);
int64_t pnlRaw = positions.unrealizedPnlRaw(btcBinance, currentPrice.raw());
double pnl = Price::fromRaw(pnlRaw).toDouble(); // Convert to readable value
5. Configure Order Routing¶
#include "flox/execution/order_router.h"
OrderRouter<4> router;
// Register executors
router.registerExecutor(binance, &binanceExecutor);
router.registerExecutor(bybit, &bybitExecutor);
router.registerExecutor(kraken, &krakenExecutor);
// Configure strategy
router.setCompositeBook(&books);
router.setClockSync(&clockSync);
router.setRoutingStrategy(RoutingStrategy::BestPrice);
router.setFailoverPolicy(FailoverPolicy::FailoverToBest);
// Route orders
ExchangeId routed;
auto err = router.route(btcBinance, Side::BUY, priceRaw, qtyRaw, orderId, &routed);
if (err == RoutingError::Success) {
// Order sent to 'routed' exchange
}
// Or route explicitly
router.routeTo(binance, btcBinance, Side::BUY, priceRaw, qtyRaw, orderId);
6. Track Split Orders¶
#include "flox/execution/split_order_tracker.h"
SplitOrderTracker tracker;
// Split parent order across exchanges
OrderId parentId = 1000;
std::array<OrderId, 3> childIds = {1001, 1002, 1003};
Quantity totalQty = Quantity::fromDouble(10.0); // 10 BTC total
tracker.registerSplit(parentId, childIds, totalQty.raw(), nowNs);
// Update on child events
tracker.onChildFill(1001, Quantity::fromDouble(4.0).raw()); // 4 BTC filled
tracker.onChildComplete(1001, true);
// Check status
auto* state = tracker.getState(parentId);
double fillRatio = state->fillRatio(); // 0.4
bool done = state->allDone();
Routing Strategies¶
| Strategy | Description | Use Case |
|---|---|---|
BestPrice |
Route to exchange with best price | Default, maximize fill price |
LowestLatency |
Route to exchange with lowest RTT | Time-sensitive orders |
LargestSize |
Route to exchange with most liquidity | Large orders |
RoundRobin |
Distribute evenly | Load balancing |
Explicit |
Use order's target exchange | Manual control |
Error Handling¶
auto err = router.route(symbol, side, price, qty, orderId);
switch (err) {
case RoutingError::Success:
break;
case RoutingError::NoExecutor:
// No executor registered or all disabled
break;
case RoutingError::ExchangeDisabled:
// Target exchange temporarily disabled
break;
case RoutingError::InvalidSymbol:
// Symbol not found
break;
}