A production-ready Contract for Difference (CFD) trading platform built with modern technologies. This full-stack monorepo enables users to trade cryptocurrency CFDs with leverage, real-time price updates, and automated risk management including stop-loss, take-profit, and automatic liquidation.
The platform achieves sub-30ms trade execution latency through in-memory order processing. Trade requests flow from the web interface through the API server to Redis Streams, where the trading engine processes orders in-memory for instant execution. Database persistence happens asynchronously via background workers, ensuring high-performance trading while maintaining data consistency.
The platform follows a microservices architecture with clear separation of concerns:
- Web Application: Next.js frontend providing the trading interface
- API Server: Express.js REST API for user authentication and trade requests
- Trading Engine: High-performance order processing and risk management service
- Price Poller: Real-time price feed service connecting to external exchanges
- Database Worker: Background service for asynchronous database operations
- Message Queue: Redis Streams for inter-service communication
- Database: PostgreSQL with Prisma ORM for data persistence
- Next.js 15: React framework with App Router
- TailwindCSS: Utility-first CSS framework
- Zustand: State management
- Lightweight Charts: Trading chart visualization
- Bun: JavaScript runtime and package manager
- Express.js: Web framework
- Prisma ORM: Database toolkit
- PostgreSQL: Relational database
- Redis Streams: Message queue for inter-service communication
- WebSocket: Real-time bidirectional communication
cfd-platform/
├── apps/
│ ├── web/ # Next.js frontend application
│ │ ├── app/ # Next.js App Router pages
│ │ ├── components/ # React components
│ │ ├── hooks/ # Custom React hooks
│ │ └── store/ # Zustand state management
│ ├── server/ # Express.js API server
│ │ ├── src/
│ │ │ ├── controllers/ # Request handlers
│ │ │ ├── middlewares/ # Express middlewares
│ │ │ ├── routes/ # API route definitions
│ │ │ ├── types/ # TypeScript type definitions
│ │ │ └── utils/ # Utility functions
│ ├── engine/ # Trading engine service
│ │ └── src/
│ │ ├── service/ # Business logic services
│ │ └── Store/ # In-memory data stores
│ ├── price_poller/ # Price feed service
│ └── db_worker/ # Database worker service
├── packages/
│ ├── database/ # Prisma schema and client
│ │ └── prisma/ # Database migrations
│ ├── redis-client/ # Redis Streams client utilities
│ ├── config-eslint/ # ESLint configuration
│ └── config-typescript/ # TypeScript configuration
└── docker-compose.yml # Docker services configuration
- Long and Short Positions: Trade both directions with leverage
- Leverage Trading: Configurable leverage for amplified positions
- Real-time Price Updates: Live market data via WebSocket connections
- Order Management: Open, close, and monitor positions
- Stop Loss: Automatic position closure at specified loss threshold
- Take Profit: Automatic position closure at specified profit target
- Automatic Liquidation: Margin call protection with automatic position closure
- Position Tracking: Real-time PnL calculation and position monitoring
- User Authentication: JWT-based secure authentication
- Account Management: User registration and login
- Balance Management: Real-time balance updates with trade execution
- Trade History: View open and closed positions
- Responsive UI: Modern, responsive trading interface
- Event-Driven Architecture: Redis Streams-based message queue for service communication
- In-Memory Stores: High-performance order and price management
- Snapshot System: State persistence and recovery
- Asynchronous Processing: Background workers for database operations
- Type Safety: End-to-end TypeScript implementation
- Monorepo Structure: Efficient development and build pipeline
- User identification and authentication
- Account balance tracking
- Last login timestamp
- Relationship to trades
- Supported trading assets (BTC, ETH, SOL)
- Asset metadata (symbol, name, image, decimals)
- Order type (long/short)
- Order status (open/closed/pending)
- Entry and exit prices
- Profit and loss calculation
- Leverage and margin information
- Stop loss and take profit levels
- Liquidation status
- System state snapshots for recovery
- JSON data storage for flexible state management
The frontend Next.js application providing the trading interface. Features include:
- Trading dashboard with real-time charts
- Order placement interface
- Position management
- Asset selection sidebar
- Responsive resizable panels
Express.js REST API handling:
- User authentication (signup, signin)
- Trade operations (open, close)
- Order queries (open orders, closed orders)
- Asset information
- JWT token validation
Core trading logic service responsible for:
- Order validation and execution
- Margin calculation
- PnL computation
- Liquidation monitoring
- Stop loss and take profit execution
- User balance management
- Redis Streams message consumption and production
Real-time price feed service:
- WebSocket connection to external exchange (Backpack Exchange)
- Price normalization and processing
- Redis Streams price update publishing
- WebSocket server for frontend connections
Background service for:
- Asynchronous database writes
- Order persistence
- User balance updates
- Trade history storage
- Node.js >= 18
- Bun >= 1.2.17
- Docker and Docker Compose
- PostgreSQL database
- Redis (or Docker Compose setup)
- Clone the repository:
git clone https://github.com/sincerelyyyash/cfd-platform.git
cd cfd-platform- Install dependencies:
bun install- Set up environment variables:
Create
.envfiles in the following locations:
packages/database/.envapps/web/.envapps/server/.env
Required environment variables (local development):
DATABASE_URL="postgresql://user:password@localhost:5432/cfd_platform"
SESSION_SECRET="your-secret-key"
REDIS_URL="redis://localhost:6379"
WS_PORT=8080 # local WebSocket port
PORT=8000 # local API server portWhen running via Docker Compose, these values are provided by
docker-compose.yml:
DATABASE_URL=postgresql://postgres:postgres@postgres:5432/cfd_platformREDIS_URL=redis://redis:6379PORT=3001(API server)WS_PORT=3002(price WebSocket)
- Run database migrations (local development):
bun run db:migrate:dev- Seed the database (optional, local development):
bun run db:seed- Start all services in development mode (without Docker):
bun run devThis will start all services concurrently using Turborepo.
This setup starts PostgreSQL, Redis, migrations, API server, trading engine, price poller, and DB worker in Docker.
The web app (apps/web) is not started in Docker by design.
- From the project root, build and start all services:
docker-compose up --buildThis will:
- Start
postgresandredis - Run Prisma migrations once via the
migrateservice - Start:
server(API) onhttp://localhost:3001price_poller(WebSocket) onws://localhost:3002enginedb_worker
- To run in the background:
docker-compose up -d --build- To stop everything:
docker-compose down- To stop and remove all data volumes (clean slate):
docker-compose down -v- To rebuild only the API server image (for API code changes):
docker-compose build server
docker-compose up -d serverStart services individually:
# Web application
cd apps/web && bun run dev
# API server
cd apps/server && bun run dev
# Trading engine
cd apps/engine && bun run dev
# Price poller
cd apps/price_poller && bun run dev
# Database worker
cd apps/db_worker && bun run devBuild all packages and apps:
bun run buildRun ESLint across all packages:
bun run lintFormat code with Prettier:
bun run format# Create and apply migration
bun run db:migrate:dev
# Deploy migrations (production)
bun run db:migrate:deploy
# Push schema changes (development)
bun run db:push
# Seed database
bun run db:seedPOST /api/v1/user/signup- User registrationPOST /api/v1/user/signin- User loginGET /api/v1/user/me- Get current user (protected)
POST /api/v1/trade/open- Open a new position (protected)POST /api/v1/trade/close- Close an existing position (protected)GET /api/v1/trade/open-orders- List open positions (protected)GET /api/v1/trade/closed-orders- List closed positions (protected)
GET /api/v1/asset- Get all available trading assets
- Long: Buy position expecting price increase
- Short: Sell position expecting price decrease
- Margin = (Quantity × Entry Price) / Leverage
- Short positions require 110% of calculated margin
Profit and Loss is calculated based on:
- Order type (long/short)
- Entry price vs current price
- Position quantity
- Leverage multiplier
Positions are automatically liquidated when:
- Equity (margin + PnL) <= 0
- Stop loss price is reached
- Take profit price is reached
The price poller service exposes a WebSocket server for real-time price updates:
- Local dev (bun):
ws://localhost:8080 - Docker Compose:
ws://localhost:3002
Redis connectivity in Docker:
- In-cluster services use
REDIS_URL=redis://redis:6379. - From the host, use
redis://localhost:6379.
Message Format:
{
"asset": "BTC",
"price": 45000,
"bid": 44950,
"ask": 45050,
"decimals": 4
}The platform uses the following Redis Streams for inter-service communication:
trade_stream: Server requests and price updates flow to the trading engineengine_stream: Engine responses and database operation requests flow to the server and DB worker
The trading engine maintains in-memory stores for:
- OrderStore: Active orders and positions
- PriceStore: Latest asset prices
- UserStore: User balances and account information
Periodic snapshots are taken to enable:
- System recovery after restart
- State persistence
- Data consistency
- JWT-based authentication
- Password hashing with bcrypt
- CORS configuration
- Input validation with Zod
- Protected API routes
- In-memory stores for low-latency order processing
- Asynchronous database operations via workers
- Event-driven architecture for scalability
- Efficient decimal arithmetic for financial calculations
- WebSocket connections for real-time updates
The platform uses fixed-point arithmetic for financial calculations:
- Balance: 2 decimal places (multiplier: 100)
- Price: 4 decimal places (multiplier: 10,000)
- Quantity: 4 decimal places (multiplier: 10,000)