package com.example.trading.gateway.order; import java.math.BigDecimal; import java.time.LocalDateTime; import quickfix.FieldNotFound; import quickfix.field.ClOrdID; import quickfix.field.OrdType; import quickfix.field.OrderQty; import quickfix.field.Price; import quickfix.field.SenderCompID; import quickfix.field.Side; import quickfix.field.Symbol; import quickfix.field.TargetCompID; import quickfix.field.TransactTime; import quickfix.fix44.NewOrderSingle; /** Dictionary validation stays in QuickFIX/J; demo business constraints live here. */ public final class FixOrderMapper { public IncomingOrder map(NewOrderSingle message) throws FieldNotFound { // Preserve FieldNotFound so the engine can apply its normal protocol rejection rules. String clientOrderId = message.getString(ClOrdID.FIELD); String senderCompId = message.getHeader().getString(SenderCompID.FIELD); String targetCompId = message.getHeader().getString(TargetCompID.FIELD); String symbol = message.getString(Symbol.FIELD); char fixSide = message.getChar(Side.FIELD); char orderType = message.getChar(OrdType.FIELD); BigDecimal quantity = message.getDecimal(OrderQty.FIELD); LocalDateTime transactTime = message.getUtcTimeStamp(TransactTime.FIELD); requireNonblank(clientOrderId, "ClOrdID (11)"); requireNonblank(symbol, "Symbol (55)"); OrderSide side = switch (fixSide) { case Side.BUY -> OrderSide.BUY; case Side.SELL -> OrderSide.SELL; default -> throw new OrderValidationException("Unsupported Side (54): " + fixSide + "; only BUY and SELL are supported"); }; if (orderType != OrdType.LIMIT) { throw new OrderValidationException("Unsupported OrdType (40): " + orderType + "; only limit orders are supported"); } long shares; try { shares = quantity.longValueExact(); } catch (ArithmeticException invalidQuantity) { throw new OrderValidationException("OrderQty (38) must be a positive integer share count" + " within the Java long range: " + quantity); } if (shares <= 0) { throw new OrderValidationException("OrderQty (38) must be positive: " + quantity); } BigDecimal price = message.getDecimal(Price.FIELD); if (price.signum() <= 0) { throw new OrderValidationException("Price (44) must be positive: " + price); } return new IncomingOrder(clientOrderId, senderCompId, targetCompId, symbol, side, shares, price, transactTime); } private static void requireNonblank(String value, String field) { if (value.isBlank()) { throw new OrderValidationException(field + " must not be blank"); } } }