Skip to content

Instantly share code, notes, and snippets.

@markcheno
Created March 8, 2017 21:25
Show Gist options
  • Save markcheno/cc5badae374561aaf40212f7f038967e to your computer and use it in GitHub Desktop.
Save markcheno/cc5badae374561aaf40212f7f038967e to your computer and use it in GitHub Desktop.
# Gist example of IB wrapper ...
#
# Download API from http://interactivebrokers.github.io/#
#
# Install python API code /IBJts/source/pythonclient $ python3 setup.py install
#
# Note: The test cases, and the documentation refer to a python package called IBApi,
# but the actual package is called ibapi. Go figure.
#
# Get the latest version of the gateway:
# https://www.interactivebrokers.com/en/?f=%2Fen%2Fcontrol%2Fsystemstandalone-ibGateway.php%3Fos%3Dunix
# (for unix: windows and mac users please find your own version)
#
# Run the gateway
#
# user: edemo
# pwd: demo123
#
# Now I'll try and replicate the historical price example
import time
from ibapi.wrapper import EWrapper
from ibapi.client import EClient
from ibapi.contract import Contract as IBcontract
from threading import Thread
import datetime
INVALID_TIME=-1
DEFAULT_HISTORIC_DATA_ID=50
DEFAULT_GET_CONTRACT_ID=43
IB_WARNINGS=[2106, 2107]
class TestWrapper(EWrapper):
"""
The wrapper deals with the action coming back from the IB gateway or TWS instance
We override methods in EWrapper that will get called when this action happens, like currentTime
Extra methods are added as we need to store the results in this objects _my_data store
These all have the form init_ or is_
"""
def __init__(self):
setattr(self, "_my_errors", None)
setattr(self, "_my_warnings", None)
setattr(self, "_my_historic_data_dict", dict())
setattr(self, "_my_historic_data_finished_flags", dict())
setattr(self, "_my_contract_details", dict())
setattr(self, "_my_contract_details_finished_flags", dict())
## error handling code
def init_error(self):
self._my_errors=None
self.my_warnings=None
def is_error(self):
an_error_if=self.get_error() is not None
return an_error_if
def get_error(self):
return self._my_errors
def error(self, id, errorCode, errorString):
## Overriden method
errormsg = "IB error id %d errorcode %d string %s" % (id, errorCode, errorString)
if errorCode in IB_WARNINGS:
self._my_warnings=errormsg
else:
self._my_errors=errormsg
## get contract details code
def init_contractdetails(self, reqId):
self._my_contract_details[str(reqId)]=[]
self._my_contract_details_finished_flags[str(reqId)]=False
def get_contract_details(self, reqId):
if str(reqId) not in self._my_contract_details.keys():
self.init_contractdetails(reqId)
return self._my_contract_details[str(reqId)]
def is_contract_detail_finished(self, reqId):
finished_flags_dict=self._my_contract_details_finished_flags
if str(reqId) not in finished_flags_dict:
finished_flags_dict[str(reqId)]=False
return finished_flags_dict[str(reqId)]
def contractDetails(self, reqId, contractDetails):
## overridden method
self._my_contract_details[str(reqId)].append(contractDetails)
def contractDetailsEnd(self, reqId):
## overriden method
self._my_contract_details_finished_flags[reqId]=True
## Historic data code
def init_historicprices(self, tickerid):
self._my_historic_data_dict[str(tickerid)]=[]
self._my_historic_data_finished_flags[str(tickerid)]=False
def historicalData(self, tickerid , date:str, open:float, high:float,
low:float, close:float, volume:int, barCount:int,
WAP:float, hasGaps:int):
## Overriden method
## Note I'm choosing to ignore barCount, WAP and hasGaps but you could use them if you like
bardata=(date, open, high, low, close, volume)
historic_data_dict=self._my_historic_data_dict
## Add on to the current data
if str(tickerid) not in historic_data_dict.keys():
self.init_historicprices(tickerid)
historic_data_dict[str(tickerid)].append(bardata)
def historicalDataEnd(self, tickerid, start:str, end:str):
## overriden method
currently_finished_flag=self.is_historic_data_finished(tickerid)
currently_finished_flag=True
def is_historic_data_finished(self, tickerid):
finished_flags_dict=self._my_historic_data_finished_flags
if str(tickerid) not in finished_flags_dict:
finished_flags_dict[str(tickerid)]=False
return finished_flags_dict[str(tickerid)]
def get_historic_data(self, tickerid):
historic_data_dict=self._my_historic_data_dict
if str(tickerid) not in historic_data_dict.keys():
self.init_historicprices(tickerid)
return historic_data_dict[str(tickerid)]
class TestClient(EClient):
"""
The client method
We don't override native methods, but instead call them from our own wrappers
"""
def __init__(self, wrapper):
## Set up with a wrapper inside
EClient.__init__(self, wrapper)
def resolve_ib_contract(self, ibcontract, reqId=DEFAULT_GET_CONTRACT_ID):
"""
From a partially formed contract, returns a fully fledged version
:returns fully resolved IB contract
"""
self.wrapper.init_error()
## Make a place to store the data we're going to return
self.init_contractdetails(reqId)
print("Getting full contract details from the server... ")
self.reqContractDetails(reqId, ibcontract)
## Run a loop until we get a valid time, an error, or get bored waiting
MAX_WAIT_SECONDS = 10
start_time = time.time()
finished = False
while not finished:
new_contract_details = self.wrapper.get_contract_details(reqId)
finished = self.wrapper.is_contract_detail_finished(reqId)
iserror = self.wrapper.is_error()
if (time.time() - start_time) > MAX_WAIT_SECONDS:
print("Exceeded maximum wait for wrapper to confirm finished - seems to be normal behaviour")
finished = True
if iserror:
finished = True
if iserror:
print("Error happened")
print(self.get_error())
if len(new_contract_details)==0:
print("Failed to get additional contract details")
return ibcontract
if len(new_contract_details)>1:
print("got multiple contracts using first one")
new_contract_details=new_contract_details[0]
resolved_ibcontract=new_contract_details.summary
return resolved_ibcontract
def get_IB_historical_data(self, ibcontract, durationStr="1 Y", barSizeSetting="1 day",
tickerid=DEFAULT_HISTORIC_DATA_ID):
"""
Returns historical prices for a contract, up to today
ibcontract is a Contract
:returns list of prices in 4 tuples: Open high low close volume
"""
self.wrapper.init_error()
print("Getting historical data from the server... ")
## Make a place to store the data we're going to return
self.init_historicprices(tickerid)
today = datetime.datetime.now()
# Request some historical data. Native method in EClient
self.reqHistoricalData(
tickerid, # tickerId,
ibcontract, # contract,
today.strftime("%Y%m%d %H:%M:%S %Z"), # endDateTime,
durationStr, # durationStr,
barSizeSetting, # barSizeSetting,
"TRADES", # whatToShow,
1, # useRTH,
1, # formatDate
[] ## chartoptions not used
)
## Run a loop until we get a valid time, an error, or get bored waiting
MAX_WAIT_SECONDS = 10
start_time = time.time()
finished = False
while not finished:
historic_data = self.wrapper.get_historic_data(tickerid)
finished = self.wrapper.is_historic_data_finished(tickerid)
iserror = self.wrapper.is_error()
if (time.time() - start_time) > MAX_WAIT_SECONDS:
print("Exceeded maximum wait for wrapper to confirm finished- seems normal")
finished = True
if iserror:
finished = True
self.cancelHistoricalData(tickerid)
if iserror:
print("Error happened")
print(self.get_error())
return historic_data
class TestApp(TestWrapper, TestClient):
def __init__(self, ipaddress, portid, clientid):
TestWrapper.__init__(self)
TestClient.__init__(self, wrapper=self)
self.connect(ipaddress, portid, clientid)
thread = Thread(target = self.run)
thread.start()
setattr(self, "_thread", thread)
#if __name__ == '__main__':
app = TestApp("127.0.0.1", 4001, 1)
ibcontract = IBcontract()
ibcontract.secType = "FUT"
ibcontract.lastTradeDateOrContractMonth="201809"
ibcontract.symbol="GE"
ibcontract.exchange="GLOBEX"
resolved_ibcontract=app.resolve_ib_contract(ibcontract)
historic_data = app.get_IB_historical_data(resolved_ibcontract)
print(historic_data)
app.disconnect()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment