Exchanges
ECN trading infrastructure - order books, matching engine, and market data on fXYZ Network
Exchange Infrastructure
fXYZ Network includes a complete Electronic Communication Network (ECN) for peer-to-peer trading, built with a professional-grade matching engine.
Package: @fxyz/trading
The ECN is implemented in the packages/trading module with TypeScript types, order book, and matching engine.
ECN Architecture
The Electronic Communication Network provides:
| Component | Description |
|---|---|
| Order Book | Price-time priority bid/ask aggregation |
| Matching Engine | Real-time order matching with partial fills |
| Market Data | OHLCV candles, ticker data, trade history |
| Settlement | Integration with Solana for on-chain settlement |
Design Principles
- Price-Time Priority: Orders matched at best price, then by arrival time
- Partial Fills: Large orders can fill across multiple price levels
- Time-in-Force: GTC, IOC, FOK, GTD order types
- Fee Structure: Maker/taker fee model
Trading Pairs
Trading pairs define the relationship between base and quote assets:
interface TradingPair {
id: string; // e.g., "FLORIN-USDC"
baseAsset: string; // e.g., "FLORIN"
quoteAsset: string; // e.g., "USDC"
baseMint: string; // Solana mint address
quoteMint: string; // Solana mint address
minOrderSize: number;
maxOrderSize: number;
tickSize: number; // Price precision
lotSize: number; // Quantity precision
makerFee: number; // e.g., 0.001 = 0.1%
takerFee: number;
isActive: boolean;
}Active Pairs
| Pair | Base | Quote | Status |
|---|---|---|---|
| FLORIN-USDC | Florin | USDC | Active |
| FLORIN-SOL | Florin | SOL | Active |
| SOL-USDC | SOL | USDC | Active |
Order Types
Order Sides
| Side | Description |
|---|---|
| BUY | Buying base asset with quote asset |
| SELL | Selling base asset for quote asset |
Order Types
| Type | Description |
|---|---|
| LIMIT | Execute at specified price or better |
| MARKET | Execute immediately at best available price |
| STOP_LIMIT | Limit order triggered at stop price |
Time-in-Force
| TIF | Name | Behavior |
|---|---|---|
| GTC | Good Till Cancelled | Remains open until filled or cancelled |
| IOC | Immediate Or Cancel | Fill immediately, cancel remainder |
| FOK | Fill Or Kill | Fill completely or cancel entirely |
| GTD | Good Till Date | Expires at specified time |
Order Book
The order book aggregates orders at each price level:
interface OrderBookLevel {
price: number;
quantity: number; // Total quantity at this price
orderCount: number; // Number of orders
}
interface OrderBook {
pairId: string;
bids: OrderBookLevel[]; // Sorted descending (best bid first)
asks: OrderBookLevel[]; // Sorted ascending (best ask first)
lastTradePrice?: number;
lastTradeQuantity?: number;
spread?: number; // Best ask - best bid
midPrice?: number; // (Best ask + best bid) / 2
timestamp: Date;
}Order Book Operations
import { OrderBookEngine } from '@fxyz/trading';
const book = new OrderBookEngine('FLORIN-USDC');
// Get current state
const snapshot = book.getSnapshot(50); // Top 50 levels
// Market data
const bestBid = book.getBestBidLevel();
const bestAsk = book.getBestAskLevel();
const spread = book.getSpread();Matching Engine
The matching engine processes orders with price-time priority:
import { MatchingEngine, OrderSide, OrderType, TimeInForce } from '@fxyz/trading';
const engine = new MatchingEngine();
// Register trading pair
engine.registerPair({
id: 'FLORIN-USDC',
baseAsset: 'FLORIN',
quoteAsset: 'USDC',
baseMint: '...',
quoteMint: '...',
minOrderSize: 1,
maxOrderSize: 100000,
tickSize: 0.0001,
lotSize: 1,
makerFee: 0.001,
takerFee: 0.002,
isActive: true,
createdAt: new Date(),
});
// Submit order
const result = engine.submitOrder({
pairId: 'FLORIN-USDC',
userId: 'user-123',
walletAddress: 'ABC...XYZ',
side: OrderSide.BUY,
type: OrderType.LIMIT,
price: 1.05,
quantity: 100,
timeInForce: TimeInForce.GTC,
});
if (result.status === 'ACCEPTED') {
console.log('Order ID:', result.order.id);
console.log('Trades:', result.trades.length);
}Event Callbacks
// Trade executed
engine.onTrade = (trade) => {
console.log(`Trade: ${trade.quantity} @ ${trade.price}`);
// Trigger settlement
};
// Order status changed
engine.onOrderUpdate = (order) => {
console.log(`Order ${order.id}: ${order.status}`);
};Trades
When orders match, trades are generated:
interface Trade {
id: string;
pairId: string;
makerOrderId: string;
takerOrderId: string;
makerId: string;
takerId: string;
makerWallet: string;
takerWallet: string;
side: OrderSide; // Taker's side
price: number;
quantity: number;
makerFee: number;
takerFee: number;
settlementStatus: SettlementStatus;
settlementTxId?: string;
createdAt: Date;
settledAt?: Date;
}Settlement Status
| Status | Description |
|---|---|
| PENDING | Awaiting settlement |
| ESCROW | Funds in escrow account |
| SETTLING | Settlement in progress |
| SETTLED | Successfully settled on-chain |
| FAILED | Settlement failed |
| REFUNDED | Funds returned to parties |
Market Data
OHLCV Candles
interface OHLCV {
pairId: string;
interval: string; // "1m", "5m", "1h", "1d"
open: number;
high: number;
low: number;
close: number;
volume: number;
quoteVolume: number;
tradeCount: number;
timestamp: Date;
}Ticker
interface Ticker {
pairId: string;
lastPrice: number;
bidPrice: number;
askPrice: number;
highPrice24h: number;
lowPrice24h: number;
volume24h: number;
priceChange24h: number;
priceChangePercent24h: number;
tradeCount24h: number;
timestamp: Date;
}GraphQL API
Trading operations are exposed via GraphQL:
# Queries
query GetOrderBook($pairId: String!, $depth: Int) {
orderBook(pairId: $pairId, depth: $depth) {
bids { price quantity orderCount }
asks { price quantity orderCount }
spread
midPrice
}
}
query GetMyOrders($status: [OrderStatus]) {
myOrders(status: $status) {
id
side
type
price
quantity
filledQuantity
status
}
}
# Mutations
mutation SubmitOrder($input: OrderInput!) {
submitOrder(input: $input) {
order { id status }
trades { id price quantity }
status
message
}
}
mutation CancelOrder($orderId: String!) {
cancelOrder(orderId: $orderId) {
id
status
}
}WebSocket Events
Real-time market data via WebSocket:
type WSEventType =
| 'ORDER_BOOK_SNAPSHOT'
| 'ORDER_BOOK_UPDATE'
| 'TRADE'
| 'TICKER'
| 'ORDER_UPDATE'
| 'BALANCE_UPDATE';
interface WSMessage<T> {
type: WSEventType;
pairId?: string;
data: T;
timestamp: Date;
sequence?: number;
}Subscribing to Updates
// Connect to WebSocket
const ws = new WebSocket('wss://api.fxyz.network/ws');
// Subscribe to order book
ws.send(JSON.stringify({
action: 'subscribe',
channel: 'orderbook',
pairId: 'FLORIN-USDC',
}));
// Handle updates
ws.onmessage = (event) => {
const msg = JSON.parse(event.data);
if (msg.type === 'ORDER_BOOK_UPDATE') {
updateLocalOrderBook(msg.data);
}
};External Data Sources
The network integrates with external data providers:
Price Feeds
| Source | Data | Endpoint |
|---|---|---|
| CoinGecko | Digital asset prices | /api/prices |
| BIS | FX rates, turnover | /api/fx/bis |
| RWA.xyz | Tokenized assets | /api/rwa/* |
Data Cron Jobs
| Job | Schedule | Purpose |
|---|---|---|
asset-prices | Every 5 min | Update digital asset prices |
fx-rates | Hourly | Update FX rates |
bis-data | Daily | BIS market data |
rwa-sync | Daily | RWA.xyz data |
UI Components
The trading UI is available at app.fxyz.network/markets/trade:
Order Form
- Buy/Sell tabs
- Limit/Market order types
- Price and quantity inputs
- Order preview with fees
Order Book Display
- Real-time bid/ask levels
- Depth visualization
- Spread indicator
- Mid-price display
Trade History
- Recent trades for pair
- User's trade history
- Settlement status tracking