undefined

Order Management

Placing Orders

The Next Valid Identifier

Perhaps the most important event received after successfully connecting to the TWS is the LYNXApi.EWrapper.nextValidId, which is also triggered after invoking the LYNXApi.EClient.reqIds method. As its name indicates, the nextValidId event provides the next valid identifier needed to place an order. This identifier is nothing more than the next number in the sequence. This means that if there is a single client application submitting orders to an account, it does not have to obtain a new valid identifier every time it needs to submit a new order. It is enough to increase the last value received from the nextValidId method by one. For example, if the valid identifier for your first API order is 1, the next valid identifier would be 2 and so on.

However if there are multiple client applications connected to one account, it is necessary to use an order ID with new orders which is greater than all previous order IDs returned to the client application in openOrder or orderStatus callbacks. For instance, if the client is set as the Master client, it will automatically receive order status and trade callbacks from orders placed from other clients. In such a case, any orderID used in placeOrder must be greater than the orderIDs returned in these status callbacks. Alternatively if the function reqAllOpenOrders is used by a client, subsequent orders placed by that client must have order IDs greater than the order IDs of all orders returned because of that function call. You can always use the LYNXApi.EClient.reqIds method in the event that your client application loses track of the sequence.

** Python **

# The parameter is always ignored.
self.reqIds(-1)

** Java **

//The parameter is always ignored.
client.reqIds(-1);

The above will result in LYNXApi.EWrapper.nextValidId callback being invoked:

** Python **

class TestWrapper(wrapper.EWrapper):

def nextValidId(self, orderId: int):

super().nextValidId(orderId)
logging.debug("setting nextValidOrderId: %d", orderId)
self.nextValidOrderId = orderId
print("NextValidId:", orderId)

** Java **

public class EWrapperImpl implements EWrapper {

    @Override
    public void nextValidId(int orderId) {
        System.out.println("Next Valid Id: ["+orderId+"]");
        currentOrderId = orderId;
    }

[!WARNING] The next valid identifier is persistent between TWS sessions.

If necessary, you can reset the order ID sequence within the API Settings dialogue. Note however that the order sequence Id can only be reset if there are no active API orders.

order_id_reset.png

Orders are submitted via the LYNXApi.EClient.placeOrder method. From the snippet below, note how a variable holding the nextValidId is incremented automatically:

** Python **

self.simplePlaceOid = self.nextOrderId()
self.placeOrder(self.simplePlaceOid, ContractSamples.USStock(),
OrderSamples.LimitOrder("SELL", 1, 50))

** Java **

client.placeOrder(nextOrderId++, ContractSamples.USStock(), OrderSamples.LimitOrder("SELL", 1, 50));

Immediately after the order was submitted correctly, the TWS will start sending events concerning the order's activity via LYNXApi.EWrapper.openOrder and LYNXApi.EWrapper.orderStatus

