Skip to content

drag0sd0g/MariaAlpha

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

50 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Screenshot 2026-05-03 at 19 09 21

MariaAlpha

Full-stack algorithmic trading engine — see Technical Design Document for architecture and details.

What Phase 1 Delivers

Phase 1 is complete. MariaAlpha ships as a working, end-to-end algorithmic trading engine with the following capabilities:

  • Live market data ingestion — simulated CSV replay (default) or Alpaca IEX WebSocket, with auto-reconnect and per-symbol in-memory order book
  • VWAP execution strategy enhanced by an ML signal — LightGBM classifier (15 technical indicators) called over gRPC with a Resilience4j circuit breaker; strategy hot-swap at runtime via REST
  • Risk check chain — five composable checks (max notional, max position, max portfolio exposure, max open orders, daily loss limit) with trading halt and resume
  • Dual exchange routing — simulated exchange (configurable fill latency + slippage) or Alpaca paper trading (REST + trade_updates WebSocket), switchable via EXECUTION_PROFILE
  • Real-time position and P&L tracking — mark-to-market unrealized P&L, portfolio aggregates, fill history, all persisted in PostgreSQL
  • Transaction Cost Analysis — slippage vs. arrival mid, implementation shortfall, VWAP benchmark deviation, and spread cost per completed order
  • React UI — portfolio dashboard, order entry with WebSocket live updates, daily P&L chart, served from nginx in Docker Compose
  • Full observability — Grafana LGTM stack (Alloy, Prometheus, Loki, Tempo, Grafana 11) with a provisioned Trading Pipeline dashboard covering all 7 services
  • Production-grade CI — lint, unit, integration, and end-to-end tests gating every pull request; CodeQL and Snyk security scans

The Phase 1 acceptance test — SimulatedHappyPathE2ETest — boots the full Docker Compose stack (15 long-running services + a one-shot Kafka topics initializer) and traverses the complete Tick-to-Trade pipeline on every CI run. See docs/phase-1-completion.md for the full record of what was built.

Phase 2 work has begun landing on main: the Smart Order Router (issue 2.1.1), the advanced order-type handlers IOC / FOK / GTC / Iceberg (2.1.3, 2.1.4), and the umbrella Helm chart for Kubernetes deployment (2.7.1) are all shipped. Remaining Phase 2 items — TWAP / Momentum / Implementation Shortfall strategies, Redis distributed position cache, regime classifier, Playwright UI e2e tests, and the docker image publish workflow — are tracked in §11 of the Technical Design Document.

Prerequisites

  • just — command runner used for all project tasks

    brew install just   # macOS
    
  • Docker — required for infrastructure services

Quickstart

A clean-checkout walkthrough. Tested on macOS 14 (Apple Silicon) and Ubuntu 22.04 with Docker Desktop ≥ 25.0 and 8 GB RAM allocated.

1. Clone and configure

git clone https://github.com/drag0sd0g/MariaAlpha.git
cd MariaAlpha
cp .env.example .env
# .env defaults are fine for local dev. For docker-compose, changing
# MARIAALPHA_API_KEY requires rebuilding the UI image
# (`docker compose build ui && docker compose up -d ui`) because Vite bakes the
# value into the bundle at build time. The Helm chart avoids this — the UI's
# init container renders the key into /config.js at pod start, so secret
# rotation is a `helm upgrade` + pod restart with no image rebuild.

2. Build everything

just build

This compiles every Java module, builds the React UI bundle, runs static checks (Spotless, Checkstyle, SpotBugs, ESLint, Prettier, ruff, mypy), and runs every unit and Testcontainers integration test except e2e. Expected runtime: ~5 minutes on a warm Gradle cache.

3. Bring up the stack

just run

Runs docker compose up -d and starts 15 long-running containers plus a one-shot kafka-init container that creates topics and exits. First-time runs build all service images (~3 minutes); subsequent runs reuse the cache. Wait ~90 seconds after just run returns for every service to become healthy.

4. Verify

just verify

Expected output:

Polling /actuator/health on every service...
  ✓ market-data-gateway
  ✓ strategy-engine
  ✓ execution-engine
  ✓ order-manager
  ✓ post-trade
  ✓ api-gateway
  ✓ ml-signal-service
  ✓ ui
  ✓ grafana

5. Open the UI

URL What
http://localhost:5173/ MariaAlpha UI — Dashboard + Order Entry
http://localhost:3001/ Grafana (anonymous admin, no login)
http://localhost:9090/ Prometheus
http://localhost:8080/actuator/health API Gateway aggregate health

Kubernetes Quickstart (Helm on OrbStack)

