Programming Interactive Brokers Java API in a Console Application – Real Time Data
转载-http://holowczak.com/ib-api-java-realtime/
Goals of the Tutorial
The purpose of this tutorial is to demonstrate the basic functionality of the Interactive Brokers (IB) Java Application Programming Interface (API) used for accessing real-time market data in the IB trading platform. Concepts learned in this tutorial can be applied to larger projects such as building quote screens, capturing live market data and developing automated trading systems.
Original version: October 28, 2014
Revised version: April 21, 2016
Tutorial Pre-Requisites
Prior to starting this tutorial, participants should have a basic user-level familiarity with the Interactive Brokers Trader Workstation (TWS) application. Follow this link for notes on installing the Interactive Brokers Gateway and API under the Linux Operating System. In addition, some experience with Java programming within Linux OS is preferable. All of the examples in this tutorial were developed using Ubuntu Linux with the OpenJDK (Java Development Kit). If you are new to programming in Java you should first complete a Java Programming tutorial such as: one of the tutorial on this page.
The following environment was used to develop the steps outlined in this tutorial:
- Host OS: Windows 7 Professional 64-Bit.
- Virtualization: Virtual Box version 4.3.10 (VirtualBox-4.3.10-93012-Win.exe) downloaded from http://www.oracle.com/technetwork/server-storage/virtualbox/downloads/index.html
- Linux Operating System: Ubuntu Linux 14.04 (Trusty Tahr) Desktop downloaded from http://releases.ubuntu.com/14.04/
- Interactive Brokers Gateway Build 944.3c April 29, 2014.
- IB TWS API version 9.71.01 (with some additional notes added for version 9.72)
- Compilers installed in Ubuntu Linux:
$ g++ -version gcc/g++ (Ubuntu 4.8.2-19ubuntu1) 4.8.2 Copyright (C) 2013 Free Software Foundation, Inc.
- Java Version:
$ java -version java version "1.7.0_55" OpenJDK Runtime Environment (IcedTea 2.4.7) (7u55-2.4.7-1ubuntu1) OpenJDK Client VM (build 24.51-b03, mixed mode, sharing)
If you do not have these compilers installed, use the apt-get command (or other software installer such as yum) to download and install them. For example, to install the OpenJDK 7 Java Development Kit under Debian or Ubuntu Linux use the command:
$ sudo apt-get install openjdk-7-jdk
To install OpenJDK under CentOS, Fedora and other RedHat/Oracle distributions that use YUM, use the following command as root user:
yum install java-1.7.0-openjdk
Important Notes on Interactive Brokers API Versions
This tutorial was originally created based on API Version: API 9.71 Release Date: April 24 2015. This API works fine with Java 1.7 JDK in the environment outlined above and all of the instructions included in this tutorial should work fine.
API Version: API beta 9.72 Release Date: Oct 22 2015 (and later) can also be used with some changes to the source code, however JDK 1.8 is now required. OpenJDK 1.8 is not directly downloadable on Ubuntu 14.04. However you may use Oracle’s official JDK 1.8 and/or upgrade your Linux to a newer version with OpenJDK 1.8 available.
Be sure to select the appropriate JRE and JDK compiler using the commands:
sudo update-alternatives --config java sudo update-alternatives --config javac
Interactive Brokers routinely releases new versions of the IB Gateway, Trader workstation application and the API. In a given release, some of the method calls and parameters to those calls may change. Any changes will be reflected in their on-line documentation.
Interactive Brokers typically shuts down their system every Saturday to do maintenance. If you are trying log in over a weekend and cannot, it is probably because their systems are down for maintenance.
The next section provides some links to on-line documentation for the Interactive Brokers API.
On-Line Resources and Documentation
The following are resources available (as of March 7, 2014) on the IB web site:
- Main IB web site: www.interactivebrokers.com
- Basic information about the TWS and API (Application Programming Interfaces): https://www.interactivebrokers.com/en/index.php?f=5041
- In the above page click on the “Interface Comparison” tab, there are links to specific documentation including the “User’s Guide”: https://www.interactivebrokers.com/en/software/api/api.htm
- The TWS API Beginner’s Guide (using MS Excel): http://www.interactivebrokers.com/download/ExcelApiBeginners.pdf
- Interactive Brokers also schedules webinars that discuss various aspects of programming and trading with he API. https://www.interactivebrokers.com/en/index.php?f=3734&p=webinars
On the next page we will present an overview of the Interactive Brokers API.
Overview of the Interactive Brokers API
In this section I will introduce some of the basic features of the Interactive Brokers API. Much more detailed and complete information is available in the IB documentation.
Architecture
The IB API system runs on Windows, Linux and MacOS. Applications that use the API make a connection through either the IB Trader Workstation client or the IB Gateway application.
In other words, users will either log in to the their IB account through Trader Workstation or through the IB Gateway and these applications take care of all of the communications to/from the IB data centers. The application we write using the IB API will connect to either TWS or the IB Gateway. All of the interactions thus happen “through” TWS or the IB Gateway. Below is a basic diagram of the interactions.
Most users will run the IB Gateway or TWS on the same computer where they are developing and running their custom applications that use the IB API. However this is not a firm requirement. One can run the IB Gateway or Trader Workstation on a separate computer or server. Some additional configuration steps are typically required and are referenced later on in this tutorial.
Event-driven programming
All communications between the host programming language and the IB API are accomplished through an event-driven architecture. Requests for data, orders, and other services are submitted by calling various methods in the API. The results of the request (data, order confirmations, etc.) are returned from IB as a series of events. This mode of communications is often called a Publish/Subscribe model (abbreviated Pub/Sub). We make a “Subscription” for a certain type of data, say the last traded price of a stock, and then our application receives “publications” of that data each time a new trade happens at an exchange.
Our user applications will need to have code in place to capture these incoming data events and process them accordingly. In most cases, this event processing code will run in a separate processing thread. This multi-threading architecture is already set up in the C#, Java and ActiveX flavors of the IB API.
Some of the more commonly used API method/function calls and their event responses are listed below:
Purpose | Request Method | Response events | Cancel |
---|---|---|---|
Connect to IB | connect | errMsg | disconnect |
Real-time Market data | reqMktData | tickPrice, tickSize, etc. | cancelMktData |
Market Depth Data | reqMktDepth | updateMktDepth | cancelMktDepth |
Real-time bars | reqRealTimeBars | realtimeBar | cancelRealTimeBars |
Historical data | reqHistoricalData | historicalData | cancelHistoricalData |
Place an Order | placeOrder | orderStatus | cancelOrder |
Request contract details | reqContractDetails | contractDetails | n/a |
Calculate an options price | calculateOptionPrice | tickOptionComputation | |
Account Updates | reqAccountUpdates | updateAccountTime updateAccountValue updatePortfolioEx | reqAccountUpdates |
Interactive Brokers provides several different interfaces to their proprietary API. For VBA (Excel) and Visual Basic programmers they offer a Component Object Model (COM) object and Excel formulas that can be used to access data and submit orders. If you are new to the IB API, then this approach provides a great way to get to know the major functions of the API. The IB ActiveX API provides a Windows ActiveX wrapper and Windows Control (called Tws Control) for the COM object that can be embedded into any Windows programming language such as Visual Basic, VBA, Visual C++, C#, J#, F#, Delphi and other Windows-centric programming environments.
Interactive Brokers also offers Java and C/C++ POSIX compliant versions of their APIs. This tutorial focuses on working with the Java API under Linux. As mentioned above, the Java API takes care of the multi-threading support to handle incoming data events.
Interactive Brokers “Real Time” Data
The Interactive Brokers real-time data feed is a bit of a misnomer. For most financial instruments (equities, options, futures, FX, etc.) IB will deliver a snapshot or “conflated” data feed when real-time data is requested. In the case of equities, only when a price, size or volume changes, will a new message be sent from the IB data center to the client. In addition, price and size changes are sampled over a 250 ms period before being sent (US options: 100 ms, FX Quotes: 5 ms). The clear disadvantage to this type of snapshot data feed is that it would make any trading strategy that relies on receiving every tick of the market difficult to implement. The advantages of this type of data feed are that the cost for real-time data is relatively low and during period of market turmoil, there is less of a chance of a trading application (client) being overwhelmed by messages.
One way to receive real-time data is to subscribe (at additional cost) to Level 2 or Order Book data feeds. These have the advantage of sending a message for every change to an order book for a security. However, given the increased message traffic, the default (most basic) set of market data lines are limit to three concurrent order book subscriptions.
In the next section we will introduce how Interactive Brokers identifies different financial instruments.
Identifying Stocks, Options and Futures in Interactive Brokers
Interactive Brokers API identifies a particular financial instrument using an object class named Contract. The Contract has the following attributes (properties):
Property | Description |
---|---|
conId | Unique contract identifier |
symbol | Stock symbol or underlying symbol for Options or Futures |
secType | Type of instrument: Stock=STK, Option=OPT, Future=FUT, etc. |
expiry | For Options or Futures: The expiration data in the format YYYYMM |
strike | For Options: The Options Strike Price |
right | For Options: The Options “PUT” or “CALL” rights |
multiplier | The contract multiplier for Futures or Options |
exchange | The destination of order or request. “SMART” = IB smart order router |
primaryExchange | The primary listing exchange where the instrument trades. NYSE, NASDAQ, AMEX, BATS, ARCA, PHLX etc. |
currency | The currency of the exchange USD or GBP or CAD or EUR, etc. |
localSymbol | The local exchange symbol of the instrument |
includeExpired | Include expired futures or options contracts in the request |
secIdType | The type of security identifier such as CUSIP, ISIN, SEDOL or RIC |
secId | The identifier of the type specified in secIdType. For example AAPL.O |
In each of the examples, before a data request is made or before an order is submitted, an object of class Contract will be created and its attributes will be populated with appropriate values used to identify the financial instrument. For example, to access market data for Microsoft stock, set the attributes:
conID = 0; | // Contract ID |
secType = "STK"; | // Security type is a stock (STK) |
symbol = "MSFT"; | // Microsoft stock symbol |
exchange = "SMART"; | // Use IB’s Smart Order router to get the prices |
currency = "USD"; | // USD Currency |
To access a June 2016 $35 Call option on Microsoft set the attributes:
conID = 0; | // Contract ID |
secType = "OPT"; | // Security type is an Option (OPT) |
symbol = "MSFT"; | // Google’s stock symbol |
expiry = "20160618"; | // June 18, 2016 Expiry |
strike = 35; | // $35.00 strike price |
right = "CALL"; | // Call option |
multiplier = "100"; | // multiplier 100 shares per contract for options |
exchange = "SMART"; | // Use IB’s Smart Order router to get the prices |
currency = "USD"; | // USD Currency |
To access a June 2017 Crude Oil Futures contract set the attributes:
conID = 0; | // Contract ID |
secType = "FUT"; | // Security type is an Future (FUT) |
symbol = "CL"; | // Crude Oil underlying symbol (CL) |
expiry = "201706"; | // June 2017 Expiry |
exchange = "SMART"; | // Use IB’s Smart Order router to get the prices |
To access a foreign exchange quote such as Euro/USD:
conID = 0; | // Contract ID |
secType = "CASH"; | // Security type is Cash / FX |
symbol = "EUR"; | // Euro underlying (base currency) symbol (EUR/USD quote) |
exchange = "IDEALPRO"; | // Use the IDEALPRO FX data source |
currency = "USD"; | // Quoted currency is USD |
Note that when reading currency quotes, consider the “Base” currency and the “quoted” currency.
In the next section we will cover building the Java API for the Interactive Brokers API.
Building the Interactive Brokers Java API
Before we can use the Java API, we will need to compile the API source code into a set of Java class files that we can include (import) into our own application. By following the instructions on installing the IB Gateway and API under Linux, we should end up with the Java API codes saved in directory: IBJts/source/JavaClient. In my examples, I created an “ib” directory and then installed the IB Gateway and API in the IBJts directory within it. So the Java API files look like this:
rholowczak@rholowczak-VirtualBox: cd ib/IBJts/source/JavaClient rholowczak@rholowczak-VirtualBox:~/ib/IBJts/source/JavaClient$ ls -l total 12 drwxrwxr-x 3 rholowczak rholowczak 4096 Aug 20 18:01 bin -rw-rw-r-- 1 rholowczak rholowczak 295 Jan 30 2014 build.xml drwxrwxr-x 3 rholowczak rholowczak 4096 Feb 12 2014 com rholowczak@rholowczak-VirtualBox:~/ib/IBJts/source/JavaClient$
The build.xml file is an ANT script that gives the parameters for building the Java API. To build the API, just issue the “ant” command from this directory:
rholowczak@rholowczak-VirtualBox:~/ib/IBJts/source/JavaClient$ ant Buildfile: /home/rholowczak/ib/IBJts/source/JavaClient/build.xml CreateJar: BUILD SUCCESSFUL Total time: 0 seconds
Check to see that the JAR file was indeed created:
rholowczak@rholowczak-VirtualBox:~/ib/IBJts/source/JavaClient$ ls -lg ../../javaclient.jar -rw-rw-r-- 1 rholowczak 179355 Oct 27 14:02 ../../javaclient.jar
Unpacking the TwsApi.jar file for version 9.72
Version 9.72 of the API has all of the Java Class files stored in the TwsApi.jar file.
To extract all of these files, issue the following command:
jar xf TwsApi.jar
Adding the Java API to the CLASSPATH
The Java compiler and Java runtime will need to know where they can find the class files for the API. One way to do this is to add the Java API class files to the CLASSPATH environment variable. For example, the following shell command will set up the CLASSPATH for the current directory (“.”), the main Interactive Brokers directory (“ib/IBJts”) and the API directory. All of these are under my account (/home/rholowczak). We will also preserve any existing entries in the CLASSPATH environment variable.
$ CLASSPATH=$CLASSPATH:.:/home/rholowczak/ib/IBJts:/home/rholowczak/ib/IBJts/source/JavaClient/bin:/home/rholowczak/ib/IBJts/source/JavaClient
Always check to make sure the CLASSPATH is set properly:
$ echo $CLASSPATH .:/home/rholowczak/ib/IBJts:/home/rholowczak/ib/IBJts/source/JavaClient/bin
In the next section we will begin programming real-time data using the Interactive Brokers API.
Starting the Real-Time Market Data Project
Streaming market data applications are programmed to accept new data when it is available from the market center or data distributor. To accomplish this, a “handler” method must be created that will automatically be executed each time new data arrives.
Start by creating a new folder for your project. If you use an Integrated Development Environment such as Eclipse, set up a new project using your IDE. For this example, we will use a simple text editor and the javac command-line compiler.
For this project I created a folder under myprojects named RealTimeData as shown below:
$ pwd /home/rholowczak/ib/IBJts/myprojects/RealTimeData
Creating a source code file
Create a new Java source code file named RealTimeData.java. Use any text editor such as kedit, gedit, pico, vi, etc. to edit the new file.
Import Classes to support IB API
The first portion of the program will import the necessary classes to support the IB API:
// RealTimeData.java // Version 1.0 // 20141028 // R. Holowczak // Import Java utilities and Interactive Brokers API import java.util.Vector; import com.ib.client.Contract; import com.ib.client.ContractDetails; import com.ib.client.EClientSocket; import com.ib.client.EWrapper; import com.ib.client.Execution; import com.ib.client.Order; import com.ib.client.OrderState; import com.ib.client.TagValue; import com.ib.client.CommissionReport; import com.ib.client.UnderComp; |
Creating the RealTimeData public class
For this application our main class will be RealTimeData and it will constitute an implementation of the Interactive Brokers EWrapper class. The EWrapper class is the public interface to the IB API. All of the methods available within the API must have an implementation, even if that implementation is empty.
There are two object we will need to have available for use throughout the RealTimeData class. The nextOrderId will hold the next order identifier we can use when we are making requests. The EClientSocket object named client will represent our connection to Interactive Brokers (via the IB Gateway). All of our requests for market data and for placing orders will happen through this client object. The details of the EClientSocket are implemented in the EClientSocket.java file in the Java API. Of particular importance for our purposes is the fact that the EClientSocket spawns an independent thread for reading implemented as an EReader object. This thread works asynchronously in the background to receive events such as market data, status updates and other messages from the Interactive Brokers data center.
// RealTimeData Class is an implementation of the // IB API EWrapper class public class RealTimeData implements EWrapper { // Keep track of the next Order ID private int nextOrderID = 0; // The IB API Client Socket object private EClientSocket client = null; |
In the next section we will use the client object to initiate a connection to IB and then to send out a request for market data.
Connecting to IB through the API
In this section we continue our RealTimeData class with the public constructor implementation. For this implementation we will instantiate the client object and then call the eConnect method to connect to IB.
Any method we run against the client object will be done asynchronously. In other words, our program will not wait for the connection to IB to be completed. It will make the connection request and then continue on with the next line of code in the program. As a result, we will need to implement a loop to wait while the connection is established. For example, if we are connecting through Trader Workstation (TWS) the user may be prompted to allow the connection to go through so our application needs to wait until this happens otherwise the rest of the code will try and work with a potentially closed connection. There are a few ways to check for a connection. The isConnected() is one way but in newer versions of the API might return true before ready to actually run any request methods. Another approach is to check NextOrderId which will automatically be updated with the next available OrderId once the Gateway (or TWS) is ready for requess.
Once the connection is established, a Contract object named contract will be created. A EUR/USD FX quote is used for this example.
Starting with the API version 9.71, several of the API calls require a new parameter called Market Data Options. This is implemented as a TagValue List. For most of our purposes this list may remain empty. So a TagValue list is set up next.
Finally everything is in place and we can make the call to reqMktData with the following parameters:
- ConnectionID – Connection Identifier for this request
- Contract – The financial instrument we are requesting data on
- Ticks – Any custom tick values we are looking for (null in this case)
- Snapshot – When set to false the request will give us streaming data, true gives one data snapshot
- MarketDataOptions – tagValue list of additional options (API 9.71 and newer)
For earlier versions of the API (prior to 9.71) you may leave off the last parameter.
public RealTimeData () { // Create a new EClientSocket object client = new EClientSocket (this); // Connect to the TWS or IB Gateway application // Leave null for localhost // Port Number (should match TWS/IB Gateway configuration client.eConnect (null, 7496, 0); // Pause here for connection to complete try { while (! (client.isConnected())); // Can also try: while (client.NextOrderId <= 0); } catch (Exception e) { } // Create a new contract Contract contract = new Contract (); contract.m_symbol = "EUR"; contract.m_exchange = "IDEALPRO"; contract.m_secType = "CASH"; contract.m_currency = "USD"; // Create a TagValue list Vector<TagValue> mktDataOptions = new Vector<TagValue>(); // Make a call to reqMktData to start off data retrieval with parameters: // ConID - Connection Identifier. // Contract - The financial instrument we are requesting data on // Ticks - Any custom tick values we are looking for (null in this case) // Snapshot - false give us streaming data, true gives one data snapshot // MarketDataOptions - tagValue list of additional options (API 9.71 and newer) client.reqMktData(0, contract, null, false, mktDataOptions); // At this point our call is done and any market data events // will be returned via the tickPrice method } // end RealTimeData |
Once the call is made to the client.reqMktData method, this execution thread finishes up and the API EWrapper execution thread waits for new data events to arrive. To handle these different events, we will need to implement each handler method included in the EWrapper. This step is discussed in the next section.
Add Code to Handle Data events
In this section we will add code to handle the incoming data. This code will be handled by a series of public void methods that are part of our EWrapper implementation in RealTimeData. The vast majority of methods need not have any actual code implemented but the method definitions need to be in place. For our example, our current program will not do any work with bond contracts and so as a result we can simply create an empty implementation for the public void bondContractDetails method.
The complete list for API version 9.71 is shown below. If you are working with an older version of the API, some of these methods may not apply. If you are working with a newer version of the API, you may need to include additional methods to handle new message types.
public void bondContractDetails(int reqId, ContractDetails contractDetails) { } public void contractDetails(int reqId, ContractDetails contractDetails) { } public void contractDetailsEnd(int reqId) { } public void fundamentalData(int reqId, String data) { } public void bondContractDetails(ContractDetails contractDetails) { } public void contractDetails(ContractDetails contractDetails) { } public void currentTime(long time) { } public void displayGroupList(int requestId, String contraftInfo) { } public void displayGroupUpdated(int requestId, String contractInfo) { } public void verifyCompleted(boolean completed, String contractInfo) { } public void verifyMessageAPI(String message) { } public void execDetails(int orderId, Contract contract, Execution execution) { } public void execDetailsEnd(int reqId) { } public void historicalData(int reqId, String date, double open, double high, double low, double close, int volume, int count, double WAP, boolean hasGaps) { } public void managedAccounts(String accountsList) { } public void commissionReport(CommissionReport cr) { } public void position(String account, Contract contract, int pos, double avgCost) { } public void positionEnd() { } public void accountSummary(int reqId, String account, String tag, String value, String currency) { } public void accountSummaryEnd(int reqId) { } public void accountDownloadEnd(String accountName) { } public void openOrder(int orderId, Contract contract, Order order, OrderState orderState) { } public void openOrderEnd() { } public void orderStatus(int orderId, String status, int filled, int remaining, double avgFillPrice, int permId, int parentId, double lastFillPrice, int clientId, String whyHeld) { } public void receiveFA (int faDataType, String xml) { } public void scannerData(int reqId, int rank, ContractDetails contractDetails, String distance, String benchmark, String projection, String legsStr) { } public void scannerDataEnd(int reqId) { } public void scannerParameters(String xml) { } public void tickEFP(int symbolId, int tickType, double basisPoints, String formattedBasisPoints, double impliedFuture, int holdDays, String futureExpiry, double dividendImpact, double dividendsToExpiry) { } public void tickGeneric(int symbolId, int tickType, double value) { } public void tickOptionComputation( int tickerId, int field, double impliedVol, double delta, double optPrice, double pvDividend, double gamma, double vega, double theta, double undPrice) { } public void deltaNeutralValidation(int reqId, UnderComp underComp) { } public void updateAccountTime(String timeStamp) { } public void updateAccountValue(String key, String value, String currency, String accountName) { } public void updateMktDepth(int symbolId, int position, int operation, int side, double price, int size) { } public void updateMktDepthL2(int symbolId, int position, String marketMaker, int operation, int side, double price, int size) { } public void updateNewsBulletin(int msgId, int msgType, String message, String origExchange) { } public void updatePortfolio(Contract contract, int position, double marketPrice, double marketValue, double averageCost, double unrealizedPNL, double realizedPNL, String accountName) { } public void marketDataType(int reqId, int marketDataType) { } public void tickSnapshotEnd(int tickerId) { } public void connectionClosed() { } public void realtimeBar(int reqId, long time, double open, double high, double low, double close, long volume, double wap, int count) { } public void tickSize(int orderId, int field, int size) { } public void tickString(int orderId, int tickType, String value) { } |
Additional Methods with Implementations
The following methods will have some implementation included. The first three are overloaded methods for different types of errors that can be thrown. The forth method below generates a new unique OrderID that gets triggered from Interactive Brokers.
public void error(Exception e) { // Print out a stack trace for the exception e.printStackTrace (); } public void error(String str) { // Print out the error message System.err.println (str); } public void error(int id, int errorCode, String errorMsg) { // Overloaded error event (from IB) with their own error // codes and messages System.err.println ("error: " + id + "," + errorCode + "," + errorMsg); } public void nextValidId (int orderId) { // Return the next valid OrderID nextOrderID = orderId; } |
The next section discusses the tickPrice implementation and the main method to finish off the program.
The tickPrice Method Implementation
Finally, we get to the heart of the program. The tickPrice method will be triggered every time data arrives at our application. When tickPrice is called by the EWrapper execution thread, it will contain four parameters:
- The orderId will match the id passed in at the call to reqMktData
- The field parameter will include an integer that indicate which price field is being updated.
- The price field will contain the actual price
- The canAutoExecute field is a flag indicating if this quote is eligible for AutoEx
public void tickPrice(int orderId, int field, double price, int canAutoExecute) { try { // Print out the current price. // field will provide the price type: // 1 = bid, 2 = ask, 4 = last // 6 = high, 7 = low, 9 = close System.out.println("tickPrice: " + symbolId + "," + field + "," + price); } catch (Exception e) { e.printStackTrace (); } } // end tickPrice |
The main implementation
The final portion of the program implements the main. Essentially all we need to do is create a new RealTimeData object and let the constructor do all of the work of connecting to IB, calling the reqMktData and then sitting back to wait for market data events.
public static void main (String args[]) { try { // Create an instance // At this time a connection will be made // and the request for market data will happen RealTimeData myData = new RealTimeData(); } catch (Exception e) { e.printStackTrace (); } } // end main } // end public class RealTimeData
In the next section we will compile and test the application.
Saving, Compiling and Testing the Realtime Application
At this point we are ready to finally run the application. Follow this last set of steps to Save, Compile and test the application.
Make sure the RealTimeData.java file is saved.
Run the Java compiler using the command:
javac RealTimeData.java
If there are no errors, the RealTimeData.class file will be created. Briefly, some common errors you may encounter are:
- If the compiler can not find the IB Classes, you will receive compiler errors such as:
RealTimeData.java:8: error: package com.ib.client does not exist. import com.ib.client.Contract;
Make sure the CLASSPATH environment variable points to the .class files included with the Java API and/or the TwsApi.jar file included with API version 9.72.
- If you are missing any implementations of methods included in the EWrapper class, you will receive an error like the following:
RealTimeData.java:21: error: RealTimeData is not abstract and does not override abstract method tickString(int,int,String) in EWrapper
Make sure you include an implementation (even if it is an empty one) for each message type supported by EWrapper.
- If you receive errors from the Java Compiler such as:
warning: /home/rholowczak/ib/IBJts/source/JavaClient/com/ib/client/Contract.class: major version 52 is newer than 51, the highest major version supported by this compiler.
This may indicate you are using JDK version 1.7 (or older) to try and work with the newer API version 9.72. See the notes on the first page of this tutorial regarding working with newer versions of the API and the requirements for JDK 1.8.
Testing the RealTimeData Application
If everything has compiled properly, run the application:
rholowczak@rholowczak-VirtualBox:~/ib/IBJts/myprojects/RealTimeData$ java RealTimeData Server Version:70 TWS Time at connection:20141028 13:21:13 EST error: -1,2104,Market data farm connection is OK:cashfarm tickPrice: 0,6,1.27645 tickPrice: 0,7,1.2685 tickPrice: 0,9,1.2699 tickPrice: 0,1,1.2743 tickPrice: 0,2,1.27435 tickPrice: 0,1,1.27425 tickPrice: 0,2,1.2743 tickPrice: 0,1,1.2742 tickPrice: 0,2,1.27425 tickPrice: 0,1,1.27415 tickPrice: 0,2,1.2742 tickPrice: 0,2,1.27425 tickPrice: 0,1,1.2742 tickPrice: 0,1,1.27425 tickPrice: 0,2,1.2743 |
This program will continue running until you stop it by pressing Control-C. Note that immediately after calling reqMktData, Interactive Brokers responds with a series of tickPrice messages to give the high, low and close prices. These are followed by a series of Bid and Ask prices.
On the next page I offer some debugging tips and then follow up with some exercises to try.
Debugging Tips
One of the most common issues people face is getting the Contract details correct. In some cases, IB is inconsistent in what combinations of symbol, exchange, primaryExchange, currency, etc. they will accept in order to identify a security. For example, it is possible to have all of the right Contract details for a security but get one property (such as primary Exchange) wrong. In that case the whole request will be rejected.
In general, my approach is to start with a minimum amount of contact details (leave all others as the defaults) and work up from there. Specifying the currency (USD) is helpful to distinguish between international stocks, ADRs and domestic stocks.
Some exercises to extend the functionality of the real-time data application are given in the next section.
Interactive Brokers API RealTime Data Exercises
Complete the following exercises to extend the basic real-time data application developed in this tutorial.
Note: Before starting these exercises, you may wish to make a copy of the original project just created.
Exercise 1: Display changes to Sizes
Modify the above program to receive changes to Sizes. Add code to respond to the tickSize event. To do this, add code similar to the following:
public void tickSize (int orderId, int field, int size) { // field will provide the size type: // 0=bid size, 3=ask size, 5=last size, 8=volume System.out.println("tickSize: " + orderId + "," + field + "," + size); } |
Server Version:70 TWS Time at connection:20141028 14:43:54 EST error: -1,2104,Market data farm connection is OK:cashfarm tickPrice: 0,6,1.27645 tickPrice: 0,7,1.2685 tickPrice: 0,9,1.2699 tickPrice: 0,1,1.2741 tickSize: 0,0,6314000 tickPrice: 0,2,1.27415 tickSize: 0,3,20314000 |
Exercise 2: Calculate and Display Moving Average
Modify the above program to retrieve the latest price and then calculate and display a moving average based on the last traded price.
- Declare two new variables :
// Keep track of prices for Moving Average private double priceTotal; private int numberOfPrices;
- In the RealTimeData constructor method initialize each variable to 0 before the call to reqMktData
- In the tickPrice method, declare a local variable named movingAverage as a double.
- Add code in the tickPrice method to count up the number of last traded prices (increment numberOfTrades) and to total up the prices in priceTotal. Do this only if the field is 4 so we know it is a last traded price.
- Calculate the average and display along with the last traded price.
Below is an example showing average trade prices for the E-Mini S&P 500 futures contract:
Server Version:70 TWS Time at connection:20141028 15:27:53 EST error: -1,2104,Market data farm connection is OK:ibdemo tickPrice: 0,4,1938.5, 1938.5 tickPrice: 0,4,1938.75, 1938.625 tickPrice: 0,4,1938.5, 1938.5833333333333 tickPrice: 0,4,1938.25, 1938.5 tickPrice: 0,4,1938.0, 1938.4 tickPrice: 0,4,1938.25, 1938.375 tickPrice: 0,4,1938.5, 1938.392857142857 tickPrice: 0,4,1938.25, 1938.375 tickPrice: 0,4,1938.0, 1938.3333333333333 tickPrice: 0,4,1938.5, 1938.35 tickPrice: 0,4,1938.25, 1938.340909090909 |
Exercise 4: Calculate and Display a 20 Period Moving Average
Modify the above program to retrieve the latest price and then calculate and display a 20 period moving average.
That is, instead of just accumulating prices and averaging them, you will collect the most recent 20 prices and average those.
Some general steps for this exercise are to first create an array object with room for 20 of the most recent prices. Use this array like a queue in that once it is full, the oldest price is removed before the most recent price is added. For each new price (after 20 prices have been accumulated) iterate through the array and calculate the average price.
The complete source code for the basic RealTimeData application is given on the next two pages.
Complete Source code for the Real Time Data Java Application (API 9.71)
// RealTimeData.java // IB API Version 9.71 // Version 1.0 // 20141028 // R. Holowczak // Import Java utilities and Interactive Brokers API import java.util.Vector; import com.ib.client.Contract; import com.ib.client.ContractDetails; import com.ib.client.EClientSocket; import com.ib.client.EWrapper; import com.ib.client.Execution; import com.ib.client.Order; import com.ib.client.OrderState; import com.ib.client.TagValue; import com.ib.client.CommissionReport; import com.ib.client.UnderComp; // RealTimeData Class is an implementation of the // IB API EWrapper class public class RealTimeData implements EWrapper { // Keep track of the next ID private int nextOrderID = 0; // The IB API Client Socket object private EClientSocket client = null; public RealTimeData () { // Create a new EClientSocket object client = new EClientSocket (this); // Connect to the TWS or IB Gateway application // Leave null for localhost // Port Number (should match TWS/IB Gateway configuration client.eConnect (null, 7496, 0); // Pause here for connection to complete try { // Thread.sleep (1000); while (! (client.isConnected())); // Can also try: while (client.NextOrderId <= 0); } catch (Exception e) { } // Create a new contract Contract contract = new Contract (); contract.m_symbol = "EUR"; contract.m_exchange = "IDEALPRO"; contract.m_secType = "CASH"; contract.m_currency = "USD"; // Create a TagValue list Vector mktDataOptions = new Vector(); // Make a call to start off data retrieval client.reqMktData(0, contract, null, false, mktDataOptions); // At this point our call is done and any market data events // will be returned via the tickPrice method } // end RealTimeData public void bondContractDetails(int reqId, ContractDetails contractDetails) { } public void contractDetails(int reqId, ContractDetails contractDetails) { } public void contractDetailsEnd(int reqId) { } public void fundamentalData(int reqId, String data) { } public void bondContractDetails(ContractDetails contractDetails) { } public void contractDetails(ContractDetails contractDetails) { } public void currentTime(long time) { } public void displayGroupList(int requestId, String contraftInfo) { } public void displayGroupUpdated(int requestId, String contractInfo) { } public void verifyCompleted(boolean completed, String contractInfo) { } public void verifyMessageAPI(String message) { } public void execDetails(int orderId, Contract contract, Execution execution) { } public void execDetailsEnd(int reqId) { } public void historicalData(int reqId, String date, double open, double high, double low, double close, int volume, int count, double WAP, boolean hasGaps) { } public void managedAccounts(String accountsList) { } public void commissionReport(CommissionReport cr) { } public void position(String account, Contract contract, int pos, double avgCost) { } public void positionEnd() { } public void accountSummary(int reqId, String account, String tag, String value, String currency) { } public void accountSummaryEnd(int reqId) { } public void accountDownloadEnd(String accountName) { } public void openOrder(int orderId, Contract contract, Order order, OrderState orderState) { } public void openOrderEnd() { } public void orderStatus(int orderId, String status, int filled, int remaining, double avgFillPrice, int permId, int parentId, double lastFillPrice, int clientId, String whyHeld) { } public void receiveFA(int faDataType, String xml) { } public void scannerData(int reqId, int rank, ContractDetails contractDetails, String distance, String benchmark, String projection, String legsStr) { } public void scannerDataEnd(int reqId) { } public void scannerParameters(String xml) { } public void tickEFP(int symbolId, int tickType, double basisPoints, String formattedBasisPoints, double impliedFuture, int holdDays, String futureExpiry, double dividendImpact, double dividendsToExpiry) { } public void tickGeneric(int symbolId, int tickType, double value) { } public void tickOptionComputation( int tickerId, int field, double impliedVol, double delta, double optPrice, double pvDividend, double gamma, double vega, double theta, double undPrice) { } public void deltaNeutralValidation(int reqId, UnderComp underComp) { } public void updateAccountTime(String timeStamp) { } public void updateAccountValue(String key, String value, String currency, String accountName) { } public void updateMktDepth(int symbolId, int position, int operation, int side, double price, int size) { } public void updateMktDepthL2(int symbolId, int position, String marketMaker, int operation, int side, double price, int size) { } public void updateNewsBulletin(int msgId, int msgType, String message, String origExchange) { } public void updatePortfolio(Contract contract, int position, double marketPrice, double marketValue, double averageCost, double unrealizedPNL, double realizedPNL, String accountName) { } public void marketDataType(int reqId, int marketDataType) { } public void tickSnapshotEnd(int tickerId) { } public void connectionClosed() { } public void realtimeBar (int reqId, long time, double open, double high, double low, double close, long volume, double wap, int count) { } public void error(Exception e) { // Print out a stack trace for the exception e.printStackTrace (); } public void error(String str) { // Print out the error message System.err.println (str); } public void error(int id, int errorCode, String errorMsg) { // Overloaded error event (from IB) with their own error // codes and messages System.err.println ("error: " + id + "," + errorCode + "," + errorMsg); } public void nextValidId (int orderId) { // Return the next valid OrderID nextOrderID = orderId; } public void tickPrice(int orderId, int field, double price, int canAutoExecute) { try { // Print out the current price // field will provide the price type: // 1 = bid, 2 = ask, 4 = last // 6 = high, 7 = low, 9 = close System.out.println("tickPrice: " + orderId + "," + field + "," + price); } catch (Exception e) { e.printStackTrace (); } } public void tickSize (int orderId, int field, int size) { } public void tickString (int orderId, int tickType, String value) { } public static void main (String args[]) { try { // Create an instance // At this time a connection will be made // and the request for market data will happen RealTimeData myData = new RealTimeData(); } catch (Exception e) { e.printStackTrace (); } } // end main } // end public class RealTimeData
Complete Source code for the Real Time Data Java Application (API 9.72-BETA)
The following source code is modified from the original version to work with IB TWS API version 9.72 (BETA).
Note that each new revision of the BETA 9.72 API has added more EWrapper methods. As of April 19, 2016, the code below will work with the API version 9.72.14.
// RealTimeData.java // API Version 9.72.14 (BETA) // Version 1.2 // 20160421 // R. Holowczak // Import Java utilities and Interactive Brokers API import java.util.Vector; import java.util.Set; // Needed for IB API 9.72.14 import com.ib.client.Contract; import com.ib.client.ContractDetails; import com.ib.client.EClientSocket; import com.ib.client.EWrapper; import com.ib.client.Execution; import com.ib.client.Order; import com.ib.client.OrderState; import com.ib.client.TagValue; import com.ib.client.CommissionReport; // import com.ib.client.UnderComp; // Comment out for API version 9.72 // Add the following for API version 9.72 import com.ib.client.DeltaNeutralContract; // Add for API version 9.72 import com.ib.client.EJavaSignal; import com.ib.client.EReader; import com.ib.client.EWrapperMsgGenerator; // RealTimeData Class is an implementation of the // IB API EWrapper class public class RealTimeData implements EWrapper { // Add for API 9.72 private EJavaSignal m_signal = new EJavaSignal(); private EReader m_reader; // Keep track of the next ID private int nextOrderID = 0; // The IB API Client Socket object private EClientSocket client = null; // Keep track of prices for Moving Average private double priceTotal; private int numberOfPrices; public RealTimeData () { // Initialize to 0 priceTotal = 0.0; numberOfPrices = 0; // Create a new EClientSocket object version 9.71 // client = new EClientSocket (this); client = new EClientSocket( this, m_signal); // Connect to the TWS or IB Gateway application // Leave null for localhost // Port Number (should match TWS/IB Gateway configuration client.eConnect (null, 7496, 0); // Pause here for connection to complete try { // Thread.sleep (1000); while (! (client.isConnected())); // Can also try: while (client.NextOrderId <= 0); } catch (Exception e) { }; // API Version 9.72 Launch EReader Thread m_reader = new EReader(client, m_signal); m_reader.start(); new Thread() { public void run() { processMessages(); } }.start(); // Create a new contract Contract contract = new Contract (); contract.symbol("ES"); //contract.expiry("20160318"); contract.lastTradeDateOrContractMonth("20160318"); contract.exchange("GLOBEX"); contract.secType("FUT"); contract.currency("USD"); // Create a TagValue list Vector<TagValue> mktDataOptions = new Vector<TagValue>(); // Make a call to start off data retrieval client.reqMktData(0, contract, null, false, mktDataOptions); // At this point our call is done and any market data events // will be returned via the tickPrice method } // end RealTimeData private void processMessages() { while(true) { try { m_reader.processMsgs(); } catch (Exception e) { error(e); } } // end while } // end processMessages() // New for API version 9.72.14 public void securityDefinitionOptionalParameter(int reqId, String exchange, int underlyingConId, String tradingClass, String multiplier, Set expirations, Set strikes) { // TODO Auto-generated method stub } // New for API version 9.72.14 public void securityDefinitionOptionalParameterEnd(int reqId) { // TODO Auto-generated method stub } // New for API version 9.72.14 public void accountUpdateMulti( int reqId, String account, String modelCode, String key, String value, String currency) { // TODO Auto-generated method stub } // New for API version 9.72.14 public void accountUpdateMultiEnd( int reqId) { // TODO Auto-generated method stub } // New for API version 9.72.14 public void positionMulti( int reqId, String account, String modelCode, Contract contract, double pos, double avgCost) { // TODO Auto-generated method stub } // New for API version 9.72.14 public void positionMultiEnd( int reqId) { // TODO Auto-generated method stub } public void bondContractDetails(int reqId, ContractDetails contractDetails) { } public void contractDetails(int reqId, ContractDetails contractDetails) { } public void contractDetailsEnd(int reqId) { } public void fundamentalData(int reqId, String data) { } public void bondContractDetails(ContractDetails contractDetails) { } public void contractDetails(ContractDetails contractDetails) { } public void currentTime(long time) { } public void displayGroupList(int requestId, String contraftInfo) { } public void displayGroupUpdated(int requestId, String contractInfo) { } // Add for API version 9.72 public void verifyAndAuthCompleted(boolean isSuccessful, String errorText) { } // Add for API version 9.72 public void verifyAndAuthMessageAPI(String apiData, String xyzChallange) { } public void verifyCompleted(boolean completed, String contractInfo) { } public void verifyMessageAPI(String message) { } public void execDetails(int orderId, Contract contract, Execution execution) { } public void execDetailsEnd(int reqId) { } public void historicalData(int reqId, String date, double open, double high, double low, double close, int volume, int count, double WAP, boolean hasGaps) { } public void managedAccounts(String accountsList) { } public void commissionReport(CommissionReport cr) { } // For API Version 9.72 pos is now a double public void position(String account, Contract contract, double pos, double avgCost) { } // Below is API version 9.71 public void position(String account, Contract contract, int pos, double avgCost) { } public void positionEnd() { } public void accountSummary(int reqId, String account, String tag, String value, String currency) { } public void accountSummaryEnd(int reqId) { } public void accountDownloadEnd(String accountName) { } public void openOrder(int orderId, Contract contract, Order order, OrderState orderState) { } public void openOrderEnd() { } // For API Version 9.72 public void orderStatus(int orderId, String status, double filled, double remaining, double avgFillPrice, int permId, int parentId, double lastFillPrice, int clientId, String whyHeld) { } // For API Version 9.71 public void orderStatus(int orderId, String status, int filled, int remaining, double avgFillPrice, int permId, int parentId, double lastFillPrice, int clientId, String whyHeld) { } public void receiveFA(int faDataType, String xml) { } public void scannerData(int reqId, int rank, ContractDetails contractDetails, String distance, String benchmark, String projection, String legsStr) { } public void scannerDataEnd(int reqId) { } public void scannerParameters(String xml) { } public void tickEFP(int symbolId, int tickType, double basisPoints, String formattedBasisPoints, double impliedFuture, int holdDays, String futureExpiry, double dividendImpact, double dividendsToExpiry) { } public void tickGeneric(int symbolId, int tickType, double value) { } public void tickOptionComputation( int tickerId, int field, double impliedVol, double delta, double optPrice, double pvDividend, double gamma, double vega, double theta, double undPrice) { } // public void deltaNeutralValidation(int reqId, UnderComp underComp) public void deltaNeutralValidation(int reqId, DeltaNeutralContract underComp) { } public void updateAccountTime(String timeStamp) { } public void updateAccountValue(String key, String value, String currency, String accountName) { } public void updateMktDepth(int symbolId, int position, int operation, int side, double price, int size) { } public void updateMktDepthL2(int symbolId, int position, String marketMaker, int operation, int side, double price, int size) { } public void updateNewsBulletin(int msgId, int msgType, String message, String origExchange) { } // For API Version 9.72 public void updatePortfolio(Contract contract, double position, double marketPrice, double marketValue, double averageCost, double unrealizedPNL, double realizedPNL, String accountName) { } // For API Version 9.71: public void updatePortfolio(Contract contract, int position, double marketPrice, double marketValue, double averageCost, double unrealizedPNL, double realizedPNL, String accountName) { } public void marketDataType(int reqId, int marketDataType) { } public void tickSnapshotEnd(int tickerId) { } public void connectionClosed() { } // Add connectAck for API version 9.72 public void connectAck() { } public void realtimeBar (int reqId, long time, double open, double high, double low, double close, long volume, double wap, int count) { } public void error(Exception e) { // Print out a stack trace for the exception e.printStackTrace (); } public void error(String str) { // Print out the error message System.err.println (str); } public void error(int id, int errorCode, String errorMsg) { // Overloaded error event (from IB) with their own error // codes and messages System.err.println ("error: " + id + "," + errorCode + "," + errorMsg); } public void nextValidId (int orderId) { // Return the next valid OrderID nextOrderID = orderId; } public void tickPrice(int orderId, int field, double price, int canAutoExecute) { double movingAverage = 0.0; try { // Print out the current price // field will provide the price type: // 1 = bid, 2 = ask, 4 = last // 6 = high, 7 = low, 9 = close if (field == 4) { numberOfPrices++; priceTotal += price; movingAverage = priceTotal / numberOfPrices; System.out.println("tickPrice: " + orderId + "," + field + "," + price + ", " + movingAverage); } } catch (Exception e) { e.printStackTrace (); } } public void tickSize (int orderId, int field, int size) { // field will provide the size type: // 0=bid size, 3=ask size, 5=last size, 8=volume //System.out.println("tickSize: " + orderId + "," + field + "," + size); } public void tickString (int orderId, int tickType, String value) { } public static void main (String args[]) { try { // Create an instance // At this time a connection will be made // and the request for market data will happen RealTimeData myData = new RealTimeData(); } catch (Exception e) { e.printStackTrace (); } } // end main } // end public class RealTimeData