  • Advisors executing allocation orders will receive execution details and commissions for the allocation order itself. To receive allocation details and commissions for a specific subaccount LYNXApi.EClient.reqExecutions can be used.
  • An order can be sent to TWS but not transmitted to the LYNX server by setting the LYNXApi.Order.Transmit flag in the order class to False. Untransmitted orders will only be available within that TWS session (not for other usernames) and will be cleared on restart. Also, they can be cancelled or transmitted from the API but not viewed while they remain in the "untransmitted" state.

The openOrder callback

The LYNXApi.EWrapper.openOrder method delivers an LYNXApi.Order object representing an open order within the system. In addition, LYNXApi.EWrapper.openOrder returns an an LYNXApi.OrderState object that is used to return estimated pre-trade margin and commission information in response to invoking LYNXApi.EClient.placeOrder with a LYNXApi.Order object that has the flag LYNXApi.Order.WhatIf flag set to True. See also: Checking Margin Changes.

** Python **

class TestWrapper(wrapper.EWrapper):

def openOrder(self, orderId: OrderId, contract: Contract, order: Order,
    orderState: OrderState):

super().openOrder(orderId, contract, order, orderState)
print("OpenOrder. ID:", orderId, "Symbol:", contract.symbol, "SecType:", contract.secType,"Exchange:", contract.exchange, "Action:", order.action, "OrderType:", order.orderType,"TotalQuantity:", order.totalQuantity, "Status:", orderState.status)

if order.whatIf and orderState is not None:
            print("WhatIf. OrderId: ", orderId, "initMarginBefore:", orderState.initMarginBefore, "maintMarginBefore:", orderState.maintMarginBefore,"equityWithLoanBefore:", orderState.equityWithLoanBefore, "initMarginChange:", orderState.initMarginChange, "maintMarginChange:", orderState.maintMarginChange, "equityWithLoanChange:", orderState.equityWithLoanChange, "initMarginAfter:", orderState.initMarginAfter, "maintMarginAfter:", orderState.maintMarginAfter,"equityWithLoanAfter:", orderState.equityWithLoanAfter)

order.contract = contract
self.permId2ord[order.permId] = order

** Java **

public class EWrapperImpl implements EWrapper {

@Override
public void openOrder(int orderId, Contract contract, Order order,
        OrderState orderState) {
    System.out.println(EWrapperMsgGenerator.openOrder(orderId, contract, order,             orderState));
}

The orderStatus callback

The LYNXApi.EWrapper.orderStatus method contains all relevant information on the current status of the order execution-wise (i.e. amount filled and pending, filling price, etc.).

** Python **

class TestWrapper(wrapper.EWrapper):

def orderStatus(self, orderId: OrderId, status: str, filled: float, remaining: float, avgFillPrice: float, permId: int, parentId: int, lastFillPrice: float, clientId: int,
whyHeld: str, mktCapPrice: float):

super().orderStatus(orderId, status, filled, remaining,avgFillPrice, permId, parentId, lastFillPrice, clientId, whyHeld, mktCapPrice)
print("OrderStatus. Id:", orderId, "Status:", status, "Filled:", filled,"Remaining:", remaining, "AvgFillPrice:", avgFillPrice,"PermId:", permId, "ParentId:", parentId, "LastFillPrice:",lastFillPrice, "ClientId:", clientId, "WhyHeld:",whyHeld, "MktCapPrice:", mktCapPrice)

** Java **

public class EWrapperImpl implements EWrapper {

@Override
public void orderStatus(int orderId, String status, double filled,
        double remaining, double avgFillPrice, int permId, int parentId,
        double lastFillPrice, int clientId, String whyHeld, double mktCapPrice) {
    System.out.println("OrderStatus. Id: "+orderId+", Status: "+status+", Filled"+filled+", Remaining: "+remaining
            +", AvgFillPrice: "+avgFillPrice+", PermId: "+permId+", ParentId: "+parentId+", LastFillPrice: "+lastFillPrice+
            ", ClientId: "+clientId+", WhyHeld: "+whyHeld+", MktCapPrice: "+mktCapPrice);
}

Automatic Order Status Messages (without invoking reqOpenOrders or reqAllOpenOrders)

  • Clients with the ID of the client submitting the order will receive order status messages indicating changes in the order status.
  • The client with Master Client ID (set in TWS/LYNXG) will receive order status messages for all clients.
  • Client ID 0 will receive order status messages for its own (client ID 0) orders and also for orders submitted manually from TWS.

Possible Order States

  • ApiPending - indicates order has not yet been sent to LYNX server, for instance if there is a delay in receiving the security definition. Uncommonly received.

  • PendingSubmit - indicates the order was sent from TWS, but confirmation has not been received that it has been received by the destination. Most commonly because exchange is closed.

  • PendingCancel - indicates that a request has been sent to cancel an order but confirmation has not been received of its cancellation.

  • PreSubmitted - indicates that a simulated order type has been accepted by the LYNX system and that this order has yet to be elected. The order is held in the LYNX system until the election criteria are met. At that time the order is transmitted to the order destination as specified.

  • Submitted - indicates that your order has been accepted at the order destination and is working.

  • ApiCancelled - after an order has been submitted and before it has been acknowledged, an API client client can request its cancellation, producing this state.

  • Cancelled - indicates that the balance of your order has been confirmed cancelled by the LYNX system. This could occur unexpectedly when LYNX or the destination has rejected your order.

  • Filled - indicates that the order has been completely filled.

  • Inactive: indicates an order is not working, possible reasons include:

