Overview
The Exness Trading Platform uses three database systems, each optimized for specific use cases:
PostgreSQL Primary database for user accounts, orders, and transactional data
TimescaleDB Time-series database for historical market data and candles
MongoDB Document store for account snapshots and backups
PostgreSQL Configuration
Official PostgreSQL 16 Alpine Linux image for minimal footprint.
Docker container identifier.
Maps host port 5434 to container port 5432 to avoid conflicts with local PostgreSQL.
Default username and password for development. Change these credentials in production. Never use default credentials.
Connection String
Development (Local)
Docker Compose
Production
DATABASE_URL = postgresql://postgresql:postgresql@localhost:5434/exness
Health Check
PostgreSQL includes a health check to ensure the database is ready:
healthcheck :
test : [ "CMD-SHELL" , "pg_isready -U postgresql -d exness" ]
interval : 10s
timeout : 5s
retries : 5
Services wait for this health check to pass before starting.
Prisma Schema
The platform uses Prisma ORM for type-safe database operations. The schema is defined in packages/db/prisma/schema.prisma.
Schema Overview
generator client {
provider = "prisma-client-js"
output = "../generated/prisma"
}
datasource db {
provider = "postgresql"
url = env ( "DATABASE_URL" )
}
User Model
Stores user account information:
model User {
id Int @id @default ( autoincrement ())
userID String @unique
email String @unique
balance Float @default ( 0 )
Orders Orders []
}
Auto-incrementing primary key.
Unique user identifier for application logic.
Unique email address for authentication.
User’s account balance in USD. Defaults to 0.
One-to-many relationship with Orders model.
Orders Model
Stores trading order information:
model Orders {
orderId String @id
userId String
user User @relation ( fields : [ userId ], references : [ userID ], onDelete : Cascade )
symbol Symbol
type OrderSide
quantity Float
leverage Int
takeProfit Float ?
stopLoss Float ?
stippage Float ?
openPrice Float
closePrice Float
openTime DateTime
closeTime DateTime
profitLoss Float
@@index ( [ userId ] )
}
Unique order identifier (primary key).
Foreign key referencing User.userID with cascade delete.
Trading pair: btc, sol, or eth.
Order direction: buy or sell.
Order quantity in base currency.
Leverage multiplier (1x, 2x, 5x, etc.).
Optional take profit price level.
Optional stop loss price level.
Price at which the order was opened.
Price at which the order was closed.
Realized profit or loss in USD.
Enums
enum Symbol {
btc
sol
eth
}
enum OrderSide {
buy
sell
}
Database Migrations
The platform uses Prisma Migrate for database schema management.
Migration Process
Migration Service
The db-migrate service runs automatically on startup: db-migrate :
command : >
sh -c "
echo 'Waiting for PostgreSQL...' &&
until bun run --silent -e 'import pkg from \"pg\"; const {Client} = pkg; const client = new Client({connectionString: process.env.DATABASE_URL}); await client.connect(); await client.end();' 2>/dev/null; do
sleep 2;
done &&
echo 'PostgreSQL is ready!' &&
cd /app/packages/db &&
echo 'Running Prisma migrations...' &&
bun run db:deploy &&
echo 'Migrations completed!'
"
Wait for Database
The migration service waits for PostgreSQL to be healthy before proceeding.
Run Migrations
Executes bun run db:deploy which runs prisma migrate deploy. This applies all pending migrations to the database.
Signal Completion
The service exits successfully, triggering dependent services to start.
Migration History
The platform includes the following migrations:
20250929102453 - Initial User Model
20250929103906 - Update User Model
20250929104346 - Unique User ID
20251003093154 - Add Orders Model
20251101140859 - Add Balance Column
-- CreateTable
CREATE TABLE " User " (
"id" SERIAL PRIMARY KEY ,
"userID" TEXT NOT NULL UNIQUE ,
"email" TEXT NOT NULL UNIQUE
);
Manual Migration Commands
Create New Migration
Apply Migrations
Generate Prisma Client
Reset Database (Development Only)
# In development
cd packages/db
bun run db:migrate
# This runs: prisma migrate dev --skip-generate
Never run prisma migrate reset in production. This will delete all data in the database.
TimescaleDB Configuration
Image
timescale/timescaledb:latest-pg16
TimescaleDB with PostgreSQL 16 for time-series data.
Docker container identifier.
Maps host port 5433 to container port 5432.
TimescaleDB database name.
Default username and password for development.
Connection Configuration
Environment Variables
Connection Pool
TIMESCALE_DB_USER = myuser
TIMESCALE_DB_PASSWORD = mypassword
TIMESCALE_DB_HOST = timescaledb # Docker service name
TIMESCALE_DB_PORT = 5432 # Internal port
TIMESCALE_DB_NAME = mydb
TimescaleDB Features
TimescaleDB uses hypertables for efficient time-series storage: -- Create candle data table
CREATE TABLE candles (
time TIMESTAMPTZ NOT NULL ,
symbol TEXT NOT NULL ,
open DOUBLE PRECISION NOT NULL ,
high DOUBLE PRECISION NOT NULL ,
low DOUBLE PRECISION NOT NULL ,
close DOUBLE PRECISION NOT NULL ,
volume DOUBLE PRECISION NOT NULL
);
-- Convert to hypertable
SELECT create_hypertable( 'candles' , 'time' );
Pre-computed aggregations for fast chart loading: -- 1-minute candles
CREATE MATERIALIZED VIEW candles_1m
WITH ( timescaledb . continuous ) AS
SELECT
time_bucket( '1 minute' , time ) AS bucket,
symbol,
first ( open , time ) AS open ,
max (high) AS high,
min (low) AS low,
last ( close , time ) AS close ,
sum (volume) AS volume
FROM candles
GROUP BY bucket, symbol;
-- 5-minute candles
CREATE MATERIALIZED VIEW candles_5m
WITH ( timescaledb . continuous ) AS
SELECT
time_bucket( '5 minutes' , time ) AS bucket,
symbol,
first ( open , time ) AS open ,
max (high) AS high,
min (low) AS low,
last ( close , time ) AS close ,
sum (volume) AS volume
FROM candles
GROUP BY bucket, symbol;
Automatic data cleanup for old records: -- Drop data older than 90 days
SELECT add_retention_policy( 'candles' , INTERVAL '90 days' );
Compress old data to save space: -- Enable compression
ALTER TABLE candles SET (
timescaledb . compress ,
timescaledb . compress_segmentby = 'symbol'
);
-- Compress data older than 7 days
SELECT add_compression_policy( 'candles' , INTERVAL '7 days' );
MongoDB Configuration
Official MongoDB 7.0 image.
Docker container identifier.
Database for account snapshots.
Root credentials for MongoDB.
Connection String
Development (Local)
Docker Compose
MONGODB_URL = 'mongodb://admin:admin123@localhost:27017/exness_snapshots?authSource=admin'
The connection string must be enclosed in quotes due to special characters. The authSource=admin parameter is required for authentication.
Prisma Studio
A visual database management tool is available at http://localhost:5555
Accessing Prisma Studio
Ensure Services are Running
Verify exness-prisma-studio is running.
Explore Data
View all User records
Browse Orders with filters
Edit data directly (development only)
Execute custom queries
Prisma Studio should only be used in development. Never expose it in production environments.
Backup and Restore
PostgreSQL Backup
Backup Database
Restore Database
# Backup to file
docker compose exec postgres pg_dump -U postgresql exness > backup.sql
# Backup with compression
docker compose exec postgres pg_dump -U postgresql exness | gzip > backup.sql.gz
TimescaleDB Backup
# Backup TimescaleDB
docker compose exec timescaledb pg_dump -U myuser mydb > timescale_backup.sql
# Restore TimescaleDB
docker compose exec -T timescaledb psql -U myuser mydb < timescale_backup.sql
MongoDB Backup
# Backup MongoDB
docker compose exec mongodb mongodump \
--username admin \
--password admin123 \
--authenticationDatabase admin \
--db exness_snapshots \
--out /data/backup
# Restore MongoDB
docker compose exec mongodb mongorestore \
--username admin \
--password admin123 \
--authenticationDatabase admin \
--db exness_snapshots \
/data/backup/exness_snapshots
Troubleshooting
Check migration logs: docker compose logs db-migrate
Common issues:
PostgreSQL not ready (increase retries)
Connection string incorrect
Missing migration files
Manually run migrations: docker compose run --rm db-migrate
Can't connect to PostgreSQL
Verify database is healthy: docker compose ps postgres
docker compose logs postgres
# Test connection
docker compose exec postgres psql -U postgresql -d exness -c "SELECT 1;"
Check port conflicts: # Port 5434 should be available
lsof -i :5434
TimescaleDB not accessible
Verify TimescaleDB extension: docker compose exec timescaledb psql -U myuser -d mydb -c "SELECT * FROM pg_extension WHERE extname = 'timescaledb';"
Check connection: docker compose exec timescaledb pg_isready -U myuser -d mydb
Prisma Studio won't start
Ensure migrations completed: docker compose logs db-migrate
docker compose logs prisma-studio
Prisma Studio depends on successful migrations.
Next Steps
Environment Variables Configure database connection strings