An alternative to docker-compose: deploy the same stack to a local Kubernetes cluster via the umbrella Helm chart in charts/mariaalpha/.

brew install helm                            # one-time
orb start k8s                                # OrbStack ships a single-node cluster
just k8s-up                                  # build images, helm dep update, helm install (≈3 min cold)
just k8s-test                                # helm test: actuator-health + iceberg-parent → FILLED
open http://mariaalpha.orb.local             # UI
open http://grafana.mariaalpha.orb.local     # Grafana dashboards
just k8s-down                                # uninstall + drop PVCs (dev-cluster only)

Detailed install/upgrade/rollback recipes live in docs/runbooks/helm-install.md. Secret rotation is documented in docs/runbooks/helm-rotate-secrets.md.

6. Try the API

Every API call requires the X-API-Key header. The default key is local-dev-key.

# List all available strategies.
curl -fsS -H "X-API-Key: local-dev-key" \
    http://localhost:8080/api/strategies

# Bind VWAP to AAPL.
curl -X PUT -H "X-API-Key: local-dev-key" \
    -H "Content-Type: application/json" \
    -d '{"strategyName":"VWAP"}' \
    http://localhost:8080/api/strategies/AAPL

# Configure VWAP — 100 shares spread evenly between 14:30 and 16:00 NY time.
curl -X PUT -H "X-API-Key: local-dev-key" \
    -H "Content-Type: application/json" \
    -d '{
          "targetQuantity": 100,
          "side": "BUY",
          "startTime": "14:30:00",
          "endTime": "16:00:00",
          "volumeProfile": [
            {"startTime":"14:30:00","endTime":"16:00:00","volumeFraction":1.0}
          ]
        }' \
    http://localhost:8080/api/strategies/VWAP/parameters

# Place a manual LIMIT order.
curl -X POST -H "X-API-Key: local-dev-key" \
    -H "Content-Type: application/json" \
    -d '{"symbol":"AAPL","side":"BUY","orderType":"LIMIT","quantity":10,"limitPrice":180.00}' \
    http://localhost:8080/api/execution/orders

# Read positions and portfolio summary.
curl -fsS -H "X-API-Key: local-dev-key" http://localhost:8080/api/positions
curl -fsS -H "X-API-Key: local-dev-key" http://localhost:8080/api/portfolio/summary

7. Tear down

just stop

To wipe all state (Postgres, Kafka logs, Grafana dashboards):

docker compose down -v

Troubleshooting

Symptom Likely cause Fix
MARIAALPHA_API_KEY must be set on just run .env not copied cp .env.example .env
port 5432 already in use local Postgres already running stop it or change POSTGRES_PORT_HOST in .env
ui container restarting stale build args (e.g. API key changed) docker compose build --no-cache ui
UI loads but API calls return 401 UI bundle built with a different key than the gateway expects rebuild: docker compose build ui && just run
tradingHalted: true immediately daily-loss limit tripped from a prior run curl -X POST -H "X-API-Key: $KEY" http://localhost:8080/api/execution/resume
No data on UI dashboard strategy-engine has no symbols routed yet PUT /api/strategies/AAPL (see step 6)

Infrastructure Services

Service Port Notes
PostgreSQL 16 5432 Credentials via .env
Kafka (KRaft) 9092 Single-node, no ZooKeeper
Prometheus 9090 Metrics storage, remote-write enabled
Grafana 3001 Dashboards — anonymous admin access
Alloy 12345 Telemetry collector UI; OTLP on 4317/4318
UI (nginx) 5173 Static SPA + reverse-proxy to api-gateway

Loki (logs) and Tempo (traces) run within the Docker network, reachable by Alloy and Grafana.

API Gateway (port 8080)

Single front door for the React UI and external clients. Implements:

  • REST routing to all backend services (/api/...).
  • API key authentication via X-API-Key header (or ?apiKey= query parameter for browser WebSocket clients).
  • Real-time WebSocket fan-out from Kafka to UI clients.

Configuration

Env var Required Description
MARIAALPHA_API_KEY yes Shared secret. Without it the gateway rejects every request with HTTP 401.
KAFKA_BOOTSTRAP_SERVERS yes Kafka cluster (default localhost:9092).
STRATEGY_ENGINE_URL optional Default http://localhost:8082.
ORDER_MANAGER_URL optional Default http://localhost:8086.
EXECUTION_ENGINE_URL optional Default http://localhost:8084.
POST_TRADE_URL optional Default http://localhost:8088.
ANALYTICS_SERVICE_URL optional Default http://localhost:8095.
MARKET_DATA_GATEWAY_URL optional Default http://localhost:8079.

Quickstart

export MARIAALPHA_API_KEY=local-dev-key
just run
./gradlew :api-gateway:bootRun

