Liquidity Docs

On-Chain DEX

Central Limit Order Book (CLOB) architecture, matching engine, order types, settlement, and WebSocket feeds

Liquidity.io operates a fully on-chain Central Limit Order Book (CLOB) exchange built on the Liquidity Network. The DEX provides deterministic matching with price-time priority, atomic settlement, and sub-millisecond latency for institutional trading workloads.

Architecture

The DEX is a multi-layer system with specialized components for each phase of the trade lifecycle.

Client Layer (SDKs, Web UI, Trading Bots)
        |
Gateway Layer (Load Balancer, Rate Limiter, Auth, WebSocket/gRPC)
        |
Application Layer (Order Management, Risk Engine, Market Data)
        |
Core Engine Layer (Matching Engine, Clearing, Settlement)
        |
Consensus Layer (DAG Consensus, Validator Network, State Machine)
        |
Storage Layer (Order DB, Trade History, State DB, Archive)

Matching Engine

The matching engine is the core of the DEX. Each trading pair has its own order book backed by a B-tree for O(log n) price-level operations.

Key design choices:

  • Lock-free data structures -- atomic operations for concurrent order processing without mutex contention
  • Memory pooling -- object reuse via sync.Pool to minimize garbage collection pressure
  • Integer price levels -- all prices are represented as integers internally to avoid floating-point errors
  • Circular trade buffers -- zero-copy recording of executed trades
  • Multiple backends -- the engine auto-selects the optimal implementation for the hardware

Performance

The DEX supports multiple execution backends with verified benchmarks:

Core orderbook (Pure Go, Apple M1 Max):

Book DepthLatencyThroughput
100 levels3,191ns313,376 orders/sec
1,000 levels2,877ns347,631 orders/sec
10,000 levels3,080ns324,721 orders/sec

GPU acceleration (MLX, Apple M2 Ultra): 434,782,609 orders/sec at 2ns latency.

FIX 4.4 multi-engine benchmarks:

EngineNewOrderSingleExecutionReportMarketDataSnapshot
Pure Go163K/sec124K/sec332K/sec
Hybrid Go/C++167K/sec378K/sec616K/sec
Pure C++444K/sec804K/sec1.08M/sec
Rust484K/sec232K/sec586K/sec
GPU (MLX)3.12M/sec4.27M/sec5.95M/sec

See Performance Benchmarks for full results and reproduction steps.


Order Types

TypeBehaviorTime-in-Force
marketExecute immediately at best available priceIOC
limitExecute at specified price or better, rest on bookGTC, IOC, FOK, GTD, POST_ONLY
stop_lossTrigger market order when price crosses thresholdGTC
stop_loss_limitTrigger limit order when price crosses thresholdGTC
take_profitTrigger market order at profit targetGTC
take_profit_limitTrigger limit order at profit targetGTC
limit_makerLimit order that is rejected if it would immediately match (maker-only)GTC

Time-in-Force

ValueDescription
GTCGood till cancelled -- order stays on book until filled or cancelled
IOCImmediate or cancel -- fill what is available, cancel the rest
FOKFill or kill -- fill the entire order or reject it completely
GTDGood till date -- expires at a specified timestamp
POST_ONLYRejected if it would take liquidity (maker-only)

Hidden and Iceberg Orders

The matching engine supports hidden orders (completely invisible to the book) and iceberg orders (only a visible slice is shown). These are available through the execution algorithms in the SDK. See Execution & Algorithms for details.


Matching Algorithm

The engine uses price-time priority (FIFO):

  1. Orders are first ranked by price -- the best price has priority (highest bid, lowest ask)
  2. At the same price level, orders are ranked by arrival time -- earlier orders fill first
  3. When an incoming order matches one or more resting orders, fills are generated atomically
  4. Partially filled orders remain on the book with the unfilled quantity

Matching Rules

  • A market buy sweeps the ask side from lowest price upward until the order is fully filled
  • A limit buy at price P matches any resting asks at price P or lower
  • A stop order is held off-book until the trigger price is reached, then converted to a market or limit order
  • Post-only orders are rejected if they would match immediately, ensuring the submitter always earns the maker fee