    • it is invalid or triggered an error. A corresponding error code is expected to the error() function.
    • the order is to short shares but the order is being held while shares are being located.
    • an order is placed manually in TWS while the exchange is closed.
    • an order is blocked by TWS due to a precautionary setting and appears there in an untransmitted state

[!WARNING|label:Important notes] Concerning LYNXApi.EWrapper.orderStatus :

  • Typically there are duplicate orderStatus messages with the same information that will be received by a client. This corresponds to messages sent back from TWS, the LYNX server, or the exchange.
  • There are not guaranteed to be orderStatus callbacks for every change in order status. For example with market orders when the order is accepted and executes immediately, there commonly will not be any corresponding orderStatus callbacks. For that reason it is recommended to monitor the LYNXApi.EWrapper.execDetails function in addition to LYNXApi.EWrapper.orderStatus .
  • Beginning in API v973.04, a parameter mktCapPrice is included in the orderStatus callback. If an order has been price-capped, mktCapPrice will indicate the price at which it has been capped.

Attaching Orders

Advanced orders such as Bracket Orders or Hedging Orders involve attaching child orders to a parent. This can be easily done via the LYNXApi.Order.ParentId attribute by assigning a child order's LYNXApi.Order.ParentId to an existing order's LYNXApi.Order.OrderId. When an order is attached to another, the system will keep the child order 'on hold' until its parent fills. Once the parent order is completely filled, its children will automatically become active.

[!WARNING] When attaching orders and to prevent accidental executions it is a very good idea to make use of the LYNXApi.Order.Transmit flag as demonstrated in Bracket Orders

Modifying Orders

Modification of an API order can be done if the API client is connected to a session of TWS with the same username of TWS and using the same API client ID. The function LYNXApi.EClient.placeOrder can then be called with the same fields as the open order, except for the parameter to modify. This includes the LYNXApi.Order.OrderId , which must match the LYNXApi.Order.OrderId of the open order. It is not generally recommended to try to change order fields aside from order price, size, and tif (for DAY -> IOC modifications). To change other parameters, it might be preferable to instead cancel the open order, and create a new one.

  • To modify or cancel an individual order placed manually from TWS, it is necessary to connect with client ID 0 and then bind the order before attempting to modify it. The process of binding assigns the order an API order ID; prior to binding it will be returned to the API with an API order ID of 0. Orders with API order ID 0 cannot be modified/cancelled from the API. The function reqOpenOrders binds orders open at that moment which do not already have an API order ID, and the function reqAutoOpenOrders binds future orders automatically. The function reqAllOpenOrders does not bind orders.

  • To modify API orders when connecting to a different session of TWS (logged in with a different username than used for the original order), it is necessary to first bind the order with client ID 0 in the same manner as manual TWS orders are bound before they can be modified. The binding assignment of API order IDs is independent for each TWS user, so the same order can have different API order IDs for different users. The permID returned in the API Order class which is assigned by TWS can be used to identify an order in an account uniquely.

