Low-latency Spring Boot backend for a stock brokerage platform: trading and portfolio
1.7K
A low-latency stock brokerage web application built with Spring Boot backend and Angular frontend.
A project-level quality automation toolkit is available in quality/README.md.
It includes:
The application ships with a full, free observability stack: Metrics (Prometheus + Grafana), Distributed Logs (Loki + Promtail), and Traces (Tempo). It lives in a separate docker-compose.observability.yml so you can reuse it for any number of applications.
| Component | Container | Host Port | Purpose |
|---|---|---|---|
| Angular / nginx | stockbrokerage-frontend | 80 | Web UI |
| Spring Boot | stockbrokerage-backend | 8080 | REST API + Prometheus scrape endpoint |
| PostgreSQL 16 | stockdb-postgres | 5432 | Relational data |
| Redis 7 | stockdb-redis | 6379 | Cache + sessions |
| Prometheus | obs-prometheus | 9090 | Metrics collection |
| Grafana | obs-grafana | 3000 | Dashboards, logs & traces UI |
| Loki | obs-loki | 3100 (internal) | Log aggregation |
| Tempo | obs-tempo | 3200 (API) / 9411 (Zipkin) | Distributed tracing |
| Promtail | obs-promtail | — | Log shipper (tails logs/*.json) |
graph LR
BROWSER["fa:fa-globe Browser"]
subgraph APP["Application Stack · docker-compose.yml"]
direction TB
FE["nginx + Angular\n:80"]
BE["Spring Boot\n:8080\n/actuator/prometheus"]
PG[("PostgreSQL 16\n:5432")]
RD[("Redis 7\n:6379")]
end
LOGFILE["logs/\nstock-brokerage.json"]
subgraph OBS["Observability Stack · docker-compose.observability.yml"]
direction TB
PROM["Prometheus\n:9090"]
GF["Grafana\n:3000"]
LK["Loki\n:3100"]
PRTL["Promtail"]
TMPO["Tempo\n:3200 (API)\n:9411 (Zipkin)"]
end
BROWSER -->|":80"| FE
BROWSER -->|":3000 dashboards"| GF
FE -->|":8080"| BE
BE --> PG
BE --> RD
BE -. "Brave spans\n:9411" .-> TMPO
BE -->|"JSON logs"| LOGFILE
PROM -->|"scrape /actuator/prometheus"| BE
PRTL -->|"tail"| LOGFILE
PRTL -->|"push :3100"| LK
GF -->|"query"| PROM
GF -->|"query"| LK
GF -->|"query"| TMPO
# Pin the Docker image version used by docker-compose.yml.
$env:APP_VERSION = "1.8.0"
# Application only
start-app.bat
# Application + observability
start-app.bat obs
# Application + observability using the Compose overlay
docker compose -f docker-compose.yml -f docker-compose.observability.yml up -d
# Stop application + observability
stop-app.bat all
| URL | Purpose | Credentials |
|---|---|---|
| http://localhost | Web UI | see accounts below |
| http://localhost:8080/swagger-ui.html | API docs | — |
| http://localhost:3000 | Grafana dashboards | admin / admin |
| http://localhost:9090 | Prometheus query | — |
| http://localhost:8080/actuator/prometheus | Raw metrics | — |
| http://localhost:8080/actuator/health | Health check | — |
Two dashboards are auto-provisioned on first boot — no manual import required:
Dashboards → Stock Brokerage → Stock Brokerage Overview
Panels: API Traffic (RPS, error rate, latency P50/P95/P99), JVM (heap, non-heap, GC), CPU & threads, HikariCP pool, Batch Jobs (prediction + trend run history, symbol coverage, weight update rates, error stream, rate-limit hits).
Dashboards → Stock Brokerage → Spring Boot Overview
Panels: HTTP Request Rate, Error Rate, Latency (P50/P95/P99), JVM Heap, Non-Heap, GC pauses, CPU, HikariCP pool, Log events by level, Thread count, Process uptime.
How provisioning works: Grafana reads
observability/grafana/provisioning/on startup. Datasources (prometheus,loki,tempo) are provisioned with fixed UIDs so dashboard panel references always resolve. Dashboard JSONs inobservability/grafana/dashboards/are loaded automatically — no clicking "Import" needed.
traceIdobservability/prometheus/prometheus.yml:
- job_name: 'my-other-app'
static_configs:
- targets: ['host.docker.internal:8081']
labels:
application: 'my-other-app'
metrics_path: '/actuator/prometheus'
observability/promtail/promtail-config.yml pointing to the new app's JSON log file.curl -X POST http://localhost:9090/-/reloadFor environments where running separate compose files is not practical, a single Docker image bundles all nine services (app + observability):
# Build
docker build -f Dockerfile.allinone-obs -t vkdocker/stock-brokerage-allinone-obs .
# Run (exposes all UI ports + app port)
docker run -d `
-p 80:80 `
-p 3000:3000 `
-p 9090:9090 `
--name stockapp-full `
vkdocker/stock-brokerage-allinone-obs
# Wait ~90 s, then open:
# http://localhost – Web UI
# http://localhost:3000 – Grafana (admin / admin)
# http://localhost:9090 – Prometheus
# Install Chocolatey (if not already installed)
Set-ExecutionPolicy Bypass -Scope Process -Force; [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072; iex ((New-Object System.Net.WebClient).DownloadString('https://community.chocolatey.org/install.ps1'))
# Install tools
choco install openjdk -y
choco install maven -y
choco install nodejs -y
choco install docker-desktop -y
choco install git -y
# Install Homebrew (if not already installed)
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
# Install tools
brew install openjdk@23
brew install maven
brew install node
brew install --cask docker
brew install git
sudo apt update
sudo apt install openjdk-23-jdk maven nodejs npm docker.io docker-compose git -y
Extract the project zip file to your desired location:
# Example
unzip ws-trd-1.zip -d ~/projects/
cd ~/projects/ws-trd-1
Start PostgreSQL and Redis using Docker Compose:
docker-compose up -d
Verify services are running:
docker ps
You should see both stockdb-postgres and stockdb-redis containers running.
The application is pre-configured with default settings. If needed, you can modify:
Backend Configuration (src/main/resources/application.yml):
Frontend Configuration (frontend/src/app/services/api.service.ts):
# Navigate to project root
cd ws-trd-1
# Clean and build (skips tests for faster build)
mvn clean package -DskipTests
# Run the backend
mvn spring-boot:run
The backend will start on http://localhost:8080
Open a new terminal:
# Navigate to frontend directory
cd ws-trd-1/frontend
# Install dependencies
npm install
# Start development server
npm run dev
The frontend will start on http://localhost:4200
admin1 / Password: pass1234admin2 / Password: pass1234client1 / Password: pass1234 (Alice Johnson - $100,000)client2 / Password: pass1234 (Bob Smith - $50,000)client3 / Password: pass1234 (Charlie Brown - $75,000)client4 / Password: pass1234 (Diana Prince - $150,000)client5 / Password: pass1234 (Eve Davis - $25,000)config/throttle-config.yaml at the project root; the registry auto-reloads within 60 seconds (or use POST /api/admin/resilience/reload for immediate effect)| Service | TPS | Notes |
|---|---|---|
| TradeService | 5 | Core order execution |
| AccountService | 5 | Cash operations |
| PortfolioService | 10 | Real-time portfolio |
| StockPriceService | 10 | Yahoo Finance proxy |
| StockPricePredictionService | 2 | Compute-heavy |
| TrendAnalysisService | 3 | ML weights |
| AuthService | 3 | Login protection |
| ReconciliationService | 1 | Background only |
| Batch/Audit services | disabled | No throttle |
Too Many Requests when throttled; circuit breaker returns 503 when open# Check if port 8080 is available
netstat -ano | findstr :8080 # Windows
lsof -i :8080 # macOS/Linux
# Check database connection
docker logs stockdb-postgres
docker logs stockdb-redis
# Clear node modules and reinstall
cd frontend
rm -rf node_modules package-lock.json
npm install
# Restart Docker containers
docker-compose down
docker-compose up -d
# Wait for health checks
docker ps
# Login
POST http://localhost:8080/api/auth/login
Content-Type: application/json
{
"username": "client1",
"password": "pass1234"
}
# Execute Market Order
POST http://localhost:8080/api/trade/execute
Authorization: Basic {credentials}
{
"clientId": 1,
"symbol": "TQQQ",
"quantity": 10,
"price": 55.43,
"type": "BUY",
"orderType": "MARKET"
}
# Execute Limit Order
POST http://localhost:8080/api/trade/execute
Authorization: Basic {credentials}
{
"clientId": 1,
"symbol": "TECL",
"quantity": 5,
"price": 60.00,
"type": "BUY",
"orderType": "LIMIT"
}
# Get Portfolio Summary
GET http://localhost:8080/api/portfolio/client/{clientId}/summary
Authorization: Basic {credentials}
ws-trd-1/
├── src/
│ ├── main/
│ │ ├── java/
│ │ │ └── com/example/stockbrokerage/
│ │ │ ├── config/ # Configuration classes
│ │ │ ├── controller/ # REST controllers
│ │ │ ├── dto/ # Data transfer objects
│ │ │ ├── entity/ # JPA entities
│ │ │ ├── exception/ # Exception handling
│ │ │ ├── repository/ # Data repositories
│ │ │ └── service/ # Business logic
│ │ │ ├── TradeService.java
│ │ │ ├── PortfolioService.java
│ │ │ ├── LimitOrderScheduler.java
│ │ │ ├── ReconciliationService.java
│ │ │ └── StockPriceService.java
│ │ └── resources/
│ │ ├── application.yml # App configuration
│ │ └── rules/ # Drools rule files
│ └── test/ # Unit tests
├── frontend/
│ └── src/
│ └── app/
│ ├── components/ # Angular components
│ └── services/ # Angular services
├── docker-compose.yml # Application services (postgres, redis, backend, frontend)
├── docker-compose.observability.yml # Observability stack (Prometheus, Grafana, Loki, Tempo, Promtail)
├── observability/
│ ├── prometheus/prometheus.yml # Prometheus scrape config
│ ├── grafana/
│ │ ├── provisioning/datasources/ # Auto-provisioned Prometheus + Loki + Tempo
│ │ ├── provisioning/dashboards/
│ │ └── dashboards/spring-boot.json # Pre-built Spring Boot dashboard
│ ├── loki/loki-config.yml
│ ├── promtail/promtail-config.yml
│ └── tempo/tempo-config.yml
├── Dockerfile.allinone # All-in-one image (app only, 4 processes)
├── Dockerfile.allinone-obs # All-in-one image with observability (9 processes)
├── start-app.bat # Windows: start app [+ observability]
├── stop-app.bat # Windows: stop app [+ observability]
├── pom.xml # Maven dependencies
└── README.md # This file
To move this project to another machine:
# On current machine - commit and push
git add .
git commit -m "Latest changes"
git push origin main
# On new machine - clone
git clone <your-repo-url>
cd ws-trd-1
Exclude these folders/files (they will be regenerated):
target/frontend/node_modules/frontend/dist/frontend/.angular/.git/ (if not using git)logs/Create ZIP:
# Windows PowerShell
Compress-Archive -Path ws-trd-1 -DestinationPath ws-trd-1-portable.zip
# macOS/Linux
zip -r ws-trd-1-portable.zip ws-trd-1 -x "*/target/*" "*/node_modules/*" "*/dist/*" "*/.angular/*" "*/logs/*"
Transfer the ZIP file to new machine
Extract and follow Setup Instructions above
For production deployment:
Update Configuration:
Build for Production:
# Backend
mvn clean package
# Frontend
cd frontend
npm run build
Deploy:
target/stock-brokerage-1.0-SNAPSHOT.jarfrontend/dist/browser/This project is for educational purposes.
For issues or questions:
docker logs stockdb-postgres or docker logs stockdb-redisRun the application:
mvn spring-boot:run
Or run the JAR directly:
java -jar target/stock-brokerage-1.0-SNAPSHOT.jar
The backend will start on http://localhost:8080
cd frontend
npm install
npm start
The frontend will start on http://localhost:4200
Once the backend is running, access Swagger UI at:
POST /api/trades - Execute a new tradeGET /api/trades - Get all tradesGET /api/trades/{id} - Get trade by IDGET /api/trades/client/{clientId} - Get trades by clientGET /api/admin/clients - Get all clientsPOST /api/admin/clients - Create new clientPUT /api/admin/clients/{id} - Update clientDELETE /api/admin/clients/{id} - Delete clientGET /api/admin/rules - Get all rulesPOST /api/admin/rules - Create new rulePUT /api/admin/rules/{id} - Update ruleDELETE /api/admin/rules/{id} - Delete ruleGET /api/admin/resilience/status - Current throttle config, per-service overrides, and live circuit-breaker statesPOST /api/admin/resilience/reload - Force-reload config/throttle-config.yaml immediately (without waiting 60 s)The application uses Drools rule engine for:
Rule types include:
FRAUD_CHECK - Fraud detection rulesRISK_LIMIT - Risk management rulesTRADING_HOURS - Trading time restrictionsPOSITION_LIMIT - Position size limitsPRICE_VALIDATION - Price validation rulesThe system includes multiple fraud detection layers:
Client Management
Rule Management
Trade Monitoring
All events are logged to:
logs/stock-brokerage.logContent type
Image
Digest
sha256:3a6ae1212…
Size
233.7 MB
Last updated
5 months ago
docker pull vkdocker/stock-brokerage-backend