jonnyd42 jonnyd42 - 4 months ago 98
Java Question

IB Java API: Extracting ticker data (real time bars) for multiple contracts

I'm doing some self-learning and experimentation with algorithmic trading and the IB API. I decided to use Java but I'm open to switching to C++. I went through an online tutorial that walks you through the code shown below but was wondering about extending it past just one stock. I want to go through all SP500 stocks and check ticker data to make decisions based on that.

The code below will create a contract for and get data for Microsoft but I'd like to get data for all 500 stocks. All of the other methods defined in the EWrapper interface were left out of the post for more ease of readability.

I'm thinking that I need to store the ticker symbols in a file, parse this, and add each contract one by one to a vector. However, I'm not sure about how to monitor the data after that. It would be nice if I could just sequentially loop through each ticker and make a request for data but I believe the stream is processed on an asynchronous thread (correct me if wrong.)

So how do I go through all 500 stocks and check their ticker data?

Code snippets and explanations would be appreciated. Thanks!

// 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;

// RealTimeBars Class is an implementation of the
// IB API EWrapper class
public class RealTimeBars implements EWrapper
{
// Keep track of the next ID
private int nextOrderID = 0;
// The IB API Client Socket object
private EClientSocket client = null;

public RealTimeBars ()
{
// 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()));
} catch (Exception e) {
e.printStackTrace ();

};
// Create a new contract
Contract contract = new Contract ();
contract.m_symbol = "MSFT";
contract.m_exchange = "SMART";
contract.m_secType = "STK";
contract.m_primaryExch = "NASDAQ";
contract.m_currency = "USD";
// Create a TagValue list
Vector<TagValue> realTimeBarsOptions = new Vector<TagValue>();
// Make a call to start off data retrieval
client.reqRealTimeBars(0, contract,
5, // Bar Size 5 seconds
"TRADES", // whatToShow
false, // useRTH
realTimeBarsOptions);
// At this point our call is done and any market data events
// will be returned via the realtimeBar method

}

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
RealTimeBars myData = new RealTimeBars();
}
catch (Exception e)
{
e.printStackTrace ();
}
}


}

Answer

I don't know how this will work for all 500, but you can try. The data is from https://raw.githubusercontent.com/datasets/s-and-p-500-companies/master/data/constituents.csv

package sp;

import com.ib.client.CommissionReport;
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.TickType;
import com.ib.client.UnderComp;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;

public class SP implements EWrapper{
    //just a sample, like this so you can just use Files.lines instead.
    private static List<String> lines = Arrays.asList(new String[]{
        "Symbol,Name,Sector",
        "MMM,3M Company,Industrials",
        "ABT,Abbott Laboratories,Health Care",
        "ABBV,AbbVie,Health Care",
        "ACN,Accenture plc,Information Technology",
        "ATVI,Activision Blizzard,Information Technology",
        "AYI,Acuity Brands Inc,Industrials",
        "ADBE,Adobe Systems Inc,Information Technology",
        "AAP,Advance Auto Parts,Consumer Discretionary",
        "AES,AES Corp,Utilities",
        "AET,Aetna Inc,Health Care",
        "AMG,Affiliated Managers Group Inc,Financials",
        "AFL,AFLAC Inc,Financials",
        "A,Agilent Technologies Inc,Health Care",
        "APD,Air Products & Chemicals Inc,Materials",
        "AKAM,Akamai Technologies Inc,Information Technology",
    });

    //obviously make some real classes and don't make everything static
    static class Data{
        String symbol;
        List<Double> prices = new ArrayList<>();
        double lastPrice = -1;

        public Data(String symbol) {
            this.symbol = symbol;
        }

        public boolean isBuyStk(){
            return lastPrice < 10;
        }
    }

    //use a map so we can look up data from tickerID in callback
    private static Map<Integer, Data> dataMap= new HashMap<>();

    private static EClientSocket api = new EClientSocket(new SP());

