Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

36 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

Emporia Trading Platform

Documentation JaCoCo Coverage Java 21 Spring Boot React 19 Discord

πŸ“– Comprehensive Platform Documentation & Architectural Specifications are published on the Emporia GitHub Wiki!

Emporia is an enterprise-grade, distributed stock trading platform built with Java 21, Spring Boot 4.0.7, React 19, Apache Kafka, gRPC, and PostgreSQL. Its deployable boundaries strictly follow business capabilities: static data, user preferences, market data, order command handling, and order management are independent services, with execution routing isolated behind its own service.


πŸ“š Documentation & GitHub Wiki

For in-depth architectural guides, domain design patterns, microservice deep dives, and trading business logic formulas, explore the Emporia GitHub Wiki:

Section Description
πŸ“– Trading Terminology Glossary Financial terms: Order types (Limit, Market, Stop, TIF), BBO/NBBO, SOR, VWAP, P&L.
πŸ“ Architecture & Order Flow System architecture flow, port matrix, REST vs. Kafka EDA, database ownership.
πŸ“¨ Order Command Service REST intake, listing snapshot validation, KafkaCommandGateway result correlation.
βš™οΈ Order Management Service State machine authority, OrderCommandHandler, ExecutionCommandHandler, idempotency.
🎯 Execution Service Algorithmic routing (DMA, SMART NBBO selector, VWAP slicer), venue gateways.
πŸ“œ Order Lifecycle & Invariants State machine invariants, tick/lot size checks, late fill accounting.
🧠 Order Routing & Execution Smart Order Routing (SOR) venue splitting & VWAP time-slicing logic.
πŸ“Š Market Data & Pricing L1/L2 order books, Price-Time priority matching, micro-price formulas.
πŸ’Ό Portfolio & Risk Controls Long/short positions, cost basis, Mark-to-Market P&L, fat-finger price collars.
🧩 Design Patterns Catalog CQRS, Event-Driven Architecture, Saga, Strategy, State Machine patterns.
πŸ“¦ Microservices Overview Deep-dive into all 9 microservices, Gateway, and OAuth2 Authorization.
⚑ Exchange-Core Integration Ultra-low latency LMAX Disruptor ring-buffer matching engine integration.
πŸ§ͺ Testing & Verification 91.95% JaCoCo coverage, Testcontainers PostgreSQL specs, Fray concurrency tests.
πŸ› οΈ Deployment & Operations Environment prerequisites, Docker Compose, Maven builds, React UI startup.

Architecture

flowchart TD
    Browser[React :3001] -->|OIDC + PKCE / Bearer token| Gateway[Spring Cloud Gateway :8082]
    Gateway --> Auth[Authentication :9000]
    Gateway --> Static[Static data :8081]
    Gateway --> Preferences[User preferences :8083]
    Gateway --> Market[Market data :8084]
    Gateway --> OrderCommands[Order command service :8085]
    Gateway --> Orders[Order management :8086]
    Execution[Execution service :8087] -->|client credentials| Auth
    ExchangeCore[exchange-core simulation] -->|risk seed + durable snapshots| Portfolio[Portfolio service :8088]
    ExchangeCore -->|bearer token| Auth

    Preferences -->|listing snapshots| Static
    Market -->|listing snapshots| Static
    Market -->|client credentials| Auth
    Fix[FIX simulator gRPC sources] -->|incremental books| Market
    Alpaca[Alpaca IEX] -->|snapshot + WebSocket| Market
    OrderCommands -->|validate listing| Static

    OrderCommands -->|CREATE / MODIFY / CANCEL / CANCEL_ALL| Commands[[emporia.order.commands.v1]]
    Commands --> Orders
    Orders -->|correlated outcome| Results[[emporia.order.results.v1]]
    Results --> OrderCommands
    Orders -->|immutable state events| OrderLog[[emporia.orders.v1]]
    OrderLog --> Execution
    Execution -->|SMART/VWAP child CREATE| Commands
    Execution -->|FILL / REJECT / venue CANCEL| ExecutionCommands[[emporia.execution.commands.v1]]
    ExecutionCommands --> Orders
    Execution -->|recover active parents and children| Orders
    Execution -->|same-instrument listings| Static
    Execution -->|venue quotes| Market

    Auth --> AuthDb[(PostgreSQL\nemporia_authentication)]
    Static --> StaticDb[(PostgreSQL\nemporia_static_data)]
    Preferences --> PreferencesDb[(PostgreSQL\nemporia_client_config)]
    Orders --> OrderDb[(PostgreSQL\nemporia_order_data)]
    Execution --> ExecutionDb[(PostgreSQL\nemporia_execution)]
    Portfolio --> PortfolioDb[(PostgreSQL\nemporia_portfolio)]
