package jtrade.marketfeed;
import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.NavigableMap;
import java.util.Timer;
import java.util.TreeMap;
import java.util.concurrent.atomic.AtomicInteger;
import jtrade.JTradeException;
import jtrade.Symbol;
import jtrade.Symbol.SymbolType;
import jtrade.SymbolFactory;
import jtrade.util.Configurable;
import jtrade.util.Util;
import org.joda.time.DateTime;
import org.joda.time.Days;
import org.joda.time.Duration;
import org.joda.time.Interval;
import org.joda.time.format.DateTimeFormat;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
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.ScannerSubscription;
import com.ib.client.TickType;
import com.ib.client.UnderComp;
/**
* @author jonkle
*
*/
public class IBMarketFeed extends AbstractMarketFeed implements EWrapper {
private static final Logger logger = LoggerFactory.getLogger(IBMarketFeed.class);
private static final int historicalRequestTimeOutMillis = 30000;
private static final int maxBarsPerHistoricalRequest = 2000;
private static final double maxHistoricalRequestsPerSecond = 0.067;
private static final int maxHistoricalRequestRetries = 2;
private static final Duration realTimeBarSize = new Duration(5000);
private static final int defaultPort = 4000;
public final Configurable<String> SERVER_HOSTS = new Configurable<String>("SERVER_HOSTS", "localhost:4000");
public final Configurable<Integer> CLIENT_ID = new Configurable<Integer>("CLIENT_ID", 1);
protected String[] hosts;
protected int[] ports;
protected int currHost;
protected EClientSocket socket;
protected int serverVersion;
protected boolean connect;
protected int clientId;
protected String accountCode;
protected AtomicInteger nextValidReqId;
protected String lastMessage;
protected Map<Integer, IBTickRequest> tickRequestsById;
protected Map<Integer, IBBarRequest> barRequestsById;
protected Map<Integer, IBHistoricalRequest> historicalRequestsById;
protected Map<Integer, IBScannerRequest> scannerRequestsById;
protected Map<Integer, IBContractRequest> contractRequestsById;
protected String marketScannerParams;
protected Timer timer;
protected long lastHistoricalRequestMillis;
protected List<BarListener> barListeners;
protected List<TickListener> tickListeners;
public IBMarketFeed() {
this(null, -1, (File) null);
}
public IBMarketFeed(int clientId) {
this(null, clientId, (File) null);
}
public IBMarketFeed(String hosts, int clientId) {
this(hosts, clientId, (File) null);
}
public IBMarketFeed(String hosts, int clientId, String dataDir) {
this(hosts, clientId, new File(dataDir));
}
public IBMarketFeed(String hosts, int clientId, File dataDir) {
super(dataDir);
if (hosts == null) {
hosts = SERVER_HOSTS.get();
}
if (clientId < 0) {
clientId = CLIENT_ID.get();
}
String[] tmp = Util.split(hosts, ',');
this.hosts = new String[tmp.length];
this.ports = new int[tmp.length];
for (int i = 0; i < tmp.length; i++) {
String[] hp = Util.split(tmp[i].trim().toLowerCase(), ':');
if (hp.length == 1) {
this.hosts[i] = hp[0].trim();
this.ports[i] = defaultPort;
} else {
this.hosts[i] = hp[0].trim();
this.ports[i] = Integer.parseInt(hp[1]);
}
}
this.clientId = clientId;
nextValidReqId = new AtomicInteger();
tickRequestsById = new HashMap<Integer, IBTickRequest>();
barRequestsById = new HashMap<Integer, IBBarRequest>();
historicalRequestsById = new HashMap<Integer, IBHistoricalRequest>();
scannerRequestsById = new HashMap<Integer, IBScannerRequest>();
contractRequestsById = new HashMap<Integer, IBContractRequest>();
barListeners = new ArrayList<BarListener>();
tickListeners = new ArrayList<TickListener>();
}
@Override
protected void finalize() throws Throwable {
disconnect();
}
@Override
public synchronized void connect() {
if (isConnected()) {
return;
}
connect = true;
try {
doConnect();
} catch (JTradeException e) {
logger.warn(e.getMessage());
} catch (Throwable t) {
logger.error(t.getMessage(), t);
}
timer = new Timer(true);
timer.scheduleAtFixedRate(new java.util.TimerTask() {
@Override
public void run() {
try {
if (connect && (socket == null || !socket.isConnected())) {
doConnect();
}
} catch (JTradeException e) {
logger.warn(e.getMessage());
} catch (Throwable t) {
logger.error(t.getMessage(), t);
}
}
}, 15000, 15000);
timer.scheduleAtFixedRate(new java.util.TimerTask() {
@Override
public void run() {
DateTime now = new DateTime();
if (now.getSecondOfMinute() == 0) {
fireMinuteEvent(now);
if (now.getMinuteOfHour() == 0) {
fireHourEvent(now);
if (now.getHourOfDay() == 0) {
fireDayEvent(now);
}
}
// Sometimes ticks stop being received, this might
// workaround that.
refreshTickRequests();
}
if (tickRequestsById.size() > 0) {
for (IBTickRequest req : tickRequestsById.values()) {
if (req.barListeners.size() > 0) {
buildBar(req, -1, -1, -1, null);
}
}
}
}
}, new DateTime().millisOfSecond().roundCeilingCopy().toDate(), 1000);
}
protected synchronized void doConnect() {
socket = new EClientSocket(this);
socket.eConnect(hosts[currHost], ports[currHost], clientId);
if (!socket.isConnected()) {
int failedHost = currHost;
currHost = (currHost + 1) % hosts.length;
throw new JTradeException(String.format("Could not connect to TWS using %s@%s:%s", clientId, hosts[failedHost], ports[failedHost]));
}
// IB Log levels: 1=SYSTEM 2=ERROR 3=WARNING 4=INFORMATION 5=DETAIL
socket.setServerLogLevel(3);
socket.reqNewsBulletins(true);
serverVersion = socket.serverVersion();
logger.info("Connected to TWS v{} using {}@{}:{}, data folder is {}",
new Object[] { serverVersion, clientId, hosts[currHost], ports[currHost], dataDir.getAbsolutePath() });
}
@Override
public synchronized void disconnect() {
connect = false;
if (timer != null) {
timer.cancel();
timer = null;
}
if (socket == null || !socket.isConnected()) {
return;
}
removeAllListeners();
if (socket != null && socket.isConnected()) {
socket.cancelNewsBulletins();
socket.eDisconnect();
}
socket = null;
logger.info("Disconnected from TWS v{} using {}@{}:{}", new Object[] { serverVersion, clientId, hosts[currHost], hosts[currHost] });
}
@Override
public boolean isConnected() {
return socket != null && socket.isConnected();
}
protected void checkConnected() {
if (!isConnected()) {
throw new JTradeException(String.format("%s is not connected to TWS at %s@%s", getClass().getSimpleName(), clientId, SERVER_HOSTS.get()));
}
}
private synchronized void refreshTickRequests() {
long now = System.currentTimeMillis();
for (IBTickRequest req : new ArrayList<IBTickRequest>(tickRequestsById.values())) {
if (req.timestamp + 30000 > now) {
continue;
}
logger.debug("Refreshing, removing {}", req);
tickRequestsById.remove(req.id);
if (req.marketDepth) {
socket.cancelMktDepth(req.id);
}
socket.cancelMktData(req.id);
Contract contract = makeContract(req.symbol);
req.id = nextValidReqId.incrementAndGet();
logger.debug("Refreshing, adding {}", req);
socket.reqMktData(req.id, contract, "", false);
if (req.marketDepth) {
socket.reqMktDepth(req.id, contract, 10);
}
tickRequestsById.put(req.id, req);
}
}
protected Contract makeContract(Symbol symbol) {
String code = symbol.getCode();
String exchange = symbol.getExchange();
String currency = symbol.getCurrency();
String securityType = null;
String expiry = null;
String multiplier = String.valueOf(symbol.getMultiplier());
double strike = 0.0;
String right = null;
int conId = 0;
boolean includeExpired = false;
if (symbol.isStock()) {
securityType = "STK";
} else if (symbol.isFuture()) {
securityType = "FUT";
expiry = symbol.getExpiry() != null ? symbol.getExpiry().toString("yyyyMMdd") : null;
includeExpired = true;
} else if (symbol.isIndex()) {
securityType = "IND";
} else if (symbol.isOption()) {
securityType = "OPT";
expiry = symbol.getExpiry() != null ? symbol.getExpiry().toString("yyyyMMdd") : null;
strike = symbol.getStrike();
right = symbol.getOptionRight().toString();
includeExpired = true;
} else if (symbol.isCash()) {
securityType = "CASH";
}
return makeContract(code, exchange, currency, securityType, expiry, multiplier, strike, right, conId, includeExpired);
}
protected Contract makeContract(String symbol, String exchange, String currency, String securityType, String expiry, String multiplier, double strike,
String right, int conId, boolean includeExpired) {
Contract contract = new Contract();
contract.m_symbol = symbol;
contract.m_exchange = exchange;
contract.m_currency = currency;
contract.m_secType = securityType;
contract.m_expiry = expiry;
contract.m_multiplier = multiplier;
contract.m_strike = strike;
contract.m_right = right;
contract.m_conId = conId;
contract.m_includeExpired = includeExpired;
return contract;
}
protected Symbol toSymbol(Contract contract) {
return toSymbol(contract, null);
}
protected Symbol toSymbol(Contract contract, ContractDetails details) {
StringBuilder fullCode = new StringBuilder(contract.m_symbol).append('-')
.append(contract.m_exchange != null ? contract.m_exchange : contract.m_primaryExch).append('-').append(contract.m_currency).append('-');
if ("FUT".equals(contract.m_secType)) {
fullCode.append("FUTURE");
} else if ("STK".equals(contract.m_secType)) {
fullCode.append("STOCK");
} else if ("IND".equals(contract.m_secType)) {
fullCode.append("INDEX");
} else if ("OPT".equals(contract.m_secType)) {
fullCode.append("OPTION");
} else {
fullCode.append("CASH");
}
if (contract.m_expiry != null) {
fullCode.append('-').append(contract.m_expiry);
}
if (contract.m_strike > 0) {
if (contract.m_strike - (long) contract.m_strike != 0.0) {
fullCode.append('-').append(String.format("%.2f", contract.m_strike)).append('-').append(contract.m_right.toUpperCase());
} else {
fullCode.append('-').append((long) contract.m_strike).append('-').append(contract.m_right.toUpperCase());
}
}
if (contract.m_right != null) {
if (contract.m_right.equals("C") || contract.m_right.equals("CALL")) {
fullCode.append('-').append("CALL");
} else if (contract.m_right.equals("P") || contract.m_right.equals("PUT")) {
fullCode.append('-').append("PUT");
}
}
Symbol s = SymbolFactory.getSymbol(fullCode.toString());
// s.multiplier = contract.m_multiplier != null ?
// Integer.parseInt(contract.m_multiplier) : s.multiplier;
// s.minTick = details != null ? details.m_minTick : s.minTick;
return s;
}
@Override
protected NavigableMap<DateTime, Bar> fetchHistoricalData(Symbol symbol, DateTime fromDate, DateTime toDate, int barSizeSeconds) {
NavigableMap<DateTime, Bar> result = new TreeMap<DateTime, Bar>();
DateTime start = toDate.isAfterNow() ? new DateTime(toDate.getZone()) : toDate;
if (symbol.getExpiry() != null && start.isAfter(symbol.getExpiry())) {
start = symbol.getExpiry();
}
DateTime end = null;
logger.info("Fetching historical data for symbol {} {}-{}", new Object[] { symbol, fromDate, start });
int retries = 0;
while (true) {
end = start;
if (barSizeSeconds < 86400) {
start = end.minusSeconds(Math.min(maxBarsPerHistoricalRequest * barSizeSeconds, 86400));
} else {
start = end.minusSeconds(maxBarsPerHistoricalRequest * barSizeSeconds);
}
if (start.isBefore(fromDate)) {
start = fromDate;
}
while (1000.0 / (System.currentTimeMillis() - lastHistoricalRequestMillis) >= maxHistoricalRequestsPerSecond) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
}
}
lastHistoricalRequestMillis = System.currentTimeMillis();
IBHistoricalRequest req = requestHistoricalData(symbol, start, end, barSizeSeconds);
if (req.failed) {
throw new JTradeException(String.format("Historical data for symbol %s could not be fetched: %s", symbol, req.error));
}
for (Bar bar : req.bars.values()) {
if ((bar.getDateTime().isEqual(fromDate) || bar.getDateTime().isAfter(fromDate)) && bar.getDateTime().isBefore(toDate)) {
result.put(bar.getDateTime(), bar);
}
}
if (req.bars.isEmpty()) {
retries++;
start = end;
} else {
start = req.bars.firstKey();
}
if (start.isBefore(fromDate) || start.isEqual(fromDate) || retries > maxHistoricalRequestRetries) {
break;
}
}
return result;
}
public IBHistoricalRequest requestHistoricalData(Symbol symbol, DateTime fromDate, DateTime toDate, int barSizeSeconds) {
checkConnected();
if (historicalRequestsById.size() > 0) {
throw new IllegalStateException(String.format("%s historical request already in progress.", getClass().getSimpleName()));
}
Interval interval = new Interval(fromDate, toDate);
if ((interval.toDurationMillis() / 1000) / barSizeSeconds > maxBarsPerHistoricalRequest) {
throw new IllegalStateException(String.format("%s cannot request %s > %s ticks historical data at once.", getClass().getSimpleName(),
(interval.toDurationMillis() / 1000) / barSizeSeconds, maxBarsPerHistoricalRequest));
}
String endDateStr = toDate.toString("yyyyMMdd HH:mm:ss");
String durationStr = null;
String barSizeSetting = null;
switch (barSizeSeconds) {
case 1:
durationStr = (interval.toDurationMillis() / 1000) + " S";
barSizeSetting = "1 secs";
break;
case 5:
durationStr = (interval.toDurationMillis() / 1000) + " S";
barSizeSetting = "5 secs";
break;
case 15:
durationStr = (interval.toDurationMillis() / 1000) + " S";
barSizeSetting = "15 secs";
break;
case 30:
durationStr = (interval.toDurationMillis() / 1000) + " S";
barSizeSetting = "30 secs";
break;
case 60:
durationStr = (interval.toDurationMillis() / 1000) + " S";
barSizeSetting = "1 min";
break;
case 120:
durationStr = (interval.toDurationMillis() / 1000) + " S";
barSizeSetting = "2 mins";
break;
case 180:
durationStr = (interval.toDurationMillis() / 1000) + " S";
barSizeSetting = "3 mins";
break;
case 300:
durationStr = (interval.toDurationMillis() / 1000) + " S";
barSizeSetting = "5 mins";
break;
case 900:
durationStr = (interval.toDurationMillis() / 1000) + " S";
barSizeSetting = "15 mins";
break;
case 1800:
durationStr = (interval.toDurationMillis() / 1000) + " S";
barSizeSetting = "30 mins";
break;
case 3600:
durationStr = (interval.toDurationMillis() / 1000) + " S";
barSizeSetting = "1 hour";
break;
case 86400:
durationStr = (Days.daysIn(interval).getDays()) + " D";
barSizeSetting = "1 day";
break;
default:
throw new IllegalArgumentException("Invalid bar size: " + barSizeSeconds);
}
if (logger.isDebugEnabled())
logger.debug("Requesting historical data: {} {} {} {}", new Object[] { symbol, endDateStr, durationStr, barSizeSetting });
// Fetch TRADES
int requestId = nextValidReqId.incrementAndGet();
IBHistoricalRequest req = new IBHistoricalRequest(requestId, symbol, Duration.standardSeconds(barSizeSeconds));
historicalRequestsById.put(requestId, req);
socket.reqHistoricalData(requestId, makeContract(symbol), endDateStr, durationStr, barSizeSetting, symbol.isCash() ? "MIDPOINT" : "TRADES", 0, 2);
long timeOut = System.currentTimeMillis() + historicalRequestTimeOutMillis;
try {
while (!req.done && !req.failed && timeOut > System.currentTimeMillis()) {
Thread.sleep(500);
}
} catch (InterruptedException e) {
}
historicalRequestsById.remove(requestId);
return req;
}
@Override
public Tick getLastTick(Symbol symbol) {
for (Iterator<Entry<Integer, IBTickRequest>> entries = tickRequestsById.entrySet().iterator(); entries.hasNext();) {
Map.Entry<Integer, IBTickRequest> entry = entries.next();
if (entry.getValue().symbol.equals(symbol)) {
return entry.getValue().prevTick;
}
}
return null;
}
@Override
public Bar getLastBar(Symbol symbol) {
for (Iterator<Entry<Integer, IBTickRequest>> entries = tickRequestsById.entrySet().iterator(); entries.hasNext();) {
Map.Entry<Integer, IBTickRequest> entry = entries.next();
if (entry.getValue().symbol.equals(symbol)) {
return entry.getValue().prevBar;
}
}
return null;
}
@Override
public synchronized void addTickListener(TickListener listener) {
logger.info("Adding TickListener {} for all symbols", listener.getClass().getSimpleName());
tickListeners.add(listener);
}
@Override
public synchronized void removeTickListener(TickListener listener) {
boolean removed = tickListeners.remove(listener);
if (removed) {
logger.info("Removed TickListener {} for all symbols", listener.getClass().getSimpleName());
}
removeTickListener(null, listener);
}
@Override
public void addTickListener(Symbol symbol, TickListener listener) {
addTickListener(symbol, listener, false, null);
}
@Override
public synchronized void addTickListener(Symbol symbol, TickListener listener, boolean marketDepth, Cleaner cleaner) {
checkConnected();
marketDepth = marketDepth && !symbol.isIndex();
Integer reqId = null;
IBTickRequest req = null;
for (Iterator<Entry<Integer, IBTickRequest>> entries = tickRequestsById.entrySet().iterator(); entries.hasNext();) {
Map.Entry<Integer, IBTickRequest> entry = entries.next();
if (entry.getValue().symbol.equals(symbol)) {
reqId = entry.getKey();
req = entry.getValue();
if (req.tickListeners.contains(listener)) {
return;
}
}
}
if (marketDepth) {
logger.info("Adding TickListener {} with marketdepth for symbol {} using cleaner {}",
new Object[] { listener.getClass().getSimpleName(), symbol, cleaner });
} else {
logger.info("Adding TickListener {} for symbol {} using cleaner {}", new Object[] { listener.getClass().getSimpleName(), symbol, cleaner });
}
if (reqId == null) {
Contract contract = makeContract(symbol);
reqId = nextValidReqId.incrementAndGet();
socket.reqContractDetails(reqId, contract);
contractRequestsById.put(reqId, new IBContractRequest(reqId));
reqId = nextValidReqId.incrementAndGet();
socket.reqMktData(reqId, contract, "", false);
if (marketDepth) {
socket.reqMktDepth(reqId, contract, 10);
}
req = new IBTickRequest(reqId, symbol, marketDepth, cleaner);
tickRequestsById.put(reqId, req);
}
req.tickListeners.add(listener);
}
@Override
public synchronized void removeTickListener(Symbol symbol, TickListener listener) {
for (Iterator<Entry<Integer, IBTickRequest>> entries = tickRequestsById.entrySet().iterator(); entries.hasNext();) {
Map.Entry<Integer, IBTickRequest> entry = entries.next();
if (symbol == null || entry.getValue().symbol.equals(symbol)) {
boolean removed = entry.getValue().tickListeners.remove(listener);
if (removed) {
logger.info("Removed TickListener {} for symbol {}", listener.getClass().getSimpleName(), entry.getValue().symbol);
}
if (entry.getValue().tickListeners.isEmpty() && entry.getValue().barListeners.isEmpty()) {
logger.info("Removed last TickListener for symbol {}, cancelling market data request {}", entry.getValue().symbol, entry.getValue());
entries.remove();
if (isConnected()) {
if (entry.getValue().marketDepth) {
socket.cancelMktDepth(entry.getKey());
}
socket.cancelMktData(entry.getKey());
}
}
}
}
}
@Override
public synchronized void addBarListener(BarListener listener) {
logger.info("Adding BarListener {} for all symbols", listener.getClass().getSimpleName());
barListeners.add(listener);
}
@Override
public synchronized void removeBarListener(BarListener listener) {
boolean removed = barListeners.remove(listener);
if (removed) {
logger.info("Removed BarListener {} for all symbols", listener.getClass().getSimpleName());
}
removeBarListener(null, listener);
}
@Override
public void addBarListener(Symbol symbol, BarListener listener) {
addBarListener(symbol, listener, 60, null);
}
@Override
public synchronized void addBarListener(Symbol symbol, BarListener listener, int barSize, Cleaner cleaner) {
checkConnected();
Integer reqId = null;
IBTickRequest req = null;
for (Iterator<Entry<Integer, IBTickRequest>> entries = tickRequestsById.entrySet().iterator(); entries.hasNext();) {
Map.Entry<Integer, IBTickRequest> entry = entries.next();
if (entry.getValue().symbol.equals(symbol)) {
reqId = entry.getKey();
req = entry.getValue();
if (req.barSize != null && req.barSize.getStandardSeconds() != barSize) {
throw new IllegalArgumentException("Existing listener uses different bar size: " + req.barSize.getStandardSeconds());
}
if (req.barListeners.contains(listener)) {
return;
}
}
}
logger.info("Adding BarListener {} for symbol {}", listener.getClass().getSimpleName(), symbol);
if (reqId == null) {
Contract contract = makeContract(symbol);
reqId = nextValidReqId.incrementAndGet();
socket.reqContractDetails(reqId, contract);
contractRequestsById.put(reqId, new IBContractRequest(reqId));
reqId = nextValidReqId.incrementAndGet();
// socket.reqRealTimeBars(reqId, contract, 5, "TRADES", false);
socket.reqMktData(reqId, contract, "", false);
req = new IBTickRequest(reqId, symbol, barSize, cleaner);
tickRequestsById.put(reqId, req);
} else {
req.setBarSize(barSize);
}
req.barListeners.add(listener);
}
@Override
public synchronized void removeBarListener(Symbol symbol, BarListener listener) {
for (Iterator<Entry<Integer, IBTickRequest>> entries = tickRequestsById.entrySet().iterator(); entries.hasNext();) {
Map.Entry<Integer, IBTickRequest> entry = entries.next();
if (symbol == null || entry.getValue().symbol.equals(symbol)) {
boolean removed = entry.getValue().barListeners.remove(listener);
if (removed) {
logger.info("Removed BarListener {} for symbol {}", listener.getClass().getSimpleName(), entry.getValue().symbol);
}
if (entry.getValue().tickListeners.isEmpty() && entry.getValue().barListeners.isEmpty()) {
logger.info("Removed last BarListener for symbol {}, cancelling market data request {}", entry.getValue().symbol, entry.getValue());
entries.remove();
if (isConnected()) {
socket.cancelMktData(entry.getKey());
}
}
}
}
}
@Override
public synchronized void removeAllListeners() {
super.removeAllListeners();
barListeners.clear();
tickListeners.clear();
for (IBTickRequest req : new ArrayList<IBTickRequest>(tickRequestsById.values())) {
for (BarListener listener : new ArrayList<BarListener>(req.barListeners)) {
removeBarListener(null, listener);
}
for (TickListener listener : new ArrayList<TickListener>(req.tickListeners)) {
removeTickListener(null, listener);
}
}
}
@Override
public void historicalData(int reqId, String date, double open, double high, double low, double close, int volume, int count, double wap, boolean hasGaps) {
try {
if (logger.isDebugEnabled())
logger.debug("historicalData: {} {} {} {} {} {} {} {} {} {}", new Object[] { reqId, date, open, low, high, close, volume, count, wap, hasGaps });
IBHistoricalRequest req = historicalRequestsById.get(reqId);
if (req == null) {
socket.cancelHistoricalData(reqId);
return;
}
req.timestamp = System.currentTimeMillis();
if (date.startsWith("finished")) {
req.done = true;
return;
}
DateTime dateTime = null;
if (req.barSize.getMillis() == 86400000) {
dateTime = DateTimeFormat.forPattern("yyyyMMdd").parseDateTime(date);
} else {
dateTime = new DateTime(Long.parseLong(date) * 1000);
}
Bar bar = new Bar(req.barSize, req.symbol, dateTime, open, high, low, close, wap, volume, count);
req.bars.put(bar.dateTime, bar);
} catch (Throwable t) {
// Do not allow exceptions come back to the socket -- it will cause
// disconnects
logger.error(t.getMessage(), t);
}
}
@Override
public void tickPrice(int reqId, int tickType, double price, int canAutoExecute) {
tick(reqId, tickType, price, -1, null);
}
@Override
public void tickSize(int reqId, int tickType, int size) {
tick(reqId, tickType, -1, size, null);
}
@Override
public void tickString(int reqId, int tickType, String value) {
tick(reqId, tickType, -1, -1, value);
}
@Override
public void tickGeneric(int reqId, int tickType, double value) {
tick(reqId, tickType, value, -1, null);
}
private void tick(int reqId, int tickType, double doubleValue, int intValue, String stringValue) {
try {
if (logger.isDebugEnabled())
logger.debug("tick: reqId: {}, tickType: {}, doubleValue: {}, intValue: {}, stringValue: {}", new Object[] { reqId, tickType, doubleValue, intValue,
stringValue });
IBTickRequest req = tickRequestsById.get(reqId);
if (req != null) {
buildTick(req, tickType, doubleValue, intValue, stringValue);
if (req.barListeners.size() > 0) {
buildBar(req, tickType, doubleValue, intValue, stringValue);
}
}
if (req == null) {
logger.warn("No matching request for tick event, cancelling market data: reqId: {}, tickType: {}, doubleValue: {}, intValue: {}, stringValue: {}",
new Object[] { reqId, tickType, doubleValue, intValue, stringValue });
socket.cancelMktData(reqId);
}
} catch (Throwable t) {
// Do not allow exceptions come back to the socket -- it will cause
// disconnects
logger.error(t.getMessage(), t);
}
}
private void buildBar(IBTickRequest req, int tickType, double doubleValue, int intValue, String stringValue) {
try {
synchronized (req) {
Bar bar = req.currBar;
long now = System.currentTimeMillis();
if (bar == null) {
bar = req.currBar = new Bar(req.barSize, req.symbol, new DateTime(now - (now % req.barSizeMillis)));
} else if (bar.dateTime.getMillis() + req.barSizeMillis <= now) {
boolean valid = isValidBar(req);
if (!valid && req.prevBar != null) {
bar.open = bar.high = bar.low = bar.close = bar.wap = req.prevBar.close;
bar.volume = bar.trades = 0;
valid = true;
}
if (valid) {
int len = req.barListeners.size();
for (int i = 0; i < len; i++) {
BarListener listener = req.barListeners.get(i);
try {
listener.onBar(bar);
} catch (Throwable t) {
logger.error(t.getMessage(), t);
}
}
len = barListeners.size();
for (int i = 0; i < len; i++) {
BarListener listener = barListeners.get(i);
try {
listener.onBar(bar);
} catch (Throwable t) {
logger.error(t.getMessage(), t);
}
}
req.prevBar = bar;
bar = req.currBar = new Bar(req.barSize, req.symbol, new DateTime(now - (now % req.barSizeMillis)));
}
}
switch (tickType) {
case TickType.BID:
case TickType.ASK:
// If currency then only bids and asks are received
if (!req.symbol.isCash()) {
break;
}
case TickType.LAST:
if (bar.open == 0.0) {
bar.open = doubleValue;
bar.high = Double.NEGATIVE_INFINITY;
bar.low = Double.POSITIVE_INFINITY;
}
bar.high = Math.max(bar.high, doubleValue);
bar.low = Math.min(bar.low, doubleValue);
bar.close = doubleValue;
req.lastPrice = doubleValue;
break;
case TickType.VOLUME:
if (intValue > 0) {
// If new volume is lower than previous, reset has
// occured.
if (intValue < req.barVolume) {
bar.volume = intValue;
bar.wap = req.lastPrice;
} else if (req.barVolume > 0) {
int lastSize = (intValue - req.barVolume);
bar.wap = Util.round((bar.wap * bar.volume + req.lastPrice * lastSize) / (bar.volume + lastSize), 2);
bar.volume += lastSize;
bar.trades++;
} else {
bar.volume = 0;
bar.wap = req.lastPrice;
}
req.barVolume = intValue;
}
break;
case TickType.LAST_TIMESTAMP:
if (logger.isDebugEnabled()) {
logger.debug("Tick timestamp {}", new DateTime(Long.parseLong(stringValue) * 1000));
}
// bar.dateTime = new DateTime(Long.parseLong(stringValue) *
// 1000);
break;
case TickType.BID_SIZE:
case TickType.ASK_SIZE:
case TickType.OPEN:
case TickType.CLOSE:
case TickType.HIGH:
case TickType.LOW:
case TickType.LAST_SIZE:
default:
break;
}
}
} catch (Throwable t) {
// Do not allow exceptions come back to the socket -- it will cause
// disconnects
logger.error(t.getMessage(), t);
}
}
private void buildTick(IBTickRequest req, int tickType, double doubleValue, int intValue, String stringValue) {
try {
// Ignore last size since it is not consistent, use volume change
// instead.
if (tickType == TickType.LAST_SIZE) {
return;
}
req.timestamp = System.currentTimeMillis();
Tick currTick = req.currTick;
if (currTick == null) {
currTick = req.currTick = new Tick(req.symbol);
}
currTick.dateTime = new DateTime(req.timestamp);
currTick.lastSize = 0;
switch (tickType) {
case TickType.ASK:
currTick.ask = doubleValue;
break;
case TickType.BID:
currTick.bid = doubleValue;
break;
case TickType.LAST:
currTick.price = doubleValue;
break;
// case TickType.OPEN:
// break;
// case TickType.CLOSE:
// break;
// case TickType.HIGH:
// break;
// case TickType.LOW:
// break;
case TickType.ASK_SIZE:
currTick.askSize = intValue;
break;
case TickType.BID_SIZE:
currTick.bidSize = intValue;
break;
// case TickType.LAST_SIZE:
// Ignore last size since it is not consistent, use volume change
// instead.
// tick.lastSize = intValue;
// break;
case TickType.VOLUME:
// If new volume is lower than previous, reset has occured.
if (intValue > 0) {
if (intValue < currTick.volume) {
currTick.lastSize = intValue;
currTick.volume = intValue;
} else if (currTick.volume > 0) {
currTick.lastSize = intValue - currTick.volume;
currTick.volume = intValue;
} else {
currTick.lastSize = 0;
currTick.volume = intValue;
}
}
break;
case TickType.LAST_TIMESTAMP:
if (logger.isDebugEnabled()) {
logger.debug("Tick timestamp {}", new DateTime(Long.parseLong(stringValue) * 1000));
}
// tick.dateTime = new DateTime(Long.parseLong(stringValue) *
// 1000);
break;
}
if (isValidTick(req)) {
int len = req.tickListeners.size();
for (int i = 0; i < len; i++) {
TickListener listener = req.tickListeners.get(i);
try {
listener.onTick(currTick);
} catch (Throwable t) {
logger.error(t.getMessage(), t);
}
}
len = tickListeners.size();
for (int i = 0; i < len; i++) {
TickListener listener = tickListeners.get(i);
try {
listener.onTick(currTick);
} catch (Throwable t) {
logger.error(t.getMessage(), t);
}
}
req.prevTick = currTick;
req.currTick = new Tick(currTick);
}
} catch (Throwable t) {
// Do not allow exceptions come back to the socket -- it will cause
// disconnects
logger.error(t.getMessage(), t);
}
}
private boolean isValidBar(IBBarRequest req) {
Bar curr = req.currBar;
if (curr.close <= 0 || curr.high <= 0 || curr.low <= 0 || curr.close <= 0) {
return false;
}
if (curr.high < curr.low) {
return false;
}
if (req.cleaner != null && Double.isNaN(req.cleaner.update(curr.getDateTime(), curr.getPrice()))) {
return false;
}
return true;
}
private boolean isValidBar(IBTickRequest req) {
Bar curr = req.currBar;
if (curr.close <= 0 || curr.high <= 0 || curr.low <= 0 || curr.close <= 0) {
return false;
}
if (curr.high < curr.low) {
return false;
}
if (req.barCleaner != null && Double.isNaN(req.barCleaner.update(curr.getDateTime(), curr.getPrice()))) {
return false;
}
return true;
}
private boolean isValidTick(IBTickRequest req) {
Tick currTick = req.currTick;
Tick prevTick = req.prevTick;
if (req.marketDepth && currTick.marketDepth == null) {
return false;
}
if (currTick.getSymbol().isIndex()) {
if (currTick.price <= 0 && !currTick.getSymbol().isTickIndex()) {
return false;
}
if (prevTick != null) {
if (currTick.price == prevTick.price) {
return false;
}
}
return true;
}
if (currTick.askSize <= 0 || currTick.bidSize <= 0) {
return false;
}
if (currTick.ask <= 0 || currTick.bid <= 0 || currTick.ask == currTick.bid) {
return false;
}
if (prevTick != null) {
if (currTick.ask == prevTick.ask && currTick.askSize == prevTick.askSize && currTick.bid == prevTick.bid && currTick.bidSize == prevTick.bidSize
&& currTick.price == prevTick.price && currTick.lastSize == prevTick.lastSize && currTick.volume == prevTick.volume) {
return false;
}
}
if (req.tickCleaner != null && Double.isNaN(req.tickCleaner.update(currTick.dateTime, currTick.price))) {
return false;
}
return true;
}
@Override
public void updateMktDepth(int reqId, int position, int operation, int side, double price, int size) {
try {
if (logger.isDebugEnabled())
logger.debug("reqId: {}, position: {}, operation: {}, side: {}, price: {}, size: {}", new Object[] { reqId, position, operation, side, price, size });
if (price < 0) {
return;
}
IBTickRequest req = tickRequestsById.get(reqId);
if (req == null) {
logger.warn(
"No matching request for market depth event, cancelling market data: reqId: {}, position: {}, operation: {}, side: {}, price: {}, size: {}",
new Object[] { reqId, position, operation, side, price, size });
socket.cancelMktData(reqId);
return;
}
if (req.currTick == null) {
return;
}
MarketDepth md = req.currTick.marketDepth;
if (md == null) {
md = req.currTick.marketDepth = new MarketDepth(10);
}
double[] prices = (side == 0 ? md.askPrices : md.bidPrices); // ASK
// 0,BID
// 1
int[] sizes = (side == 0 ? md.askSizes : md.bidSizes); // ASK 0,BID
// 1
switch (operation) {
case 0: // INSERT
case 1: // UPDATE
prices[position] = price;
sizes[position] = size;
break;
case 2: // DELETE
prices[position] = 0;
sizes[position] = 0;
break;
}
} catch (Throwable t) {
// Do not allow exceptions come back to the socket -- it will cause
// disconnects
logger.error(t.getMessage(), t);
}
}
@Override
public void realtimeBar(int reqId, long time, double open, double high, double low, double close, long volume, double wap, int count) {
try {
if (logger.isDebugEnabled())
logger.debug("realtimeBar: reqId: {}, time: {}, open: {}, high: {}, low: {}, close: {}, volume: {}, wap: {}, count: {}", new Object[] { reqId, time,
open, high, low, close, volume, wap, count });
IBBarRequest req = barRequestsById.get(reqId);
if (req == null) {
socket.cancelRealTimeBars(reqId);
barRequestsById.remove(reqId);
return;
}
req.timestamp = System.currentTimeMillis();
Bar bar = new Bar(realTimeBarSize, req.symbol, new DateTime(time * 1000), open, high, low, close, wap, (int) volume, count);
if (isValidBar(req)) {
for (BarListener listener : req.listeners) {
try {
listener.onBar(bar);
} catch (Throwable t) {
logger.error(t.getMessage(), t);
}
}
}
req.prevBar = bar;
} catch (Throwable t) {
// Do not allow exceptions come back to the socket -- it will cause
// disconnects
logger.error(t.getMessage(), t);
}
}
@Override
public void error(Exception e) {
if (!connect && e.getMessage() != null && e.getMessage().startsWith("Socket closed")) {
return;
}
logger.error(e.getMessage(), e);
}
@Override
public void error(String error) {
logger.error(error);
}
@Override
public void error(int reqId, int errorCode, String errorMsg) {
try {
Object req = barRequestsById.get(reqId);
if (req == null) {
req = tickRequestsById.get(reqId);
}
if (req == null) {
req = historicalRequestsById.get(reqId);
}
if (req == null) {
req = scannerRequestsById.get(reqId);
}
if (req == null) {
req = reqId;
}
String message = errorCode + ": " + errorMsg;
lastMessage = message;
if (req instanceof IBRequest) {
((IBRequest) req).error = message;
}
switch (errorCode) {
case 162:
// Historical data request returned no data or pacing violation
if (req instanceof IBHistoricalRequest) {
logger.warn("Received error for request {}: {}", req, message);
((IBHistoricalRequest) req).done = true;
} else if (req instanceof IBScannerRequest) {
logger.debug("Received error for request {}: {}", req, message);
((IBScannerRequest) req).done = true;
}
break;
case 200: // 200: bad contract
logger.warn("Received error for request {}: {}", req, message);
if (req instanceof IBHistoricalRequest) {
((IBHistoricalRequest) req).failed = true;
} else if (req instanceof IBScannerRequest) {
((IBScannerRequest) req).done = true;
}
break;
case 202: // 202: Cancelled order
break;
case 321: // 321: server error when validating an API client request
logger.warn("Received error for request {}: {}", req, message);
if (req instanceof IBHistoricalRequest) {
((IBHistoricalRequest) req).failed = true;
} else if (req instanceof IBScannerRequest) {
((IBScannerRequest) req).done = true;
}
break;
case 309: // 309: market depth requested for more than 3 symbols
logger.warn("Received error for request {}: {}", req, message);
// socket.cancelMktDepth(reqId);
break;
case 317: // 317: Market depth data has been reset
logger.warn("Received error for request {}: {}", req, message);
break;
case 326:
logger.info("Unable connect as the client id is already in use. Retry with a unique client id: {}", message);
disconnect();
break;
case 399:
logger.info("Received order message for request {}: {}", req, message);
break;
case 504:
// Not connected
logger.warn("Not connected: {}", message);
case 1100:
// Connectivity between IB and TWS has been lost.
logger.warn("Connectivity between IB and TWS has been lost: {}", message);
break;
case 1101:
case 1102:
case 2100:
logger.info("Received account message: {}", message);
break;
case 2104: // Market data farm connection is OK
case 2106: // HMDS data farm connection is OK
case 2107: // HMDS data farm connection is inactive but should be available upon demand
case 2108: // Market data farm connection is inactive but should be available upon demand
case 2119: // Market data farm is connecting
//logger.info("Connectivity: {}", message);
break;
default:
logger.warn("Received error for request {}: {}", req, message);
}
} catch (Throwable t) {
// Do not allow exceptions come back to the socket -- it will cause
// disconnects
logger.error(t.getMessage(), t);
}
}
public String getMarketScannerParameters() {
// if (logger.isDebugEnabled())
// logger.debug(String.format("Requesting historical data: %s %s %s %s",
// symbol, endDateStr, durationStr, barSizeSetting));
marketScannerParams = null;
socket.reqScannerParameters();
try {
while (marketScannerParams == null) {
Thread.sleep(500);
}
} catch (InterruptedException e) {
}
return marketScannerParams;
}
@Override
public void scannerParameters(String xml) {
marketScannerParams = xml;
}
public List<Symbol> scanMarket(ScannerType type, ScannerInstrument instrument, ScannerLocation location) {
return scanMarket(type, instrument, location, false);
}
public List<Symbol> scanMarket(ScannerType type, ScannerInstrument instrument, ScannerLocation location, boolean exhaustive) {
List<Symbol> result = new ArrayList<Symbol>();
if (!exhaustive) {
IBScannerRequest data = requestScannerSubscription(type, instrument, location, Double.MAX_VALUE, Double.MAX_VALUE);
result.addAll(data.symbols);
return result;
}
double increment = 1.0;
double toPrice = 0;
double fromPrice = 0;
double maxPrice = 100000;
while (true) {
fromPrice = toPrice;
toPrice = fromPrice + increment;
IBScannerRequest data = requestScannerSubscription(type, instrument, location, fromPrice, toPrice);
if (data.symbols.size() >= 50) {
toPrice = fromPrice;
increment /= 2;
continue;
}
result.addAll(data.symbols);
if (toPrice > maxPrice) {
break;
}
if (data.symbols.size() < 25) {
increment *= 2;
}
}
return result;
}
public IBScannerRequest requestScannerSubscription(ScannerType type, ScannerInstrument instrument, ScannerLocation location, double minPrice, double maxPrice) {
checkConnected();
if (logger.isDebugEnabled())
logger.debug("requestScannerSubscription: {} {} {} {} {}", new Object[] { type, instrument, location, minPrice, maxPrice });
ScannerSubscription params = new ScannerSubscription();
// params.scanCode("MOST_ACTIVE");
// params.instrument("STOCK.EU");
// params.locationCode("STK.EU.SFB");
// params.scanCode("TOP_PERC_GAIN");
// params.instrument("IND.US");
// params.locationCode("IND.US");
params.scanCode(type.toString());
params.instrument(instrument.toString());
params.locationCode(location.toString());
params.abovePrice(minPrice);
params.belowPrice(maxPrice);
params.aboveVolume(0);
params.averageOptionVolumeAbove(0);
params.marketCapAbove(0);
params.marketCapBelow(1.0E100);
params.stockTypeFilter("ALL");
int requestId = nextValidReqId.incrementAndGet();
IBScannerRequest req = new IBScannerRequest(requestId);
scannerRequestsById.put(requestId, req);
socket.reqScannerSubscription(requestId, params);
long timeOut = System.currentTimeMillis() + historicalRequestTimeOutMillis;
try {
while (!req.done && timeOut > System.currentTimeMillis()) {
Thread.sleep(500);
}
} catch (InterruptedException e) {
}
scannerRequestsById.remove(requestId);
return req;
}
@Override
public void scannerData(int reqId, int rank, ContractDetails details, String distance, String benchmark, String projection, String legsStr) {
if (logger.isDebugEnabled())
logger.debug("scannerData: {} {} {} {} {} {} {}", new Object[] { reqId, rank, Util.toString(details.m_summary), Util.toString(details),
distance, benchmark, projection, legsStr });
try {
IBScannerRequest req = scannerRequestsById.get(reqId);
req.symbols.add(toSymbol(details.m_summary, details));
} catch (Throwable t) {
// Do not allow exceptions come back to the socket -- it will cause
// disconnects
logger.error(t.getMessage(), t);
}
}
@Override
public void scannerDataEnd(int reqId) {
if (logger.isDebugEnabled())
logger.debug("scannerDataEnd: {}", reqId);
try {
socket.cancelScannerSubscription(reqId);
// IBScannerRequest req = scannerRequestsById.get(reqId);
// req.done = true;
} catch (Throwable t) {
// Do not allow exceptions come back to the socket -- it will cause
// disconnects
logger.error(t.getMessage(), t);
}
}
public List<Symbol> findSymbols(String code, String exchange, String currency, SymbolType type) {
checkConnected();
Contract contract = makeContract(new Symbol(code, exchange, currency, type));
int reqId = nextValidReqId.incrementAndGet();
IBContractRequest req = new IBContractRequest(reqId);
contractRequestsById.put(reqId, req);
socket.reqContractDetails(reqId, contract);
long timeOut = System.currentTimeMillis() + historicalRequestTimeOutMillis;
try {
while (!req.done && timeOut > System.currentTimeMillis()) {
Thread.sleep(500);
}
} catch (InterruptedException e) {
}
contractRequestsById.remove(reqId);
return req.symbols;
}
@Override
public void contractDetails(int reqId, ContractDetails contractDetails) {
try {
if (logger.isDebugEnabled())
logger.debug("contractDetails: {} {}", reqId, Util.toString(contractDetails));
IBContractRequest req = contractRequestsById.get(reqId);
if (req == null) {
return;
}
req.symbols.add(toSymbol(contractDetails.m_summary, contractDetails));
} catch (Throwable t) {
logger.error(t.getMessage(), t);
}
}
@Override
public void contractDetailsEnd(int reqId) {
try {
if (logger.isDebugEnabled())
logger.debug("contractDetailsEnd: {}", reqId);
IBContractRequest req = contractRequestsById.get(reqId);
if (req == null) {
return;
}
req.done = true;
} catch (Throwable t) {
logger.error(t.getMessage(), t);
}
}
class IBRequest {
int id;
Symbol symbol;
String error;
long timestamp;
IBRequest(int id, Symbol symbol) {
this.id = id;
this.symbol = symbol;
this.timestamp = System.currentTimeMillis();
}
@Override
public int hashCode() {
return id;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
return obj != null && obj instanceof IBRequest && id == ((IBRequest) obj).id;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append(getClass().getSimpleName());
sb.append(" [id=");
sb.append(id);
sb.append(", ");
if (symbol != null) {
sb.append("symbol=");
sb.append(symbol);
}
sb.append(", ts=");
sb.append(new DateTime(timestamp));
sb.append("]");
return sb.toString();
}
}
class IBTickRequest extends IBRequest {
boolean marketDepth;
Tick prevTick;
Tick currTick;
Cleaner tickCleaner;
List<TickListener> tickListeners;
long barSizeMillis;
Duration barSize;
int barVolume;
double lastPrice;
Bar prevBar;
Bar currBar;
Cleaner barCleaner;
List<BarListener> barListeners;
public IBTickRequest(int id, Symbol symbol, boolean marketDepth, Cleaner cleaner) {
super(id, symbol);
this.marketDepth = marketDepth;
this.tickListeners = new ArrayList<TickListener>(4);
this.barListeners = new ArrayList<BarListener>(4);
this.tickCleaner = cleaner;
}
public IBTickRequest(int id, Symbol symbol, int barSizeSeconds, Cleaner cleaner) {
super(id, symbol);
this.barSizeMillis = barSizeSeconds * 1000;
this.barSize = new Duration(barSizeMillis);
this.tickListeners = new ArrayList<TickListener>(4);
this.barListeners = new ArrayList<BarListener>(4);
this.barCleaner = cleaner;
}
public void setBarSize(int barSize) {
this.barSizeMillis = barSize * 1000;
this.barSize = new Duration(barSizeMillis);
}
}
class IBBarRequest extends IBRequest {
Bar prevBar;
Bar currBar;
List<BarListener> listeners;
Cleaner cleaner;
public IBBarRequest(int id, Symbol symbol) {
super(id, symbol);
this.listeners = new ArrayList<BarListener>();
}
}
class IBHistoricalRequest extends IBRequest {
boolean failed;
boolean done;
NavigableMap<DateTime, Bar> bars;
Duration barSize;
public IBHistoricalRequest(int id, Symbol symbol, Duration barSize) {
super(id, symbol);
this.bars = new TreeMap<DateTime, Bar>();
this.barSize = barSize;
}
}
class IBScannerRequest {
int id;
boolean done;
List<Symbol> symbols;
int lastSize;
public IBScannerRequest(int id) {
this.id = id;
this.symbols = new ArrayList<Symbol>();
}
@Override
public int hashCode() {
return id;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
return obj != null && obj instanceof IBScannerRequest && id == ((IBScannerRequest) obj).id;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append(getClass().getSimpleName());
sb.append(" [id=");
sb.append(id);
sb.append("]");
return sb.toString();
}
}
class IBContractRequest {
int id;
boolean done;
List<Symbol> symbols;
int lastSize;
public IBContractRequest(int id) {
this.id = id;
this.symbols = new ArrayList<Symbol>();
}
@Override
public int hashCode() {
return id;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
return obj != null && obj instanceof IBContractRequest && id == ((IBContractRequest) obj).id;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append(getClass().getSimpleName());
sb.append(" [id=");
sb.append(id);
sb.append("]");
return sb.toString();
}
}
@Override
public void connectionClosed() {
}
@Override
public void tickEFP(int tickerId, int tickType, double basisPoints, String formattedBasisPoints, double impliedFuture, int holdDays, String futureExpiry,
double dividendImpact, double dividendsToExpiry) {
}
@Override
public void orderStatus(int orderId, String status, int filled, int remaining, double avgFillPrice, int permId, int parentId, double lastFillPrice,
int clientId, String whyHeld) {
}
@Override
public void openOrder(int orderId, Contract contract, Order order, OrderState orderState) {
}
@Override
public void openOrderEnd() {
}
@Override
public void updateAccountValue(String key, String value, String currency, String accountName) {
}
@Override
public void updatePortfolio(Contract contract, int position, double marketPrice, double marketValue, double averageCost, double unrealizedPNL,
double realizedPNL, String accountName) {
}
@Override
public void updateAccountTime(String timeStamp) {
}
@Override
public void accountDownloadEnd(String accountName) {
}
@Override
public void nextValidId(int orderId) {
}
@Override
public void bondContractDetails(int reqId, ContractDetails contractDetails) {
}
@Override
public void execDetails(int reqId, Contract contract, Execution execution) {
}
@Override
public void execDetailsEnd(int reqId) {
}
@Override
public void updateMktDepthL2(int tickerId, int position, String marketMaker, int operation, int side, double price, int size) {
}
@Override
public void updateNewsBulletin(int msgId, int msgType, String message, String origExchange) {
}
@Override
public void managedAccounts(String accountsList) {
}
@Override
public void receiveFA(int faDataType, String xml) {
}
@Override
public void currentTime(long time) {
}
@Override
public void fundamentalData(int reqId, String data) {
}
@Override
public void deltaNeutralValidation(int reqId, UnderComp underComp) {
}
public enum ScannerType {
TOP_PERC_GAIN, TOP_PERC_LOSE, MOST_ACTIVE, MOST_ACTIVE_USD, MOST_ACTIVE_AVG_USD, HALTED, HIGH_DIVIDEND_YIELD_IB, TOP_TRADE_COUNT, TOP_TRADE_RATE
}
public enum ScannerInstrument {
STK, STOCK_EU, STOCK_HK, IND_EU, IND_US, IND_HK, FUT_EU, FUT_US, FUT_HK;
@Override
public String toString() {
return super.toString().replace('_', '.');
}
}
public enum ScannerLocation {
IND_US, IND_EU, IND_HK, STK_US, STK_US_MAJOR, STK_NYSE, STK_NASDAQ, STK_AMEX, STK_ARCA, FUT_US, FUT_GLOBEX, FUT_ECBOT, FUT_IPE, FUT_NYBOT, FUT_NYMEX, FUT_NYSELIFFE, STK_EU, STK_EU_AEB, STK_EU_IBIS, STK_EU_LSE, STK_EU_SBF, STK_EU_SFB, STK_EU_VIRTX, FUT_EU, FUT_EU_BELFOX, FUT_EU_DTB, FUT_EU_FTA, FUT_EU_IDEM, FUT_EU_LIFFE, FUT_EU_MEFFRV, FUT_EU_MONEP;
@Override
public String toString() {
return super.toString().replace('_', '.');
}
}
@Override
public void tickOptionComputation(int tickerId, int field, double impliedVol, double delta, double optPrice, double pvDividend, double gamma, double vega,
double theta, double undPrice) {
// TODO Auto-generated method stub
}
@Override
public void tickSnapshotEnd(int reqId) {
// TODO Auto-generated method stub
}
@Override
public void marketDataType(int reqId, int marketDataType) {
// TODO Auto-generated method stub
}
@Override
public void commissionReport(CommissionReport commissionReport) {
// TODO Auto-generated method stub
}
@Override
public void position(String account, Contract contract, int pos, double avgCost) {
// TODO Auto-generated method stub
}
@Override
public void positionEnd() {
// TODO Auto-generated method stub
}
@Override
public void accountSummary(int reqId, String account, String tag, String value, String currency) {
// TODO Auto-generated method stub
}
@Override
public void accountSummaryEnd(int reqId) {
// TODO Auto-generated method stub
}
}