How to open position on hedging and non-hedging accounts
Brief
The article describes how to open a position on hedging and non-hedging accounts.
Details
How to Open Position
Positions are opened by orders. So, to open a position, create a request to make an order.
When the order is created, it appears in the Orders
table. It stays in the table until it is executed.
Depending on market liquidity and position maintenance (that is, the way trade operations are performed on an account), there are a few possible results of the order execution. To process the results, it is necessary to monitor table events.
Section 1 of the article discusses how to process the basic case when the result of the order execution is a newly opened position. The section also discusses how to the process the case when the order is rejected.
Sections 2 and 3 discuss other cases that may happen on the Real market when the order is sent on external execution, namely, partial execution.
Section 4 discusses how to process all the possible results of the order execution.
1. Most Usual Case
1.1. Create and Send Order Request
To request an order creation, you should fill an instance of O2GValueMap
with corresponding parameters.
The set of parameters depends on a particular order type. Please refer to the list of order types for the detailed description on how to fill the value map for each particular order type.
Below is an example of creating and sending a request for an Open Market order.
private static boolean openPosition(O2GSession session, String accountID, String offerID, String buySell, int amount, ResponseListener responseListener) throws InterruptedException {
boolean result;
O2GRequestFactory requestFactory = session.getRequestFactory();
if (requestFactory == null) {
System.out.println("Cannot create request factory");
return false;
}
O2GValueMap valuemap = requestFactory.createValueMap();
valuemap.setString(O2GRequestParamsEnum.COMMAND, Constants.Commands.CreateOrder);
valuemap.setString(O2GRequestParamsEnum.ORDER_TYPE, Constants.Orders.TrueMarketOpen);
valuemap.setString(O2GRequestParamsEnum.ACCOUNT_ID, accountID);
valuemap.setString(O2GRequestParamsEnum.OFFER_ID, offerID);
valuemap.setString(O2GRequestParamsEnum.BUY_SELL, buySell);
valuemap.setInt(O2GRequestParamsEnum.AMOUNT, amount);
valuemap.setString(O2GRequestParamsEnum.CUSTOM_ID, "OpenMarketOrder");
O2GRequest request = requestFactory.createOrderRequest(valuemap);
if (request != null) {
responseListener.setRequestID(request.getRequestId()); // Store requestId
session.sendRequest(request);
responseListener.waitEvents(); // Wait for the response or the error
result = true;
} else {
System.out.println(requestFactory.getLastError());
result = false;
}
return result;
}
RequestID
should be saved. It is necessary to monitor the order creation.
1.2. Process Request Response
After sending a request for an order creation to the server, process results of request execution. Possible results are as follows:
Request completed
It means that the order has been validated and accepted.
Actually, the onRequestCompleted
event does not mean that the order has been successfully executed.
So you should monitor the execution of your order to know the results of the execution.
Important: The onRequestCompleted
event should not be used as the confirmation of an order creation.
Instead, you should monitor appearance of the order in the Orders
table (see the "Monitor Appearing Order in Orders Table" section below).
Request failed
It means that the order has not passed the initial verification. For example, an incorrect account was specified, there is insufficient margin, etc.
To process notifications about the request response, an instance of the class implementing the IO2GResponseListener
interface should be subscribed to a session object by using the subscribeResponse
method. To process notifications about request failures, you should use the IO2GResponseListener.onRequestFailed
method.
Below is an example of the IO2GResponseListener
interface implementation.
class ResponseListener implements IO2GResponseListener
{
public void onRequestCompleted(String requestID, O2GResponse response) {
//the method should NOT be used as the confirmation of an order creation
}
public void onRequestFailed(String requestID, String error) {
System.out.println("Request failed: " + error);
TableListener.onRequestFailed(requestID, error); // If table manager is used
}
public void onTablesUpdates(O2GResponse response) {
//the method should be implemented when table manager is not used
}
}
1.3. Result of Request Execution: Order and Trade
When the order is created, it appears in the Orders
table. It stays in the table until it is executed. When the order is executed, a position is opened and appears in the Trades
table. To monitor the appearance the order and the trade in the tables, it is necessary to subscribe to table events.
1.3.1. How to Subscribe to Table Events
There are two ways to subscribe for table events:
OnTableUpdates
You should handle table updates in the onTablesUpdates()
method by implementing the IO2GResponseListener
interface.
You should create an instance of the O2GTablesUpdatesReader
interface by using O2GResponseReaderFactory
.
TableListener
Create a table listener class that implements the IO2GTableListener
interface. Then create an object of that class and subscribe the table listener object to a table event. For details, please read “How to use table manager in ForexConnect API.”
You should subscribe to the following events:
onAdded
for Orders table (further it will be referred as onOrderAdded
event),
onAdded
for Trades table (further it will be referred as onTradeAdded
event).
1.3.2. How to Monitor Appearance of Order in Orders Table
As it has been said above, the onRequestCompleted
event cannot be used as the confirmation of the order creation. You should check if the order is actually added to the Orders table. To do so, check OnOrderAdded
events for the Orders
table. You should make sure that the event satisfies the following criteria:
The RequestID
order field must be the same as the RequestID
of the order request you have sent;
The Type
order field must be the same as the type of your order. It is important because the table can contain several orders with the same RequestID. For example, if a market order with stop and limit orders attached is created, the table contains several orders with the same RequestID
. So you should use the Type
field to uniquely identify the order.
Once you get such an event, you should retrieve OrderID
. OrderID
is necessary to monitor further order execution.
1.3.3. How to Monitor Appearance of Position in Trades Table
To monitor appearance of a position in the Trades
table, check OnTradeAdded
events for the Trades
table.
When the event occurs, it is necessary to check that the added trade is opened by your order. To do so, make sure that the OpenOrderID
field of the added trade is the same as the OrderID
of your order.
1.4. Order Rejection
Sometimes there is no liquidity on the market. It may lead to failure to fill an order and consequently to the order rejection.
There is also possible a case when the market price significantly moves away from the order price.
For the Open Range and the Open Limit orders, it may also lead to the order rejection.
In case of rejection, a rejection message will appear in the Messages table.
To process this case, it is necessary to monitor onAdded
for Messages table (further it will be referred as onMessagesAdded
event).
To make sure that an onMessageAdded
event occurs for your order,
it is necessary to check that the Feature
field of the message is the same as "Market conditions" and
the message text
contains your OrderID
.
1.5. Diagram and Sample for Processing Happy Day Case
Below is the state machine diagram that demonstrates what events should be processed to monitor the creation and the execution of an order and what actions should be performed depending on the order status.
Below is an example of the diagram implementation.
[hide]
package openposition;
import java.util.ArrayList;
import java.util.List;
import com.fxcore2.*;
public class OrderMonitor {
public enum ExecutionResult {
Executing,
Executed,
PartialRejected,
FullyRejected,
Canceled
}
private enum OrderState {
OrderExecuting,
OrderExecuted,
OrderCanceled,
OrderRejected
}
private final String MarketCondition = "5";
private O2GOrderRow mOrder;
private O2GTradeRow mTrade;
private OrderState mState;
private ExecutionResult mResult;
private int mTotalAmount;
private int mRejectAmount;
private String mRejectMessage;
public OrderMonitor(O2GOrderRow order) {
mOrder = order;
mTrade = null;
mState = OrderState.OrderExecuting;
mResult = ExecutionResult.Executing;
mTotalAmount = 0;
mRejectAmount = 0;
mRejectMessage = "";
}
public static boolean isOpeningOrder(O2GOrderRow order) {
return order.getType().startsWith("O");
}
public static boolean isClosingOrder(O2GOrderRow order) {
return order.getType().startsWith("C");
}
public void onTradeAdded(O2GTradeRow tradeRow) {
String tradeOrderID = tradeRow.getOpenOrderID();
String orderID = mOrder.getOrderID();
if (tradeOrderID.equals(orderID)) {
mTrade = tradeRow;
if (mState == OrderState.OrderExecuted ||
mState == OrderState.OrderRejected ||
mState == OrderState.OrderCanceled) {
if (isAllTradesReceived()) {
setResult(true);
}
}
}
}
public void onOrderDeleted(O2GOrderRow order) {
String deletedOrderID = order.getOrderID();
String orderID = mOrder.getOrderID();
if (deletedOrderID.equals(orderID)) {
// Store Reject amount
if (order.getStatus().startsWith("R")) {
mState = OrderState.OrderRejected;
mRejectAmount = order.getAmount();
mTotalAmount = order.getOriginAmount() - mRejectAmount;
if (!mRejectMessage.isEmpty() && isAllTradesReceived()) {
setResult(true);
}
} else if (order.getStatus().startsWith("C")) {
mState = OrderState.OrderCanceled;
mRejectAmount = order.getAmount();
mTotalAmount = order.getOriginAmount() - mRejectAmount;
if (isAllTradesReceived()) {
setResult(false);
}
} else {
mRejectAmount = 0;
mTotalAmount = order.getOriginAmount();
mState = OrderState.OrderExecuted;
if (isAllTradesReceived()) {
setResult(true);
}
}
}
}
public void onMessageAdded(O2GMessageRow message) {
if (mState == OrderState.OrderRejected ||
mState == OrderState.OrderExecuting) {
boolean isRejectMessage = checkAndStoreMessage(message);
if (mState == OrderState.OrderRejected && isRejectMessage) {
setResult(true);
}
}
}
public O2GOrderRow getOrder() {
return mOrder;
}
public O2GTradeRow getTrade() {
return mTrade;
}
public int getRejectAmount() {
return mRejectAmount;
}
public String getRejectMessage() {
return mRejectMessage;
}
public ExecutionResult getResult() {
return mResult;
}
public boolean isOrderCompleted() {
return mResult != ExecutionResult.Executing;
}
private boolean checkAndStoreMessage(O2GMessageRow message) {
String feature;
feature = message.getFeature();
if (feature.equals(MarketCondition)) {
String text = message.getText();
int findPos = text.indexOf(mOrder.getOrderID());
if (findPos >= 0) {
mRejectMessage = message.getText();
return true;
}
}
return false;
}
private boolean isAllTradesReceived() {
if (mState == OrderState.OrderExecuting) {
return false;
}
int currentTotalAmount = 0;
if (mTrade != null) {
currentTotalAmount += mTrade.getAmount();
}
return currentTotalAmount == mTotalAmount;
}
private void setResult(boolean success) {
if (success) {
if (mRejectAmount == 0) {
mResult = ExecutionResult.Executed;
} else {
mResult = (mTrade == null) ? ExecutionResult.FullyRejected : ExecutionResult.PartialRejected;
}
} else {
mResult = ExecutionResult.Canceled;
}
}
}
An example on how to subscribe the order monitor by using onTableUpdates
is given below.
[hide]
public class ResponseListener implements IO2GResponseListener {
private O2GSession mSession;
private String mRequestID;
private O2GTradeRow mTrade;
private OrderMonitor mOrderMonitor;
private Semaphore mSemaphore;
public ResponseListener(O2GSession session) {
mRequestID = "";
mOrderMonitor = null;
mTrade = null;
mSemaphore = new Semaphore(0);
mSession = session;
}
public void setRequestID(String requestID) {
mRequestID = requestID;
}
public void waitEvents() throws InterruptedException {
mSemaphore.acquire(1);
}
public O2GTradeRow getTrade() {
return mTrade;
}
// Implementation of IO2GResponseListener interface public method onRequestCompleted
public void onRequestCompleted(String requestID, O2GResponse response) {
//the method should NOT be used as the confirmation of an order creation
}
// Implementation of IO2GResponseListener interface public method onRequestFailed
public void onRequestFailed(String requestID, String error) {
if (mRequestID.equals(requestID)) {
System.out.println("Request failed: " + error);
mSemaphore.release();
}
}
// Implementation of IO2GResponseListener interface public method onTablesUpdates
public void onTablesUpdates(O2GResponse response) {
O2GResponseReaderFactory factory = mSession.getResponseReaderFactory();
if (factory != null) {
O2GTablesUpdatesReader reader = factory.createTablesUpdatesReader(response);
for (int i = 0; i < reader.size(); i++) {
switch (reader.getUpdateTable(i)) {
case ACCOUNTS:
O2GAccountRow account = reader.getAccountRow(i);
//Show balance updates
System.out.format("Balance: %.2f%n", account.getBalance());
break;
case ORDERS:
O2GOrderRow orderRow = reader.getOrderRow(i);
if (mRequestID.equals(orderRow.getRequestID())) {
switch (reader.getUpdateType(i)) {
case INSERT:
if ((OrderMonitor.isClosingOrder(orderRow) || OrderMonitor.isOpeningOrder(orderRow)) &&
mOrderMonitor == null) {
System.out.println("The order has been added. Order ID: " + orderRow.getOrderID() +
" Rate: " + orderRow.getRate() +
" Time In Force: " + orderRow.getTimeInForce());
mOrderMonitor = new OrderMonitor(orderRow);
}
break;
case DELETE:
if (mOrderMonitor != null) {
System.out.println("The order has been deleted. Order ID: " + orderRow.getOrderID());
mOrderMonitor.onOrderDeleted(orderRow);
if (mOrderMonitor.isOrderCompleted()) {
printResult();
mSemaphore.release();
}
}
break;
}
}
break;
case TRADES:
O2GTradeRow trade = reader.getTradeRow(i);
if (reader.getUpdateType(i) == O2GTableUpdateType.INSERT) {
if (mOrderMonitor != null) {
mOrderMonitor.onTradeAdded(trade);
if (mOrderMonitor.isOrderCompleted()) {
mTrade = mOrderMonitor.getTrade();
printResult();
mSemaphore.release();
}
}
}
break;
case MESSAGES:
O2GMessageRow message = reader.getMessageRow(i);
if (reader.getUpdateType(i) == O2GTableUpdateType.INSERT) {
if (mOrderMonitor != null) {
mOrderMonitor.onMessageAdded(message);
if (mOrderMonitor.isOrderCompleted()) {
printResult();
mSemaphore.release();
}
}
}
break;
}
}
}
}
private void printResult() {
if (mOrderMonitor != null) {
OrderMonitor.ExecutionResult result = mOrderMonitor.getResult();
O2GTradeRow trade;
O2GOrderRow order = mOrderMonitor.getOrder();
String orderID = order.getOrderID();
trade = mOrderMonitor.getTrade();
switch (result) {
case Canceled:
if (trade != null) {
printTrade(trade, orderID);
System.out.println("A part of the order has been canceled. Amount = " + mOrderMonitor.getRejectAmount());
} else {
System.out.println("The order: OrderID = " + orderID + " has been canceled");
System.out.println("The cancel amount = " + mOrderMonitor.getRejectAmount());
}
break;
case FullyRejected:
System.out.println("The order has been rejected. OrderID = " + orderID);
System.out.println("The rejected amount = " + mOrderMonitor.getRejectAmount());
System.out.println("Rejection cause: " + mOrderMonitor.getRejectMessage());
break;
case Executed:
printTrade(trade, orderID);
break;
}
}
}
private void printTrade(O2GTradeRow trade, String orderID) {
if (trade == null) {
return;
}
System.out.println("For the order: OrderID = " + orderID + " the following positions have been opened: ");
String tradeID = trade.getTradeID();
int amount = trade.getAmount();
double rate = trade.getOpenRate();
System.out.println("Trade ID: " + tradeID + "; Amount: " + amount + "; Rate: " + rate);
}
}
An example on how to subscribe the order monitor by using TableListener
can be found below.
[hide]
public class TableListener implements IO2GTableListener {
private String mRequestID;
private O2GTradeRow mTrade;
private OrderMonitor mOrderMonitor;
private Semaphore mSemaphore;
public TableListener(){
mRequestID = "";
mOrderMonitor = null;
mTrade = null;
mSemaphore = new Semaphore(0);
}
public void setRequestID(String requestID) {
mRequestID = requestID;
}
public void waitEvents() throws InterruptedException {
mSemaphore.acquire(1);
}
public O2GTradeRow getTrade() {
return mTrade;
}
public void onRequestFailed(String requestID, String error) {
if (mRequestID.equals(requestID)) {
System.out.println("Request failed: " + error);
mSemaphore.release();
}
}
public void subscribeTableListener(O2GTableManager manager) {
O2GOrdersTable ordersTable = (O2GOrdersTable)manager.getTable(O2GTableType.ORDERS);
O2GTradesTable tradesTable = (O2GTradesTable)manager.getTable(O2GTableType.TRADES);
O2GMessagesTable messagesTable = (O2GMessagesTable)manager.getTable(O2GTableType.MESSAGES);
ordersTable.subscribeUpdate(O2GTableUpdateType.INSERT, this);
ordersTable.subscribeUpdate(O2GTableUpdateType.DELETE, this);
tradesTable.subscribeUpdate(O2GTableUpdateType.INSERT, this);
messagesTable.subscribeUpdate(O2GTableUpdateType.INSERT, this);
}
public void unsubscribeTableListener(O2GTableManager manager) {
O2GOrdersTable ordersTable = (O2GOrdersTable)manager.getTable(O2GTableType.ORDERS);
O2GTradesTable tradesTable = (O2GTradesTable)manager.getTable(O2GTableType.TRADES);
O2GMessagesTable messagesTable = (O2GMessagesTable)manager.getTable(O2GTableType.MESSAGES);
ordersTable.unsubscribeUpdate(O2GTableUpdateType.INSERT, this);
ordersTable.unsubscribeUpdate(O2GTableUpdateType.DELETE, this);
tradesTable.unsubscribeUpdate(O2GTableUpdateType.INSERT, this);
messagesTable.unsubscribeUpdate(O2GTableUpdateType.INSERT, this);
}
// Implementation of IO2GTableListener interface public method onAdded
public void onAdded(String rowID, O2GRow rowData) {
O2GTableType type = rowData.getTableType();
switch (type) {
case ORDERS:
O2GOrderRow orderRow = (O2GOrderRow)rowData;
if(mRequestID.equals(orderRow.getRequestID())) {
if ((OrderMonitor.isClosingOrder(orderRow) || OrderMonitor.isOpeningOrder(orderRow)) &&
mOrderMonitor == null) {
System.out.println("The order has been added. Order ID: " + orderRow.getOrderID() +
" Rate: " + orderRow.getRate() +
" Time In Force: " + orderRow.getTimeInForce());
mOrderMonitor = new OrderMonitor(orderRow);
}
}
break;
case TRADES:
O2GTradeRow tradeRow = (O2GTradeRow)rowData;
if (mOrderMonitor != null) {
mOrderMonitor.onTradeAdded(tradeRow);
if (mOrderMonitor.isOrderCompleted()) {
mTrade = mOrderMonitor.getTrade();
printResult();
mSemaphore.release();
}
}
break;
case MESSAGES:
O2GMessageRow messageRow = (O2GMessageRow)rowData;
if (mOrderMonitor != null) {
mOrderMonitor.onMessageAdded(messageRow);
if (mOrderMonitor.isOrderCompleted()) {
printResult();
mSemaphore.release();
}
}
break;
}
}
// Implementation of IO2GTableListener interface public method onChanged
public void onChanged(String rowID, O2GRow rowData) {
}
// Implementation of IO2GTableListener interface public method onDeleted
public void onDeleted(String rowID, O2GRow rowData) {
if (rowData.getTableType() == O2GTableType.ORDERS) {
O2GOrderRow orderRow = (O2GOrderRow)rowData;
if (mRequestID.equals(orderRow.getRequestID())) {
if (mOrderMonitor != null) {
System.out.println("The order has been deleted. Order ID: " + orderRow.getOrderID());
mOrderMonitor.onOrderDeleted(orderRow);
if (mOrderMonitor.isOrderCompleted()) {
printResult();
mSemaphore.release();
}
}
}
}
}
// Implementation of IO2GTableListener interface public method onStatus
public void onStatusChanged(O2GTableStatus status) {
}
private void printResult() {
if (mOrderMonitor != null) {
OrderMonitor.ExecutionResult result = mOrderMonitor.getResult();
O2GTradeRow trade;
O2GOrderRow order = mOrderMonitor.getOrder();
String orderID = order.getOrderID();
trade = mOrderMonitor.getTrade();
switch (result) {
case Canceled:
if (trade != null) {
printTrade(trade, orderID);
System.out.println("A part of the order has been canceled. Amount = " + mOrderMonitor.getRejectAmount());
} else {
System.out.println("The order: OrderID = " + orderID + " has been canceled");
System.out.println("The cancel amount = " + mOrderMonitor.getRejectAmount());
}
break;
case FullyRejected:
System.out.println("The order has been rejected. OrderID = " + orderID);
System.out.println("The rejected amount = " + mOrderMonitor.getRejectAmount());
System.out.println("Rejection cause: " + mOrderMonitor.getRejectMessage());
break;
case Executed:
printTrade(trade, orderID);
break;
}
}
}
private void printTrade(O2GTradeRow trade, String orderID) {
if (trade == null) {
return;
}
System.out.println("For the order: OrderID = " + orderID + " the following positions have been opened: ");
String tradeID = trade.getTradeID();
int amount = trade.getAmount();
double rate = trade.getOpenRate();
System.out.println("Trade ID: " + tradeID + "; Amount: " + amount + "; Rate: " + rate);
}
}
class ResponseListener implements IO2GResponseListener {
private TableListener mTableListener;
// Constructor
public ResponseListener(TableListener tableListener) {
mTableListener = tableListener;
}
// Implementation of IO2GResponseListener interface public method onRequestCompleted
public void onRequestCompleted(String requestID, O2GResponse response) {
//the method should NOT be used as the confirmation of an order creation
}
// Implementation of IO2GResponseListener interface public method onRequestFailed
public void onRequestFailed(String requestID, String error) {
mTableListener.onRequestFailed(requestID, error);
}
// Implementation of IO2GResponseListener interface public method onTablesUpdates
public void onTablesUpdates(O2GResponse response) {
}
}
2. What is Partial Execution
2.1. No Dealing Desk
In the no dealing desk execution model, a broker passes on to his clients the best prices provided by one of the liquidity providers with a fixed mark-up
for each currency pair. A broker relies on these external providers with regard to currency pricing.
Although this model promotes efficiency and competition for market pricing,
there are certain limitations on liquidity that can affect the final execution of an order.
A partial fill occurs when only a part of the order can be executed at the price requested due to the limited liquidity.
The rest of the order will be filled at the next best available price or cancelled entirely depending on the order type (see the “Order Time in Force” section for details).
A partial fill is possible on Real accounts only.
Partial orders give a trader the opportunity to enter a part of the position instead of rejecting the entire order.
2.2. Order Time in Force
With different order types a trader has different time-in-force options that provide additional instructions on how long an order should remain active until it is executed or expires:
Fill Or Kill (FOK);
Immediate Or Cancel (IOC);
Good Till Cancelled (GTC);
Day (DAY).
Please see Orders State Machines for the description of time-in-force options for different order types.
An order with the GTC
and the IOC
time-in-force options can be executed partially. It means that one order may result in a number of positions opened/closed at different prices. A GTC
order is filled in portions until it is completely executed. An IOC
order is filled in portions to the maximum extent possible (until the liquidity pool dries up), the rest of the order is rejected. In case of partial closing, the system closes a position in the filled amount and opens a new position in the remaining amount. The system does so until the order is entirely filled (GTC
orders) or the liquidity dries up (IOC
orders).
3. Process Partial Execution
In case of partial filling, one order results in a number of positions opened at different prices. Depending on the order time in force, some part(s) of the order may also be rejected.
To monitor the partial execution, it is necessary to process the following events:
OnTradeAdded
The OpenOrderID
field of the added trade must be the same as the OrderID of your order.
OnMessageAdded
The Feature
field of the message must be the same as "Market conditions" and the message text must contain your OrderID.
To define whether or not the order execution is completed, you need to:
Process the onDeleted
for Orders table (further it will be referred as onOrderDeleted
event).
When the order execution is completed, the order is deleted. In case of deletion, the order status is 'F' if the order is filled completely and ‘R’ if the order is rejected completely or partially. If the order is filled completely, a new trade(s) is inserted in the Trades
table. If the order is rejected completely or partially, thea rejection message is inserted toin the Messages
table. The rejected amount is contained in the order amount field.
Note that the order deletion, the trade addition, and the message rejection notification can be received randomly.
Check if the total amount of resulting opened positions (including the amount of rejected order parts, if any) matches the amount of the order specified in the order request.
4. All Variants of Order Execution
In addition to the described execution results (an opened position, a completely or partially rejected order), there is also possible a case when the order execution results in closure of the existing position(s).
This case is possible on accounts where hedging is not allowed. Note that hedging is not allowed on all the U.S. accounts. On such accounts, opening of a position for the symbol that already has an open position(s) of the opposite trade operation always causes closure of the open position(s). Note that if the amount of an open order exceeds the total amount of the currently opened positions on that symbol, the result of the order execution will be closure of the existing positions and opening of a new position in the rest of the amount.
For example, there are three long (buy) positions for EUR/USD totaling 50k. You create an open sell order in the amount of 70k for EUR/USD. The result of the order execution will be closure of the existing positions and opening of a new position in the amount of 20k.
To monitor the position(s) closure, it is necessary to check onAdded
events of ClosedTrades table (further it will be referred as onClosedTradeAdded
event). The CloseOrderID
field of the closed trade must be the same as the OrderID
of your order.
Please see below the summary table of all possible variants of the order execution and execution results.
Variant of Order Execution |
Execution Result |
An opened position |
A new position will appear in the Trades table. The position amount must be equal to the amount specified in the order request. |
A closed position |
A closed position will appear in the Closed Trades table. The position amount must not be less than the amount specified in the order request. |
A partially opened/closed position |
Either a new position will appear in the Trades table or a closed position will appear in the Closed Trades table. The position amount must be less than the amount specified in the order request. |
A completely rejected order |
A rejection message will appear in the Messages table. An order with the "R " status will appear in the Orders table. |
A partially rejected order |
Either a new position will appear in the Trades table or a closed position will appear in the Closed Trades table (depending on the account type).
A rejection message will appear in the Messages table. |
The state machine diagram below shows all possible execution variants and all events that must be processed to monitor the order execution. The diagram also shows what actions should be performed depending on the order status.
Below is an example of the diagram implementation.
[hide]
public class OrderMonitor {
public enum ExecutionResult {
Executing,
Executed,
PartialRejected,
FullyRejected,
Canceled
}
private enum OrderState {
OrderExecuting,
OrderExecuted,
OrderCanceled,
OrderRejected
}
private final String MarketCondition = "5";
private O2GOrderRow mOrder;
private List<O2GTradeRow> mTrades;
private List<O2GClosedTradeRow> mClosedTrades;
private OrderState mState;
private ExecutionResult mResult;
private int mTotalAmount;
private int mRejectAmount;
private String mRejectMessage;
public OrderMonitor(O2GOrderRow order) {
mOrder = order;
mTrades = new ArrayList<O2GTradeRow>();
mClosedTrades = new ArrayList<O2GClosedTradeRow>();
mState = OrderState.OrderExecuting;
mResult = ExecutionResult.Executing;
mTotalAmount = 0;
mRejectAmount = 0;
mRejectMessage = "";
}
public static boolean isOpeningOrder(O2GOrderRow order) {
return order.getType().startsWith("O");
}
public static boolean isClosingOrder(O2GOrderRow order) {
return order.getType().startsWith("C");
}
public void onTradeAdded(O2GTradeRow tradeRow) {
String tradeOrderID = tradeRow.getOpenOrderID();
String orderID = mOrder.getOrderID();
if (tradeOrderID.equals(orderID)) {
mTrades.add(tradeRow);
if (mState == OrderState.OrderExecuted ||
mState == OrderState.OrderRejected ||
mState == OrderState.OrderCanceled) {
if (isAllTradesReceived()) {
setResult(true);
}
}
}
}
public void onClosedTradeAdded(O2GClosedTradeRow closedTradeRow) {
String closedTradeOrderID = closedTradeRow.getCloseOrderID();
String orderID = mOrder.getOrderID();
if (closedTradeOrderID.equals(orderID)) {
mClosedTrades.add(closedTradeRow);
if (mState == OrderState.OrderExecuted ||
mState == OrderState.OrderRejected ||
mState == OrderState.OrderCanceled) {
if (isAllTradesReceived()) {
setResult(true);
}
}
}
}
public void onOrderDeleted(O2GOrderRow order) {
String deletedOrderID = order.getOrderID();
String orderID = mOrder.getOrderID();
if (deletedOrderID.equals(orderID)) {
// Store Reject amount
if (order.getStatus().startsWith("R")) {
mState = OrderState.OrderRejected;
mRejectAmount = order.getAmount();
mTotalAmount = order.getOriginAmount() - mRejectAmount;
if (!mRejectMessage.isEmpty() && isAllTradesReceived()) {
setResult(true);
}
} else if (order.getStatus().startsWith("C")) {
mState = OrderState.OrderCanceled;
mRejectAmount = order.getAmount();
mTotalAmount = order.getOriginAmount() - mRejectAmount;
if (isAllTradesReceived()) {
setResult(false);
}
} else {
mRejectAmount = 0;
mTotalAmount = order.getOriginAmount();
mState = OrderState.OrderExecuted;
if (isAllTradesReceived()) {
setResult(true);
}
}
}
}
public void onMessageAdded(O2GMessageRow message) {
if (mState == OrderState.OrderRejected ||
mState == OrderState.OrderExecuting) {
boolean isRejectMessage = checkAndStoreMessage(message);
if (mState == OrderState.OrderRejected && isRejectMessage) {
setResult(true);
}
}
}
public O2GOrderRow getOrder() {
return mOrder;
}
public List<O2GTradeRow> getTrades() {
return mTrades;
}
public List<O2GClosedTradeRow> getClosedTrades() {
return mClosedTrades;
}
public int getRejectAmount() {
return mRejectAmount;
}
public String getRejectMessage() {
return mRejectMessage;
}
public ExecutionResult getResult() {
return mResult;
}
public boolean isOrderCompleted() {
return mResult != ExecutionResult.Executing;
}
private boolean checkAndStoreMessage(O2GMessageRow message) {
String feature;
feature = message.getFeature();
if (feature.equals(MarketCondition)) {
String text = message.getText();
int findPos = text.indexOf(mOrder.getOrderID());
if (findPos >= 0) {
mRejectMessage = message.getText();
return true;
}
}
return false;
}
private boolean isAllTradesReceived() {
if (mState == OrderState.OrderExecuting) {
return false;
}
int currentTotalAmount = 0;
for (int i = 0; i < mTrades.size(); i++) {
currentTotalAmount += mTrades.get(i).getAmount();
}
for (int i = 0; i < mClosedTrades.size(); i++) {
currentTotalAmount += mClosedTrades.get(i).getAmount();
}
return currentTotalAmount == mTotalAmount;
}
private void setResult(boolean success) {
if (success) {
if (mRejectAmount == 0) {
mResult = ExecutionResult.Executed;
} else {
mResult = (mTrades.size()==0 && mClosedTrades.size()==0) ? ExecutionResult.FullyRejected : ExecutionResult.PartialRejected;
}
} else {
mResult = ExecutionResult.Canceled;
}
}
}
An example on how to subscribe the order monitor by using onTableUpdates
is provided below.
[hide]
public class ResponseListener implements IO2GResponseListener {
private O2GSession mSession;
private String mRequestID;
private List<O2GTradeRow> mTrades;
private OrderMonitor mOrderMonitor;
private Semaphore mSemaphore;
public ResponseListener(O2GSession session) {
mRequestID = "";
mOrderMonitor = null;
mTrades = new ArrayList<O2GTradeRow>();
mSemaphore = new Semaphore(0);
mSession = session;
}
public void setRequestID(String requestID) {
mRequestID = requestID;
}
public void waitEvents() throws InterruptedException {
mSemaphore.acquire(1);
}
public List<O2GTradeRow> getTrades() {
return mTrades;
}
// Implementation of IO2GResponseListener interface public method onRequestCompleted
public void onRequestCompleted(String requestID, O2GResponse response) {
//the method should NOT be used as the confirmation of an order creation
}
// Implementation of IO2GResponseListener interface public method onRequestFailed
public void onRequestFailed(String requestID, String error) {
if (mRequestID.equals(requestID)) {
System.out.println("Request failed: " + error);
mSemaphore.release();
}
}
// Implementation of IO2GResponseListener interface public method onTablesUpdates
public void onTablesUpdates(O2GResponse response) {
O2GResponseReaderFactory factory = mSession.getResponseReaderFactory();
if (factory != null) {
O2GTablesUpdatesReader reader = factory.createTablesUpdatesReader(response);
for (int i = 0; i < reader.size(); i++) {
switch (reader.getUpdateTable(i)) {
case ACCOUNTS:
O2GAccountRow account = reader.getAccountRow(i);
//Show balance updates
System.out.format("Balance: %.2f%n", account.getBalance());
break;
case ORDERS:
O2GOrderRow orderRow = reader.getOrderRow(i);
if (mRequestID.equals(orderRow.getRequestID())) {
switch (reader.getUpdateType(i)) {
case INSERT:
if ((OrderMonitor.isClosingOrder(orderRow) || OrderMonitor.isOpeningOrder(orderRow)) &&
mOrderMonitor == null) {
System.out.println("The order has been added. Order ID: " + orderRow.getOrderID() +
" Rate: " + orderRow.getRate() +
" Time In Force: " + orderRow.getTimeInForce());
mOrderMonitor = new OrderMonitor(orderRow);
}
break;
case DELETE:
if (mOrderMonitor != null) {
System.out.println("The order has been deleted. Order ID: " + orderRow.getOrderID());
mOrderMonitor.onOrderDeleted(orderRow);
if (mOrderMonitor.isOrderCompleted()) {
printResult();
mSemaphore.release();
}
}
break;
}
}
break;
case TRADES:
O2GTradeRow trade = reader.getTradeRow(i);
if (reader.getUpdateType(i) == O2GTableUpdateType.INSERT) {
if (mOrderMonitor != null) {
mOrderMonitor.onTradeAdded(trade);
if (mOrderMonitor.isOrderCompleted()) {
mTrades = mOrderMonitor.getTrades();
printResult();
mSemaphore.release();
}
}
}
break;
case CLOSED_TRADES:
O2GClosedTradeRow closedTrade = reader.getClosedTradeRow(i);
if (reader.getUpdateType(i) == O2GTableUpdateType.INSERT) {
if (mOrderMonitor != null) {
mOrderMonitor.onClosedTradeAdded(closedTrade);
if (mOrderMonitor.isOrderCompleted()) {
printResult();
mSemaphore.release();
}
}
}
break;
case MESSAGES:
O2GMessageRow message = reader.getMessageRow(i);
if (reader.getUpdateType(i) == O2GTableUpdateType.INSERT) {
if (mOrderMonitor != null) {
mOrderMonitor.onMessageAdded(message);
if (mOrderMonitor.isOrderCompleted()) {
printResult();
mSemaphore.release();
}
}
}
break;
}
}
}
}
private void printResult() {
if (mOrderMonitor != null) {
OrderMonitor.ExecutionResult result = mOrderMonitor.getResult();
List<O2GTradeRow> trades;
List<O2GClosedTradeRow> closedTrades;
O2GOrderRow order = mOrderMonitor.getOrder();
String orderID = order.getOrderID();
trades = mOrderMonitor.getTrades();
closedTrades = mOrderMonitor.getClosedTrades();
switch (result) {
case Canceled:
if (trades.size() > 0) {
printTrades(trades, orderID);
printClosedTrades(closedTrades, orderID);
System.out.println("A part of the order has been canceled. Amount = " + mOrderMonitor.getRejectAmount());
} else {
System.out.println("The order: OrderID = " + orderID + " has been canceled");
System.out.println("The cancel amount = " + mOrderMonitor.getRejectAmount());
}
break;
case FullyRejected:
System.out.println("The order has been rejected. OrderID = " + orderID);
System.out.println("The rejected amount = " + mOrderMonitor.getRejectAmount());
System.out.println("Rejection cause: " + mOrderMonitor.getRejectMessage());
break;
case PartialRejected:
printTrades(trades, orderID);
printClosedTrades(closedTrades, orderID);
System.out.println("A part of the order has been rejected. Amount = " + mOrderMonitor.getRejectAmount());
System.out.println("Rejection cause: " + mOrderMonitor.getRejectMessage());
break;
case Executed:
printTrades(trades, orderID);
printClosedTrades(closedTrades, orderID);
break;
}
}
}
private void printTrades(List<O2GTradeRow> trades, String orderID) {
if (trades.size() == 0) {
return;
}
System.out.println("For the order: OrderID = " + orderID + " the following positions have been opened: ");
for (int i = 0; i < trades.size(); i++) {
O2GTradeRow trade = trades.get(i);
String tradeID = trade.getTradeID();
int amount = trade.getAmount();
double rate = trade.getOpenRate();
System.out.println("Trade ID: " + tradeID + "; Amount: " + amount + "; Rate: " + rate);
}
}
private void printClosedTrades(List<O2GClosedTradeRow> closedTrades, String orderID) {
if (closedTrades.size() == 0) {
return;
}
System.out.println("For the order: OrderID = " + orderID + " the following positions have been closed: ");
for (int i = 0; i < closedTrades.size(); i++) {
O2GClosedTradeRow closedTrade = closedTrades.get(i);
String tradeID = closedTrade.getTradeID();
int amount = closedTrade.getAmount();
double rate = closedTrade.getCloseRate();
System.out.println("Closed Trade ID: " + tradeID + "; Amount: " + amount + "; Closed Rate: " + rate);
}
}
}
An example on how to subscribe the order monitor by using TableListener
can be studied below.
[hide]
public class TableListener implements IO2GTableListener {
private String mRequestID;
private List<O2GTradeRow> mTrades;
private OrderMonitor mOrderMonitor;
private Semaphore mSemaphore;
public TableListener(){
mRequestID = "";
mOrderMonitor = null;
mTrades = new ArrayList<O2GTradeRow>();
mSemaphore = new Semaphore(0);
}
public void setRequestID(String requestID) {
mRequestID = requestID;
}
public void waitEvents() throws InterruptedException {
mSemaphore.acquire(1);
}
public List<O2GTradeRow> getTrades() {
return mTrades;
}
public void onRequestFailed(String requestID, String error) {
if (mRequestID.equals(requestID)) {
System.out.println("Request failed: " + error);
mSemaphore.release();
}
}
public void subscribeTableListener(O2GTableManager manager) {
O2GOrdersTable ordersTable = (O2GOrdersTable)manager.getTable(O2GTableType.ORDERS);
O2GTradesTable tradesTable = (O2GTradesTable)manager.getTable(O2GTableType.TRADES);
O2GMessagesTable messagesTable = (O2GMessagesTable)manager.getTable(O2GTableType.MESSAGES);
O2GClosedTradesTable closedTradesTable = (O2GClosedTradesTable)manager.getTable(O2GTableType.CLOSED_TRADES);
ordersTable.subscribeUpdate(O2GTableUpdateType.INSERT, this);
ordersTable.subscribeUpdate(O2GTableUpdateType.DELETE, this);
tradesTable.subscribeUpdate(O2GTableUpdateType.INSERT, this);
closedTradesTable.subscribeUpdate(O2GTableUpdateType.INSERT, this);
messagesTable.subscribeUpdate(O2GTableUpdateType.INSERT, this);
}
public void unsubscribeTableListener(O2GTableManager manager) {
O2GOrdersTable ordersTable = (O2GOrdersTable)manager.getTable(O2GTableType.ORDERS);
O2GTradesTable tradesTable = (O2GTradesTable)manager.getTable(O2GTableType.TRADES);
O2GMessagesTable messagesTable = (O2GMessagesTable)manager.getTable(O2GTableType.MESSAGES);
O2GClosedTradesTable closedTradesTable = (O2GClosedTradesTable)manager.getTable(O2GTableType.CLOSED_TRADES);
ordersTable.unsubscribeUpdate(O2GTableUpdateType.INSERT, this);
ordersTable.unsubscribeUpdate(O2GTableUpdateType.DELETE, this);
tradesTable.unsubscribeUpdate(O2GTableUpdateType.INSERT, this);
closedTradesTable.unsubscribeUpdate(O2GTableUpdateType.INSERT, this);
messagesTable.unsubscribeUpdate(O2GTableUpdateType.INSERT, this);
}
// Implementation of IO2GTableListener interface public method onAdded
public void onAdded(String rowID, O2GRow rowData) {
O2GTableType type = rowData.getTableType();
switch (type) {
case ORDERS:
O2GOrderRow orderRow = (O2GOrderRow)rowData;
if(mRequestID.equals(orderRow.getRequestID())) {
if ((OrderMonitor.isClosingOrder(orderRow) || OrderMonitor.isOpeningOrder(orderRow)) &&
mOrderMonitor == null) {
System.out.println("The order has been added. Order ID: " + orderRow.getOrderID() +
" Rate: " + orderRow.getRate() +
" Time In Force: " + orderRow.getTimeInForce());
mOrderMonitor = new OrderMonitor(orderRow);
}
}
break;
case TRADES:
O2GTradeRow tradeRow = (O2GTradeRow)rowData;
if (mOrderMonitor != null) {
mOrderMonitor.onTradeAdded(tradeRow);
if (mOrderMonitor.isOrderCompleted()) {
mTrades = mOrderMonitor.getTrades();
printResult();
mSemaphore.release();
}
}
break;
case CLOSED_TRADES:
O2GClosedTradeRow closedTradeRow = (O2GClosedTradeRow)rowData;
if (mOrderMonitor != null) {
mOrderMonitor.onClosedTradeAdded(closedTradeRow);
if (mOrderMonitor.isOrderCompleted()) {
printResult();
mSemaphore.release();
}
}
break;
case MESSAGES:
O2GMessageRow messageRow = (O2GMessageRow)rowData;
if (mOrderMonitor != null) {
mOrderMonitor.onMessageAdded(messageRow);
if (mOrderMonitor.isOrderCompleted()) {
printResult();
mSemaphore.release();
}
}
break;
}
}
// Implementation of IO2GTableListener interface public method onChanged
public void onChanged(String rowID, O2GRow rowData) {
}
// Implementation of IO2GTableListener interface public method onDeleted
public void onDeleted(String rowID, O2GRow rowData) {
if (rowData.getTableType() == O2GTableType.ORDERS) {
O2GOrderRow orderRow = (O2GOrderRow)rowData;
if (mRequestID.equals(orderRow.getRequestID())) {
if (mOrderMonitor != null) {
System.out.println("The order has been deleted. Order ID: " + orderRow.getOrderID());
mOrderMonitor.onOrderDeleted(orderRow);
if (mOrderMonitor.isOrderCompleted()) {
printResult();
mSemaphore.release();
}
}
}
}
}
// Implementation of IO2GTableListener interface public method onStatus
public void onStatusChanged(O2GTableStatus status) {
}
private void printResult() {
if (mOrderMonitor != null) {
OrderMonitor.ExecutionResult result = mOrderMonitor.getResult();
List<O2GTradeRow> trades;
List<O2GClosedTradeRow> closedTrades;
O2GOrderRow order = mOrderMonitor.getOrder();
String orderID = order.getOrderID();
trades = mOrderMonitor.getTrades();
closedTrades = mOrderMonitor.getClosedTrades();
switch (result) {
case Canceled:
if (trades.size() > 0) {
printTrades(trades, orderID);
printClosedTrades(closedTrades, orderID);
System.out.println("A part of the order has been canceled. Amount = " + mOrderMonitor.getRejectAmount());
} else {
System.out.println("The order: OrderID = " + orderID + " has been canceled");
System.out.println("The cancel amount = " + mOrderMonitor.getRejectAmount());
}
break;
case FullyRejected:
System.out.println("The order has been rejected. OrderID = " + orderID);
System.out.println("The rejected amount = " + mOrderMonitor.getRejectAmount());
System.out.println("Rejection cause: " + mOrderMonitor.getRejectMessage());
break;
case PartialRejected:
printTrades(trades, orderID);
printClosedTrades(closedTrades, orderID);
System.out.println("A part of the order has been rejected. Amount = " + mOrderMonitor.getRejectAmount());
System.out.println("Rejection cause: " + mOrderMonitor.getRejectMessage());
break;
case Executed:
printTrades(trades, orderID);
printClosedTrades(closedTrades, orderID);
break;
}
}
}
private void printTrades(List<O2GTradeRow> trades, String orderID) {
if (trades.size() == 0) {
return;
}
System.out.println("For the order: OrderID = " + orderID + " the following positions have been opened: ");
for (int i = 0; i < trades.size(); i++) {
O2GTradeRow trade = trades.get(i);
String tradeID = trade.getTradeID();
int amount = trade.getAmount();
double rate = trade.getOpenRate();
System.out.println("Trade ID: " + tradeID + "; Amount: " + amount + "; Rate: " + rate);
}
}
private void printClosedTrades(List<O2GClosedTradeRow> closedTrades, String orderID) {
if (closedTrades.size() == 0) {
return;
}
System.out.println("For the order: OrderID = " + orderID + " the following positions have been closed: ");
for (int i = 0; i < closedTrades.size(); i++) {
O2GClosedTradeRow closedTrade = closedTrades.get(i);
String tradeID = closedTrade.getTradeID();
int amount = closedTrade.getAmount();
double rate = closedTrade.getCloseRate();
System.out.println("Closed Trade ID: " + tradeID + "; Amount: " + amount + "; Closed Rate: " + rate);
}
}
}
class ResponseListener implements IO2GResponseListener {
private TableListener mTableListener;
// Constructor
public ResponseListener(TableListener tableListener) {
mTableListener = tableListener;
}
// Implementation of IO2GResponseListener interface public method onRequestCompleted
public void onRequestCompleted(String requestID, O2GResponse response) {
//the method should NOT be used as the confirmation of an order creation
}
// Implementation of IO2GResponseListener interface public method onRequestFailed
public void onRequestFailed(String requestID, String error) {
mTableListener.onRequestFailed(requestID, error);
}
// Implementation of IO2GResponseListener interface public method onTablesUpdates
public void onTablesUpdates(O2GResponse response) {
}
}
back