From 4702abb2a2169904f4dc9c6b4bab1106f894362e Mon Sep 17 00:00:00 2001 From: Colin DuPlantis Date: Sat, 1 Apr 2023 12:18:51 -0700 Subject: [PATCH 1/2] MATP-1141 Cancel/Replace Doesn't Format Order Ticket View --- .../ui/trade/event/ReplaceOrderEvent.java | 10 ++++ .../ui/trade/view/AbstractFixMessageView.java | 17 ++++--- .../view/orderticket/OrderTicketView.java | 48 ++++++++++++++++++- 3 files changed, 65 insertions(+), 10 deletions(-) diff --git a/photon/src/main/java/org/marketcetera/ui/trade/event/ReplaceOrderEvent.java b/photon/src/main/java/org/marketcetera/ui/trade/event/ReplaceOrderEvent.java index 5a6c290932..2597b49aaf 100644 --- a/photon/src/main/java/org/marketcetera/ui/trade/event/ReplaceOrderEvent.java +++ b/photon/src/main/java/org/marketcetera/ui/trade/event/ReplaceOrderEvent.java @@ -2,6 +2,7 @@ import java.util.Properties; +import org.marketcetera.core.Pair; import org.marketcetera.trade.ExecutionReport; import org.marketcetera.trade.HasExecutionReport; import org.marketcetera.ui.trade.view.orderticket.OrderTicketViewFactory; @@ -57,6 +58,15 @@ public Properties getProperties() { return windowProperties; } + /* (non-Javadoc) + * @see org.marketcetera.ui.events.NewWindowEvent#getWindowSize() + */ + @Override + public Pair getWindowSize() + { + return Pair.create(850.0, + 200.0); + } /** * Create a new ReplaceOrderEvent instance. * diff --git a/photon/src/main/java/org/marketcetera/ui/trade/view/AbstractFixMessageView.java b/photon/src/main/java/org/marketcetera/ui/trade/view/AbstractFixMessageView.java index c704567b2b..d592b89a02 100644 --- a/photon/src/main/java/org/marketcetera/ui/trade/view/AbstractFixMessageView.java +++ b/photon/src/main/java/org/marketcetera/ui/trade/view/AbstractFixMessageView.java @@ -77,7 +77,6 @@ protected void onStart() // TODO need to preserve column order // TODO add page size widget // TODO implement sorting - // TODO need to set initial view size to something reasonable tradeClientService = serviceManager.getService(TradeClientService.class); initializeTradeMessageListener(); mainLayout = new VBox(); @@ -192,13 +191,13 @@ protected void initializeContextMenu(TableView inTableView) SendOrderResponse response = tradeClientService.send(orderCancel); if(response.getFailed()) { uiMessageService.post(new NotificationEvent("Cancel Order", - "Unable to submit cancel: " + response.getOrderId() + " " + response.getMessage(), - AlertType.ERROR)); + "Unable to submit cancel: " + response.getOrderId() + " " + response.getMessage(), + AlertType.ERROR)); return; } else { uiMessageService.post(new NotificationEvent("Cancel Order", - "Cancel order " + response.getOrderId() + " submitted", - AlertType.INFORMATION)); + "Cancel order " + response.getOrderId() + " submitted", + AlertType.INFORMATION)); } }); replaceOrderMenuItem = new MenuItem("Replace Order"); @@ -207,8 +206,8 @@ protected void initializeContextMenu(TableView inTableView) ExecutionReport executionReport = tradeClientService.getLatestExecutionReportForOrderChain(report.getOrderId()); if(executionReport == null) { uiMessageService.post(new NotificationEvent("Replace Order", - "Unable to replace " + report.getOrderId() + ": no execution report", - AlertType.ERROR)); + "Unable to replace " + report.getOrderId() + ": no execution report", + AlertType.ERROR)); return; } String executionReportXml; @@ -216,8 +215,8 @@ protected void initializeContextMenu(TableView inTableView) executionReportXml = xmlService.marshall(executionReport); } catch (Exception e) { uiMessageService.post(new NotificationEvent("Replace Order", - "Unable to replace " + report.getOrderId() + ": " + PlatformServices.getMessage(e), - AlertType.ERROR)); + "Unable to replace " + report.getOrderId() + ": " + PlatformServices.getMessage(e), + AlertType.ERROR)); return; } Properties replaceProperties = new Properties(); diff --git a/photon/src/main/java/org/marketcetera/ui/trade/view/orderticket/OrderTicketView.java b/photon/src/main/java/org/marketcetera/ui/trade/view/orderticket/OrderTicketView.java index aa28e05f13..1897a81871 100644 --- a/photon/src/main/java/org/marketcetera/ui/trade/view/orderticket/OrderTicketView.java +++ b/photon/src/main/java/org/marketcetera/ui/trade/view/orderticket/OrderTicketView.java @@ -699,7 +699,6 @@ public void handle(MouseEvent inEvent) styleService.addStyleToAll(adviceSeparator, adviceLabel, rootLayout); -// orderTicketLayout.prefWidthProperty().bind(rootLayout.widthProperty()); orderTicketLayout.prefHeightProperty().bind(rootLayout.widthProperty()); rootLayout.prefHeightProperty().bind(getParentWindow().heightProperty()); rootLayout.prefWidthProperty().bind(getParentWindow().widthProperty()); @@ -707,6 +706,7 @@ public void handle(MouseEvent inEvent) adviceSeparator, adviceLabel); serviceManager.getService(AdminClientService.class).addClientStatusListener(this); + fillFromExecutionReport(replaceExecutionReportOption); } /** * Create a new OrderTicketView instance. @@ -801,6 +801,52 @@ public void run() ); } } + /** + * Format the order ticket from the given report option, if present. + * + * @param inReportOption an Optional<ExecutionReport> value + */ + private void fillFromExecutionReport(Optional inReportOption) + { + if(inReportOption.isEmpty()) { + return; + } + ExecutionReport report = inReportOption.get(); + if(report.getBrokerId() != null) { + brokerComboBox.valueProperty().set(report.getBrokerId()); + } + if(report.getSide() != null) { + sideComboBox.valueProperty().set(report.getSide()); + } + if(report.getLeavesQuantity() != null) { + quantityTextField.setText(report.getLeavesQuantity().toPlainString()); + } + if(report.getInstrument() != null) { + symbolTextField.setText(report.getInstrument().getSymbol()); + } + if(report.getOrderType() != null) { + orderTypeComboBox.setValue(report.getOrderType()); + } + if(report.getPrice() != null) { + priceTextField.setText(report.getPrice().toPlainString()); + } + if(report.getTimeInForce() != null) { + timeInForceComboBox.setValue(report.getTimeInForce()); + } + if(report.getText() != null) { + textTextField.setText(report.getText()); + } + if(report.getAccount() != null) { + accountTextField.setText(report.getAccount()); + } + if(report.getLastMarket() != null) { + // TODO this might not be correct + exDestinationTextField.setText(report.getLastMarket()); + } + } + /** + * Calculate the new order price from the most recent quote events, if possible. + */ private void updatePegToMidpointPrice() { if(pegToMidpointBidEventProperty.get() == null || pegToMidpointAskEventProperty.get() == null) { From a043f868b3f43876152887427026b0b6af5d912d Mon Sep 17 00:00:00 2001 From: Colin DuPlantis Date: Sat, 1 Apr 2023 13:01:38 -0700 Subject: [PATCH 2/2] MATP-1125 Add Cancel All Open Orders to Open Order View --- .../ui/trade/view/AbstractFixMessageView.java | 75 ++++++++++++------ .../trade/view/openorders/OpenOrderView.java | 35 ++++++++ photon/src/main/resources/images/erase.png | Bin 0 -> 373 bytes 3 files changed, 86 insertions(+), 24 deletions(-) create mode 100644 photon/src/main/resources/images/erase.png diff --git a/photon/src/main/java/org/marketcetera/ui/trade/view/AbstractFixMessageView.java b/photon/src/main/java/org/marketcetera/ui/trade/view/AbstractFixMessageView.java index d592b89a02..a001d99bfe 100644 --- a/photon/src/main/java/org/marketcetera/ui/trade/view/AbstractFixMessageView.java +++ b/photon/src/main/java/org/marketcetera/ui/trade/view/AbstractFixMessageView.java @@ -53,6 +53,7 @@ import javafx.scene.control.cell.PropertyValueFactory; import javafx.scene.input.Clipboard; import javafx.scene.input.ClipboardContent; +import javafx.scene.layout.FlowPane; import javafx.scene.layout.Region; import javafx.scene.layout.VBox; @@ -80,6 +81,7 @@ protected void onStart() tradeClientService = serviceManager.getService(TradeClientService.class); initializeTradeMessageListener(); mainLayout = new VBox(); + aboveTableLayout = new FlowPane(); reportsTableView = new TableView<>(); reportsTableView.setPlaceholder(getPlaceholder()); TableViewSelectionModel selectionModel = reportsTableView.getSelectionModel(); @@ -103,7 +105,8 @@ public void changed(ObservableValue inObservable, reportsTableView.prefWidthProperty().bind(getParentWindow().widthProperty()); reportsTableView.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY); mainLayout.prefHeightProperty().bind(getParentWindow().heightProperty()); - mainLayout.getChildren().addAll(reportsTableView, + mainLayout.getChildren().addAll(aboveTableLayout, + reportsTableView, pagination); currentPage = 0; pageSize = 10; @@ -165,6 +168,37 @@ public void receiveTradeMessage(TradeMessage inTradeMessage) }; tradeClientService.addTradeMessageListener(tradeMessageListener); } + /** + * Cancel the order from the given report. + * + * @param inReport a FixClazz value + */ + protected void cancelOrder(FixClazz inReport) + { + ExecutionReport executionReport = tradeClientService.getLatestExecutionReportForOrderChain(inReport.getOrderId()); + if(executionReport == null) { + uiMessageService.post(new NotificationEvent("Cancel Order", + "Unable to cancel " + inReport.getOrderId() + ": no execution report", + AlertType.ERROR)); + return; + } + OrderCancel orderCancel = Factory.getInstance().createOrderCancel(executionReport); + SLF4JLoggerProxy.info(this, + "{} sending {}", + SessionUser.getCurrent().getUsername(), + orderCancel); + SendOrderResponse response = tradeClientService.send(orderCancel); + if(response.getFailed()) { + uiMessageService.post(new NotificationEvent("Cancel Order", + "Unable to submit cancel: " + response.getOrderId() + " " + response.getMessage(), + AlertType.ERROR)); + return; + } else { + uiMessageService.post(new NotificationEvent("Cancel Order", + "Cancel order " + response.getOrderId() + " submitted", + AlertType.INFORMATION)); + } + } /** * Initialize the context menu for the FIX table. * @@ -176,29 +210,7 @@ protected void initializeContextMenu(TableView inTableView) cancelOrderMenuItem = new MenuItem("Cancel Order"); cancelOrderMenuItem.setOnAction(event -> { FixClazz report = inTableView.getSelectionModel().getSelectedItem(); - ExecutionReport executionReport = tradeClientService.getLatestExecutionReportForOrderChain(report.getOrderId()); - if(executionReport == null) { - uiMessageService.post(new NotificationEvent("Cancel Order", - "Unable to cancel " + report.getOrderId() + ": no execution report", - AlertType.ERROR)); - return; - } - OrderCancel orderCancel = Factory.getInstance().createOrderCancel(executionReport); - SLF4JLoggerProxy.info(this, - "{} sending {}", - SessionUser.getCurrent().getUsername(), - orderCancel); - SendOrderResponse response = tradeClientService.send(orderCancel); - if(response.getFailed()) { - uiMessageService.post(new NotificationEvent("Cancel Order", - "Unable to submit cancel: " + response.getOrderId() + " " + response.getMessage(), - AlertType.ERROR)); - return; - } else { - uiMessageService.post(new NotificationEvent("Cancel Order", - "Cancel order " + response.getOrderId() + " submitted", - AlertType.INFORMATION)); - } + cancelOrder(report); }); replaceOrderMenuItem = new MenuItem("Replace Order"); replaceOrderMenuItem.setOnAction(event -> { @@ -293,6 +305,17 @@ protected void enableContextMenuItems(FixClazz inNewValue) cancelOrderMenuItem.setDisable(!orderStatus.isCancellable()); replaceOrderMenuItem.setDisable(!orderStatus.isCancellable()); } + /** + * Get the layout for above-the-table controls. + * + *

