A Spring Boot service that translates REST HTTP calls into FIX 4.4 protocol messages and back. It acts as a bridge so that applications with no knowledge of FIX can participate in FX trading workflows by sending simple JSON HTTP requests.
sequenceDiagram
participant Client as Client Application
participant Gateway as FIX Gateway
participant Counterparty as FIX Counterparty
Note over Client,Counterparty: Quote Request Flow
Client->>Gateway: POST /quote
Gateway->>Counterparty: QuoteRequest (FIX R)
Counterparty-->>Gateway: Quote (FIX S)
Gateway-->>Client: JSON Quote
Note over Client,Counterparty: Order Execution Flow
Client->>Gateway: POST /order
Gateway->>Counterparty: NewOrderSingle (FIX D)
Counterparty-->>Gateway: ExecutionReport (FIX 8)
Gateway-->>Client: JSON Fill
- Features
- Requirements
- Quick Start
- Configuration
- API Reference
- Running with Docker
- Local Development (Mock Simulator)
- Architecture
- Contributing
- License
- Three REST endpoints covering the full RFQ trading lifecycle:
POST /quote— request an exchange rate quote (no trade execution)POST /order— execute a trade against a previously obtained quotePOST /quote-order— request a quote and execute immediately in one call
- FIX 4.4 via QuickFIX/J with dual concurrent sessions (market data + trading)
- PostgreSQL persistence — every quote request and execution report is stored
- API key authentication — configurable multi-key support via
X-API-Keyheader - Reactive stack — Spring WebFlux with virtual threads (JDK 21)
- Configurable weekly sequence reset — keeps FIX sequence numbers in sync with the counterparty
| Dependency | Version |
|---|---|
| JDK | 21 |
| Maven | 3.9+ |
| PostgreSQL | 13+ |
git clone https://github.com/your-org/fix-http-server.git
cd fix-http-server
mvn clean package -DskipTestsCopy the example files into a secrets/ directory (gitignored):
mkdir secrets
cp secrets.template/application.properties/application.properties.example.txt secrets/application.properties
cp secrets.template/quickfixj.cfg.example.txt secrets/quickfixj.cfgEdit both files — at minimum set your database credentials and API key:
# secrets/application.properties
spring.datasource.password=your_db_password
api.security.keys=your-api-key# secrets/quickfixj.cfg — set your FIX counterparty details
[SESSION]
SenderCompID=YOUR.SENDER.ID
TargetCompID=COUNTERPARTY.TARGET.ID
SocketConnectHost=fix.counterparty.example.com
SocketConnectPort=9999CREATE DATABASE fix_gateway;java \
-Dspring.config.location=classpath:application.properties,file:secrets/application.properties \
-Dquickfixj.config=file:secrets/quickfixj.cfg \
-jar target/fix-http-server-*.jarThe server starts on port 9005 by default.
# Health check (no auth required)
curl http://localhost:9005/health
# Request a quote
curl -X POST http://localhost:9005/quote \
-H "Content-Type: application/json" \
-H "X-API-Key: your-api-key" \
-d '{"quoteRequestId":"QR-001","symbol":"EUR/USD","side":"BUY","orderQuantity":100000}'All settings support environment variable overrides. The pattern is:
property.name=${ENV_VAR_NAME:default_value}| Property | Env var | Default | Description |
|---|---|---|---|
server.port |
SERVER_PORT |
9005 |
HTTP port |
spring.datasource.url |
SPRING_DATASOURCE_URL |
jdbc:postgresql://localhost:5432/fix_gateway |
PostgreSQL JDBC URL |
spring.datasource.username |
SPRING_DATASOURCE_USERNAME |
postgres |
DB user |
spring.datasource.password |
SPRING_DATASOURCE_PASSWORD |
— | Must be set |
api.security.keys |
API_SECURITY_KEYS |
— | Must be set. Comma-separated list of valid API keys |
quickfixj.config |
QUICKFIXJ_CONFIG |
classpath:config/quickfixj.cfg |
Path to QuickFIX/J config file |
| Property | Env var | Default | Description |
|---|---|---|---|
fix.tag15.enabled |
FIX_TAG15_ENABLED |
true |
Include Currency (Tag 15) in QuoteRequest. Set to false for non-production environments where the counterparty does not expect this tag |
fix.reset.day |
FIX_RESET_DAY |
MONDAY |
Day of week for weekly sequence number reset |
fix.reset.time |
FIX_RESET_TIME |
07:55 |
Time for weekly reset (HH:mm, 24-hour) |
fix.reset.timezone |
FIX_RESET_TIMEZONE |
UTC |
Timezone for the reset schedule |
The gateway expects two FIX sessions with specific names in the session IDs:
- ST_STREAMING (
SenderCompIDcontainingMKT) — market data / quote requests - ST_TRADING (
SenderCompIDcontainingTRD) — order execution
See secrets.template/quickfixj.cfg.example.txt for a full annotated example.
All trading endpoints require X-API-Key: <key> header. The /health endpoint is public.
Request an exchange rate quote without executing a trade.
// Request
{
"quoteRequestId": "QR-12345",
"symbol": "EUR/USD",
"side": "BUY",
"orderQuantity": 100000,
"settlementType": "TOD"
}
// Response
{
"quoteRequestId": "QR-12345",
"quoteId": "QUOTE-98765",
"symbol": "EUR/USD",
"side": "BUY",
"price": 3.6250,
"bidPrice": 3.6240,
"offerPrice": 3.6250,
"quantity": 100000,
"settlementType": "TOD",
"settlementDate": "20251223",
"validUntil": "2025-12-23T15:02:18Z",
"timestamp": "2025-12-23T15:01:48Z"
}Execute a trade using a previously obtained quote. Requires quoteRequestId from a prior /quote call.
// Request
{
"clientOrderId": "ORDER-12345",
"symbol": "EUR/USD",
"side": "BUY",
"orderQuantity": 100000,
"quoteRequestId": "QR-12345"
}
// Response
{
"clientOrderId": "ORDER-12345",
"orderId": "GW-ORD-001",
"execId": "EXEC-001",
"orderStatus": "FILLED",
"executionType": "FILL",
"symbol": "EUR/USD",
"side": "BUY",
"orderQuantity": 100000.0,
"executedQuantity": 100000.0,
"executedPrice": 1.1467,
"settlementType": "TOD",
"settlementDate": "20260619",
"timestamp": "2026-06-19T15:02:09Z"
}Request a quote and execute in a single call. Use when you do not need to review the quote before trading.
// Request
{
"clientOrderId": "ORDER-12345",
"symbol": "EUR/USD",
"side": "BUY",
"orderQuantity": 100000,
"settlementType": "TOD"
}Response is the same shape as /order.
{
"code": "INVALID_API_KEY",
"message": "Invalid API key",
"timestamp": "2025-12-04T10:30:00.000Z"
}| HTTP status | Code | Meaning |
|---|---|---|
| 400 | VALIDATION_ERROR |
Missing or invalid request field |
| 401 | MISSING_API_KEY |
X-API-Key header absent |
| 401 | INVALID_API_KEY |
Key not recognised |
| 408 / 504 | REQUEST_TIMEOUT |
FIX counterparty did not respond in time |
| 500 | INTERNAL_ERROR |
Unexpected server error |
# Build image
docker build -t fix-http-server .
# Or use docker-compose (requires secrets/ directory to be populated first)
docker-compose upThe docker-compose.yml mounts ./secrets/ (gitignored) into the container — populate it with your application.properties and quickfixj.cfg before starting.
The bundled quickfixj.cfg (in src/main/resources/config/) connects to a local mock FIX simulator on ports 9877 (market data) and 9878 (trading). You can run any FIX 4.4-compatible acceptor on those ports, for example QuickFIX/J's executor sample.
To skip the FIX connection entirely during development (e.g. for REST layer testing only), you can wire a stub ApplicationAdapter in a test Spring profile.
┌─────────────────────────────────────────────────────────┐
│ Spring WebFlux │
│ QuoteController OrderController QuoteOrderController│
└──────────────────────────┬──────────────────────────────┘
│
┌──────────────────────────▼──────────────────────────────┐
│ Service layer │
│ QuoteService OrderService QuoteOrderService │
└──────────────────────────┬──────────────────────────────┘
┌────────────┴────────────┐
▼ ▼
┌─────────────────────┐ ┌───────────────────────────┐
│ QuickFIX/J │ │ PostgreSQL (JPA) │
│ │ │ │
│ ST_STREAMING (MKT) │ │ quote_requests │
│ ST_TRADING (TRD) │ │ orders │
└─────────────────────┘ └───────────────────────────┘
│
▼
FIX Counterparty
Session handling: QuickFixSessionManager manages both FIX sessions. Quote requests are sent via ST_STREAMING; order executions via ST_TRADING. Pending requests are held in an in-memory ConcurrentHashMap with configurable timeouts.
Weekly reset: WeeklyResetScheduler sends a Logon with ResetSeqNumFlag=Y on the configured day/time to re-synchronise sequence numbers with the counterparty.
See CONTRIBUTING.md.
For commercial support, enterprise-grade deployments or consulting services, contact us at: 🌐 https://paicore.tech