Settlement

All trades settle atomically on-chain. The clearinghouse executes settlement as a single transaction:

  1. Trade Matching -- the matching engine pairs buy and sell orders
  2. Clearing -- the clearinghouse verifies the trade and records it
  3. Settlement -- buyer and seller balances are updated atomically (debit/credit in one transaction)
  4. Confirmation -- participants are notified via WebSocket/SSE

There is no counterparty risk. Settlement is final at the consensus layer with 1ms deterministic finality.

Consensus

The LQDTY chain uses DAG-based Quasar consensus with the dual-certificate protocol:

  • 1ms block finality -- 1,000 blocks/second, deterministic and irreversible
  • Parallel validation -- DAG structure enables concurrent transaction processing
  • Post-quantum consensus -- hybrid BLS + Ringtail lattice threshold signatures
  • Adaptive thresholds -- vote threshold converges from 55% to 65% over 10 rounds
  • K=20 sample, 80% quorum -- 16 of 20 validators must agree per round

See Post-Quantum Security for the full cryptographic specification.


WebSocket Feeds

The DEX provides real-time data via WebSocket for low-latency integrations, and SSE for simpler setups.

Connection

wss://api.liquidity.io/ws          (production)
wss://api.liquidity.io/ws         (Liquidity.io ATS)
ws://localhost:8080/ws            (local development)

Subscribe to Order Book

{
  "type": "subscribe",
  "channel": "orderbook",
  "symbol": "BTC-USDT",
  "depth": 20
}

Snapshot response:

{
  "type": "orderbook_snapshot",
  "symbol": "BTC-USDT",
  "bids": [[83400, 1.5], [83399, 2.0]],
  "asks": [[83401, 1.2], [83402, 0.8]],
  "timestamp": 1710807360
}

Incremental update:

{
  "type": "orderbook_update",
  "symbol": "BTC-USDT",
  "bids": [[83400, 1.8]],
  "asks": [[83401, 0]],
  "timestamp": 1710807361
}

A size of 0 means the price level has been removed from the book.

Subscribe to Trades

{
  "type": "subscribe",
  "channel": "trades",
  "symbol": "BTC-USDT"
}

Trade event:

{
  "type": "trade",
  "symbol": "BTC-USDT",
  "price": 83421.50,
  "size": 0.5,
  "side": "buy",
  "timestamp": 1710807360
}

REST API

Base URL

https://api.liquidity.io/v1        (production)
https://api.liquidity.io/v1       (Liquidity.io ATS)

Get Order Book

GET /orderbook/{symbol}?depth=20

Response:

{
  "symbol": "BTC-USDT",
  "bids": [
    { "price": 83400, "size": 1.5 },
    { "price": 83399, "size": 2.0 }
  ],
  "asks": [
    { "price": 83401, "size": 1.2 },
    { "price": 83402, "size": 0.8 }
  ],
  "timestamp": 1710807360
}

Place Order

POST /orders
X-API-Key: {api_key}
X-API-Secret: {api_secret}
Content-Type: application/json

{
  "symbol": "BTC-USDT",
  "side": "buy",
  "type": "limit",
  "price": 83000,
  "size": 0.1,
  "time_in_force": "GTC"
}

Response:

{
  "order_id": "123e4567-e89b-12d3-a456-426614174000",
  "symbol": "BTC-USDT",
  "side": "buy",
  "type": "limit",
  "price": 83000,
  "size": 0.1,
  "status": "open",
  "created_at": 1710807360
}

Cancel Order

DELETE /orders/{order_id}
X-API-Key: {api_key}
X-API-Secret: {api_secret}

Get Recent Trades

GET /trades?symbol=BTC-USDT&limit=100

Code Examples

TypeScript

import { Client, Config, NativeVenueConfig } from '@liquidityio/trading';

const config = new Config()
  .withNative('lx-dex', NativeVenueConfig.liquidDex('https://api.liquidity.io/v1'))
  .withVenuePriority(['lx-dex'])
  .withSmartRouting(false);

const client = new Client(config);
await client.connect();