  • Currently (as of TWS version 970) the process of order binding from the API cancels/resubmits an order working on an exchange. This may affect the order's place in the exchange queue. Enhancements are planned to allow for API binding with modification of exchange queue priority.

Cancelling Orders

An order can be cancelled from the API with the functions LYNXApi.EClient.cancelOrder and LYNXApi.EClient.reqGlobalCancel . cancelOrder can only be used to cancel an order that was placed originally by a client with the same client ID (or from TWS for client ID 0). It takes one argument, which is the original order ID.

** Python **

self.cancelOrder(self.simplePlaceOid)

** Java **

client.cancelOrder(cancelID);

LYNXApi.EClient.reqGlobalCancel will cancel all open orders, regardless of how they were originally placed.

** Python **

self.reqGlobalCancel()

** Java **

client.reqGlobalCancel();

Retrieving currently active orders

As long as an order is active, it is possible to retrieve it using the TWS API. Orders submitted via the TWS API will always be bound to the client application (i.e. client Id) they were submitted from meaning only the submitting client will be able to modify the placed order. Three different methods are provided to allow for maximum flexibility. Active orders will be returned via the LYNXApi.EWrapper.openOrder and LYNXApi.EWrapper.orderStatus methods as already described in The openOrder callback and The orderStatus callback sections

[!NOTE] It is not possible to obtain cancelled or fully filled orders.

API client's orders

The LYNXApi.EClient.reqOpenOrders method allows to obtain all active orders submitted by the client application connected with the exact same client Id with which the order was sent to the TWS. If client 0 invokes reqOpenOrders, it will cause currently open orders placed from TWS manually to be 'bound', i.e. assigned an order ID so that they can be modified or cancelled by the API client 0. In API versions after 973.07 there will be LYNXApi.EWrapper.orderBound callback in response to newly bound orders that indicates the mapping between the permID (unique account-wide) and API Order ID (specific to an API client). In the API settings in Global Configuration, is a setting checked by default "Use negative numbers to bind automatic orders" which will specify how manual TWS orders are assigned an API order ID.

** Python **

self.reqOpenOrders()

** Java **

client.reqOpenOrders();

All submitted orders

To obtain those orders created via the TWS API regardless of the submitting client application, make use of the LYNXApi.EClient.reqAllOpenOrders function.

** Python **

self.reqAllOpenOrders()

** Java **

client.reqAllOpenOrders();

Manually submitted orders

Finally, LYNXApi.EClient.reqAutoOpenOrders can only be invoked by client with ID 0. It will cause future orders placed from TWS to be 'bound', i.e. assigned an order ID such that they can be accessed by the cancelOrder or placeOrder (for modification) functions by client ID 0.

** Python **

self.reqAutoOpenOrders(True)

** Java **

client.reqAutoOpenOrders(true);

[!WARNING] Only those applications connecting with client Id 0 will be able to take over manually submitted orders

Through the TWS' API settings it is possible to configure this method's behaviour to some extent. As shown in the image below, manually placed orders can be given a negative order Id which can serve to easily tell manual from API submitted orders. The TWS' tooltip elaborates further:

tws_autobind.png

Receiving Order Information

Active orders will be delivered via The openOrder callback and The orderStatus callback callbacks. When all orders have been sent to the client application you will receive a LYNXApi.EWrapper.openOrderEnd event:

** Python **

class TestWrapper(wrapper.EWrapper):

def openOrder(self, orderId: OrderId, contract: Contract, order: Order,orderState: OrderState):
super().openOrder(orderId, contract, order, orderState)
print("OpenOrder. ID:", orderId, "Symbol:", contract.symbol, "SecType:", contract.secType,"Exchange:", contract.exchange, "Action:", order.action, "OrderType:", order.orderType, "TotalQuantity:", order.totalQuantity, "Status:", orderState.status)

if order.whatIf and orderState is not None:
    print("WhatIf. OrderId: ", orderId, "initMarginBefore:",
          orderState.initMarginBefore, "maintMarginBefore:",
          orderState.maintMarginBefore,"equityWithLoanBefore:",
          orderState.equityWithLoanBefore, "initMarginChange:",
          orderState.initMarginChange, "maintMarginChange:",
          orderState.maintMarginChange, "equityWithLoanChange:",
          orderState.equityWithLoanChange, "initMarginAfter:",
          orderState.initMarginAfter, "maintMarginAfter:",
          orderState.maintMarginAfter,"equityWithLoanAfter:",
          orderState.equityWithLoanAfter)

order.contract = contract
self.permId2ord[order.permId] = order

def orderStatus(self, orderId: OrderId, status: str, filled: float,remaining: float, avgFillPrice: float, permId: int,parentId: int, lastFillPrice: float, clientId: int,
whyHeld: str, mktCapPrice: float):
super().orderStatus(orderId, status, filled, remaining,avgFillPrice, permId, parentId, lastFillPrice, clientId, whyHeld, mktCapPrice)

print("OrderStatus. Id:", orderId, "Status:", status, "Filled:", filled,"Remaining:", remaining, "AvgFillPrice:", avgFillPrice,"PermId:", permId, "ParentId:", parentId, "LastFillPrice:",lastFillPrice, "ClientId:", clientId, "WhyHeld:",whyHeld, "MktCapPrice:", mktCapPrice)


def openOrderEnd(self):
super().openOrderEnd()
print("OpenOrderEnd")
logging.debug("Received %d openOrders", len(self.permId2ord))

** Java **

public class EWrapperImpl implements EWrapper {

@Override
public void openOrder(int orderId, Contract contract, Order order,
        OrderState orderState) {
    System.out.println(EWrapperMsgGenerator.openOrder(orderId, contract, order, orderState));
}

@Override
public void orderStatus(int orderId, String status, double filled,
        double remaining, double avgFillPrice, int permId, int parentId,
        double lastFillPrice, int clientId, String whyHeld, double mktCapPrice) {
    System.out.println("OrderStatus. Id: "+orderId+", Status: "+status+", Filled"+filled+", Remaining: "+remaining
            +", AvgFillPrice: "+avgFillPrice+", PermId: "+permId+", ParentId: "+parentId+", LastFillPrice: "+lastFillPrice+
            ", ClientId: "+clientId+", WhyHeld: "+whyHeld+", MktCapPrice: "+mktCapPrice);
}

@Override
public void openOrderEnd() {
    System.out.println("OpenOrderEnd");
}

Order Binding Notification

When an order is bound by API client 0 there will be callback to LYNXApi.EWrapper.orderBound . This indicates the mapping between API order ID and permID. \not yet implemented

** Python **

def orderBound(self, orderId: int, apiClientId: int, apiOrderId: int):
super().orderBound(orderId, apiClientId, apiOrderId)
print("OrderBound.", "OrderId:", orderId, "ApiClientId:", apiClientId, "ApiOrderId:", apiOrderId)

** Java **

@Override
public void orderBound(long orderId, int apiClientId, int apiOrderId) {                     System.out.println(EWrapperMsgGenerator.orderBound(orderId, apiClientId,             apiOrderId));
    } 

Executions and Commissions

When an order is filled either fully or partially, the LYNXApi.EWrapper.execDetails and LYNXApi.EWrapper.commissionReport events will deliver LYNXApi.Execution and LYNXApi.CommissionReport objects. This allows to obtain the full picture of the order's execution and the resulting commissions.

