Created
March 8, 2017 21:25
-
-
Save markcheno/cc5badae374561aaf40212f7f038967e to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# 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