Skip to main content

Features & Capabilities

QUANTAF provides a comprehensive suite of features for enterprise financial system testing.

Protocol Support 🌐

FIX Protocol

Multi-Version Support

Full FIX 4.2, 4.4, and 5.0 support through QuickFIX/J integration.

Session Management (FixSessionManager):

  • Manages multiple FIX sessions across different protocol versions simultaneously
  • Starts/stops SocketInitiator (trader side) and SocketAcceptor (exchange stub) instances
  • Each version has its own config file (quickfix-FIX42.cfg, quickfix-FIX44.cfg, quickfix-FIX50.cfg)

WireMock-like Stubbing (FixStubRegistry):

  • Predicate-based message matching (when(predicate).respondWith(generator))
  • Sequential response support (respondWith()thenRespondWith())
  • Configurable response delays (withDelay(Duration.ofMillis(500)))
  • Call counting and descriptive naming
  • Thread-safe via CopyOnWriteArrayList

Message Building (FixMessageBuilder):

  • Fluent builder for NewOrderSingle (35=D), ExecutionReport (35=8), OrderCancelRequest (35=F)
  • Type-safe enum setters for Side, OrderType, TimeInForce, ExecType
  • Convenience methods: fromOrderConfig(), rejectionFor(), fillFor()

Client-Server Architecture:

  • FixInitiatorWrapper — Client-side Application that sends orders and correlates responses by ClOrdID using CompletableFuture
  • FixAcceptorWrapper — Server-side Application (exchange stub) that routes incoming messages through FixInterceptor
  • FixInterceptor — Evaluates incoming messages against the stub registry, auto-generates responses with CompID swapping

Message Queue (MQ)

Pluggable MQ Support

Generic MessageBroker interface with ActiveMQ Artemis implementation.

MessageBroker interface provides:

  • publish(destination, payload) — Send messages to queues/topics
  • listen(destination, timeout) — Receive single message with timeout
  • listenWithFilter(destination, predicate, timeout) — Filtered message receive
  • isConnected() — Connectivity check
  • close() — Resource cleanup

Implementations:

BrokerClassStatus
ActiveMQ ArtemisActiveMqBrokerFully implemented (Jakarta JMS)
IBM MQIbmMqBrokerSkeleton — ready for IBM MQ client libs
KafkaConfiguration placeholder in quantaf.yml

REST API

OAuth2-Ready HTTP Testing

RestAssured wrapper with automatic token lifecycle management.

RestClientWrapper provides:

  • get(path), get(path, queryParams) — GET requests with optional query parameters
  • post(path, body) — POST with JSON body
  • put(path, body) — PUT with JSON body
  • delete(path) — DELETE requests
  • Automatic OAuth2 bearer token injection via OAuth2TokenManager

OAuth2TokenManager features:

  • Acquires tokens via client_credentials grant type
  • Caches tokens in memory
  • Auto-refreshes 60 seconds before expiry
  • Thread-safe (synchronized methods)
  • Manual invalidation support

PortfolioApiClient — typed REST client example:

  • getPortfolioPosition(accountId) — Account-level positions
  • getPositionForSymbol(accountId, symbol) — Symbol-specific positions
  • isTradeSettled(accountId, orderId) — Settlement status check

ISO 20022 (SWIFT) XML Generation

AI-Powered ISO 20022 Messages

Generate SWIFT XML messages from natural language intent via SmartStub.

Supported message types (templates):

MessageSchemaDescription
pacs.008urn:iso:std:iso:20022:tech:xsd:pacs.008.001.02FI to FI Customer Credit Transfer
camt.053urn:iso:std:iso:20022:tech:xsd:camt.053.001.02Bank to Customer Statement
sese.023urn:iso:std:iso:20022:tech:xsd:sese.023.001.01Securities Settlement Transaction

Features:

  • LLM-powered generation with SWIFT-specific system prompt
  • Template fallback for deterministic CI runs
  • Response caching for reproducible test execution
  • Pre-loading cached responses for test setup (cacheResponse(intent, xml))

