@Test public void orderPartialFill() { TestOrder order1 = new TestOrder("C1", symbol, Side.Sell, 3, OrdType.Limit, new BigDecimal("12.94")); List<MutableMessage> responses = engine.onOrder(userId, order1); assertEquals(1, responses.size()); TestOrder order2 = new TestOrder("C2", symbol, Side.Sell, 2, OrdType.Limit, new BigDecimal("12.95")); List<MutableMessage> responses2 = engine.onOrder(userId, order2); assertEquals(1, responses2.size()); TestOrder order3 = new TestOrder("C3", symbol, Side.Sell, 3, OrdType.Limit, new BigDecimal("12.96")); List<MutableMessage> responses3 = engine.onOrder(userId, order3); assertEquals(1, responses3.size()); TestOrder order4 = new TestOrder("C4", symbol, Side.Buy, 7, OrdType.Limit, new BigDecimal("12.95")); List<MutableMessage> responses4 = engine.onOrder(userId, order4); assertEquals(3, responses4.size()); for (Message aResponse : responses4) { assertTrue(aResponse instanceof TestExecution); TestExecution anExecution = (TestExecution) aResponse; switch (anExecution.clOrdId) { case "C1": assertEquals(OrdStatus.Filled, anExecution.ordStatus); assertEquals(0, anExecution.leavesQty); assertEquals(3, anExecution.cumQty); break; case "C2": assertEquals(OrdStatus.Filled, anExecution.ordStatus); assertEquals(0, anExecution.leavesQty); assertEquals(2, anExecution.cumQty); break; case "C4": assertEquals(OrdStatus.PartiallyFilled, anExecution.ordStatus); assertEquals(2, anExecution.leavesQty); assertEquals(5, anExecution.cumQty); break; default: fail("bad match"); } } }
public List<MutableMessage> onOrder(String source, NewOrderSingle order) { List<MutableMessage> responses = new ArrayList<>(); var orderBook = orderBooks.get(order.getSymbol()); if (null == orderBook) { orderBook = new OrderBook(); orderBooks.put(order.getSymbol(), orderBook); } WorkingOrder workingOrder = new WorkingOrder(order, source, getOrderId(), clock.instant()); SortedSet<WorkingOrder> possibleMatches = orderBook.findMatches(workingOrder); List<Integer> fillQtys = new ArrayList<>(); List<BigDecimal> fillPxs = new ArrayList<>(); int fillIndex = 0; Iterator<WorkingOrder> matchesIter = possibleMatches.iterator(); while (matchesIter.hasNext()) { WorkingOrder possibleMatch = matchesIter.next(); final int fillQty = Math.min(workingOrder.getLeavesQty(), possibleMatch.getLeavesQty()); fillQtys.add(fillIndex, fillQty); possibleMatch.execute(fillQtys.get(fillIndex)); workingOrder.execute(fillQtys.get(fillIndex)); fillPxs.add(fillIndex, possibleMatch.getPrice()); OrdStatus ordStatus = (possibleMatch.getLeavesQty() == 0) ? OrdStatus.Filled : OrdStatus.PartiallyFilled; MutableExecutionReport executionReport = populateExecutionReportTrade(possibleMatch, fillQtys.subList(fillIndex, fillIndex + 1), fillPxs.subList(fillIndex, fillIndex + 1), ordStatus); responses.add(executionReport); if (possibleMatch.getLeavesQty() == 0) { matchesIter.remove(); } fillIndex++; if (workingOrder.getLeavesQty() == 0) { break; } } if ((workingOrder.getLeavesQty() > 0) && (workingOrder.getOrdType() != OrdType.Market)) { orderBook.addOrder(workingOrder); } if ((workingOrder.getLeavesQty() > 0) && (workingOrder.getOrdType() == OrdType.Market)) { workingOrder.close(); MutableExecutionReport executionReport = populateExecutionReportTrade(workingOrder, fillQtys, fillPxs, OrdStatus.Canceled); responses.add(executionReport); } else if (workingOrder.getCumQty() == 0) { MutableExecutionReport executionReport = populateExecutionReportAccepted(workingOrder); responses.add(executionReport); } else { OrdStatus ordStatus = (workingOrder.getLeavesQty() == 0) ? OrdStatus.Filled : OrdStatus.PartiallyFilled; MutableExecutionReport executionReport = populateExecutionReportTrade(workingOrder, fillQtys, fillPxs, ordStatus); responses.add(executionReport); } return responses; }
MatchEngine { public List<MutableMessage> onOrder(String source, NewOrderSingle order) { List<MutableMessage> responses = new ArrayList<>(); var orderBook = orderBooks.get(order.getSymbol()); if (null == orderBook) { orderBook = new OrderBook(); orderBooks.put(order.getSymbol(), orderBook); } WorkingOrder workingOrder = new WorkingOrder(order, source, getOrderId(), clock.instant()); SortedSet<WorkingOrder> possibleMatches = orderBook.findMatches(workingOrder); List<Integer> fillQtys = new ArrayList<>(); List<BigDecimal> fillPxs = new ArrayList<>(); int fillIndex = 0; Iterator<WorkingOrder> matchesIter = possibleMatches.iterator(); while (matchesIter.hasNext()) { WorkingOrder possibleMatch = matchesIter.next(); final int fillQty = Math.min(workingOrder.getLeavesQty(), possibleMatch.getLeavesQty()); fillQtys.add(fillIndex, fillQty); possibleMatch.execute(fillQtys.get(fillIndex)); workingOrder.execute(fillQtys.get(fillIndex)); fillPxs.add(fillIndex, possibleMatch.getPrice()); OrdStatus ordStatus = (possibleMatch.getLeavesQty() == 0) ? OrdStatus.Filled : OrdStatus.PartiallyFilled; MutableExecutionReport executionReport = populateExecutionReportTrade(possibleMatch, fillQtys.subList(fillIndex, fillIndex + 1), fillPxs.subList(fillIndex, fillIndex + 1), ordStatus); responses.add(executionReport); if (possibleMatch.getLeavesQty() == 0) { matchesIter.remove(); } fillIndex++; if (workingOrder.getLeavesQty() == 0) { break; } } if ((workingOrder.getLeavesQty() > 0) && (workingOrder.getOrdType() != OrdType.Market)) { orderBook.addOrder(workingOrder); } if ((workingOrder.getLeavesQty() > 0) && (workingOrder.getOrdType() == OrdType.Market)) { workingOrder.close(); MutableExecutionReport executionReport = populateExecutionReportTrade(workingOrder, fillQtys, fillPxs, OrdStatus.Canceled); responses.add(executionReport); } else if (workingOrder.getCumQty() == 0) { MutableExecutionReport executionReport = populateExecutionReportAccepted(workingOrder); responses.add(executionReport); } else { OrdStatus ordStatus = (workingOrder.getLeavesQty() == 0) ? OrdStatus.Filled : OrdStatus.PartiallyFilled; MutableExecutionReport executionReport = populateExecutionReportTrade(workingOrder, fillQtys, fillPxs, ordStatus); responses.add(executionReport); } return responses; } }
MatchEngine { public List<MutableMessage> onOrder(String source, NewOrderSingle order) { List<MutableMessage> responses = new ArrayList<>(); var orderBook = orderBooks.get(order.getSymbol()); if (null == orderBook) { orderBook = new OrderBook(); orderBooks.put(order.getSymbol(), orderBook); } WorkingOrder workingOrder = new WorkingOrder(order, source, getOrderId(), clock.instant()); SortedSet<WorkingOrder> possibleMatches = orderBook.findMatches(workingOrder); List<Integer> fillQtys = new ArrayList<>(); List<BigDecimal> fillPxs = new ArrayList<>(); int fillIndex = 0; Iterator<WorkingOrder> matchesIter = possibleMatches.iterator(); while (matchesIter.hasNext()) { WorkingOrder possibleMatch = matchesIter.next(); final int fillQty = Math.min(workingOrder.getLeavesQty(), possibleMatch.getLeavesQty()); fillQtys.add(fillIndex, fillQty); possibleMatch.execute(fillQtys.get(fillIndex)); workingOrder.execute(fillQtys.get(fillIndex)); fillPxs.add(fillIndex, possibleMatch.getPrice()); OrdStatus ordStatus = (possibleMatch.getLeavesQty() == 0) ? OrdStatus.Filled : OrdStatus.PartiallyFilled; MutableExecutionReport executionReport = populateExecutionReportTrade(possibleMatch, fillQtys.subList(fillIndex, fillIndex + 1), fillPxs.subList(fillIndex, fillIndex + 1), ordStatus); responses.add(executionReport); if (possibleMatch.getLeavesQty() == 0) { matchesIter.remove(); } fillIndex++; if (workingOrder.getLeavesQty() == 0) { break; } } if ((workingOrder.getLeavesQty() > 0) && (workingOrder.getOrdType() != OrdType.Market)) { orderBook.addOrder(workingOrder); } if ((workingOrder.getLeavesQty() > 0) && (workingOrder.getOrdType() == OrdType.Market)) { workingOrder.close(); MutableExecutionReport executionReport = populateExecutionReportTrade(workingOrder, fillQtys, fillPxs, OrdStatus.Canceled); responses.add(executionReport); } else if (workingOrder.getCumQty() == 0) { MutableExecutionReport executionReport = populateExecutionReportAccepted(workingOrder); responses.add(executionReport); } else { OrdStatus ordStatus = (workingOrder.getLeavesQty() == 0) ? OrdStatus.Filled : OrdStatus.PartiallyFilled; MutableExecutionReport executionReport = populateExecutionReportTrade(workingOrder, fillQtys, fillPxs, ordStatus); responses.add(executionReport); } return responses; } MatchEngine(MutableResponseMessageFactory responsMessageFactory);  MatchEngine(MutableResponseMessageFactory messageFactory, Clock clock); }
MatchEngine { public List<MutableMessage> onOrder(String source, NewOrderSingle order) { List<MutableMessage> responses = new ArrayList<>(); var orderBook = orderBooks.get(order.getSymbol()); if (null == orderBook) { orderBook = new OrderBook(); orderBooks.put(order.getSymbol(), orderBook); } WorkingOrder workingOrder = new WorkingOrder(order, source, getOrderId(), clock.instant()); SortedSet<WorkingOrder> possibleMatches = orderBook.findMatches(workingOrder); List<Integer> fillQtys = new ArrayList<>(); List<BigDecimal> fillPxs = new ArrayList<>(); int fillIndex = 0; Iterator<WorkingOrder> matchesIter = possibleMatches.iterator(); while (matchesIter.hasNext()) { WorkingOrder possibleMatch = matchesIter.next(); final int fillQty = Math.min(workingOrder.getLeavesQty(), possibleMatch.getLeavesQty()); fillQtys.add(fillIndex, fillQty); possibleMatch.execute(fillQtys.get(fillIndex)); workingOrder.execute(fillQtys.get(fillIndex)); fillPxs.add(fillIndex, possibleMatch.getPrice()); OrdStatus ordStatus = (possibleMatch.getLeavesQty() == 0) ? OrdStatus.Filled : OrdStatus.PartiallyFilled; MutableExecutionReport executionReport = populateExecutionReportTrade(possibleMatch, fillQtys.subList(fillIndex, fillIndex + 1), fillPxs.subList(fillIndex, fillIndex + 1), ordStatus); responses.add(executionReport); if (possibleMatch.getLeavesQty() == 0) { matchesIter.remove(); } fillIndex++; if (workingOrder.getLeavesQty() == 0) { break; } } if ((workingOrder.getLeavesQty() > 0) && (workingOrder.getOrdType() != OrdType.Market)) { orderBook.addOrder(workingOrder); } if ((workingOrder.getLeavesQty() > 0) && (workingOrder.getOrdType() == OrdType.Market)) { workingOrder.close(); MutableExecutionReport executionReport = populateExecutionReportTrade(workingOrder, fillQtys, fillPxs, OrdStatus.Canceled); responses.add(executionReport); } else if (workingOrder.getCumQty() == 0) { MutableExecutionReport executionReport = populateExecutionReportAccepted(workingOrder); responses.add(executionReport); } else { OrdStatus ordStatus = (workingOrder.getLeavesQty() == 0) ? OrdStatus.Filled : OrdStatus.PartiallyFilled; MutableExecutionReport executionReport = populateExecutionReportTrade(workingOrder, fillQtys, fillPxs, ordStatus); responses.add(executionReport); } return responses; } MatchEngine(MutableResponseMessageFactory responsMessageFactory);  MatchEngine(MutableResponseMessageFactory messageFactory, Clock clock); List<MutableMessage> onCancelRequest(String source, OrderCancelRequest cancel); List<MutableMessage> onOrder(String source, NewOrderSingle order); }
MatchEngine { public List<MutableMessage> onOrder(String source, NewOrderSingle order) { List<MutableMessage> responses = new ArrayList<>(); var orderBook = orderBooks.get(order.getSymbol()); if (null == orderBook) { orderBook = new OrderBook(); orderBooks.put(order.getSymbol(), orderBook); } WorkingOrder workingOrder = new WorkingOrder(order, source, getOrderId(), clock.instant()); SortedSet<WorkingOrder> possibleMatches = orderBook.findMatches(workingOrder); List<Integer> fillQtys = new ArrayList<>(); List<BigDecimal> fillPxs = new ArrayList<>(); int fillIndex = 0; Iterator<WorkingOrder> matchesIter = possibleMatches.iterator(); while (matchesIter.hasNext()) { WorkingOrder possibleMatch = matchesIter.next(); final int fillQty = Math.min(workingOrder.getLeavesQty(), possibleMatch.getLeavesQty()); fillQtys.add(fillIndex, fillQty); possibleMatch.execute(fillQtys.get(fillIndex)); workingOrder.execute(fillQtys.get(fillIndex)); fillPxs.add(fillIndex, possibleMatch.getPrice()); OrdStatus ordStatus = (possibleMatch.getLeavesQty() == 0) ? OrdStatus.Filled : OrdStatus.PartiallyFilled; MutableExecutionReport executionReport = populateExecutionReportTrade(possibleMatch, fillQtys.subList(fillIndex, fillIndex + 1), fillPxs.subList(fillIndex, fillIndex + 1), ordStatus); responses.add(executionReport); if (possibleMatch.getLeavesQty() == 0) { matchesIter.remove(); } fillIndex++; if (workingOrder.getLeavesQty() == 0) { break; } } if ((workingOrder.getLeavesQty() > 0) && (workingOrder.getOrdType() != OrdType.Market)) { orderBook.addOrder(workingOrder); } if ((workingOrder.getLeavesQty() > 0) && (workingOrder.getOrdType() == OrdType.Market)) { workingOrder.close(); MutableExecutionReport executionReport = populateExecutionReportTrade(workingOrder, fillQtys, fillPxs, OrdStatus.Canceled); responses.add(executionReport); } else if (workingOrder.getCumQty() == 0) { MutableExecutionReport executionReport = populateExecutionReportAccepted(workingOrder); responses.add(executionReport); } else { OrdStatus ordStatus = (workingOrder.getLeavesQty() == 0) ? OrdStatus.Filled : OrdStatus.PartiallyFilled; MutableExecutionReport executionReport = populateExecutionReportTrade(workingOrder, fillQtys, fillPxs, ordStatus); responses.add(executionReport); } return responses; } MatchEngine(MutableResponseMessageFactory responsMessageFactory);  MatchEngine(MutableResponseMessageFactory messageFactory, Clock clock); List<MutableMessage> onCancelRequest(String source, OrderCancelRequest cancel); List<MutableMessage> onOrder(String source, NewOrderSingle order);  }