/* * Blitz Trading */ package executionserver.fix; import BE.BEOrderUpdate; import executionserver.controller.ExecutionServerController; import executionserver.domain.ExecutionOrder; import executionserver.domain.OrderStatus; import executionserver.domain.RequestTypes; import org.apache.mina.common.IoSession; import org.apache.mina.common.WriteFuture; import org.slf4j.LoggerFactory; import quickfix.*; import quickfix.Message; import quickfix.field.*; import quickfix.fix44.*; /** * * @author Sylvio Azevedo <sylvio.azevedo@blitz-trading.com> */ public class Fix44 extends AbstractFixConnection { private final org.slf4j.Logger logger = LoggerFactory.getLogger(Fix44.class); @Override public void processRequest(ExecutionOrder order) throws SessionNotFound { Message msg = null; switch (order.getReqType()) { case RequestTypes.REQUEST_NEW_ORDER: NewOrderSingle message = new NewOrderSingle( new ClOrdID(order.getId()), new Side((char) order.getSide()), new TransactTime(), new OrdType((char) order.getOrderType())); message.set(new HandlInst(HandlInst.AUTOMATED_EXECUTION_ORDER_PRIVATE)); message.set(new Symbol(order.getSecurity())); message.set(new TimeInForce(getTimeInForce(order.getValidity()))); message.set(new OrderQty(order.getQty())); message.set(new Account(order.getAccount())); switch (order.getOrderType()) { case OrdType.ON_CLOSE: case OrdType.MARKET: case OrdType.MARKET_WITH_LEFTOVER_AS_LIMIT: break; case OrdType.STOP_LIMIT: if (order.getStopPrice() > 0) { message.set(new StopPx(order.getStopPrice())); } break; default: message.set(new Price(order.getPrice())); } if (order.getMinQty() > 0) { message.set(new MinQty(order.getMinQty())); } else{ message.set(new MinQty(0)); } if (order.getOpenQty() > 0) { message.set(new MaxFloor(order.getOpenQty())); } else { message.set(new MaxFloor(0)); } msg = (Message) message; insertCustomFields(msg, "D", order); break; case RequestTypes.REQUEST_REPLACE: database.changeId(order, generateId(order.getClientId())); OrderCancelReplaceRequest replaceMessage = new OrderCancelReplaceRequest( new OrigClOrdID(order.getLastId()), new ClOrdID(order.getId()), new Side((char) order.getSide()), new TransactTime(), new OrdType((char) order.getOrderType()) ); replaceMessage.set(new HandlInst(HandlInst.AUTOMATED_EXECUTION_ORDER_PRIVATE)); replaceMessage.set(new Symbol(order.getSecurity())); replaceMessage.set(new OrderQty(order.getQty())); replaceMessage.set(new Price(order.getPrice())); replaceMessage.set(new Account(order.getAccount())); replaceMessage.set(new TimeInForce(getTimeInForce(order.getValidity()))); msg = (Message) replaceMessage; insertCustomFields(msg, "G", order); break; case RequestTypes.REQUEST_CANCEL: database.changeId(order, generateId(order.getClientId())); OrderCancelRequest cancelMessage = new OrderCancelRequest( new OrigClOrdID(order.getLastId()), new ClOrdID(order.getId()), new Side((char) order.getSide()), new TransactTime()); cancelMessage.set(new Symbol(order.getSecurity())); cancelMessage.set(new OrderQty((long) order.getQty())); cancelMessage.set(new Account(order.getAccount())); msg = (Message) cancelMessage; insertCustomFields(msg, "F", order); break; } // send message to target. send(msg); } @Override public void fromApp(Message msg, SessionID sid) throws FieldNotFound, IncorrectDataFormat, IncorrectTagValue, UnsupportedMessageType { // Retrieve the message type MsgType msgType = new MsgType(); msg.getHeader().getField(msgType); // SecurityList message. if(msgType.getValue().equals("y")) { NoRelatedSym noRelatedSym = new NoRelatedSym(); msg.getField(noRelatedSym); int entries = noRelatedSym.getValue(); SecurityList.NoRelatedSym group = new SecurityList.NoRelatedSym(); Symbol symbol = new Symbol(); SecurityID securityId = new SecurityID(); for(int idx = 1; idx < entries; idx++) { msg.getGroup(idx, group); group.get(symbol); group.get(securityId); synchronized(database) { if(database!=null && database.isConnect()) { database.insertSymbol(symbol, securityId); } } } return; } // business rejection. if(msgType.getValue().equals("j")) { Text rejectReason = new Text(); if (msg.isSetField(rejectReason)) { msg.getField(rejectReason); logger.warn("A business reject reason has been received: " + rejectReason.getValue()); } else { logger.warn("A business reject reason has been received."); } return; } // Retrieve transaction type ExecTransType execTransType = new ExecTransType(); if (msg.isSetField(execTransType)) { msg.getField(execTransType); } // Retrieve order id. ClOrdID clOrdId = new ClOrdID(); if (msg.isSetField(clOrdId)) { msg.getField(clOrdId); } // Try to find order with related retrieved id. ExecutionOrder order; synchronized(database) { order = database.find(clOrdId.getValue()); } // Check retrieval if (order == null) { logger.error("Order with id [" + clOrdId.getValue() + "] could not be found at database, there is nothing to do."); return; } logger.info("Order [" + clOrdId.getValue() + "] has been found in database."); // Prepare Order update response. BEOrderUpdate.OrderUpdate.Builder response = BEOrderUpdate.OrderUpdate.newBuilder(); response.setOrderId(order.getId()); response.setQtyRemaining(0); response.setSymbol(order.getSecurity()); response.setSide(order.getSide()); response.setClientId(order.getClientId()); response.setAccountId(order.getAccount()); /** * Check if message type is and reject. * * 9 - OrderCancelReject */ if (msgType.getValue().equals("9")) { // Retrieve reason and send to client. Text rejectReason = new Text(); if (msg.isSetField(rejectReason)) { msg.getField(rejectReason); response.setRejectReason(rejectReason.getValue()); } else { response.setRejectReason("[ExecutionServer] A reject was sent by market without a clear reason."); } response.setStatus(OrderStatus.REJECTED); } /** * Check if message type is an execution report. * * 8 - ExecutionReport */ if (msgType.getValue().equals("8")) { logger.info("Update received for order [" + clOrdId.getValue() + "]."); ExecType execType = new ExecType(); msg.getField(execType); OrderID orderId = new OrderID(); if (msg.isSetField(orderId)) { msg.getField(orderId); } LeavesQty leavesQty = new LeavesQty(); if (msg.isSetField(leavesQty)) { msg.getField(leavesQty); } CumQty cumQty = new CumQty(); if (msg.isSetField(cumQty)) { msg.getField(cumQty); } Price price = new Price(); if (msg.isSetField(price)) { msg.getField(price); } AvgPx avgPrice = new AvgPx(); if (msg.isSetField(avgPrice)) { msg.getField(avgPrice); } synchronized(database) { database.saveExchangeId(order, orderId.getValue()); } switch (execType.getValue()) { case ExecType.PENDING_NEW: case ExecType.PENDING_CANCEL: case ExecType.PENDING_REPLACE: return; case ExecType.NEW: logger.info("NEW update identified for order [" + clOrdId.getValue() + "]. Preparing response..."); response.setMarketOrderId(orderId.getValue()); response.setQtyRemaining((long) leavesQty.getValue()); response.setInMarket(true); response.setIsVisible(true); response.setModifiable(true); response.setCancelled(false); response.setStatus(OrderStatus.NEW); synchronized(database) { logger.info("Updating order [" + clOrdId.getValue() + "] status."); database.updateStatus(order, OrderStatus.NEW); } break; case ExecType.REJECTED: String reason = "[ExecutionServer] Order request rejected without aparent reason."; Text rejectReason = new Text(); if (msg.isSetField(rejectReason)) { msg.getField(rejectReason); reason = rejectReason.getValue(); } response.setInMarket(false); response.setRejectReason(reason); response.setStatus(OrderStatus.REJECTED); synchronized(database) { database.updateStatus(order, OrderStatus.REJECTED); } break; case ExecType.SUSPENDED: response.setStatus(OrderStatus.REJECTED); response.setInMarket(false); response.setRejectReason("Suspended"); synchronized(database) { database.updateStatus(order, OrderStatus.REJECTED); } break; case ExecType.TRADE: response.setMarketOrderId(orderId.getValue()); response.setQtyRemaining((long) leavesQty.getValue()); response.setQtyExecuted((long) cumQty.getValue()); response.setPrice(price.getValue()); response.setAvgPrice(avgPrice.getValue()); response.setInMarket(false); response.setIsVisible(false); response.setModifiable(false); response.setCancelled(false); OrdStatus ordStatus = new OrdStatus(); msg.getField(ordStatus); if(ordStatus.getValue() == OrdStatus.PARTIALLY_FILLED) { response.setStatus((int) OrderStatus.PARTIAL); } else { response.setStatus((int) OrderStatus.FILLED); } LastPx lastPx = new LastPx(); if (msg.isSetField(lastPx)) { msg.getField(lastPx); response.setLastPrice(lastPx.getValue()); } LastShares lastShares = new LastShares(); if (msg.isSetField(lastShares)) { msg.getField(lastShares); response.setLastShares((long) lastShares.getValue()); } ExecID execID = new ExecID(); if (msg.isSetField(execID)) { msg.getField(execID); } Symbol symbol = new Symbol(); if (msg.isSetField(symbol)) { msg.getField(symbol); } synchronized(database) { database.updateLasts(order, lastShares.getValue(), lastPx.getValue()); if(ordStatus.getValue() == OrdStatus.PARTIALLY_FILLED) { database.updateStatus(order, OrderStatus.PARTIAL); } else { database.updateStatus(order, OrderStatus.FILLED); } } break; case ExecType.CANCELED: logger.info("CANCELED update identified for order [" + clOrdId.getValue() + "]. Preparing response..."); response.setStatus(OrderStatus.CANCELED); response.setCancelled(true); response.setInMarket(false); synchronized(database) { logger.info("Updating order [" + clOrdId.getValue() + "] status."); database.updateStatus(order, OrderStatus.CANCELED); } break; case ExecType.REPLACE: response.setStatus(OrderStatus.REPLACED); response.setInMarket(true); response.setQtyRemaining((long) leavesQty.getValue()); synchronized(database) { database.updateStatus(order, OrderStatus.REPLACED); } break; default: return; } IoSession session = ExecutionServerController.clients.get(order.getOwner()); synchronized(database) { try{ WriteFuture fut = session.write(response.build()); fut.join(); if(fut.isWritten()) { logger.info("Updating message for order [" + clOrdId.getValue() + "] has been successfully sent."); database.markSent(order); } else { database.markProblem(order); } } catch(Exception e) { database.markProblem(order); logger.error("Order Id: " + order.getClientId() + " could not be send back to client. It's queued for late try."); } } } } @Override public void loadSecurities() throws SessionNotFound { synchronized(database) { database.deleteAllSecurities(); } // Prepare SecurityListRequest message. SecurityListRequest msg = new SecurityListRequest(); msg.set(new SubscriptionRequestType(SubscriptionRequestType.SNAPSHOT)); msg.set(new SecurityReqID("1")); msg.set(new SecurityListRequestType(SecurityListRequestType.ALL_SECURITIES)); insertCustomFields(msg, "x", null); send(msg); } private void rejectRequest(ExecutionOrder order) { BEOrderUpdate.OrderUpdate.Builder response = BEOrderUpdate.OrderUpdate.newBuilder(); response.setOrderId(order.getId()); response.setQtyRemaining(0); response.setSymbol(order.getSecurity()); response.setSide(order.getSide()); response.setClientId(order.getClientId()); response.setAccountId(order.getAccount()); response.setRejectReason("[ExecutionServer] Order is not in market."); response.setStatus(OrderStatus.REJECTED); IoSession session = ExecutionServerController.clients.get(order.getOwner()); session.write(response.build()); } }