lbilli / Jib.jl

A Julia implementation of Interactive Brokers API
MIT License
62 stars 14 forks source link
api-client ib-api interactive-brokers julia

Jib

A Julia implementation of Interactive Brokers API

Jib is a native Julia 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 v176+ are supported.

The package design follows the official C++/Java IB API, which is based on an asynchronous communication model over TCP.

Installation

To install from GitHub:

] add https://github.com/lbilli/Jib.jl

Usage

The user interacts mainly with these two objects:

Other data structures, such as Contract and Order, are implemented as Julia struct 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:

using Jib

wrap = Jib.Wrapper(
         # Customized methods go here
         error= (id, errorCode, errorString, advancedOrderRejectJson) ->
                  println("Error: $(something(id, "NA")) $errorCode $errorString $advancedOrderRejectJson"),

         nextValidId= (orderId) -> println("Next OrderId: $orderId"),

         managedAccounts= (accountsList) -> println("Managed Accounts: $accountsList")

         # more method overrides can go here...
       );

# Connect to the server with clientId = 1
ib = Jib.connect(4002, 1);

# Start a background Task to process the server responses
Jib.start_reader(ib, wrap);

# Define contract
contract = Jib.Contract(symbol="GOOG",
                        secType="STK",
                        exchange="SMART",
                        currency="USD");

# Define order
order = Jib.Order();
order.action        = "BUY"
order.totalQuantity = 10
order.orderType     = "LMT"
order.lmtPrice      = 100

orderId = 1    # Should match whatever is returned by the server

# Send order
Jib.placeOrder(ib, orderId, contract, order)

# Disconnect
Jib.disconnect(ib)
Foreground vs. Background Processing

It is possible to process the server responses either within the main process or in a separate background Task:

To avoid undesired effects, the two approaches should not be mixed together on the same connection.

Implementation Details

The package does not export any name, therefore any functions or types described here need to be prefixed by Jib.*.

As Julia is not an object-oriented language, the functionality of the IB EClient class is provided here by regular functions. In particular:

Wrapper

Like the official IB EWrapper class, this struct holds the callbacks that are dispatched when responses are processed. The user provides the callback definitions as keyword arguments in the constructor, as shown above, and/or by setting the property of an existing instance.

A more comprehensive example is provided by simple_wrap(), which is used like this:

using Jib: Jib, Contract, reqContractDetails, simple_wrap, start_reader

data, wrap = simple_wrap();

ib = Jib.connect(4002, 1);
start_reader(ib, wrap);

reqContractDetails(ib, 99, Contract(conId=208813720, exchange="SMART"))

# Wait for the response and then access the "ContractDetails" result:
data[:cd]

Thanks to closures, data (a Dict in this case) is accessible by all wrap methods as well as the main program. This is one way to propagate incoming data to different parts of the program.

For more details about callback definitions and signatures, refer to the official IB EWrapper class documentation. As reference, the exact signatures used in this package are found here.

Notes

Callbacks are generally invoked with arguments and types matching the signatures as described in the official documentation. However, there are few exceptions:

These modifications make it possible to establish the rule: one callback per server response.

Consequently, historicalDataEnd() and scannerDataEnd() are redundant and are not used in this package.

Missing Values

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 Julia built-in Nothing in all circumstances.

Data Structures

Other classes that mainly hold data are also replicated. They are implemented as Julia struct or mutable struct with names, types and default values matching the IB API counterparts: e.g. Contract, Order, ComboLeg, ExecutionFilter, ScannerSubscription and Condition*.

TagValueList are implemented as Julia NamedTuple. Wherever a TagValue is needed, something like this can be used:

tagvaluelist = (tag1="value1", tag2="value2")
# or, in case of an empty list:
emptylist = (;)

Values don't need to be of type String. Int and Float64 are also allowed.