From 455a5148773573cb1102c729dac5781d747f9d0d Mon Sep 17 00:00:00 2001 From: lbilli Date: Thu, 24 Nov 2022 06:41:41 -0500 Subject: [PATCH] Add man/* Move IBWrapSimple to examples/ --- DESCRIPTION | 26 +++++----- NAMESPACE | 3 +- NEWS.md | 3 ++ R/IBClient.R | 2 +- R/zzz.R | 2 +- README.md | 89 ++++++++++++++++------------------ {R => examples}/IBWrapSimple.R | 2 +- man/IBClient.Rd | 71 +++++++++++++++++++++++++++ man/IBWrap.Rd | 79 ++++++++++++++++++++++++++++++ man/enums.Rd | 40 +++++++++++++++ man/factory.Rd | 61 +++++++++++++++++++++++ man/rib-package.Rd | 35 +++++++++++++ man/structs.Rd | 52 ++++++++++++++++++++ 13 files changed, 401 insertions(+), 64 deletions(-) create mode 100644 NEWS.md rename {R => examples}/IBWrapSimple.R (99%) create mode 100644 man/IBClient.Rd create mode 100644 man/IBWrap.Rd create mode 100644 man/enums.Rd create mode 100644 man/factory.Rd create mode 100644 man/rib-package.Rd create mode 100644 man/structs.Rd diff --git a/DESCRIPTION b/DESCRIPTION index 2f4d4ad..48752e5 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,19 +1,17 @@ Package: rib -Title: An R implementation of Interactive Brokers API -Version: 0.17.2 +Title: An Implementation of 'Interactive Brokers' API +Version: 0.18.2 Authors@R: person("Luca", "Billi", email = "noreply.section+dev@gmail.com", role = c("aut", "cre")) -Description: A native R implementation of Interactive Brokers API. - It establishes a TCP connection to a server and handles - request-response message exchanges. - Data is encoded and decoded between user and wire formats. Data strucures - mirror what is found in the official API, which is available for other - languages. +Description: Allows interaction with 'Interactive Brokers' 'Trader Workstation' + . + Handles the connection over the network and the exchange of messages. + Data is encoded and decoded between user and wire formats. + Data structures and functionality closely mirror the official implementations. Depends: - R (>= 3.4.0) -Import: - R6 (>= 2.4.0) + R (>= 3.4) +Imports: + R6 (>= 2.4) License: GPL-3 Encoding: UTF-8 -LazyData: true -URL: https://github.com/lbilli/rib/ -BugReports: https://github.com/lbilli/rib/issues/ +URL: https://github.com/lbilli/rib +BugReports: https://github.com/lbilli/rib/issues diff --git a/NAMESPACE b/NAMESPACE index a3c0490..9a5541b 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -3,12 +3,13 @@ importFrom(R6, R6Class) # Main classes export(IBClient) export(IBWrap) -export(IBWrapSimple) # Helpers export(IBContract) export(IBOrder) export(fCondition) +export(map_enum2int) +export(map_int2enum) # Structures export(ComboLeg) diff --git a/NEWS.md b/NEWS.md new file mode 100644 index 0000000..b32c950 --- /dev/null +++ b/NEWS.md @@ -0,0 +1,3 @@ +# rib 0.18.2 + +* Submission to CRAN diff --git a/R/IBClient.R b/R/IBClient.R index 654deaf..42189a0 100644 --- a/R/IBClient.R +++ b/R/IBClient.R @@ -138,7 +138,7 @@ IBClient <- R6Class("IBClient", stopifnot(length(res)==2L) - cat("server version and timestamp:", res, "\n") + message("server version: ", res[1L], " timestamp: ", res[2L]) private$serverVersion <- as.integer(res[1L]) private$serverTimestamp <- res[2L] diff --git a/R/zzz.R b/R/zzz.R index 69c558d..e03f1ef 100644 --- a/R/zzz.R +++ b/R/zzz.R @@ -1,7 +1,7 @@ .onLoad <- function(libname, pkgname) { load_data <- function(file) - utils::read.table(file= system.file("data-raw/", file, package=pkgname), + utils::read.table(file= system.file("data-raw", file, package=pkgname), header= TRUE, colClasses= "character") diff --git a/README.md b/README.md index 9d0eae2..1297d76 100644 --- a/README.md +++ b/README.md @@ -4,16 +4,15 @@ Originally inspired by [`IBrokers`](https://CRAN.R-project.org/package=IBrokers), `rib` is a native [R](https://www.r-project.org/) client that -implements [Interactive Brokers](https://www.interactivebrokers.com/) API -to communicate with their TWS or IBGateway. +implements the [Interactive Brokers](https://www.interactivebrokers.com/) API +to communicate with TWS or IBGateway. It aims to be feature complete, however it does not support legacy versions. Currently, only API versions `v165+` are supported. The package design mirrors the official C++/Java [IB API](https://interactivebrokers.github.io/tws-api/), -which is based on an asynchronous request-response communication model -over TCP. +which is based on an asynchronous communication model over TCP. ### Installation To install from GitHub, @@ -26,9 +25,9 @@ remotes::install_github("lbilli/rib") ### Usage The user interacts mainly with two classes, implemented as [`R6`](https://CRAN.R-project.org/package=R6) objects: -- `IBClient`: responsible to establish the connection and send requests to the server. -- `IBWrap`: base class holding the callbacks that are executed when the - client processes the responses. Customized versions are derived from this class. +- `IBClient`: responsible to establish the connection and send requests to the server +- `IBWrap`: base class holding the callbacks that are executed 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 of the official IB API. @@ -65,35 +64,35 @@ IBWrapCustom <- R6::R6Class("IBWrapCustom", # Instantiate wrapper and client wrap <- IBWrapCustom$new() -ic <- IBClient$new(wrap) +ic <- IBClient$new() # Connect to the server with clientId = 1 ic$connect(port=4002, clientId=1) # Check connection messages (optional) -ic$checkMsg() +ic$checkMsg(wrap) # Define contract -contract <- IBContract("GOOG") +contract <- IBContract(symbol="GOOG", secType="STK", exchange="SMART", currency="USD") # Define order -order <- IBOrder("BUY", 10, "LMT", 1000) +order <- IBOrder(action="BUY", totalQuantity=10, orderType="LMT", lmtPrice=1000) orderId <- 1 # Should match whatever is returned by the server # Send order ic$placeOrder(orderId, contract, order) -# Check messages -ic$checkMsg() +# Check inbound messages +ic$checkMsg(wrap) # Disconnect ic$disconnect() ``` -As R is single threaded, in general it is the user's responsability to code -an event loop that handles the request-response pattern: -_i.e._ there is no `Reader` in the background monitoring the connection +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: @@ -111,18 +110,18 @@ library(rib) # Instantiate wrapper, client and connect wrap <- IBWrapSimple$new() -ic <- IBClient$new(wrap) +ic <- IBClient$new() ic$connect(port=4002, clientId=1) # Send requests, e.g.: -contract <- IBContract("GOOG") +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() +ic$checkMsg(wrap) # Find results in wrap$context @@ -145,7 +144,7 @@ library(rib) # Instantiate wrapper, client and connect wrap <- IBWrapSimple$new() -ic <- IBClient$new(wrap) +ic <- IBClient$new() ic$connect(port=4002, clientId=1) # Main loop @@ -156,7 +155,7 @@ repeat { ic$reqContractDetails(...) # Wait and process responses - ic$checkMsg() + ic$checkMsg(wrap) if(done) break @@ -173,33 +172,31 @@ ic$disconnect() ##### [`IBClient`](R/IBClient.R) This implements the IB `EClient` class functionality. Among its methods: -- `new(wrap)`: constructor. Require an object derived from `IBWrap` as argument. -- `replaceWrap(wrap)`: replace the `wrap`. As the client runs in a single thread, - it is possible to swap wrappers on the fly in a connected client. +- `new()`: create a new instance. - `connect(host, port, clientId, connectOptions)`: establish a connection. - `disconnect()`: terminate the connection. -- `checkMsg(timeout, flush)`: wait for responses and dispatch callbacks. +- `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 `flush=TRUE` callbacks are not dispatched. - Return the number of responses processed. **Needs to be called regularly**. -- methods that send specific requests to the server. + 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. ##### [`IBWrap`](R/IBWrap.R) Like the official IB `EWrapper` class, this holds the callbacks that are dispatched -when responses are processed. `IBWrap` itself is a base class containing -only dummy methods. +when responses are processed. `IBWrap` itself contains only stubs. Users need to derive from it and override the desired methods. -The code [above](#usage) provides a quick view of the procedure. +The code [above](#usage) provides a template of the procedure. For a more extensive example refer to the definition of -[`IBWrapSimple`](R/IBWrapSimple.R), which is provided for -illustrative purposes and which prints out the content of the responses or store it -within `IBWrapSimple$context` for later inspection. +[`IBWrapSimple`](examples/IBWrapSimple.R), 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 again to the official IB `EWrapper` class documentation. +refer to the official IB `EWrapper` class documentation. ### Notes Callbacks are generally invoked with arguments and types matching the signatures @@ -226,21 +223,21 @@ are **not** used in this package. `marketRule()` and the `historicalTicks*()` family. ##### Missing Values -Occasionally there is the need for numerical types to represent +Occasionally, for numerical types, there is the need to represent the lack of a value. -The IB API does not adopt a uniform solution for all situations, but rather -various sentinel values are used. +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. +for 32- and 64-bit integers respectively or `1.7976931348623157E308` +for 64-bit floating point. -Within this package an effort is made to replace all these values with -R built-in `NA`. +This package makes an effort to use R built-in `NA` +in all circumstances. ##### Data Structures -Other classes that mainly hold data are also [defined](R/structs.R). +Other classes that mainly hold data are also [replicated](R/structs.R). 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` @@ -253,9 +250,9 @@ contract$conId <- 12345 contract$currency <- "USD" ``` In the case of `Contract` and `Order`, helper [functions](R/factory.R) -are also provided to prefill some common fields: +are also provided to simplify the assignment to a subset of the fields: ```R -contract <- IBContract("GOOG") +contract <- IBContract(symbol="GOOG", secType="STK", exchange="SMART", currency="USD") # Equivalent to contract <- Contract @@ -266,7 +263,7 @@ contract$currency <- "USD" ``` and ```R -order <- IBOrder(totalQuantity=100, lmtPrice=50) +order <- IBOrder(action="BUY", totalQuantity=100, orderType="LMT", lmtPrice=50) # Equivalent to order <- Order diff --git a/R/IBWrapSimple.R b/examples/IBWrapSimple.R similarity index 99% rename from R/IBWrapSimple.R rename to examples/IBWrapSimple.R index 82b33bb..315b460 100644 --- a/R/IBWrapSimple.R +++ b/examples/IBWrapSimple.R @@ -1,4 +1,4 @@ -IBWrapSimple <- R6Class("IBWrapSimple", +IBWrapSimple <- R6::R6Class("IBWrapSimple", class= FALSE, cloneable= FALSE, diff --git a/man/IBClient.Rd b/man/IBClient.Rd new file mode 100644 index 0000000..695de42 --- /dev/null +++ b/man/IBClient.Rd @@ -0,0 +1,71 @@ +\name{IBClient} +\alias{IBClient} +\docType{class} + +\title{Client Connection Class} + +\description{ +This is the main class that manages the connection with the +'Trader Workstation', sends requests and handles responses. +} + +\section{Methods}{ +\itemize{ + +\item \code{IBClient$new()}: creates a new instance. + +\item \code{$connect(host="localhost", port, clientId, connectOptions="")}: + connects to \code{host:port} and performs the initial + handshake using client identifier \code{clientId} and additional + options \code{connectOptions}. + +\item \code{$checkMsg(wrap, timeout=0.2)}: waits for and process server messages. + When available, messages are decoded and handed over to the appropriate + callback defined in \code{wrap}, which must be an instance of a child of + \code{IBWrap}. + If \code{wrap} is missing, messages are read and immediately discarded. + Returns the number of messages processed. + + This methods \bold{blocks} up to \code{timeout} seconds. + \bold{Needs to be called regularly}. + +\item \code{$disconnect()}: terminates the connection. +} + +This class is modeled after the class \code{EClient} from the official IB API +implementations. +In addition to the methods shown above, several others exist that are used to send +requests to the server. + +Refer to the official documentation for a comprehensive list of the possible +requests, including their signatures and descriptions. +} + +\seealso{ +\code{\link{IBWrap}}. + +\href{https://interactivebrokers.github.io/tws-api/classIBApi_1_1EClient.html}{\code{EClient}} +definition from the official documentation. +} + + +\examples{ +\dontrun{ +# Instantiate a wrapper +wrap <- IBWrapSimple$new() + +# Create a client and connect to a server +ic <- IBClient$new() +ic$connect(port=4002, clientId=1) + +# Make a request +stock <- IBContract(symbol="GOOG", secType="STK", exchange="SMART", currency="USD") +ic$reqContractDetails(11, stock) + +# Process responses +ic$checkMsg(wrap) + +# Disconnect +ic$disconnect() +} +} diff --git a/man/IBWrap.Rd b/man/IBWrap.Rd new file mode 100644 index 0000000..26e10dc --- /dev/null +++ b/man/IBWrap.Rd @@ -0,0 +1,79 @@ +\name{IBWrap} +\alias{IBWrap} +\docType{class} + +\title{Callbacks Wrapper Class} + +\description{ +As the communication with the server is asynchronous, +the way to control how inbound messages are processed is via +callback functions. +The class \code{IBWrap} is merely a container for these functions. + +Being a base class, its methods are just stubs whose only action is +to raise a warning when called. + +Customized functionality is provided by defining a child class +of \code{IBWrap} and overriding the appropriate methods to perform +the desired tasks. + +These methods are never called directly by the user program, rather +they are implicitly invoked within \code{IBClient$checkMsg()} +when it processes the server responses. +} + +\details{ +\code{IBWrap} is modeled after \code{EWrapper} from the official +IB API implementations. + +The official documentation provides a comprehensive list and description +of the available methods, their signatures and usage. + +The customization process follows this template: +\preformatted{ +# Class derivation: +IBWrapSimple <- R6::R6Class("IBWrapSimple", + class= FALSE, + cloneable= FALSE, + lock_class= TRUE, + + inherit= IBWrap, + + public= list( + + # Customized methods: + error= function(id, errorCode, errorString, advancedOrderRejectJson){ + + # Code to handle error messages + }, + + nextValidId= function(orderId) { + + # Code to handle the next order ID + }, + + contractDetails= function(reqId, contractDetails) { + + # Code to handle Contract description + }, + # etc. + ) +) + +# Class instantiation: +wrap <- IBWrapSimple$new() + +# Use when processing server messages by a client: +ic <- IBClient$new() + +ic$checkMsg(wrap) +} + +} + +\seealso{ +\code{\link{IBClient}}. + +\href{https://interactivebrokers.github.io/tws-api/interfaceIBApi_1_1EWrapper.html}{\code{EWrapper}} +definition from the official documentation. +} diff --git a/man/enums.Rd b/man/enums.Rd new file mode 100644 index 0000000..35c8048 --- /dev/null +++ b/man/enums.Rd @@ -0,0 +1,40 @@ +\name{enums} +\title{Enumerated Types} +\alias{map_enum2int} +\alias{map_int2enum} + +\description{ +Enumerated types are used in few places across the API. +These are types that can have only a limited set of named +constant values. + +These functions facilitate the conversion between integer value +and string representation. +} + +\usage{ +map_enum2int(enum, name) + +map_int2enum(enum, value) +} + +\arguments{ + \item{enum}{name of the enumeration type: \emph{e.g.} \code{"Condition"}, + \code{"FaDataType"}, \code{"MarketData"}, \code{"PriceTrigger"}.} + + \item{name}{string representation of \code{value}.} + + \item{value}{integer representation of \code{name}.} +} + +\value{ +\code{map_enum2int} returns the corresponding \code{value}. + +\code{map_int2enum} returns the corresponding \code{name}. +} + +\examples{ +map_enum2int("MarketData", "DELAYED") # -> 3 + +map_int2enum("MarketData", 3) # -> "DELAYED" +} diff --git a/man/factory.Rd b/man/factory.Rd new file mode 100644 index 0000000..9312480 --- /dev/null +++ b/man/factory.Rd @@ -0,0 +1,61 @@ +\name{factory} +\title{Helpers} +\alias{IBContract} +\alias{IBOrder} +\alias{fCondition} + +\description{ +Helper functions that simplify the customization of common data structures. +} + +\usage{ +IBContract(\dots) + +IBOrder(\dots) + +fCondition(type) +} + +\arguments{ + \item{\dots}{Any combination of named arguments whose names are valid for + \code{Contract} or \code{Order} respectively.} + + \item{type}{Type of condition: one of \code{"Price"}, \code{"Time"}, + \code{"Margin"}, \code{"Execution"}, \code{"Volume"} or + \code{"PercentChange"}.} +} + +\details{ +The same result is achieved by making a copy of the respective structures +and explicitly reassigning values to the desired fields. +The two approaches can be complementary. +} + +\value{ +\code{IBContract} returns a \code{Contract}. + +\code{IBOrder} returns an \code{Order}. + +\code{fCondition} returns a \code{Condition}. +} + +\seealso{ +\code{\link{Contract}}, \code{\link{Order}}. +} + +\examples{ +stock <- IBContract(symbol="GOOG", secType="STK", exchange="SMART", currency="USD") + +# Equivalent to +stock <- Contract +stock$symbol <- "GOOG" +stock$secType <- "STK" +stock$exchange <- "SMART" +stock$currency <- "USD" + +order <- IBOrder(action="BUY", totalQuantity=10, orderType="LMT", lmtPrice=99) + +condition <- fCondition("Time") +condition$is_more <- TRUE +condition$value <- "20221114-12:00" +} diff --git a/man/rib-package.Rd b/man/rib-package.Rd new file mode 100644 index 0000000..59e3ab1 --- /dev/null +++ b/man/rib-package.Rd @@ -0,0 +1,35 @@ +\name{rib-package} +\alias{rib-package} +\alias{rib} +\docType{package} + +\title{An Implementation of 'Interactive Brokers' API} + +\description{ +\pkg{rib} allows programs to interact with 'Interactive Brokers' (IB) +'Trader Workstation' (TWS). +It establishes a connection over the network and manages +the exchange of messages between client and server. +Data is encoded and decoded between user and wire formats +following closely the official implementations. +} + +\details{ +The package design mirrors the implementations that +'Interactive Brokers' makes available for other languages, +such as C++, C#, Java and Python. + +The official IB documentation is therefore the most valuable reference regarding +the API functionality and usage. +} + +\seealso{ +\url{https://github.com/lbilli/rib} + +Report bugs at \url{https://github.com/lbilli/rib/issues} + +The official +\href{https://interactivebrokers.github.io/tws-api/}{IB API documentation}. +} + +\keyword{internal} diff --git a/man/structs.Rd b/man/structs.Rd new file mode 100644 index 0000000..fe0c4d1 --- /dev/null +++ b/man/structs.Rd @@ -0,0 +1,52 @@ +\name{structs} +\title{Data Structures} +\alias{ComboLeg} +\alias{Contract} +\alias{DeltaNeutralContract} +\alias{ExecutionFilter} +\alias{Order} +\alias{ScannerSubscription} +\alias{SoftDollarTier} +\alias{WshEventData} +\docType{data} + +\description{ +The data structures used by the API are implemented as \R named lists, +possibly nested. +Templates filled with default values are defined within the package. +In order to instantiate them, no elaborate contructor is required but +a simple copy will do. + +Still, \link[=IBContract]{helper functions} are available for \code{Contract} and \code{Order}. +} + +\usage{ +ComboLeg + +Contract + +DeltaNeutralContract + +ExecutionFilter + +Order + +ScannerSubscription + +SoftDollarTier + +WshEventData +} + +\seealso{ +\code{\link{IBContract}}, \code{\link{IBOrder}}. +} + +\examples{ +stock <- Contract + +stock$symbol <- "GOOG" +stock$secType <- "STK" +stock$exchange <- "SMART" +stock$currency <- "USD" +}