In case the code below is too mangled, I uploaded the txt file version of the following note here.
One of the main challenges for beginners with Interactive Brokers' API is that it's asynchronous- you say reqHistData and then nothing happens, and then some fractions of a second later a totally different method gets fired with the data you requested as an argument. For many systems, this slight speed optimization is unnecessary. It only helps when you're requesting many different things all at once. For the speed increase, the API is significantly less beginner-friendly.
I'll explain how to make the API synchronized so that when you call an API function, it returns what you requested directly, just like a normal function. After googling it, in the technical terminology, it looks like I'm converting the API from "non-blocking asynchronous" to "blocking synchronous". You'll need IbPy if you don't already have it.
This is inappropriate for systems that rapidly react to streams of intraday data, but is useful for requesting historical data, portfolio information, executions, the time, and the next valid id.
Let's review how the API works:
The two key parts are:
1) EClientSocket - sends requests
2) EWrapper - receives requested values
After requesting something through the EClientSocket, you need to wait for a bit until all the requested values have hit the EWrapper.
In general to syncronize things we'll follow this pattern:
def syncronized_request(x):
myEClientSocket.request_x
wait
return myEWrapper.x
'request_x' is a usual EClientSocket method which you can look up in the API doc.
'wait' is defined like this-
def wait():inc_secs = .2 # a fifth of a secondwhile not myEWrapper.isDone:sleep(inc_secs)myEWrapper.isDone = False
'myEWrapper.x' pulls the requested data out of the local variable 'x' where it has been stored in your overwridden EWrapper class.
First import some libraries:
Boilerplate Code (1)
Then create your event listener subclass:
Boilerplate Code (2)
Then instantiate the requester and receiver:
myEWrapper = syncEWrapper()myEClientSocket = EClientSocket(myEWrapper)
Now we can write the two simplest syncronized API functions:
def getTime():myEClientSocket.reqCurrentTime()wait()return myEWrapper.timedef getData(contract):myEClientSocket.reqRealTimeBars(tickerId=1,contract=contract,barSize=5,whatToShow='MIDPOINT',useRTH=1)wait()myEClientSocket.cancelRealTimeBars(tickerId=1)return myEWrapper.bar
Notice that we already added the variables 'time' and 'bar' to myEWrapper
Let's add two more functions to hide the implementation:
def connect(clientId):myEClientSocket.eConnect('localhost', 7496, clientId)def disconnect():myEClientSocket.eDisconnect()
Run Boilerplate Code (3) to define my simplifying etf contract factory
Now we can use the API easily, just call:
spy = contract_etf('SPY')connect(1)getTime()getData(spy)sleep(1)getData(spy)disconnect()exit()
Now that you see it works, you can look back and see how it worked
Feel free to reuse or modify my code
------------------------
Boilerplate Code (1)
from ib.ext.EWrapper import EWrapperfrom ib.ext.EClientSocket import EClientSocketimport datetime as dtfrom time import sleep
Boilerplate Code (3)
import ib.extdef contract_etf(sym):c = ib.ext.Contract.Contract()c.m_symbol = sym.upper()c.m_secType = 'STK'c.m_exchange = 'SMART'c.m_currency = 'USD'return c
Boilerplate Code (2)
Subclassing EWrapper is the most intimidating part at first. Here's my subclass of it, which you can just copy:
class syncEWrapper(EWrapper):## variables to store requested dataisDone = Falsetime = Nonebar = None## overwridden listener functionsdef author():return 'Max F. Dama'def tickPrice(self, tickerId, field, price, canAutoExecute):raise NotImplementedError()def tickSize(self, tickerId, field, size):raise NotImplementedError()def tickOptionComputation(self, tickerId,field,impliedVol,delta,modelPrice,pvDividend):raise NotImplementedError()def tickGeneric(self, tickerId, tickType, value):raise NotImplementedError()def tickString(self, tickerId, tickType, value):raise NotImplementedError()def tickEFP(self, tickerId,tickType,basisPoints,formattedBasisPoints,impliedFuture,holdDays,futureExpiry,dividendImpact,dividendsToExpiry):raise NotImplementedError()def orderStatus(self, orderId,status,filled,remaining,avgFillPrice,permId,parentId,lastFillPrice,clientId,whyHeld):raise NotImplementedError()def openOrder(self, orderId, contract, order, orderState):raise NotImplementedError()def updateAccountValue(self, key, value, currency, accountName):raise NotImplementedError()def updatePortfolio(self, contract,position,marketPrice,marketValue,averageCost,unrealizedPNL,realizedPNL,accountName):raise NotImplementedError()def updateAccountTime(self, timeStamp):raise NotImplementedError()def nextValidId(self, orderId):passdef contractDetails(self, reqId, contractDetails):raise NotImplementedError()def bondContractDetails(self, reqId, contractDetails):raise NotImplementedError()def contractDetailsEnd(self, reqId):raise NotImplementedError()def execDetails(self, orderId, contract, execution):raise NotImplementedError()def updateMktDepth(self, tickerId,position,operation,side,price,size):raise NotImplementedError()def updateMktDepthL2(self, tickerId,position,marketMaker,operation,side,price,size):raise NotImplementedError()def updateNewsBulletin(self, msgId, msgType, message, origExchange):raise NotImplementedError()def managedAccounts(self, accountsList):raise NotImplementedError()def receiveFA(self, faDataType, xml):raise NotImplementedError()def historicalData(self, reqId,date,open,high,low,close,volume,count,WAP,hasGaps):raise NotImplementedError()def scannerParameters(self, xml):raise NotImplementedError()def scannerData(self, reqId,rank,contractDetails,distance,benchmark,projection,legsStr):raise NotImplementedError()def scannerDataEnd(self, reqId):raise NotImplementedError()def realtimeBar(self, reqId,time,open,high,low,close,volume,wap,count):self.bar = (time, open, high, low, close, volume)self.isDone = Truedef currentTime(self, time):self.time = dt.datetime.fromtimestamp(time)self.isDone = Truedef fundamentalData(self, reqId, data):raise NotImplementedError()def error(self,*err):print err
Here is a video of me executing the code so you can see how if all the jumping around was confusing.