  • Advisors executing allocation orders will receive execution details and commissions for the allocation order itself. To receive allocation details and commissions for a specific subaccount LYNXApi.EClient.reqExecutions can be used.

[!WARNING] To receive commissions reports for all clients it is necessary to connect as the Master Client ID

** Python **

class TestWrapper(wrapper.EWrapper):

def execDetails(self, reqId: int, contract: Contract, execution: Execution):
super().execDetails(reqId, contract, execution)
print("ExecDetails. ReqId:", reqId, "Symbol:", contract.symbol, "SecType:", contract.secType, "Currency:", contract.currency, execution)

def commissionReport(self, commissionReport: CommissionReport):
super().commissionReport(commissionReport)
print("CommissionReport.", commissionReport)

** Java **

public class EWrapperImpl implements EWrapper {

@Override
public void execDetails(int reqId, Contract contract, Execution execution) {
    System.out.println("ExecDetails. "+reqId+" - ["+contract.symbol()+"], ["+contract.secType()+"], ["+contract.currency()+"], ["+execution.execId()+
            "], ["+execution.orderId()+"], ["+execution.shares()+"]"  + ", [" + execution.lastLiquidity() + "]");
}

@Override
public void commissionReport(CommissionReport commissionReport) {
    System.out.println("CommissionReport. ["+commissionReport.m_execId+"] - ["+commissionReport.m_commission+"] ["+commissionReport.m_currency+"] RPNL ["+commissionReport.m_realizedPNL+"]");
}
  • Note if a correction to an execution is published it will be received as an additional LYNXApi.EWrapper.execDetails callback with all parameters identical except for the execID in the Execution object. The execID will differ only in the digits after the final period.

Requesting Executions

LYNXApi.Execution and LYNXApi.CommissionReport can be requested on demand via the LYNXApi.EClient.reqExecutions method which receives a LYNXApi.ExecutionFilter object as parameter to obtain only those executions matching the given criteria. An empty LYNXApi.ExecutionFilter object can be passed to obtain all previous executions.

** Python **

self.reqExecutions(10001, ExecutionFilter())

** Java **

client.reqExecutions(10001, new ExecutionFilter());

Once all matching executions have been delivered, an LYNXApi.EWrapper.execDetailsEnd event will be triggered.

** Python **

class TestWrapper(wrapper.EWrapper):

def execDetailsEnd(self, reqId: int):
super().execDetailsEnd(reqId)
print("ExecDetailsEnd. ReqId:", reqId)

** Java **

public class EWrapperImpl implements EWrapper {