Test Definition Modes

TestNG Framework

Run tests by extending QuantafBaseTest:

@Epic("Order Lifecycle")
@Feature("High Frequency Orders")
public class HighFrequencyOrderTest extends QuantafBaseTest {

@BeforeMethod
public void resetState() {
stubRegistry.reset();
ledger.clear();
}

@Test
@Severity(SeverityLevel.CRITICAL)
@Story("Order Fill")
@Description("Verifies a successful limit order fill with cross-source reconciliation")
public void testLimitOrderFillReconciliation() {
String clOrdId = marketMaker.generateClOrdId();
BigDecimal price = marketMaker.generatePrice(150.0, 2.0);
int volume = marketMaker.generateVolume(500);
LocalDate settlementDate = marketMaker.generateTradeDate(SettlementType.T2);

// Configure stub, build order, ingest records, reconcile
// ...

TradeLedgerAssert.assertThat(result)
.assertParity()
.assertSettlementDateMatch()
.assertAmountMatch(BigDecimal.valueOf(0.01));
}
}

TestNG Suite Configuration (testng.xml):

<suite name="QUANTAF Test Suite" parallel="classes" thread-count="4">
<test name="Unit Tests">
<classes>
<class name="io.github.vinipx.quantaf.unit.MarketMakerTest"/>
<class name="io.github.vinipx.quantaf.unit.FixStubRegistryTest"/>
<class name="io.github.vinipx.quantaf.unit.TradeLedgerAssertTest"/>
</classes>
</test>
<test name="Scenario Tests">
<classes>
<class name="io.github.vinipx.quantaf.scenarios.HighFrequencyOrderTest"/>
</classes>
</test>
<test name="BDD Tests">
<classes>
<class name="io.github.vinipx.quantaf.bdd.runners.CucumberTestRunner"/>
</classes>
</test>
</suite>

Cucumber BDD

Write business-readable Gherkin scenarios:

Feature: Order Lifecycle
As a trading platform QA engineer
I want to validate the full order lifecycle
So that I can ensure correct trade processing across FIX, MQ, and API

@smoke @fix
Scenario: Successful limit order fill
Given a FIX session is available for version "FIX44"
When I submit a "BUY" "LIMIT" order for "AAPL" at price 150.00 with quantity 100
Then the order should be filled at price 150.00
And the trade should reconcile across all sources

@regression @ai
Scenario: AI-generated order scenario
Given the AI scenario agent is available
When I generate an order from intent "Buy 500 shares of MSFT Market On Close"
Then the generated order should have side "BUY"
And the generated order should have symbol "MSFT"
And the generated order should have time in force "AT_CLOSE"

@regression @reconciliation
Scenario: Cross-source trade reconciliation
Given matching trade records exist for order "RECON-001"
| source | symbol | price | quantity | settlementDate |
| FIX | GOOG | 175.50 | 200 | 2026-02-10 |
| MQ | GOOG | 175.50 | 200 | 2026-02-10 |
| API | GOOG | 175.50 | 200 | 2026-02-10 |
When I reconcile the trade "RECON-001"
Then the reconciliation should pass
And all fields should match across sources

AI & Intelligent Features 🤖

NLP-to-FIX Translation (FixScenarioAgent)

Convert natural language descriptions to structured OrderConfiguration objects:

InputExtracted Fields
"Limit Buy 500 shares of AAPL at 150"Side=BUY, Type=LIMIT, Symbol=AAPL, Qty=500, Price=150
"Sell TSLA stop at 200"Side=SELL, Type=STOP, Symbol=TSLA, Price=200
"Buy GOOG Market On Close"Side=BUY, Type=MARKET, Symbol=GOOG, TIF=AT_CLOSE
"Order for MSFT that triggers a fat-finger rejection at 9999"Symbol=MSFT, ExecType=REJECTED, Price=9999

Dual-mode operation:

  1. LLM mode: Sends structured system prompt to LLM, parses JSON response into OrderConfiguration
  2. Template mode: Keyword-based deterministic extraction using regex patterns and a known symbol dictionary

