rib
rib copied to clipboard
Live updating dataframe
Hello, I have the following code that I am using to stream data, and hopefully create a live updating plot in R.
Can you please guide me on how I can export the wrap$context$historical field into a R dataframe that is steadily updated as the streamed data comes through?
library(rib)
tickers <- c("AAPL", "GOOG")
wrap <- IBWrapSimple$new()
ic <- IBClient$new(wrap)
ic$connect(port=7497, clientId=5)
for(i in 1:length(tickers))
ic$reqHistoricalData(i, IBContract(tickers[i]), endDateTime = "", durationStr = "1 M", barSizeSetting = "1 day", useRTH = "1",
whatToShow = "TRADES", keepUpToDate = TRUE, formatDate = "1")
repeat
ic$checkMsg()
Here's a very quick idea on how to keep historical dataframes up to date.
For semplicity, I'll store them in a list indexed the same way as tickers to make it trivial to map
ticker -> reqId -> data.frame.
library(rib)
# derive a new wrapper with customized historicalData() and historicalDataUpdate()
IBWrapTest <- R6::R6Class("IBWrapTest",
inherit= IBWrapSimple,
public= list(
initialize= function()
# list holding the data.frames
self$context$dflist <- list(),
historicalData= function(reqId, bar)
# store initial history
self$context$dflist[[reqId]] <- bar,
historicalDataUpdate= function(reqId, bar) {
df <- self$context$dflist[[reqId]]
n <- nrow(df)
if(bar$time == df$time[n])
# update the last bar
self$context$dflist[[reqId]][n, ] <- bar
else
# bar has changed, append a new one
self$context$dflist[[reqId]] <- rbind(df, bar)
}
)
)
wrap <- IBWrapTest$new()
ic <- IBClient$new(wrap)
ic$connect(port=7497, clientId=5)
tickers <- c("AAPL", "GOOG")
for(i in 1:length(tickers))
ic$reqHistoricalData(i, IBContract(tickers[i]), "", "1 D", "5 mins", "TRADES", TRUE, 1, TRUE)
repeat {
# keep history up-to-date
ic$checkMsg()
# do something with it
wrap$context$dflist[[1]] # AAPL
wrap$context$dflist[[2]] # GOOG
}
@lbilli many thanks - I see the logic, and makes sense to me.
I ran into the following error - any further guidance you can provide me?
Error in wrap$context$dflist[[1]] : subscript out of bounds
It's difficult to say from here.
But as dflist is initialized as an empty list, you'd get that error if you try to access its elements, e.g. dflist[[1]] before any data is received and historicalData() invoked.
Many thanks again @lbilli - data download and updating works like a charm.
One further question I may ask is how do you place orders in the rib framework assuming we use the above constantly updating dataframe code?
I did the following, namely inserted the calls to orderId1 and orderId2 and then the # Send order section in the loop, but it comes out with a Error: 1 103 Duplicate order id issue.
What I ultimately want to do is pull in the wrap$context$dflist[[1]] and wrap$context$dflist[[2]] into a separate function I have written, and to place trades if the ratio of the prices exceeds a certain level. There would be two trades - one long and one short. So I am confused how orderId would work.
wrap <- IBWrapTest$new()
ic <- IBClient$new(wrap)
ic$connect(port=7497, clientId=5)
tickers <- c("AAPL", "GOOG")
# Define contract
contract1 <- IBContract("GOOG")
contract2 <- IBContract("AAPL")
# Define order
order1 <- IBOrder("BUY", 10, "LMT", 1000)
orderId1 <- 1 # Should match whatever is returned by the server
order2 <- IBOrder("SELL", 10, "LMT", 1000)
orderId2 <- 1 # Should match whatever is returned by the server
for(i in 1:length(tickers))
ic$reqHistoricalData(i, IBContract(tickers[i]), "", "1 D", "5 mins", "TRADES", TRUE, 1, TRUE)
repeat {
# keep history up-to-date
ic$checkMsg()
# do something with it
wrap$context$dflist[[1]] # AAPL
wrap$context$dflist[[2]] # GOOG
# Send order
ic$placeOrder(orderId1, contract1, order1)
ic$placeOrder(orderId2, contract2, order2)
# Check messages
ic$checkMsg()
}
As per IB API documentation, each new Order needs a unique orderId that is greater than any previous Order. The orderId can be reused to modify existing orders.
Upon connection, the server always responds with nextValidId() which contains the first orderId that can be used.
If using a variation of IBWrapSimple/IBWrapTest, that number is stored in wrap$context$nextId.
So, the logic to send orders should be something like:
wrap <- IBWrapTest$new()
ic <- IBClient$new(wrap)
ic$connect(port=7497, clientId=5)
# wait to make sure you receive all initial messages
Sys.sleep(1)
ic$checkMsg()
orderId <- wrap$context$nextId
[...]
# send first order
ic$placeOrder(orderId, contract1, order1)
orderId <- orderId + 1
[...]
# send another order
ic$placeOrder(orderId, contract2, order2)
orderId <- orderId + 1
[...]