01 — System Architecture¶
As of 2026-05-28.
TL;DR¶
QuantaTrade's platform is a Node.js / TypeScript monorepo (QuantaTradeAI/platform) of small services around a separate Java matching engine (QuantaTradeAI/exchange-core, a fork of exchange-core2). The platform speaks gRPC to the engine, NATS pub/sub + request-reply between services, and REST + WebSocket to clients. The admin surface (QuantaTradeAI/admin-panel, Next.js 14) talks Connect-protocol gRPC-web to the engine's AdminService.
The runtime authority for balances is the matching engine's in-memory UserRegistry. PostgreSQL accounts + ledger_entries is the auditable mirror maintained by ledger-service. Drift between the two is a real risk and is actively guarded against by deposit-sync wiring landed 2026-05-27 (see 03-ledger-accounting.md).
High-level shape¶
%%{init: {'theme':'base','themeVariables':{'background':'#ffffff','primaryColor':'#ddf4ff','primaryBorderColor':'#0969da','primaryTextColor':'#0a0a0a','lineColor':'#1f2328','secondaryColor':'#fff8c5','tertiaryColor':'#dafbe1','clusterBkg':'#f6f8fa','clusterBorder':'#d0d7de'}}}%%
graph LR
subgraph Clients
TRADE[trading-ui<br/>Next.js]
ADMIN[admin-panel<br/>Next.js]
end
subgraph Edge["Edge / API"]
APIGW[api-gateway<br/>NestJS]
WSGW[ws-gateway<br/>WebSocket]
end
subgraph Platform["Platform services (TS)"]
OR[order-router]
LED[ledger-service]
PMS[pms-service]
RISK[risk-service]
SUB[subscription-service]
VBIN["venue-adapter-binance<br/>(PR#6, in-flight)"]
end
subgraph Engine["Matching"]
ME[exchange-core<br/>Java + exchange-core2]
end
subgraph Data
PG[(PostgreSQL<br/>Prisma)]
REDIS[(Redis)]
NATS[NATS]
end
TRADE --> APIGW
TRADE -.WS.-> WSGW
ADMIN -.gRPC-web.-> ME
APIGW --> OR
APIGW --> LED
APIGW --> PMS
APIGW --> SUB
OR -.gRPC.-> ME
OR -.NATS.-> LED
OR -.NATS.-> WSGW
OR -.NATS.-> PMS
ME -.NATS trades.-> LED
ME -.NATS trades.-> WSGW
ME -.NATS trades.-> PMS
VBIN -.NATS venue trades.-> OR
LED --> PG
PMS --> PG
APIGW --> PG
SUB --> PG
OR --> REDIS
style Engine fill:#ddf4ff
style Platform fill:#ddf4ff
style Edge fill:#ddf4ff
style Data fill:#f6f8fa
Repository layout¶
| Repo | Tech | What lives here |
|---|---|---|
QuantaTradeAI/platform |
Node.js 20, TypeScript, NestJS (api-gateway), npm workspaces, Prisma | All 8 TS services + 7 shared packages |
QuantaTradeAI/admin-panel |
Next.js 14, React 18, Connect-protocol | Operator console |
QuantaTradeAI/exchange-core |
Java 17, Spring Boot, exchange-core2 | Matching engine + AdminService gRPC |
QuantaTradeAI/trading-ui |
Next.js 15 | End-user trading UI |
QuantaTradeAI/contracts |
Solidity, Hardhat | Token, sale, vesting, staking, revenue router (M2/M5) |
QuantaTradeAI/docs |
Markdown | This document, milestone status, specs |
This reference doc set covers platform + admin-panel; the other repos are referenced where they intersect.
Services in platform/services/¶
| Service | LOC (TS) | Port | Role |
|---|---|---|---|
api-gateway |
~12k | 3001 (REST) | NestJS app: auth, accounts, orders, custody, fees, audit. Most user-facing logic. |
order-router |
~2.7k | 3003 / 50051 (gRPC) | Validates orders, runs the risk-checker, routes to matching-engine, mirrors trades. |
ledger-service |
~1k | 3007 | Double-entry ledger writer. NATS RPC ledger.credit/debit/lock/unlock/settleTrade. |
pms-service |
~3k | 3008 | Position & PnL tracking (spot now; margin in M8). |
risk-service |
~1.5k | 3009 | Margin & liquidation engine (M8 derivatives prep). Distinct from order-router's pre-trade risk-checker — see 04-risk-controls.md. |
subscription-service |
~0.6k | 3011 | Subscription tiers (3 tiers, USDC/QTRA payments). |
ws-gateway |
~1.4k | 3002 (WS) | Authenticated WS fan-out for trade events, order updates, depth feeds. |
venue-adapter-binance 🟡 |
~0.8k | 3010 | [PR#6] Read-only Binance market data → NATS. |
Shared packages in platform/packages/¶
| Package | Role |
|---|---|
@quantatrade/common |
Decimal arithmetic, idempotent retry, auth/JWT, audit helpers, validation. The arithmetic primitives (add, subtract, multiply, isLessThan, isPositive) are used by every service that touches balances. |
@quantatrade/db |
Prisma client + schema.prisma (24 models, 693 lines). |
@quantatrade/logger |
ContextLogger interface. Recent fix added logError(msg, err, meta) and logTrade(meta) — see PR#2 history. |
@quantatrade/metrics |
Prometheus client wrapper. |
@quantatrade/nats |
NATS pub/sub + request-reply wrapper. |
@quantatrade/temporal |
Temporal workflow client (used by ledger-service for backups + activity reconciliation). |
@quantatrade/types |
Shared TS types (Order, Trade, LedgerEntry, KycStatus, CustodyTransaction, etc.). 24 models worth of interfaces. |
Transport choices¶
| From → To | Protocol | Why |
|---|---|---|
| Client → api-gateway | REST + JWT | Public-API convention; idempotency via header. |
| Client → ws-gateway | WebSocket + JWT | Authenticated streams: orders, trades, depth. |
| Admin UI → exchange-core | gRPC-web (Connect protocol) | Direct to the engine for operator commands (ListOrders, CancelOrderAdmin, etc.). The admin panel does not go through the platform services for engine commands. |
| api-gateway → other services | NATS RPC + REST | Internal call shapes; idempotency via transactionId for ledger writes. |
| order-router → exchange-core | gRPC | Order placement, cancellation, market-discovery. |
| exchange-core → platform | NATS pub/sub | Trade events, deposit confirmations, order-state changes. |
| venue-adapter-binance → order-router | NATS (venue.*.trade.*) |
Reference prices for the risk-checker [PR#6]. |
Authority and trust boundaries¶
%%{init: {'theme':'base','themeVariables':{'background':'#ffffff','primaryColor':'#ddf4ff','primaryBorderColor':'#0969da','primaryTextColor':'#0a0a0a','lineColor':'#1f2328','secondaryColor':'#fff8c5','tertiaryColor':'#dafbe1','clusterBkg':'#f6f8fa','clusterBorder':'#d0d7de'}}}%%
graph TB
subgraph Trusted["Trusted: runs in the same VPC"]
APIGW[api-gateway]
OR[order-router]
LED[ledger-service]
ME[exchange-core engine]
ADMIN[admin-panel]
end
subgraph Authority["Authoritative state"]
UREG["UserRegistry<br/>in-memory in exchange-core"]
PG_LEDGER[(ledger_entries<br/>auditable mirror)]
end
subgraph External["External / less-trusted"]
BITGO[BitGo custody]
BINANCE[Binance market data]
SUMSUB[Sumsub KYC]
end
OR -- "places & cancels" --> UREG
ME -- "settle event" --> LED
LED -- "writes" --> PG_LEDGER
BITGO -- "webhook deposit.confirmed" --> APIGW
APIGW -- "deposit RPC" --> ME
APIGW -- "ledger.credit" --> LED
BINANCE -- "L2 + trades<br/>(read-only)" --> OR
SUMSUB -. KYC webhook .-> APIGW
classDef authority fill:#ddf4ff,stroke:#e65100,stroke-width:2px
class UREG,PG_LEDGER authority
Single rule that governs everything: the engine is the runtime source of truth. The ledger is an auditable mirror, not a back-pressure. A ledger write that fails does not roll back an engine state change (it's logged and alertable instead). See 03-ledger-accounting.md for the consequences and the eventual reconciliation strategy.
Deployment topology¶
Production / staging (these are the same EC2 instance today):
- 1× EC2 t3.2xlarge at 34.199.105.99
- Cloudflare DNS (DNS-only, not proxying) → nginx → docker-compose services
- 7 subdomains under quanta.emoment.tech: api, ws, admin, matching, trade, presale, investor
- Let's Encrypt certs, auto-renewed via certbot
- SSH key: ~/.ssh/quantatrade-key.pem
The split into multi-environment (separate UAT + prod) is planned but not yet executed; see 09-deployment-and-ops.md for the current single-host shape.
Cross-cutting concerns¶
| Concern | Where it lives | See |
|---|---|---|
| Authentication | api-gateway/src/auth/, JWT signed with HS256 from env, refresh via Redis |
05-services-reference.md |
| Authorisation / RBAC | Currently coarse — admin vs user. Operator-tier RBAC is M4 scope. | 06-admin-panel.md |
| Idempotency | ledger_entries.transactionId UNIQUE, IdempotencyKey table at api-gateway boundary |
03-ledger-accounting.md |
| Audit logging | AuditLog Prisma model, written from api-gateway middleware on every state-changing call |
07-data-model.md |
| Rate limiting | NestJS throttler on api-gateway routes; nginx-level for public endpoints | 09-deployment-and-ops.md |
| Secrets | .env per host, never copied between hosts. Sensitive env vars listed per service. |
09-deployment-and-ops.md |
Where the architecture diverges from the original plan¶
docs/architecture.md (original delivery-plan diagram) shows several services that do not yet exist as code:
| Planned | Code today | Plan vs reality |
|---|---|---|
| Strategy Engine (Python) | not built | M4 deliverable |
| Signal Engine (AI predictions) | not built | M7 |
| Sentiment Engine (news/social) | not built | M6 |
| Custody Service | minimal scaffold | M2 deliverable; production custody calls go through api-gateway today |
| Treasury Service | not built | M5 (revenue router) |
| KYC Service | not built (api-gateway has KYC tables only) | M4 |
| AML Monitor | not built | M4 |
| Chain Indexer | not built | M2 |
| Token, Sale, Vesting, Staking contracts | not built | M2 / M5 |
This reference doc set documents what's built. See 10-planned-modules.md for what's specified-but-not-coded and the path to it.