Known symbol dictionary: AAPL (Apple), GOOG (Google), MSFT (Microsoft), TSLA (Tesla), AMZN (Amazon)

AI Fallback Strategy

When fallbackToTemplates is enabled (default: true), the agent first tries LLM. If unavailable, it falls back to templates — ensuring tests never depend on external LLM availability.

Smart ISO 20022 Stub Generation (SmartStub)

Generate ISO 20022 (SWIFT) XML messages from intent descriptions:

SmartStub stub = new SmartStub(); // template-only

// Generate from intent
String xml = stub.generateSwiftMessage("Generate a credit transfer for $1000");
// → pacs.008 XML with credit transfer structure

// Pre-load cached responses for deterministic tests
stub.cacheResponse("settlement instruction", myXml);

Cross-Source Reconciliation 🔄

Three-Way Comparison (TradeLedger)

The reconciliation engine compares trade records from three sources (FIX, MQ, API):

Compared fields:

FieldComparison TypeNotes
priceNumeric (within tolerance)Normalized to configurable precision
quantityNumeric (within tolerance)
amountNumeric (within tolerance)
settlementDateExact matchLocalDate comparison
symbolExact matchString equality
currencyExact matchString equality
accountExact matchString equality

Numeric tolerance: Default 0.0001 with 8-digit precision (MathContext(8, HALF_EVEN)).

Assertion DSL (TradeLedgerAssert)

Fluent assertion API for reconciliation results:

TradeLedgerAssert.assertThat(result)
.assertParity() // All fields match
.assertSettlementDateMatch() // Settlement dates match
.assertAmountMatch(BigDecimal.valueOf(0.01))// Amounts within tolerance
.assertFieldMatch("symbol"); // Specific field match

Realistic Data Generation 📊

Statistical Distributions (MarketMaker)

MethodDistributionParametersOutput
generatePrice(mean, stdDev)Normal (Gaussian)mean, standard deviationBigDecimal price (always positive)
generateVolume(lambda)Poissonexpected meanint volume (minimum 1)
generateCorrelatedPrices(mean, stdDev, corr, count)Cholesky decompositioncorrelation coefficientList<BigDecimal> correlated series
generateTradeDate(settlementType)T0, T1, T2LocalDate (business day adjusted)
generateTradeTimestamp()UniformMarket hours 9:30–16:00 ETLocalDateTime
generateClOrdId()String "QUANTAF-{timestamp}-{random}"
generateAccountId(prefix)prefix stringString "{prefix}-{8digits}"

Business Calendar Support

Pre-configured market calendars:

CalendarFactory MethodHolidays
NYSEBusinessCalendar.nyse()New Year, July 4th, Christmas
LSEBusinessCalendar.lse()New Year, Christmas, Boxing Day
TSEBusinessCalendar.tse()New Year (Jan 1-3), New Year's Eve

Custom calendars:

BusinessCalendar hkex = new BusinessCalendar("HKEX", Set.of(), Set.of(
MonthDay.of(10, 1), MonthDay.of(7, 1)));
hkex.withHoliday(LocalDate.of(2026, 2, 12));

LocalDate settlement = hkex.addBusinessDays(LocalDate.now(), 2);
int days = hkex.businessDaysBetween(start, end);
boolean isOpen = hkex.isBusinessDay(date);

Extensibility

Extend QUANTAF at every layer:

Extension PointInterface/ClassHow
New message brokerMessageBrokerImplement publish(), listen(), listenWithFilter(), isConnected(), close()
New LLM providerLlmProviderImplement complete(), getProviderName(), getModelName(), isAvailable()
New market calendarBusinessCalendarCreate with constructor or add factory method
New test scenariosQuantafBaseTestExtend and get all framework components for free
Custom FIX stubsFixStubRegistryRegister with when(predicate).respondWith(generator)

Next Steps

  • See Development for extending QUANTAF with code examples
  • Review Examples for real test patterns from the codebase
  • Check Configuration for the full quantaf.yml reference