When I first began programming with the TWS (Trader Workstation) API in Java, I was very overwhelmed. Interactive Brokers provides expansive documentation regarding their API however, sometimes a wall of methods and return values can be overwhelming. At the time, something as simple as opening a connection to my TWS client with a program felt like a mountainous task. At the end of this guide you will hopefully have an understanding of the basics of the API as well as be able to create a simple app that will stream data for SPY from TWS.
It should be noted that Interactive Brokers provides a sample testbed for each of their API languages. It is a great reference for expansion at the end of this guide.
IBKR's documentation can be found here: https://www.interactivebrokers.com/campus/ibkr-api-page/twsapi-doc/#api-introduction
- A funded and opened IBKR Pro account
- The current Stable or Latest release of the TWS or IB Gateway
- The current Stable or Latest release of the TWS API (Stable 10.37 at creation)
- Google Protocol Buffer Library Version 4.29.3
It is vital that you enable API connections in your TWS client. Otherwise, you will not be able to connect to TWS at all via the API. API settings can be found in:
Global Configuration > API > Settings
You must enable the API in these settings. By default the API is read only and will not allow placing orders unless this is disabled.
Never connect an API client to your live trading account unless you are production ready. The paper trader should always be used when developing code that will be placing orders or interacting with your TWS client in any way. I am not responsible if you connect an API client to your live account.
The API is simply a method of sending and recieveing messages from the TWS client autonomously. It does this call and response with a few main classes.
The EClientSocket class houses all of the avaialble methods to send and communicate with the TWS client. Up to 32 api clients can be connected to TWS at once.
EReader captures all return messages from TWS and stores them in a queue.
EReaderSignal notifes the thread reading information from TWS whenever there are messages ready to be read.
The EWrapper houses all of the methods that TWS will return API calls through. Every API client must implement this interface to handle the events generated by TWS as you make calls. Nearly every method called by EClientSocket will result in at least one event that triggers a method in the wrapper. While it can be overwhelming looking at the large amount of methods, many are very specialized and can be overlooked for the time being. We will be using the EWrapper example from the provided testbed with only one small modification.
"Contracts" are a data structure that represent information about things like stocks, futures, or options. The are used either as a basis for creating an order, or can be used to open a data stream. A basic contract would look like this:
public static Contract SpyContract() {
Contract contract = new Contract(); // create a contract object
contract.symbol("SPY"); // Assign the symbol/ticker to be "SPY"
contract.secType("STK"); // Security Type: STK (Stock), OPT (option), etc
contract.currency("USD"); // Specified currency
contract.exchange("ARCA"); // The stock exchange to look at
return contract;
}As our goal is only one data stream we can put this method above our Main. For a larger scale application, it would be a good idea to create your own Contracts class and store all of your needed contracts there.
We'll instantiate the important classes from above:
EWrapperImpl wrapper = new EWrapperImpl();
final EClientSocket m_client = wrapper.getClient();
final EReaderSignal m_signal = wrapper.getSignal();
We instantiate the Wrapper, EclientSocket, and EReaderSignal and then establish the connection to TWS.
m_client.eConnect("127.0.0.1", 7497, 1); the eConnect method takes 3 arguments:
- Host
- Port number
- Client ID
In this example we use localhost, port 7497 (7497 for paper, 7496 for live), and client ID 1. Each connected API client is assigned an ID. The API client will only be able to see orders placed that are from its own ID with one exception. ID 0 is your master client by default. It can see all orders from all connected API clients and can also see manual orders placed. This can be used for order management in more complex applications. The designated master ID can be set in the TWS global config.
Lastly initialize the EReader using the EClientSocket and EReaderSignal and start the reader
final EReader reader = new EReader(m_client, m_signal);
reader.start();Now we spin up a new thread that will wait and listen for any return calls from TWS before processing them. In this example we wait 1 second for the ensure there is a connection to TWS before moving on. In production code, it is highly recommended that you wait for a callback before continuing rather than waiting a set amount of time.
new Thread(() -> {
while (m_client.isConnected()) {
m_signal.waitForSignal();
try {
reader.processMsgs();
} catch (Exception e) {
System.out.println("Exception: "+e.getMessage());
}
}
}).start();
Thread.sleep(1000);At this point, you are ready to send and recieve API calls from TWS.
Outside of the Main method, we established a private SpyContract method using the example above. With that, we can open a data stream. In our main method, after opening our thread and waiting, we can use the method reqMktData to request live data. We initialize a "nextId" variable as each call to the api requires a unique id number. The wrapper will automatically be sent next valid order ID on each successful connection to TWS. It is important keep track of this next valid ID in order to continue making calls to the API
int nextId = wrapper.getCurrentOrderId();
m_client.reqMktData(nextId++, SpyContract(), "", false, false, null);For now we can ignore the last 3 arguments of this method as they are outside the scope of this guide. The first argument is out "tickerId" which can be used to identify which data stream we are looking at in our callback data. The second argument is the Contract that we made previously.
Now that our data stream is open, we can move our attention to the Wrapper to handle the callback data. In our Wrapper, we can look for the method "tickPrice". This will be the method used to handle in coming price data. Data is returned with associated "tick types". Referring to the documentation (https://interactivebrokers.github.io/tws-api/tick_types.html) we can see that tickId 4 refers to the price the security was last traded at. We can filter for that tick and print the data to the console.
@Override
public void tickPrice(int tickerId, int field, double price, TickAttrib attribs) {
if (field == 4) {
System.out.println("Last Price: " + price);
}
}It's at this point it should be noted that Interactive Brokers provides what they call "Filtered Data". They will provide 4 prices per second, per data stream. Unless you are doing sub-second trading this should be more than enough data to cover your needs. We've now successfully opened a data stream that will print the price of SPY to the console. You will notice there is a "tick price" method being invoked in the wrapper as well. This is because a market data stream splits price and size relevent information into their own separate methods.
While this guide did not cover placing orders or managing orders with the API, IB's documentation should provide eerything you need to make calls now that your connection is properly set up. The EClientSocket class will be your go to reference when making API calls and the EWrapper will be where all your data is receieved with no exceptions.
Thank you to Interactive Brokers for providing a comprehensive and robust API for their cusomters to use.