Camilo
Camilo

Reputation: 449

R IBrokers reqopenorders not updating

I am querying the list of pending orders in TWS Interactive Brokers using codes from the IBrokers package.

The function reqOpenOrders(tws) has an error in it, in that it hangs as shown here, and even when it works the outputs are messy as shown here.

I have put together some snipes of code from different sources to create a function that is clear/logical to understand, while outputting a data.frame of the results.

The function is below, and it works well the first time that is used. However, when a change is done in TWS (like cancelling a pending order, or placing a trail order), the change is not updated if the function below is run in R.

If I close and open again the connection to TWS, I get a empty data.frame after running the function below. if I close all connections and try the function several times eventually it returns the updated results.

Does any one know why calling the function does not query the most updated changes in TWS?

I think it has something to do with R connectivity to TWS. It seems the function waits for the connection to eventually become available. If I restart R, the first time I run the function, the results are updated, suggesting that if all connections are restored the function works well. Any suggestion to this problem?

I wonder if all sockets and connections need to be closed after exiting the function? I tried different versions to this solution with no success.

Any help is greatly appreciated.

library(IBrokers)

Get.Open.Orders<- function(tws)
{
library(dplyr)
Open.Orders <- function(tws)
  {
   con <- tws[[1]] #connector used to communicate with TWS
   VERSION <- "1"
   writeBin(c(.twsOutgoingMSG$REQ_OPEN_ORDERS, VERSION), con) #send request for open orders
   eW  <- eWrapper()                         #Creates a custom function to get the data that was requested
   socketSelect(list(con), FALSE, NULL)      #Wait for first socket connection to become available
   curMsg <- readBin(con, character(), 1L)   #read the response received from IB            
   processMsg(curMsg, con, eW)               #Process message 
 }

#orginize the data recieved into a data.frame, selects only  set of vaiables received.
open <- data.frame()
 i <- 0
   while(i < 2)
   {
     x <- Open.Orders(tws)
       if(!is.null(x) && x[1] == 53) # OPEN_ORDER_END
       {i = i + 1 } else if(!is.null(x) && x[1] == 5) # For Open Orders
         {
           x <- data.frame(t(x), stringsAsFactors = FALSE)
           open <- bind_rows(open, x)
         }
     rm(x)
   }

if(nrow(open) > 0)
{
 open <- open %>% distinct() %>%
   rename(conId = X4, symbol = X5, sectype = X6, strike = X10,
         currency = X11, action = X13, totalQuantity = X14,
         orderType = X15, lmtPrice = X16, auxPrice = X17,
         tif = X18, outsideRTH = X19, account = X20, orderId = X25, Status = X77
  ) %>%
  select(account, orderId, conId, symbol, sectype, strike, currency,
         action, totalQuantity, orderType, lmtPrice, auxPrice, tif, Status) %>%
  mutate(orderId = as.integer(orderId)
         , totalQuantity = as.numeric(totalQuantity)
         , lmtPrice = as.numeric(lmtPrice)
         , auxPrice = as.numeric(auxPrice) )
} else
{
open <- data.frame(account = character()
                   , orderId = integer()
                   , conId = character()
                   , symbol = character()
                   , sectype = character()
                   , strike = character()
                   , currency = character()
                   , action = character()
                   , totalQuantity = numeric()
                   , orderType = character()
                   , lmtPrice = numeric()
                   , auxPrice = numeric()
                   , tif = character()
                   , Status = character()
                   , stringsAsFactors = FALSE)
 }

 assign("IB.open.orders", open, envir = .GlobalEnv)
 rm(i, Open.Orders, open)
 return(data.frame(IB.open.orders))
 }


 #now connect to IB and get active orders; it works fine first time
 tws = twsConnect()
 Get.Open.Orders (tws)

 # now go to TWS and delete any given pending order for the sake of an example. Then in R run function again to request pending orders
Get.Open.Orders (tws)

#The change in TWS is now reflected in the output, so I disconnect TWS, and connect again

twsDisconnect(tws)  #disconned TWS
tws = twsConnect()  #connect to TWS
Get.Open.Orders (tws)

#the result is an empty dataframe. If I run the three lines of code above...say 10 times, eventually the updated results are returned.

showConnections(all = TRUE)
closeAllConnections()
tws = twsConnect()
Get.Open.Orders (tws)

Upvotes: 1

Views: 252

Answers (2)

Hasselhoff
Hasselhoff

Reputation: 41

This is really a comment to the only answer above, but I used to also do this looping thing until I realized that each order actually attaches the client name and reqID to each order, so I started keeping track of them and then I can access any order from inside the environment of a function by creating a new client (using the old client ID) and reference the standing order using the reqID, and then closing and trashing the client before returning results from the function.

