An R implementation of Interactive Brokers API
Originally inspired by IBrokers,
rib is a native R
client that implements Interactive Brokers API
to communicate with TWS or IBGateway.
It aims to be feature complete, however it does not support legacy
versions. Currently, only API versions v187+ are
supported.
The package design mirrors the official C++/Java IB API, which is based on an asynchronous communication model over TCP.
To install from CRAN:
install.packages("rib")To install the latest snapshot from GitHub, assuming devtools
or at least remotes
is already installed:
remotes::install_github("lbilli/rib")The user interacts mainly with two classes, implemented as R6
objects: - IBClient: responsible to establish a connection
and send requests to the server - IBWrap: base class
holding the callbacks that are invoked when responses are processed.
User customizations are derived from this class.
Other data structures, such as Contract and
Order, are implemented as R lists, or nested lists, and
mirror the respective classes in the official IB API.
A complete minimal working example is shown. For this code to work,
an instance of IB TWS or IBGateway needs to be running on the local
machine and listening on port 4002. Note:
demo or paper account recommended!! :smirk:
library(rib)
# Define a customized callbacks wrapper
IBWrapCustom <- R6::R6Class("IBWrapCustom",
class= FALSE,
cloneable= FALSE,
lock_class= TRUE,
inherit= IBWrap,
public= list(
# Customized methods go here
error= function(id, errorTime, errorCode, errorString, advancedOrderRejectJson)
cat("Error:", id, errorTime, errorCode, errorString, advancedOrderRejectJson, "\n"),
nextValidId= function(orderId)
cat("Next OrderId:", orderId, "\n"),
managedAccounts= function(accountsList)
cat("Managed Accounts:", accountsList, "\n")
# more method overrides can go here...
)
)
# Instantiate wrapper and client
wrap <- IBWrapCustom$new()
ic <- IBClient$new()
# Connect to the server with clientId = 1
ic$connect(port=4002, clientId=1)
# Check connection messages (optional)
ic$checkMsg(wrap)
# Define contract
contract <- IBContract(symbol="GOOG", secType="STK", exchange="SMART", currency="USD")
# Define order
order <- IBOrder(action="BUY", totalQuantity=10, orderType="LMT", lmtPrice=100)
orderId <- 1 # Should match whatever is returned by the server
# Send order
ic$placeOrder(orderId, contract, order)
# Check inbound messages
ic$checkMsg(wrap)
# Disconnect
ic$disconnect()As R is single threaded, in general it is the user’s responsibility
to code some kind of event loop to periodically process incoming
messages: i.e. there is no background Reader
monitoring the connection and processing the server responses.
Two possible approaches, with or without a loop, are described:
This is the simplest case. It’s not suitable for data subscriptions or whenever a stream of messages is expected. It follows the pattern: - connect - send requests - process responses - disconnect
library(rib)
# Instantiate wrapper, client and connect
wrap <- IBWrapSimple$new()
ic <- IBClient$new()
ic$connect(port=4002, clientId=1)
# Send requests, e.g.:
contract <- IBContract(symbol="GOOG", secType="STK", exchange="SMART", currency="USD")
ic$reqContractDetails(11, contract)
# more requests can go here...
# Parse responses
# Might need to be called several times to exhaust all messages
ic$checkMsg(wrap)
# Find results in
wrap$context
# Disconnect
ic$disconnect()A more realistic application requires a loop around the previous example: - connect - repeat - send requests - process responses - if done exit - disconnect
library(rib)
# Instantiate wrapper, client and connect
wrap <- IBWrapSimple$new()
ic <- IBClient$new()
ic$connect(port=4002, clientId=1)
# Main loop
repeat {
# Application logic goes here
# e.g.: send request
ic$reqContractDetails(...)
# Wait and process responses
ic$checkMsg(wrap)
if(done)
break
# Might be convenient to have a little wait here
Sys.sleep(1)
}
# Disconnect
ic$disconnect()IBClientThis implements the IB EClient class functionality.
Among its methods: - new(): create a new instance. -
connect(host, port, clientId, connectOptions): establish a
connection. - disconnect(): terminate the connection. -
checkMsg(wrap, timeout): wait for responses and dispatch
callbacks defined in wrap, which must be an instance of a
class derived from IBWrap. If no response is available, it
blocks up to timeout seconds. If
wrap is missing, messages are taken off the wire and
discarded. Return the number of responses processed. Needs to be
called regularly! - methods that send requests to the server.
Refer to the official IB EClient class documentation for
further details and method signatures.
IBWrapLike the official IB EWrapper class, this holds the
callbacks that are dispatched when responses are processed.
IBWrap itself contains only stubs. Users need to derive
from it and override the desired methods. The code above provides a template of the procedure.
For a more extensive example refer to the definition of
IBWrapSimple, mainly provided for illustrative purposes,
which simply prints out the content of the responses or store it within
IBWrapSimple$context for later inspection.
For more details about callback definitions and signatures, refer to
the official IB EWrapper class documentation.
Callbacks are generally invoked with arguments and types matching the
signatures as described in the official documentation. However, there
are few exceptions: - tickPrice() has an extra
size argument, which is meaningful only when
TickType ∈ {BID, ASK, LAST}. In these cases, the official
IB API fires an extra tickSize() event instead. -
historicalData() is invoked only once per request,
presenting all the historical data as a single data.frame,
whereas the official IB API invokes it row-by-row. -
scannerData() is also invoked once per request and its
arguments are in fact vectors rather than single values.
These modifications make it possible to establish the rule: one callback per server response.
Consequently, (starting
from historicalDataEnd()v196 it’s sent in a separate message) and
scannerDataEnd() are redundant and are not
used in this package.
data.frame are passed to several other callbacks, such
as: softDollarTiers(), familyCodes(),
mktDepthExchanges(), smartComponents(),
newsProviders(), histogramData(),
marketRule() and the historicalTicks*()
family.
Occasionally, for numerical types, there is the need to represent the lack of a value.
IB API does not have a uniform solution across the board, but rather
it adopts a variety of sentinel values. They can be either the plain
0 or the largest representable value of a given type such
as 2147483647 and 9223372036854775807 for 32-
and 64-bit integers respectively or 1.7976931348623157E308
for 64-bit floating point.
This package makes an effort to use R built-in NA in all
circumstances.
Other classes that mainly hold data are also replicated. They are implemented as R lists,
possibly nested, with names, types and default values matching the IB
API counterparts: e.g. Contract,
Order, ComboLeg, ExecutionFilter,
ScannerSubscription and Condition.
To use them, simply make a copy and override their elements:
contract <- Contract
contract$conId <- 12345
contract$currency <- "USD"In the case of Contract and Order, helper
functions are also provided to simplify the
assignment to a subset of the fields:
contract <- IBContract(symbol="GOOG", secType="STK", exchange="SMART", currency="USD")
# Equivalent to
contract <- Contract
contract$symbol <- "GOOG"
contract$secType <- "STK"
contract$exchange <- "SMART"
contract$currency <- "USD"and
order <- IBOrder(action="BUY", totalQuantity=100, orderType="LMT", lmtPrice=110)
# Equivalent to
order <- Order
order$action <- "BUY"
order$totalQuantity <- 100
order$orderType <- "LMT"
order$lmtPrice <- 110To instantiate a Condition, invoke
fCondition(type) and fill in the appropriate fields:
condition <- fCondition("Price")
condition$conId <- 265598L
condition$exchange <- "SMART"
condition$is_more <- TRUE
condition$value <- 200TagValueList are implemented as R named character
vectors. Wherever a TagValueList is needed, something like
this can be used:
tagvaluelist <- c(tag1="value1", tag2="value2")
# or, in case of an empty list:
emptylist <- character()