// Get orderbook with VWAP calculation
const book = await client.orderbook('BTC-USDT');
console.log(`Best bid: ${book.bestBid}, Best ask: ${book.bestAsk}`);
console.log(`Spread: ${book.spread} (${book.spreadPercent}%)`);
console.log(`Mid price: ${book.midPrice}`);
console.log(`VWAP to buy 1 BTC: ${book.vwapBuy(1)}`);
console.log(`Bid liquidity (top 5): ${book.bidDepth(5)}`);

// Place a limit buy
const order = await client.limitBuy('BTC-USDT', '0.5', '83000');
console.log(`Order ${order.orderId} status: ${order.status}`);

// Place a market sell
const sellOrder = await client.sell('BTC-USDT', '0.25');
console.log(`Sold at ${sellOrder.averagePrice}`);

// Cancel an order
await client.cancelOrder(order.orderId, 'BTC-USDT', 'lx-dex');

// List open orders
const openOrders = await client.openOrders('BTC-USDT');
for (const o of openOrders) {
  console.log(`${o.orderId}: ${o.side} ${o.quantity} @ ${o.price} [${o.status}]`);
}

await client.disconnect();

Python

import asyncio
from decimal import Decimal
from lx_trading import Client, Config
from lx_trading.config import NativeVenueConfig

async def main():
    config = Config()
    config.with_native("lx-dex", NativeVenueConfig.liquid_dex("https://api.liquidity.io/v1"))

    client = Client(config)
    await client.connect()

    # Get orderbook
    book = await client.orderbook("BTC-USDT")
    print(f"Best bid: {book.best_bid}, Best ask: {book.best_ask}")
    print(f"Spread: {book.spread} ({book.spread_percent}%)")
    print(f"VWAP to buy 1 BTC: {book.vwap_buy(Decimal('1'))}")

    # Place a limit buy
    order = await client.limit_buy("BTC-USDT", Decimal("0.5"), Decimal("83000"))
    print(f"Order {order.order_id} status: {order.status}")

    # Place a market sell
    sell_order = await client.sell("BTC-USDT", Decimal("0.25"))
    print(f"Sold at {sell_order.average_price}")

    # Cancel
    await client.cancel_order(order.order_id, "BTC-USDT", "lx-dex")

    # Open orders
    open_orders = await client.open_orders("BTC-USDT")
    for o in open_orders:
        print(f"{o.order_id}: {o.side} {o.quantity} @ {o.price} [{o.status}]")

    await client.disconnect()

asyncio.run(main())

gRPC API

For ultra-low-latency integrations, the DEX also exposes a gRPC interface:

localhost:50051 (development)
grpc.liquidity.io:443 (production)

Proto definition:

syntax = "proto3";
package lxdex;

service TradingService {
  rpc PlaceOrder(OrderRequest) returns (OrderResponse);
  rpc CancelOrder(CancelRequest) returns (CancelResponse);
  rpc GetOrderBook(OrderBookRequest) returns (OrderBookResponse);
  rpc StreamTrades(StreamRequest) returns (stream Trade);
}

message OrderRequest {
  string symbol = 1;
  string side = 2;
  string type = 3;
  double price = 4;
  double size = 5;
}

message OrderResponse {
  string order_id = 1;
  string status = 2;
  int64 timestamp = 3;
}

Rate Limits

Endpoint TypeLimit
Public endpoints (orderbook, trades)100 requests/minute
Private endpoints (orders, balances)300 requests/minute
Order placement10 orders/second
WebSocket connections per user5 concurrent

Rate limit headers are included in all responses:

X-RateLimit-Limit: 100
X-RateLimit-Remaining: 95
X-RateLimit-Reset: 1710807360

Error Codes

CodeDescription
INVALID_SYMBOLTrading pair not supported
INVALID_ORDER_TYPEOrder type not recognized
INSUFFICIENT_BALANCENot enough balance for the order
ORDER_NOT_FOUNDOrder ID does not exist
RATE_LIMIT_EXCEEDEDToo many requests
UNAUTHORIZEDAuthentication required
FORBIDDENAccess denied
INTERNAL_ERRORServer error

On this page