Skip to content

Commit

Permalink
Merge pull request #1137 from Marketcetera/MATP-1125
Browse files Browse the repository at this point in the history
Matp 1125 Add Cancel All Open Orders to Open Order View
  • Loading branch information
colinduplantis committed Apr 1, 2023
2 parents 8d5fc39 + a043f86 commit ff7d3f2
Show file tree
Hide file tree
Showing 5 changed files with 147 additions and 30 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -57,6 +58,15 @@ public Properties getProperties()
{
return windowProperties;
}
/* (non-Javadoc)
* @see org.marketcetera.ui.events.NewWindowEvent#getWindowSize()
*/
@Override
public Pair<Double,Double> getWindowSize()
{
return Pair.create(850.0,
200.0);
}
/**
* Create a new ReplaceOrderEvent instance.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -77,10 +78,10 @@ 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();
aboveTableLayout = new FlowPane();
reportsTableView = new TableView<>();
reportsTableView.setPlaceholder(getPlaceholder());
TableViewSelectionModel<FixClazz> selectionModel = reportsTableView.getSelectionModel();
Expand All @@ -104,7 +105,8 @@ public void changed(ObservableValue<? extends Number> 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;
Expand Down Expand Up @@ -166,6 +168,37 @@ public void receiveTradeMessage(TradeMessage inTradeMessage)
};
tradeClientService.addTradeMessageListener(tradeMessageListener);
}
/**
* Cancel the order from the given report.
*
* @param inReport a <code>FixClazz</code> 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.
*
Expand All @@ -177,47 +210,25 @@ protected void initializeContextMenu(TableView<FixClazz> 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 -> {
FixClazz report = inTableView.getSelectionModel().getSelectedItem();
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;
try {
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();
Expand Down Expand Up @@ -294,6 +305,17 @@ protected void enableContextMenuItems(FixClazz inNewValue)
cancelOrderMenuItem.setDisable(!orderStatus.isCancellable());
replaceOrderMenuItem.setDisable(!orderStatus.isCancellable());
}
/**
* Get the layout for above-the-table controls.
*
* <p>Subclasses can add controls to this layout as desired.</p>
*
* @return a <code>FlowPane</code> value
*/
protected FlowPane getAboveTableLayout()
{
return aboveTableLayout;
}
/**
* Render the given column as a Date cell.
*
Expand Down Expand Up @@ -805,4 +827,8 @@ protected AbstractFixMessageView(Region inParentWindow,
* listens for trade messages
*/
private TradeMessageListener tradeMessageListener;
/**
* optional layout used for above-the-table
*/
private FlowPane aboveTableLayout;
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,24 @@
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;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
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;


Expand Down Expand Up @@ -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)
*/
Expand Down Expand Up @@ -95,6 +117,19 @@ protected void initializeColumns(TableView<DisplayOrderSummary> 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
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -699,14 +699,14 @@ 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());
rootLayout.getChildren().addAll(orderTicketLayout,
adviceSeparator,
adviceLabel);
serviceManager.getService(AdminClientService.class).addClientStatusListener(this);
fillFromExecutionReport(replaceExecutionReportOption);
}
/**
* Create a new OrderTicketView instance.
Expand Down Expand Up @@ -801,6 +801,52 @@ public void run()
);
}
}
/**
* Format the order ticket from the given report option, if present.
*
* @param inReportOption an <code>Optional&lt;ExecutionReport&gt;</code> value
*/
private void fillFromExecutionReport(Optional<ExecutionReport> 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) {
Expand Down
Binary file added photon/src/main/resources/images/erase.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit ff7d3f2

Please sign in to comment.