    @Override
    public void execDetailsEnd(int reqId) {
        System.out.println("ExecDetailsEnd. "+reqId+"\n");
    }

[!WARNING] Only those executions occurring since midnight for that particular account will be delivered. Older executions will generally not be available via the TWS API with LYNX Gateway.

Order Limitations

Aside from the TWS API's inherent limitation of 50 messages per second implying a maximum of 50 orders per second being sent to the TWS, there are no further API-only limitations.

Additionally, please note LYNX allows up to 15 active orders per contract per side per account.

Minimum Price Increment

The minimum increment is the minimum difference between price levels at which a contract can trade. Some trades have constant price increments at all price levels. However some contracts have difference minimum increments on different exchanges on which they trade and/or different minimum increments at different price levels. In the contractDetails class, there is a field 'minTick' which specifies the smallest possible minimum increment encountered on any exchange or price. For complete information about minimum price increment structure, there is the LYNX Contracts and Securities search site, or the API function reqMarketRule starting in API v973.03 and TWS 966.

The function reqContractDetails when used with a Contract object will return contractDetails object to the contractDetails function which has a list of the valid exchanges where the instrument trades. Also within the contractDetails object is a field called marketRuleIDs which has a list of "market rules". A market rule is defined as a rule which defines the minimum price increment given the price. The market rule list returned in contractDetails has a list of market rules in the same order as the list of valid exchanges. In this way, the market rule ID for a contract on a particular exchange can be determined.

  • Market rule for forex and forex CFDs indicates default configuration (1/2 and not 1/10 pips). It can be adjusted to 1/10 pips through TWS or LYNX Gateway Global Configuration.
  • Some non-US securities, for instance on the SEHK exchange, have a minimum lot size. This information is not available from the API but can be obtained from the LYNX Contracts and Securities search page. It will also be indicated in the error message returned from an order which does not conform to the minimum lot size.

With the market rule ID number, the corresponding rule can be found with the API function LYNXApi.EClient.reqMarketRule :

** Python **

self.reqMarketRule(26)
self.reqMarketRule(239)

** Java **

client.reqMarketRule(26);
client.reqMarketRule(240);

The rule is returned to the function LYNXApi.EWrapper.marketRule

** Python **

def marketRule(self, marketRuleId: int, priceIncrements: ListOfPriceIncrements):
    super().marketRule(marketRuleId, priceIncrements)
    print("Market Rule ID: ", marketRuleId)
        for priceIncrement in priceIncrements:
                print("Price Increment.", priceIncrement)

** Java **

@Override
public void marketRule(int marketRuleId, PriceIncrement[] priceIncrements) {
    DecimalFormat df = new DecimalFormat("#.#");
    df.setMaximumFractionDigits(340);
    System.out.println("Market Rule Id: " + marketRuleId);
    for (PriceIncrement pi : priceIncrements) {
        System.out.println("Price Increment. Low Edge: " + df.format(pi.lowEdge()) + ", Increment: " + df.format(pi.increment()));
    }
}
  • For forex, there is an option in TWS/LYNX Gateway configuration which allows trading in 1/10 pips instead of 1/5 pips (the default).
  • TWS Global Configuration -> Display -> Ticker Row -> Allow Forex trading in 1/10 pips

Checking Margin Changes

From the API it is possible to check how a specified trade execution is expected to change the account margin requirements for an account in real time. This is done by creating an Order object which has the LYNXApi.Order.WhatIf flag set to true. By default the whatif boolean in Order has a false value, but if set to True in an Order object with is passed to LYNXApi.EClient.placeOrder, instead of sending the order to a destination the LYNX server it will undergo a credit check for the expected post-trade margin requirement. The estimated post-trade margin requirement is returned to the LYNXApi.OrderState object.

This is equivalent to creating a order ticket in TWS, clicking "Preview", and viewing the information in the "Margin Impact" panel.