    public static void main(String[] args) {
        AtomicInteger tickerId = new AtomicInteger(0);
        lines.stream().skip(1).forEach(line -> {
            dataMap.put(tickerId.incrementAndGet(), new Data(line.split(",")[0]));
        });

        api.eConnect("", 4001, 123);

        Contract cont = new Contract();
        cont.m_currency = "usd";
        cont.m_exchange = "smart";
        cont.m_secType = "stk";

        dataMap.entrySet().stream().forEach(e->{
            cont.m_symbol = e.getValue().symbol;
            //request with tickerId from Map
            api.reqMktData(e.getKey(), cont, "", true, null);
        });
    }

    @Override
    public void nextValidId(int orderId) {
        //here I usually remember my next order ID and start the program
        //since it implies a succesful connection
    }

    //reqMktData snapshots are received here
    @Override
    public void tickPrice(int tickerId, int field, double price, int canAutoExecute) {
        if (field == TickType.LAST) {
            //if you just want the last price
            dataMap.get(tickerId).lastPrice = price;
            //or without snapshot, you could keep a list of prices, add your own timestamp
            dataMap.get(tickerId).prices.add(price);
            //here is where you call a buy/sell method.  It will all be done on the EReader thread
            System.out.println("buy? "+dataMap.get(tickerId).isBuyStk());
        }
        //check if all data is received and then print, disconnect
        if (dataMap.values().stream().allMatch(data -> data.lastPrice > 0)){
            dataMap.entrySet().stream().forEach(e->System.out.println(e.getKey()+" "+e.getValue().symbol+" "+e.getValue().lastPrice));
            //bad place to disconnect because you may not get all the data, use a timer
            api.eDisconnect();
        }
    }
////snip a bunch of methods but always make sure to impl errors
    @Override
    public void error(Exception e) {
        error(0,0,e.getMessage());
    }

    @Override
    public void error(String str) {
        error(0,0,str);
    }

    @Override
    public void error(int id, int errorCode, String errorMsg) {
        System.out.println(errorMsg);
    }

It runs in under a second, here's the output

Server Version:76
TWS Time at connection:20160707 12:29:33 EST
Market data farm connection is OK:usfuture
Market data farm connection is OK:cashfarm
Market data farm connection is OK:cafarm
Market data farm connection is OK:usfarm
buy? false
buy? false
buy? false
buy? false
buy? false
buy? false
buy? false
buy? false
buy? false
buy? false
buy? false
buy? false
buy? false
buy? false
buy? false
1 MMM 174.88
2 ABT 41.43
3 ABBV 63.4
4 ACN 113.45
5 ATVI 40.36
6 AYI 254.04
7 ADBE 95.06
8 AAP 161.85
9 AES 12.06
10 AET 120.5
11 AMG 134.4
12 AFL 71.26
13 A 44.37
14 APD 139.81
15 AKAM 55.15

One thing I forgot is there's a 50 message per second limit on transmitting requests so you'll have to break up the requests with pauses.

Update: I would structure a simple program like so

Data 
 Data(contract, socket){
   MyWrapper.map.put(id, this)
   socket.reqMktData
   new Strategy(Data, socket)
 }
 fields : last price or price list etc.
 method dataRecd(tick){ 
   add to list of prices
   call strategy.buy or sell()
 }

Strategy(Data, socket)
  method buy or sell() {
    calculate indicators etc..
    socket.placeOrder
    MyWrapper.put(orderid, this)
  }
  method order filled{
     if profit celebrate
  }

Main
 main{
   new EClientSocket(new MyWrapper)
   new Data(contract,socket)
 }

MyWrapper
 //can just be static if one client per program
 //but socket has a ref to wrapper otherwise
 static map (tickerId, data)
 static map (orderId, Strategy or Data)

 ...impl all

 tickPrice(id ...){
   map.get(id).call data method
 }

 orderStatus, or execution(id ..){
    map.get(id).call strategy.order filled
 }
Comments