So I'll use code like this for the take profit portion of a bracket order:

  tporder <-
      placeOrder(
        mainclient,
        contract,
        bracketaction,
        abs(adj_quantity),
        transmit = T,
        orderType = "LMT",
        limitPrice = tpp,
        tif = tif,
        stopPrice = "0.0",
        ocaGroup = ocaGroup,
        ocaType = ocaType,
        trigger = tp_trigger,
        stf = stf
      )
    
    print(mainclient$clientId)
    tporder$clientid <- mainclient$clientId

And then later to cancel the order, I'll use code like this:

if (exists("tp_data")) {
  if (tp_data[1] != "NO ORDER") {
    # first check if the order is attached to current client
    cancelOrder(current_client, tp_data$order_id)
    
    # if it's not then this code would create a new client
    # and cancel the order
    cancel_con <- connectToIB(tp_data$clientid)
    cancelOrder(cancel_con, tp_data$order_id)
    twsDisconnect(cancel_con)
    print("Take Profit Order Cancelled")
  }
}

The existing answer wouldn't work for me, because my bot runs continuously and often times I need to refresh my connection and the current client is not the same as the original client that set the order. I have to randomly assign clientId's based on time stamps. Also I trade a different asset class, so my assignment of column names is slightly different. So for the specific question at hand, my implementation might look something more like this:

   library(IBrokers)
   library(dplyr)



connectToIB <-
    function(clientId = 1,
             host = "localhost",
             port = Sys.getenv("TWS_PORT")) {
      # Create a socket connection
      tws <- twsConnect(clientId = clientId,
                        host = host,
                        port = port)
      
      # Check if the connection was successful
      if (!is.null(tws)) {
        cat("Connected to Interactive Brokers TWS API\n")
        return(tws)
      } else {
        cat("Failed to connect to Interactive Brokers TWS API\n")
        return(NULL)
      }
    }
    
    openOrders <- function(tws_con) {
      con <- tws_con[[1]] #connector used to communicate with TWS
      VERSION <- "1"
      writeBin(c(.twsOutgoingMSG$REQ_OPEN_ORDERS, VERSION), con) #send request for open orders
      eW  <- eWrapper()                         #Creates a custom function to get the data that was requested
      socketSelect(list(con), FALSE, NULL)      #Wait for first socket connection to become available
      curMsg <- readBin(con, character(), 1L)   #read the response received from IB
      processMsg(curMsg, con, eW)               #Process message
    }
    
    getOpenOrders <- function(tws_con) {
      #orginize the data recieved into a data.frame, selects only  set of vaiables received.
      open <- data.frame()
      i <- 0
      while (i < 2) {
        x <- openOrders(tws_con)
        if (!is.null(x) && x[1] == 53)
          # OPEN_ORDER_END
        {
          i = i + 1
        } else if (!is.null(x) && x[1] == 5)
          # For Open Orders
        {
          x <- data.frame(t(x), stringsAsFactors = FALSE)
          open <- bind_rows(open, x)
        }
        rm(x)
      }
      
      if (nrow(open) > 0) {
        open <- data.frame(
          open %>% distinct() %>%
            rename(
              conId = X4,
              symbol = X5,
              sectype = X6,
              multiplier = X10,
              exchange = X11,
              currency = X12,
              instrName = X13,
              tradingClass = X14,
              action = X15,
              totalQuantity = X16,
              orderType = X17,
              lmtPrice = X18,
              strike = X19,
              tif = X20,
              account = X21,
              orderId = X25,
              status = X94
            ) %>%
            select(
              account,
              orderId,
              conId,
              symbol,
              sectype,
              strike,
              currency,
              exchange,
              action,
              totalQuantity,
              multiplier,
              orderType,
              lmtPrice,
              instrName,
              tradingClass,
              tif,
              status
            ) %>%
            mutate(
              orderId = as.integer(orderId),
              totalQuantity = as.integer(totalQuantity),
              lmtPrice = as.numeric(lmtPrice)
            )
        )
      } else
      {
        open <- "NO ORDER"
      }
      
      return(open)
    }
    
new_con <- connectToIB(tp_data$clientid)
    
getOpenOrders(new_con)

twsDisconnect(new_con)

Upvotes: 0

Camilo
Camilo

Reputation: 449

This is probably not the best solution, but I found a way around getting the most updated data on open orders from TWS. The original function above seems to work only when the right connection is used. In between tries, it returns an empty data.frame. So what I did was to create a while loop that sends queries to TWS until the right connection is used. The code is below. It works and returns the right data after a handful of attempts. I figure I share the code as a temporary solution.

tws = twsConnect()

#Current pending orders
  OpenOrders=Get.Open.Orders (tws)
  showConnections(all = TRUE)    
  nOP=nrow(OpenOrders)

while (nOP==0){
  tws = twsConnect()
  OpenOrders=Get.Open.Orders (tws)  
  nOP=nrow(OpenOrders)
  quiet = TRUE
}

Upvotes: 1

Related Questions