Overview
I already understood, conceptually, how a centralized exchange (CEX) keeps order matching fast while keeping custodial balances consistent under concurrent load. This project was about actually building that.
Exchange-lab is a full-stack centralized exchange: user funds and orders are held and matched by the platform itself, through a custom in-memory matching engine, event-driven settlement over Redis, live market data over WebSockets, and TradingView-style charting — running as decoupled microservices behind Nginx. Unlike a DEX, there's no on-chain settlement or smart contract involved — matching and custody both happen server-side, the same model real exchanges like Binance or Coinbase use internally.
The core problem
The matching engine runs entirely in memory for speed, which means PostgreSQL — the system of record for every user's custodial balance — is always a step behind what the engine just decided. Handled carelessly, that gap causes double-spends or trades that silently vanish.
I solved it with row-level locking inside an ACID-compliant ledger worker, and an event-driven design where the API, matching engine, WebSocket server, and ledger worker only communicate through Redis — never directly with each other.
Highlights
- Custom price-time priority matching engine, running fully in memory
- Event-driven microservices — no direct service-to-service calls
- ACID ledger worker with row-level locking for safe concurrent settlement
- Custom Snowflake ID generation for sortable, collision-free order IDs
- TimescaleDB-backed TradingView-style charting
- Fully Dockerized, deployed behind Nginx on a Linux VM
Architecture
- A client submits an order via the Next.js frontend
- The API (Express) pushes it onto a Redis queue, tagged with a Snowflake ID
- The matching engine pops it off the queue, validates it, and matches it against the in-memory order book
- The engine publishes the result over Redis Pub/Sub
- The API, WebSocket server, and ledger worker each react to that same event independently — resolving the request, broadcasting to clients, and persisting to Postgres, respectively No service calls another directly — each one can be scaled, restarted, or replaced without touching the rest.
Tech Stack
Next.js, React, Tailwind CSS · Node.js, Express, TypeScript · Redis · PostgreSQL, TimescaleDB · Prisma · Docker, Nginx
Try it
Free virtual ₹50,000 balance, real order matching with realistic slippage — no signup required just to look around.
Live demo: xchg.viveksahu.com Source: github.com/vivek-src/exchange-lab