Loading

The browser sees one /api surface. The gateway routes requests by path and HTTP method to the service that owns each business capability.

Service ownership

Directory Port Owns
authentication 9000 OAuth2/OIDC login, users, tokens
static-data-service 8081 Instruments and exchange listings
user-preferences-service 8083 Per-user watchlists and persisted workspace layouts
market-data-service 8084 HTTP / 50551 gRPC Simulated, Alpaca IEX, or FIX-simulator market data; venue/composite books; REST, SSE, and gRPC distribution
order-command-service 8085 Authenticated create, modify, cancel, and cancel-all command boundary
order-management-service 8086 Order lifecycle, state, history, executions, and command idempotency
execution-service 8087 internal DMA venue access, best-venue SMART routing, scheduled VWAP child orders, and execution reports
portfolio-service 8088 internal Fully funded cash/equity balances and idempotent exchange snapshot receipts
gateway 8082 Browser security boundary and routing
frontend 3001 React trading workspace
trading-contracts not deployed Versioned Java/Kafka contracts shared at build time
fix-simulator-contracts not deployed Generated FIX-simulator protobuf/gRPC contracts, consumed by market-data-service and fix-market-simulator
fix-market-simulator 9876 FIX / 50051 gRPC / 8501 REST Standalone FIX/gRPC market simulator (QuickFIX/J + Guice + Jetty), an optional data source for market-data-service's FIX_SIMULATOR_CONNECTIONS mode

No running service reads or writes another Emporia service's PostgreSQL database or schema. When a service needs listing data, it calls static-data-service and forwards a bearer token. Orders store an immutable listing snapshot instead of a cross-schema foreign key.

Kafka order flow

  1. order-command-service creates a versioned command with a unique commandId and publishes it to emporia.order.commands.v1.
  2. order-management-service consumes the command, validates the transition, and updates its PostgreSQL projection in a transaction.
  3. The command result is stored in processed_order_command. A redelivered Kafka command therefore returns the same result instead of applying the change twice.
  4. order-management-service publishes the state transition to emporia.orders.v1 and a correlated response to emporia.order.results.v1.
  5. order-command-service completes the waiting browser request. If Kafka or the order processor does not answer within eight seconds, it returns a timeout or service error rather than pretending the order succeeded.

Commands are keyed by order ID (or user subject for cancel-all). The six Kafka partitions can process independent orders in parallel while maintaining the order of commands for one key.

Execution flow

  • DMA sends the order directly to the configured venue gateway. Local development uses a deterministic delayed-fill gateway.
  • SMART loads every listing for the instrument and walks executable opposite-side depth in price/time order. It creates deterministic DMA children across venues, observes the parent limit, and waits/retries when liquidity is temporarily unavailable.
  • VWAP supports absolute start/end seconds and a configurable bucket count. It emits increment-aligned catch-up children from the persisted cumulative target, parent fills, and active child exposure.
  • Child partial and final fills roll up to every parent ancestor atomically with weighted-average fill accounting.
  • Cancellation is venue-confirmed. A command records a pending targetStatus=CANCELLED; the venue acknowledgement finalizes it, while a racing execution remains valid.
  • EXECUTION_VENUE_MODE=fix enables the built-in FIXT 1.1 / FIX 5.0 SP2 source adapter for new, modify, cancel, and execution-report messages.
  • New execution consumers start at the latest order event, so introducing the service cannot accidentally execute retained historical orders.
  • On restart, execution rebuilds direct-order and SMART/VWAP runtimes from the order-management PostgreSQL projection rather than replaying creates.

See the DMA, SMART, and VWAP execution guide for strategy behavior, order examples, cancellation, recovery, and current boundaries. Service-level configuration is collected in execution-service/README.md.