Subclasses can add controls to this layout as desired.

+ * + * @return a FlowPane value + */ + protected FlowPane getAboveTableLayout() + { + return aboveTableLayout; + } /** * Render the given column as a Date cell. * @@ -804,4 +827,8 @@ protected AbstractFixMessageView(Region inParentWindow, * listens for trade messages */ private TradeMessageListener tradeMessageListener; + /** + * optional layout used for above-the-table + */ + private FlowPane aboveTableLayout; } diff --git a/photon/src/main/java/org/marketcetera/ui/trade/view/openorders/OpenOrderView.java b/photon/src/main/java/org/marketcetera/ui/trade/view/openorders/OpenOrderView.java index 5d24b0544d..3b2911b349 100644 --- a/photon/src/main/java/org/marketcetera/ui/trade/view/openorders/OpenOrderView.java +++ b/photon/src/main/java/org/marketcetera/ui/trade/view/openorders/OpenOrderView.java @@ -6,6 +6,7 @@ import org.marketcetera.persist.PageRequest; import org.marketcetera.trade.OrderID; import org.marketcetera.trade.OrderSummary; +import org.marketcetera.ui.PhotonServices; import org.marketcetera.ui.events.NewWindowEvent; import org.marketcetera.ui.trade.view.AbstractFixMessageView; import org.marketcetera.ui.view.ContentView; @@ -13,11 +14,16 @@ import org.springframework.context.annotation.Scope; import org.springframework.stereotype.Component; +import javafx.geometry.Insets; +import javafx.geometry.Pos; import javafx.scene.Node; +import javafx.scene.control.Button; import javafx.scene.control.Label; import javafx.scene.control.TableColumn; import javafx.scene.control.TableView; +import javafx.scene.control.Tooltip; import javafx.scene.control.cell.PropertyValueFactory; +import javafx.scene.image.ImageView; import javafx.scene.layout.Region; @@ -59,6 +65,22 @@ public OpenOrderView(Region inParentWindow, inEvent, inViewProperties); } + /* (non-Javadoc) + * @see org.marketcetera.ui.trade.view.AbstractFixMessageView#onStart() + */ + @Override + protected void onStart() + { + super.onStart(); + cancelOpenOrdersButton = new Button(); + cancelOpenOrdersButton.setTooltip(new Tooltip("Cancel all open orders")); + cancelOpenOrdersButton.setGraphic(new ImageView(PhotonServices.getIcon("images/erase.png"))); + cancelOpenOrdersButton.setPadding(new Insets(0,20,0,20)); + cancelOpenOrdersButton.setOnAction(event -> cancelOpenOrders()); + getAboveTableLayout().setAlignment(Pos.BASELINE_RIGHT); + getAboveTableLayout().getChildren().add(cancelOpenOrdersButton); + getAboveTableLayout().prefWidthProperty().bind(getMainLayout().widthProperty()); + } /* (non-Javadoc) * @see org.marketcetera.ui.trade.view.AbstractFixMessageView#getClientReports(org.marketcetera.persist.PageRequest) */ @@ -95,6 +117,19 @@ protected void initializeColumns(TableView inTableView) inTableView.getColumns().add(2, rootOrderIdColumn); } + /** + * Cancel all open orders displayed in the FIX table. + */ + private void cancelOpenOrders() + { + for(DisplayOrderSummary orderSummary : reportsTableView.getItems()) { + cancelOrder(orderSummary); + } + } + /** + * cancel open orders button + */ + private Button cancelOpenOrdersButton; /** * root order id table column */ diff --git a/photon/src/main/resources/images/erase.png b/photon/src/main/resources/images/erase.png new file mode 100644 index 0000000000000000000000000000000000000000..cdce9da6cda1a6a87eb1c79b3b64f4c7640d8ae8 GIT binary patch literal 373 zcmeAS@N?(olHy`uVBq!ia0vp^A|TAc1|)ksWqE-VV{wqX6T`Z5GB1G~m(&Q)G+$o^ zEg+kNfw4W4fd!-lh^2s-fq{7eBLg##W(0{XV1mnvEMP{kK?*nB{qPq^-S>2H46zV= zJ7ps;lOc~Qvrx1Fv-AbW!z?8R3}*_QFKlKI3E?@&7ox+U%lbhk@yQH@rvX|mOV{5x zcYgX?Z@rfNGdaI}{QiLJ9jnCw$)+{DC+0bHPI%h!Vs`Jzmurj{APhTyw!tKfW#xok_xh`KGZ{4^`xyjh0 r?XJ?cDEgTe~DWM4fOLKtA literal 0 HcmV?d00001