The Exness Trading Platform is built on a distributed microservices architecture designed for high performance, scalability, and real-time data processing. Each service has a specific responsibility and communicates through Redis Streams, Redis Pub/Sub, and REST APIs.
The architecture diagram is available in the source repository at packages/assets/ExnessArchitecture.png
Purpose: RESTful API server handling HTTP requests for authentication, trading, and account management.Technology: Express.js with TypeScript running on Bun runtimeKey Responsibilities:
User authentication with JWT tokens
API endpoint routing for trading operations
Balance and asset management
Historical candle data retrieval
Request validation and error handling
Code Location: apps/Backend/src/index.ts:1-50
import express from "express";import { config, redisStreams } from "@repo/config";import helmet from "helmet";import cors from 'cors';const app = express();const PORT = config.PORT;// Middleware setupapp.use(express.json({ limit: "10mb" }));app.use(express.urlencoded({ extended: true }));app.use(helmet());app.use(cors());// Initialize Redis Streams client and share via app.localsconst RedisStreams = redisStreams(config.REDIS_URL);await RedisStreams.connect();app.locals.redisStreams = RedisStreams;// Route registrationapp.use('/api/v1/auth', authRouter);app.use('/api/v1/balance', balanceRouter);app.use('/api/v1/supportedAssets', assetRouter);app.use('/api/v1/candles', candleRouter);app.use('/api/v1/trade', tradeRouter);app.listen(PORT, () => { console.log(`Server started at: ${PORT}`);});
API Endpoints:
POST /api/v1/auth/login - Email-based authentication
GET /api/v1/auth/verify - Token verification and user creation
GET /api/v1/balance - Get user balance (protected)
GET /api/v1/supportedAssets - List tradable assets
Purpose: High-performance in-memory order processing and execution engine.Technology: Bun runtime with concurrent message processingKey Responsibilities:
In-memory user balance management
Order matching and execution
Real-time trade validation
Parallel message processing (up to 10 concurrent tasks)
Idempotent operations for reliability
Code Location: apps/Engine/src/index.ts:1-73
Engine Core (apps/Engine/src/index.ts:1-73)
import { config, redisStreams, constant } from "@repo/config";import { tradeFunction } from "./functions/tradeFunction.js";const RedisStreams = redisStreams(config.REDIS_URL);await RedisStreams.connect();// Parallel processing configurationconst CONCURRENCY_LIMIT = 10; // Process up to 10 messages concurrentlyconst CONSUMER_GROUP = "engine-group";const CONSUMER_NAME = `engine-${process.pid}-${Date.now()}`;let activeTasks = 0;const taskQueue: Array<() => Promise<void>> = [];// Process a single messageasync function processMessage(result: any) { try { await tradeFunction(result); } catch (error) { console.error("Error processing message:", error); }}// Worker function to process queued tasksasync function worker() { while (true) { if (taskQueue.length > 0 && activeTasks < CONCURRENCY_LIMIT) { const task = taskQueue.shift(); if (task) { activeTasks++; task().finally(() => { activeTasks--; }); } } else { await new Promise(resolve => setTimeout(resolve, 10)); } }}// Start worker threadsconst WORKER_COUNT = Math.min(CONCURRENCY_LIMIT, 5);for (let i = 0; i < WORKER_COUNT; i++) { worker();}// Continuously consume messages from Redis Streamwhile (true) { const result = await RedisStreams.readNextFromRedisStream( constant.redisStream, 0, // Block indefinitely { consumerGroup: CONSUMER_GROUP, consumerName: CONSUMER_NAME } ); if (result) { taskQueue.push(() => processMessage(result)); console.log(`Queued message. Queue length: ${taskQueue.length}, Active: ${activeTasks}`); }}
Purpose: Real-time data distribution to frontend clients via WebSocket connections.Technology: WebSocket Server (ws) with Redis Pub/Sub integrationKey Responsibilities:
Maintain persistent WebSocket connections with clients
Subscribe to Redis Pub/Sub channels
Broadcast real-time price updates to all connected clients
Purpose: Fetches live market data from Binance WebSocket API and distributes to other services.Technology: WebSocket client with Redis integrationKey Responsibilities:
Connect to Binance WebSocket API
Subscribe to multiple trading pairs (BTC, ETH, SOL)
Calculate bid/ask spreads (0.5% from mid-price)
Publish real-time prices to Redis Pub/Sub
Queue trade data to Redis for batch processing
Send aggregated updates to Engine via Redis Streams
Purpose: Processes and stores historical market data in TimeScaleDB for time-series analysis.Technology: TimeScaleDB (PostgreSQL extension) with continuous aggregatesKey Responsibilities:
Consume trade data from Redis queue
Batch insert trades into TimeScaleDB hypertable
Handle timestamp conversion and validation
Implement conflict resolution (ON CONFLICT DO NOTHING)
Support candle generation via continuous aggregates