Local prerequisites

  • Java 21 or newer
  • Maven 3.9+
  • Node.js and npm
  • PostgreSQL running at localhost:5432 for non-Docker local runs
  • Docker with Compose for Docker-managed infrastructure or full-stack deployment
  • Exchange-Core Engine: Clone and install exchange-core (mvn clean install) into your local Maven repository before building Emporia.

Non-Docker local PostgreSQL settings:

  • Database: emporia
  • Username: your OS username by default (the role Homebrew's postgresql formula creates), not postgres β€” scripts/run-local.sh and scripts/seed-portfolio-client.sh default DB_USERNAME accordingly; override DB_USERNAME if your local Postgres uses a different role
  • Password: admin123

Flyway creates these service-owned schemas in the local emporia database: emporia_authentication, emporia_static_data, emporia_client_config, emporia_order_data, emporia_execution, and emporia_portfolio.

Run modes

Mode Spring services run in PostgreSQL runs in PostgreSQL layout
Local Host JVM Local PostgreSQL on localhost:5432 One emporia database with service-owned schemas
Infrastructure-only Docker Host JVM Docker containers exposed on 5433-5438 One PostgreSQL database/container per service that owns persistent data
Full Docker Docker containers Docker containers One PostgreSQL database/container per service that owns persistent data

See Start locally below for how each mode is brought up.

Start locally

scripts/run-local.sh (Mode 1) and scripts/run-infra-docker.sh (Mode 2) build the reactor, start every service in the background, wait for each /actuator/health to report up, and print the frontend URL. This is the supported way to bring up the stack β€” don't start services one-by-one by hand. Full Docker mode already has scripts/local-deploy.sh and the compose commands in the Docker Deployment section below.

Run all commands from the repository root.

  1. Clone and install the exchange-core dependency into your local Maven repository:

    git clone https://github.com/nvxtien/exchange-core.git
    cd exchange-core
    mvn clean install
    cd ..
  2. Start the stack. For Mode 1 (Local), first confirm the shared non-Docker PostgreSQL database is running on localhost:5432 (see Local prerequisites above):

    scripts/run-local.sh

    For Mode 2 (Infrastructure-only Docker), no local PostgreSQL is needed β€” the script starts the per-service containers itself:

    scripts/run-infra-docker.sh
  3. Open http://localhost:3001 and sign in with admin / admin123 once the script prints "stack is up".

Both scripts default execution-service to EXECUTION_VENUE_MODE=exchange-core with EXCHANGE_CORE_ACCOUNTING_MODE=full-equity-risk, and automatically seed a USD portfolio balance for the bootstrap admin so it can receive an exchange-core risk seed. market-data-service defaults to MARKET_DATA_PROVIDER=simulated; export MARKET_DATA_PROVIDER=alpaca-iex plus APCA_API_KEY_ID/APCA_API_SECRET_KEY before running either script to use live Alpaca IEX data instead:

MARKET_DATA_PROVIDER=alpaca-iex \
APCA_API_KEY_ID='your-alpaca-key-id' \
APCA_API_SECRET_KEY='your-alpaca-secret-key' \
scripts/run-local.sh

πŸ”‘ Registering Alpaca API Credentials:

  1. Sign up for a free account at alpaca.markets.
  2. Open the Paper Trading dashboard (free sandbox).
  3. Click Generate New API Key in the right-hand panel.
  4. Copy your API Key ID (APCA_API_KEY_ID) and Secret Key (APCA_API_SECRET_KEY).

To instead consume incremental order books from one or more FIX simulator gRPC sources, export MARKET_DATA_PROVIDER=fix-simulator and FIX_SIMULATOR_CONNECTIONS; see the market-data service runbook for the connection string format and behavior.

Each script logs every service to .local-run/logs/<service>.log and tracks its pid in .local-run/pids/<service>.pid.

Check service status

Health check every service (all default to GET /actuator/health without a token):

for pair in "authentication:9000" "static-data-service:8081" "user-preferences-service:8083" \
            "market-data-service:8084" "order-command-service:8085" "order-management-service:8086" \
            "execution-service:8087" "portfolio-service:8088" "gateway:8082"; do
  name="${pair%%:*}"; port="${pair##*:}"
  echo "$name: $(curl -fsS http://localhost:$port/actuator/health)"
done
curl -fsS -o /dev/null -w 'frontend: %{http_code}\n' http://localhost:3001

Other useful checks:

cat .local-run/pids/*.pid                    # pid recorded per running service
tail -f .local-run/logs/execution-service.log  # live logs for one service
docker compose ps kafka                        # Kafka container health

Stop everything either script started, including the Kafka and/or per-service PostgreSQL containers:

scripts/stop-services.sh

If you create additional trading users after the stack is up (e.g. through the admin user-management UI), seed their exchange-core portfolio balance the same way the bootstrap admin is seeded:

scripts/seed-portfolio-client.sh <username>

Manual, per-service startup

Running one service by hand (for example under a debugger) is still supported. Kafka must already be running (docker compose up -d kafka), and authentication should start before any service that validates its own OAuth tokens. Each service's own README documents its environment variables and mvn spring-boot:run / npm run dev command: authentication, static-data-service, user-preferences-service, market-data-service, order-command-service, order-management-service, execution-service, portfolio-service, gateway, and frontend.

Every service supports GET /actuator/health without a token. Kafka is healthy when docker compose ps kafka reports healthy.


Verify

The full verification runbook lives in the Testing & Verification wiki. It covers Maven verify, PMD reports, frontend checks, property tests, PostgreSQL integration tests, Fray concurrency checks, TLA+ model checking, and the OIDC/Kafka smoke test.

Run scripts/install-git-hooks.sh once per clone to enable local CI: a pre-push hook that runs the same mvn verify + frontend checks before code leaves your machine. See docs/CI_CD.md for what it checks and the on-demand local deploy script.


🐳 Docker Deployment

In addition to running services locally on your host machine, Emporia supports containerized deployment with Docker and Docker Compose. As with Start locally above, use the provided scripts rather than starting containers or services by hand.

1. Infrastructure-Only Docker Setup

This is Mode 2 from Start locally: scripts/run-infra-docker.sh starts one PostgreSQL 16 container per service that owns persistent data (ports 5433-5438) plus Apache Kafka 4.3.1 (9092), then runs every Spring Boot service and the frontend on your host JVM against those containers.

Service Host port Database Schema
authentication 5433 emporia_authentication emporia_authentication
static-data-service 5434 emporia_static_data emporia_static_data
user-preferences-service 5435 emporia_user_preferences emporia_client_config
order-management-service 5436 emporia_order_management emporia_order_data
execution-service 5437 emporia_execution emporia_execution
portfolio-service 5438 emporia_portfolio emporia_portfolio
scripts/run-infra-docker.sh

To bring up just the containers, for example to run one service manually against them (see Manual, per-service startup):

docker compose up -d
docker compose ps

2. Full-Stack Docker Container Deployment

To launch all 9 microservices, API Gateway, React UI, service-owned PostgreSQL instances, and Kafka in containers, build the Maven jars first β€” each Dockerfile copies a pre-built target/*.jar rather than building from source β€” then run scripts/local-deploy.sh:

# 1. Build and install exchange-core into your local Maven repo
git clone https://github.com/nvxtien/exchange-core.git && cd exchange-core && mvn clean install && cd ..

# 2. Build local Maven JAR artifacts
mvn clean install -DskipTests

# 3. Build images and start the full-stack containers (default: simulated market data)
scripts/local-deploy.sh

# Or with live Alpaca IEX market data:
MARKET_DATA_PROVIDER=alpaca-iex \
APCA_API_KEY_ID='your-alpaca-key-id' \
APCA_API_SECRET_KEY='your-alpaca-secret-key' \
scripts/local-deploy.sh

3. Stop Docker

One command stops everything regardless of which mode you used β€” host-JVM processes from run-local.sh/run-infra-docker.sh, the infra containers from docker-compose.yml, and the full-stack containers from docker-compose.full.yml:

scripts/stop-services.sh

Do not add -v to the docker compose commands inside it unless you intentionally want to delete the local per-service database volumes and the Kafka volume.

About

Emporia is a microservices-based trading platform built with Spring Boot, React, Kafka, and PostgreSQL, featuring real-time market data, order management, execution strategies, and OAuth2 security.

Topics

Resources

Stars

Watchers

Forks

Releases

Packages

Used by

Contributors

Languages