React UI (port 5173 in dev)

The web UI is a Vite + React 18 + TypeScript single-page app. The Phase-1 MVP exposes a Dashboard (live positions, P&L, exposure) and an Order Entry page (manual order submission, active orders, fills). Five additional pages (Market Data, RFQ, Strategies, Analytics, Reconciliation) are scaffolded as placeholders for Phase 2.

Quickstart

just run                    # bring up infra + backend services
cp ui/.env.example ui/.env.local
echo "VITE_MARIAALPHA_API_KEY=local-dev-key" >> ui/.env.local
just ui-install             # one-time: npm install
just ui-dev                 # opens http://localhost:5173

The dev server proxies /api/* and /ws/* to http://localhost:8080 (the API Gateway), so no CORS configuration is needed.

Configuration

Env var (in ui/.env.local) Required Description
VITE_MARIAALPHA_API_KEY yes Must match the key in repo-root .env.
VITE_API_BASE_URL no Leave blank for dev (uses Vite proxy). Set to http://localhost:8080 only when running vite preview against a built bundle.

Production-ish build

just ui-build               # static assets in ui/dist/
cd ui && npm run preview    # serves dist/ on http://localhost:4173

For docker-compose deployments the UI runs as an nginx container exposed on port 5173 (see docker-compose.yml). The image is built from ui/Dockerfile.

Manual order API surface

Manual orders submitted via the Order Entry page hit POST /api/execution/orders (not /api/orders). The distinction matters for external clients:

Method Path Backend Notes
POST /api/execution/orders execution-engine Submit a manual order; returns {orderId, status, acceptedAt}
DELETE /api/execution/orders/{orderId} execution-engine Cancel a manual order; 204 on success, 404 if unknown/terminal

Database

Liquibase migrations run automatically on Spring Boot service startup. Verify schema:

docker compose exec postgres psql -U mariaalpha -c '\dt'

Observability

The Grafana LGTM stack (Loki, Grafana, Tempo, Mimir/Prometheus) starts with just run. Grafana is pre-configured with all datasources and available at http://localhost:3001.

graph LR
    subgraph Application Services
        J[Java Services<br/>Micrometer + OTLP]
        P[Python Services<br/>prometheus_client + OTLP]
    end

    subgraph Observability Stack
        AL[Grafana Alloy<br/>:12345]
        PROM[Prometheus<br/>:9090]
        LOKI[Loki]
        TEMPO[Tempo]
        GF[Grafana<br/>:3001]
    end

    subgraph Infrastructure
        PG[(PostgreSQL<br/>:5432)]
        KF[Kafka<br/>:9092]
    end

    J -->|/metrics scrape| AL
    P -->|/metrics scrape| AL
    J -->|OTLP traces :4317| AL
    P -->|OTLP traces :4318| AL
    AL -->|remote write| PROM
    AL -->|push| LOKI
    AL -->|OTLP forward| TEMPO
    PROM --> GF
    LOKI --> GF
    TEMPO --> GF
Loading

Alloy is the unified telemetry collector — it scrapes Prometheus-format metrics endpoints and forwards them to Prometheus via remote write, receives OTLP traces (gRPC :4317, HTTP :4318) and forwards to Tempo, and pushes logs to Loki. Grafana queries all three backends and supports cross-linking between traces, logs, and metrics.

CI/CD

GitHub Actions workflows run on every push and PR to main:

Workflow File What it checks
CI ci.yml Java: Spotless, Checkstyle, SpotBugs, tests + JaCoCo. Python: ruff, mypy, pytest. UI: ESLint, Prettier, tsc.
CodeQL codeql.yml Security analysis for Java, Python, TypeScript (also runs weekly).
Snyk snyk.yml Dependency vulnerability scanning (requires SNYK_TOKEN secret).
PR Metadata pr-metadata.yml Auto-populates labels, milestone, assignee, and project from linked issues.

Python and UI jobs skip automatically when no source files exist yet. JaCoCo and test reports are uploaded as build artifacts. Snyk requires a SNYK_TOKEN repository secret — obtain one from snyk.io.

Branch rules

Direct pushes to main are not allowed — all changes go through pull requests. The Java (lint + test) and CodeQL (java-kotlin) checks must pass before merging. Branches are auto-deleted after merge.

About

AI-powered algorithmic trading engine covering the full desk lifecycle: market data, pricing, execution (VWAP/TWAP/IS/Close), smart order routing, real-time risk & P&L, post-trade TCA, and reconciliation. Built for extensibility: pluggable strategies, composable risk checks, and channel-agnostic execution supporting manual RFQ and electronic flow.

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors