def __init__(self, tws_uri): EWrapper.__init__(self) EClientSocket.__init__(self, anyWrapper=self) self.tws_uri = tws_uri host, port, client_id = self.tws_uri.split(':') self._host = host self._port = int(port) self.client_id = int(client_id) self._next_ticker_id = 0 self._next_request_id = 0 self._next_order_id = None self.managed_accounts = None self.symbol_to_ticker_id = {} self.ticker_id_to_symbol = {} self.last_tick = defaultdict(dict) self.bars = {} # accounts structure: accounts[account_id][currency][value] self.accounts = defaultdict( lambda: defaultdict(lambda: defaultdict(lambda: np.NaN))) self.accounts_download_complete = False self.positions = {} self.portfolio = {} self.open_orders = {} self.order_statuses = {} self.executions = defaultdict(OrderedDict) self.commissions = defaultdict(OrderedDict) self._execution_to_order_id = {} self.time_skew = None self.unrecoverable_error = False self.connect()
def __init__(self, name=None, call_msg=True, host=None, port=None, client_id=None): self.name = name self.host = host self.port = port self.client_id = client_id self.ref_nums = [0] self.wrapper = SyncWrapper() self.connection = EClientSocket(self.wrapper) self.account = self.wrapper.account self.contracts = self.wrapper.contracts self.executions_ = self.wrapper.executions self.order_messages = self.wrapper.order_messages if self.host is None: self.host = 'localhost' if self.port is None: self.port = 7496 if call_msg is False: self.wrapper.suppress = True if self.client_id is None: self.client_id = 0 # listen to execution #self.wrapper.register(self.method, events='execution') self.__connect__ = self.connection.eConnect(self.host, self.port, self.client_id) self.__gen_order_id__(1) sleep(.2)
def __init__(self, eventEngine, gatewayName='IB'): """Constructor""" super(IbGateway, self).__init__(eventEngine, gatewayName) self.host = EMPTY_STRING # 连接地址 self.port = EMPTY_INT # 连接端口 self.clientId = EMPTY_INT # 用户编号 self.tickerId = 0 # 订阅行情时的代码编号 self.tickDict = {} # tick快照字典,key为tickerId,value为VtTickData对象 self.tickProductDict = {} # tick对应的产品类型字典,key为tickerId,value为产品类型 self.orderId = 0 # 订单编号 self.orderDict = {} # 报单字典,key为orderId,value为VtOrderData对象 self.accountDict = {} # 账户字典 self.contractDict = {} # 合约字典 self.subscribeReqDict = {} # 用来保存订阅请求的字典 self.connected = False # 连接状态 self.wrapper = IbWrapper(self) # 回调接口 self.connection = EClientSocket(self.wrapper) # 主动接口
def __init__(self, parameters): # Variable initialization #self.orders = orders.OrderBook() #self.price_log = data.PriceLog() #self.parameters = parameters self.connection = EClientSocket(self) self.connection.eConnect('localhost', 7496, 0) # host, port, clientId tick_id = 1 symbol = "SLV" contract = self.makeContract(symbol) self.connection.reqMktData(tick_id, contract, [], False)
def __init__(self, tws_uri, order_update_callback): EWrapper.__init__(self) EClientSocket.__init__(self, anyWrapper=self) self.tws_uri = tws_uri host, port, client_id = self.tws_uri.split(':') self._order_update_callback = order_update_callback self._next_ticker_id = 0 self._next_order_id = None self.managed_accounts = None self.ticker_id_to_symbol = {} self.last_tick = defaultdict(dict) self.realtime_bars = {} self.accounts = {} self.accounts_download_complete = False self.positions = {} self.portfolio = {} self.orders = {} self.time_skew = None log.info("Connecting: {}:{}:{}".format(host, int(port), int(client_id))) self.eConnect(host, int(port), int(client_id)) while self.notConnected(): sleep(0.1) self._download_account_details() log.info("Managed accounts: {}".format(self.managed_accounts)) self.reqCurrentTime() self.reqIds(1) while self.time_skew is None or self._next_order_id is None: sleep(0.1) log.info("Local-Broker Time Skew: {}".format(self.time_skew))
def __init__(self, host, port, strategies, instruments, logger): self.msgs = queue.Queue() self.book_builder = BookBuilder() # strategies self.instruments = instruments self.contracts = dict() self.strategies = [] for strategy in strategies: strat = Recoil2(strategy['watch_threshold'], strategy['watch_duration'], strategy['slowdown_threshold'], strategy['slowdown_duration']) self.strategies.append(strat) # operations self.host = host self.port = port self.connection = EClientSocket(Connector(self.instruments, self.msgs)) self.next_id = None self.log = logger
class IBProvider(BaseProvider): def __init__(self): basicConfig() # These two variables are initialized in Connect method self._connection = None self._wrapper = None self._request_id = 0 def connect(self): self._wrapper = ReferenceWrapper() self._connection = EClientSocket(self._wrapper) self._connection.eConnect(IB_HOST, IB_PORT, IB_PROVIDER_CLIENT_ID) def disconnect(self): if self._connection.isConnected(): self._connection.eDisconnect() def _make_contract(self, symbol): contract = Contract() contract.m_symbol = symbol contract.m_secType = 'STK' contract.m_exchange = 'SMART' contract.m_primaryExch = 'SMART' contract.m_currency = 'USD' contract.m_localSymbol = symbol return contract def _get_next_request_id(self): self._request_id += 1 return self._request_id def get_market_depth_L1(self, symbol): contract = self._make_contract(symbol) request_id = self._get_next_request_id() self._connection.reqMktData(request_id, contract, '', True) while not self._wrapper.isExecutionRequestFinished(request_id): err = self._wrapper.getError(request_id) if err is not None: raise Exception(err) very_short_sleep() return self._wrapper.getMarketDepthL1(request_id)
import time from datetime import datetime from IBWrapper import IBWrapper, contract from ib.ext.EClientSocket import EClientSocket from ib.ext.ScannerSubscription import ScannerSubscription if __name__=="__main__": callback = IBWrapper() # Instantiate IBWrapper tws = EClientSocket(callback) # Instantiate EClientSocket host = "" port = 7496 clientId = 5000 tws.eConnect(host, port, clientId) # Connect to TWS tws.setServerLogLevel(5) accountName = "DU123456" # Change to your own account create = contract() # Instantiate contract class # Initiate attributes to receive data. At some point we need a separate class for this callback.initiate_variables() # Account and Portfolio ############################################################## # reqAccountUpdates ---> updateAccountTime self.update_AccountTime # updateAccountValue self.update_AccountValue # updatePortfolio self.update_Portfolio # accountDownloadEnd self.accountDownloadEnd_flag # reqAccountSummary ---> accountSummary self.account_Summary # cancelAccountSummary # accountSummaryEnd self.account_SummaryEnd_flag # reqPositions ---> position self.update_Position # cancelPositions
import csv import timeit import random import pandas as pd from datetime import datetime, timedelta import threading from threading import Thread import multiprocessing from multiprocessing import Process # from flask import Flask, url_for, request, render_template, redirect from apscheduler.schedulers.background import BackgroundScheduler ############################################### callback = IBWrapper() tws = EClientSocket(callback) host = "" port = 7496 clientId = 100 create = contract() callback.initiate_variables() def conn(): status = tws.isConnected() if status == False: print( "######### $$$$$$$$$$$$$$$$$$ RECONNECTING TWS SESSION $$$$$$$$$$$$$$$$$$############" ) tws.eConnect(host, port, clientId) # print("######### $$$$$$$$$$$$$$$$$$$ TWS CONNECTED $$$$$$$$$$$$$$$$$$############")
import pandas as pd from datetime import datetime, timedelta import threading from threading import Thread import multiprocessing from multiprocessing import Process # from flask import Flask, url_for, request, render_template, redirect from apscheduler.schedulers.background import BackgroundScheduler sched = BackgroundScheduler() import openpyxl ############################################### callback = IBWrapper() tws = EClientSocket(callback) host = "" port = 7496 clientId = 100 create = contract() callback.initiate_variables() acc = "DU228380" # hl_curr_ny = 'YES' df_common_inputs = pd.read_excel('C:/database/FX_DAILY/FOREX_settings.xlsx', 'common_inputs') df_london = pd.read_excel('C:/database/FX_DAILY/FOREX_settings.xlsx', 'LONDON_TRADES') df_o_val = pd.read_excel('C:/database/FX_DAILY/FOREX_settings.xlsx',
import pandas as pd import time from IBWrapper import IBWrapper, contract from ib.ext.EClientSocket import EClientSocket from ib.ext.ScannerSubscription import ScannerSubscription import re stocks=[ '700' ] if __name__ == '__main__': for x in stocks: callback = IBWrapper() tws = EClientSocket(callback) host = "127.0.0.1" port = 7496 clientId = 999 tws.eConnect(host, port, clientId) create = contract() callback.initiate_variables() contract_Details = create.create_contract(x, 'STK', 'SEHK', 'HKD') tickerId = 8001 tws.reqFundamentalData(tickerId, contract_Details, "RESC" ) time.sleep(5)
def __init__(self, wrapper): EClientSocket.__init__(self, wrapper)
class ReferenceApp: def __init__(self, host='localhost', port=7496, clientId=0): self.host = host self.port = port self.clientId = clientId self.wrapper = ReferenceWrapper() self.connection = EClientSocket(self.wrapper) @ref def eConnect(self): self.connection.eConnect(self.host, self.port, self.clientId) @ref def reqAccountUpdates(self): self.connection.reqAccountUpdates(1, '') @ref def reqOpenOrders(self): self.connection.reqOpenOrders() @ref def reqExecutions(self): filt = ExecutionFilter() self.connection.reqExecutions(0, filt) @ref def reqIds(self): self.connection.reqIds(10) @ref def reqNewsBulletins(self): self.connection.reqNewsBulletins(1) @ref def cancelNewsBulletins(self): self.connection.cancelNewsBulletins() @ref def setServerLogLevel(self): self.connection.setServerLogLevel(3) @ref def reqAutoOpenOrders(self): self.connection.reqAutoOpenOrders(1) @ref def reqAllOpenOrders(self): self.connection.reqAllOpenOrders() @ref def reqManagedAccts(self): self.connection.reqManagedAccts() @ref def requestFA(self): self.connection.requestFA(1) @ref def reqMktData(self): contract = Contract() # contract.m_symbol = 'AUD' contract.m_currency = 'USD' contract.m_secType = 'CASH' contract.m_exchange = 'IDEALPRO' self.connection.reqMktData(1, contract, '', False) @ref def reqHistoricalData(self): contract = Contract() contract.m_symbol = 'QQQQ' contract.m_secType = 'STK' contract.m_exchange = 'SMART' endtime = strftime('%Y%m%d %H:%M:%S') self.connection.reqHistoricalData( tickerId=1, contract=contract, endDateTime=endtime, durationStr='1 D', barSizeSetting='1 min', whatToShow='TRADES', useRTH=0, formatDate=1) @ref def eDisconnect(self): sleep(5) self.connection.eDisconnect()
def receiveFA(cls, faDataType, xml): """ generated source for method receiveFA """ return cls.FINANCIAL_ADVISOR + " " + EClientSocket.faMsgTypeName( faDataType) + " " + xml
long_SMA = data['close'].rolling(long_length).mean() return short_SMA.iloc[-1], long_SMA.iloc[-1] def check_existing_pos(): # 0 is no position, 1 means we have existing position # we need to write an algo to send request to IB to check # whether we have existing position # you will need a proper identifier for your position. pos = 1 return pos # connect accountName = "DU000000" callback = IBWrapper() # Instantiate IBWrapper. callback tws = EClientSocket(callback) # Instantiate EClientSocket and return data to callback host = "" port = 4002 clientId = 4002 order_id = 1001 tws.eConnect(host, port, clientId) # Connect to TWS # Let's work with APPLE stock. create = contract() callback.initiate_variables() contract_Details = create.create_contract('AAPL', 'STK', 'SMART', 'USD') ''' This should be the beginning of the while loop This is an infinite loop.
import csv import timeit import random import pandas as pd from datetime import datetime, timedelta import threading from threading import Thread import multiprocessing from multiprocessing import Process # from flask import Flask, url_for, request, render_template, redirect from apscheduler.schedulers.background import BackgroundScheduler sched = BackgroundScheduler() import openpyxl ############################################### callback = IBWrapper() tws = EClientSocket(callback) host = "" port = 7496 clientId = 100 create = contract() callback.initiate_variables() acc = "DU228380" commonwealth_curr = ['GBP', 'AUD', 'NZD', 'EUR'] def conn(): status = tws.isConnected() if status == False: # print("######### $$$$$$$$$$$$$$$$$$ RECONNECTING TWS SESSION $$$$$$$$$$$$$$$$$$############") tws.eConnect(host, port, clientId) # print("######### $$$$$$$$$$$$$$$$$$$ TWS CONNECTED $$$$$$$$$$$$$$$$$$############")
class IBMarketData(object): def __init__(self, host='127.0.0.1', port=7496, clientId=0): self.host = host self.port = port self.clientId = clientId self.wrapper = ReferenceWrapper() self.connection = EClientSocket(self.wrapper) self.logger = make_logger(self) if not self.logger.handlers: self.logger.addHandler(logging.StreamHandler()) def market_data_get(self, symbol, data_type): self.connection.reqMktData(1, ) contract = Contract() contract.m_symbol = 'QQQ' contract.m_secType = 'STK' contract.m_exchange = 'SMART' self.connection.reqMktData(1, contract, '', False) def market_data_register(self, symbol, data_type, handler, handler_ctx): pass def market_data_unregister(self, symbol, data_type): pass def _make_stock_contract(self, symbol): contract = Contract() contract.m_symbol = symbol contract.m_secType = 'STK' contract.m_exchange = 'SMART/ISLAND' return contract def _generate_ticker_id(self): global DataRequests while True: id = random.randint(1, 9999) if id not in DataRequests.keys(): return id def historical_bars_get(self, symbol, bar_type, start, end=None): self.logger.error('symbol={}, bar_type={}, start={}, end={}'.format( symbol, bar_type, start, end)) id = self._generate_ticker_id() global DataRequests data_request = DataRequest(BAR_TYPE_TO_REQ[bar_type]) DataRequests[id] = data_request if end is None: end = start if bar_type == BAR_MIN: end += timedelta(minutes=1) elif bar_type == BAR_DAY: start = datetime(year=start.year, month=start.month, day=start.day) end = datetime(year=end.year, month=end.month, day=end.day) end += timedelta(days=1) else: raise ValueError() if start.tzname() == None: start = TZ_NY.localize(start) end = TZ_NY.localize(end) start = start.astimezone(TZ_LOCAL) end = end.astimezone(TZ_LOCAL) assert end >= start end_start_delta = end - start duration = '{days} D'.format(days=end_start_delta.days + 1) if bar_type == BAR_DAY and end_start_delta.days + 1 > 365: duration = '{years} Y'.format(years=(end_start_delta.days / 365) + 1) endtime = end.strftime('%Y%m%d %H:%M:%S') args = dict( tickerId=id, contract=self._make_stock_contract(symbol), endDateTime=endtime, durationStr=duration, barSizeSetting=bar_type, whatToShow='TRADES', useRTH=0, formatDate=2 #linux time ) self.logger.error( 'reqHistoricalData(id:{} | type:"{}" | duration:"{}" | end:"{}"'. format(id, bar_type, duration, endtime)) self.connection.reqHistoricalData(**args) data_request.wait() start = start.astimezone(TZ_NY) end = end.astimezone(TZ_NY) bars = [b for b in data_request.bars if start <= b.get_time() <= end] DataRequests.pop(id) return bars def bars_register(self, symbol, handler, handler_ctx): pass def bars_unregister(self, symbol): pass def connect(self): self.connection.eConnect(self.host, self.port, self.clientId) def disconnect(self): self.connection.eDisconnect() time.sleep(0.15)
class IBClient(object): fields = {'trades': 'TRADES', 'midpoint': 'MIDPOINT', 'bid': 'BID', 'ask': 'ASK', 'bid_ask': 'BID_ASK', 'hist_vol': 'HISTORICAL_VOLATILITY', 'imp_vol': 'OPTION_IMPLIED_VOLATILITY'} def __init__(self, name=None, call_msg=True, host=None, port=None, client_id=None): self.name = name self.host = host self.port = port self.client_id = client_id self.ref_nums = [0] self.wrapper = SyncWrapper() self.connection = EClientSocket(self.wrapper) self.account = self.wrapper.account self.contracts = self.wrapper.contracts self.executions_ = self.wrapper.executions self.order_messages = self.wrapper.order_messages if self.host is None: self.host = 'localhost' if self.port is None: self.port = 7496 if call_msg is False: self.wrapper.suppress = True if self.client_id is None: self.client_id = 0 # listen to execution #self.wrapper.register(self.method, events='execution') self.__connect__ = self.connection.eConnect(self.host, self.port, self.client_id) self.__gen_order_id__(1) sleep(.2) def request_reference_id(self, integer=False): if not integer: ref_id = uuid.uuid4().hex if ref_id in self.ref_nums: return self.request_reference() else: self.ref_nums.append(ref_id) return ref_id else: ref_id = '{0:09d}'.format(np.random.randint(0, 999999999)) if ref_id > max([x for x in self.ref_nums if type(x) is int]): return int(ref_id) else: return self.request_reference(integer=True) def __gen_order_id__(self, num): self.connection.reqIds(num) return self.wrapper.order_id def method(self, sender, event, msg=None): print "[{0}] got event {1} with message {2}".format(self.name, event, msg) def __track_orders__(self): self.connection.reqAutoOpenOrders(1) def managed_accounts(self): if self.account.child_accounts: return self.account.child_accounts else: self.connection.reqManagedAccts() sleep(1) if self.account.child_accounts: return self.account.child_accounts return ['REQUEST FAILED'] def account_updates(self, acct): #get a unique id reference = self.request_reference_id() #append a new packet container to account self.account.append_request(reference, AccountPacket(acct), PortfolioPacket(acct)) self.wrapper.ref_id = reference self.connection.reqAccountUpdates(1, acct) sleep(1) return reference def place_order(self, contract, order): self.wrapper.order_id += 100 ref = self.wrapper.order_id self.connection.placeOrder(ref, contract, order) sleep(.2) return ref def cancel_order(self, id): self.connection.cancelOrder(id) def get_executions(self, efilter=None): if not efilter: efilter = ExecutionFilter() ref = self.request_reference_id(integer=True) self.connection.reqExecutions(reqId=ref, filter=efilter) sleep(3) # Todo: This is a ridiculous bottleneck return ref def get_contract(self, contract): ref = self.request_reference_id(integer=True) self.connection.reqContractDetails(ref, contract) sleep(1) return ref def portfolio(self, account): ref = self.account_updates(account) return self.account[ref]['portfolio'].messages def account_details(self, account): ref = self.account_updates(account) return self.account[ref]['account'].messages def executions(self, efilter=None): ref = self.get_executions(efilter) return self.executions_[ref] def order_status(self, order_id): sleep(.2) return [msg for msg in self.order_messages if msg['orderId'] == order_id] def disconnect(self): self.connection.eDisconnect()
# TODO: Error handling for connecting, see https://bit.ly/3dkTqj5 # TODO: get fundamental data, see https://bit.ly/3nH5rUF # import pandas as pd import time from ib.ext.EClientSocket import EClientSocket from IBWrapper import IBWrapper, contract # from ib.ext.ScannerSubscription import ScannerSubscription # import re stocks = ['5'] for x in stocks: callback = IBWrapper() tws = EClientSocket(callback) host = "127.0.0.1" port = 7496 clientId = 25 tws.eConnect(host, port, clientId) create = contract() callback.initiate_variables() contract_Details = create.create_contract(x, 'STK', 'SEHK', 'HKD') tickerId = 8001 tws.reqFundamentalData(tickerId, contract_Details, "ReportSnapshot") time.sleep(5) print(callback.fundamental_Data_data) print(callback.fundamental_Data_data.find("AATCA")) print(callback.fundamental_Data_data.find("ACFSHR"))
import pandas as pd import numpy as np import time from datetime import datetime from ib.ext.EWrapperMsgGenerator import updateAccountTime from ib.ext.Contract import Contract from ib.ext.EClientSocket import EClientSocket from ib.ext.ScannerSubscription import ScannerSubscription from ib.opt import Connection, message accountName = 'DU958186' callback = EWrapperMsgGenerator tws=EClientSocket(callback) host='' port=7497 clientId=100 tws.eConnect(host,port,clientId)
numId = 0 if __name__ == '__main__': cid = 10 # clientId: can be any number. It will show on IBGateway as folder/different user orderId = 1 # orderId accountId = 'DU594416' symbolId = '???' # contract identifier id or asset id? port = 4002 # ============================================================================= # ESTABLISH CONNECTION # ============================================================================= # instantiate data in IBWrapper, still need to use this as EWrapper # is an abstract object data = IBWrapper() tws = EClientSocket(data) tws.eConnect('localhost' , port, cid) # Initiate attributes to receive data (REQUIRED) data.initiate_variables() # ============================================================================= # ============================================================================= ''' # ============================================================================= # UPDATE YOUR PORTFOLIO WITH 2 ALTERNATIVES FUNCTION # ============================================================================= # 1. update account: reqAccountUpdates(subscribe, accountId) # subscribe: 0 - update Acc Time, 1 - update Acc Val, 2 - update Portfolio tws.reqAccountUpdates(2, accountId) # time (1 sec) is REQUIRED to get data, otherwise empty list
def __init__(self, wrapper): ## Set up with a wrapper inside EClientSocket.__init__(self, wrapper)
from IBWrapper import IBWrapper, contract from ib.ext.EClientSocket import EClientSocket import time callback = IBWrapper() tws = EClientSocket(callback) host = "" port = 7497 clientId = 100 tws.eConnect(host, port, clientId) time.sleep(3) #request ID 1 tws.reqIds(1) order_id = callback.next_ValidId + 1 create = contract() callback.initiate_variables() time.sleep(3) df = [ "AAPL", "STK", "SMART", "USD", '', '', '20170616', '50', "DU228380", "LMT", "1", "SELL", "142.26" ] contract_info = create.create_contract(df[0], df[1], df[2], df[3]) #,df[4],df[5],df[6],df[7]) order_info = create.create_order(df[8], df[9], df[10], df[11], df[12], True) order_info2 = create.create_order(df[8], df[9], df[10], df[11], df[12], "143.26", True) tws.placeOrder(order_id, contract_info, order_info)
def __init__(self, host='localhost', port=7496, clientId=0): self.host = host self.port = port self.clientId = clientId self.wrapper = ReferenceWrapper() self.connection = EClientSocket(self.wrapper)
class Bot(object): def __init__(self, host, port, strategies, instruments, logger): self.msgs = queue.Queue() self.book_builder = BookBuilder() # strategies self.instruments = instruments self.contracts = dict() self.strategies = [] for strategy in strategies: strat = Recoil2(strategy['watch_threshold'], strategy['watch_duration'], strategy['slowdown_threshold'], strategy['slowdown_duration']) self.strategies.append(strat) # operations self.host = host self.port = port self.connection = EClientSocket(Connector(self.instruments, self.msgs)) self.next_id = None self.log = logger def connect(self): template = 'Attempting to connect host: {} port: {}...' self.log.operation(template.format(self.host, self.port)) self.connection.eConnect(self.host, self.port, 0) self.log.operation('Connected.') def disconnect(self): self.log.operation('Disconnecting...') self.connection.eDisconnect() self.log.operation('Disconnected.') def request_data(self): for ticker_id, instrument in self.instruments.items(): contract = Contract() contract.m_symbol = instrument['symbol'] contract.m_currency = instrument['currency'] contract.m_secType = instrument['secType'] contract.m_exchange = instrument['exchange'] self.contracts[instrument['symbol']] = contract self.connection.reqMktData(ticker_id, contract, '', False) def run(self): while True: msg = self.msgs.get() self.log.raw(msg) if msg['type'] == 'nextValidId': self.log.order({'msg': 'new order ID', 'orderId': msg['orderId']}) self.next_id = msg['orderId'] continue tick = self.book_builder.process_raw_tick(msg) if not tick: continue self.log.data(tick) for strategy in self.strategies: signal = strategy.handle_tick(tick) if not signal: continue self.log.order(signal) order = strategy.place_order(signal) if not order: continue c = self.contracts[signal['symbol']] self.log.order({'symbol': signal['symbol'], 'qty': order.m_totalQuantity, 'type': order.m_orderType, 'goodTill': order.m_goodTillDate, 'px': order.m_lmtPrice, 'action': order.m_action}) self.connection.placeOrder(id=self.next_id, contract=c, order=order) self.next_id += 1
import time from datetime import datetime from IBWrapper import IBWrapper, contract from ib.ext.EClientSocket import EClientSocket from ib.ext.ScannerSubscription import ScannerSubscription from __future__ import print_function if __name__ == "__main__": callback = IBWrapper() # Instantiate IBWrapper tws = EClientSocket(callback) # Instantiate EClientSocket host = "" port = 7496 clientId = 5000 tws.eConnect(host, port, clientId) # Connect to TWS tws.setServerLogLevel(5) accountName = "DU123456" # Change to your own account create = contract() # Instantiate contract class # Initiate attributes to receive data. At some point we need a separate class for this callback.initiate_variables() # Account and Portfolio ############################################################## # reqAccountUpdates ---> updateAccountTime self.update_AccountTime # updateAccountValue self.update_AccountValue # updatePortfolio self.update_Portfolio # accountDownloadEnd self.accountDownloadEnd_flag # reqAccountSummary ---> accountSummary self.account_Summary # cancelAccountSummary # accountSummaryEnd self.account_SummaryEnd_flag # reqPositions ---> position self.update_Position # cancelPositions
class IB: """ Interface to Interactive Brokers. """ # Definition of the tick data "field" value. tick_type = { 0: "BID_SIZE", 1: "BID_PRICE", 2: "ASK_PRICE", 3: "ASK_SIZE", 4: "LAST_PRICE", 5: "LAST_SIZE", 6: "HIGH", 7: "LOW", 8: "VOLUME", 9: "CLOSE_PRICE", 10: "BID_OPTION_COMPUTATION", 11: "ASK_OPTION_COMPUTATION", 12: "LAST_OPTION_COMPUTATION", 13: "MODEL_OPTION_COMPUTATION", 14: "OPEN_PRICE", 15: "LOW_13_WEEK", 16: "HIGH_13_WEEK", 17: "LOW_26_WEEK", 18: "HIGH_26_WEEK", 19: "LOW_52_WEEK", 20: "HIGH_52_WEEK", 21: "AVG_VOLUME", 22: "OPEN_INTEREST", 23: "OPTION_HISTORICAL_VOL", 24: "OPTION_IMPLIED_VOL", 27: "OPTION_CALL_OPEN_INTEREST", 28: "OPTION_PUT_OPEN_INTEREST", 29: "OPTION_CALL_VOLUME" } def __init__(self, account_name: str, host: str, port: int, client_id: int): self.account_name = account_name # Instantiate IBWrapper callback. self.callback = IBWrapper() # Instantiate EClientSocket and return data to callback self.tws = EClientSocket(self.callback) # Connect to TWS. self.tws.eConnect(host, port, client_id) # Instantiate contract class. self.create = contract() self.callback.initiate_variables() def request_account_data(self): """ Requests the relevant account data. http://interactivebrokers.github.io/tws-api/account_updates.html http://interactivebrokers.github.io/tws-api/classIBApi_1_1EClient.html#aea1b0d9b6b85a4e0b18caf13a51f837f """ self.callback.update_AccountValue = [] self.callback.update_Portfolio = [] self.callback.update_AccountTime = None print(f'Requesting account data.') self.tws.reqAccountUpdates(subscribe=True, acctCode=self.account_name) # Retrieve the data from the callback when it arrives. while True: # Don't check for portfolio, as it may be empty to begin with. if all([ len(self.callback.update_AccountValue) > 0, self.callback.update_AccountTime ]): account_data = pd.DataFrame( self.callback.update_AccountValue, columns=['key', 'value', 'currency', 'accountName']) portfolio = pd.DataFrame( self.callback.update_Portfolio, columns=[ 'Contract ID', 'Currency', 'Expiry', 'Include Expired', 'Local Symbol', 'Multiplier', 'Primary Exchange', 'Right', 'Security Type', 'Strike', 'Symbol', 'Trading Class', 'Position', 'Market Price', 'Market Value', 'Average Cost', 'Unrealised PnL', 'Realised PnL', 'Account Name' ]) account_time = self.callback.update_AccountTime break print(f'Waiting for account data. ') time.sleep(2) # Unsubscribe. self.tws.reqAccountUpdates(subscribe=False, acctCode=self.account_name) self.callback.update_AccountValue = [] self.callback.update_Portfolio = [] self.callback.update_AccountTime = None return account_data, portfolio, account_time def request_portfolio(self): """ Requests the portfolio. http://interactivebrokers.github.io/tws-api/account_updates.html http://interactivebrokers.github.io/tws-api/interfaceIBApi_1_1EWrapper.html#a790ccbe25033df73996f36a79ce2ce5a """ self.callback.update_AccountValue = [] print(f'Requesting portfolio data.') self.tws.reqAccountUpdates(subscribe=True, acctCode=self.account_name) # Retrieve the data from the callback when it arrives. while True: if len(self.callback.update_AccountValue) > 0: data = pd.DataFrame( self.callback.update_AccountValue, columns=['key', 'value', 'currency', 'accountName']) break print(f'Waiting for portfolio data.') time.sleep(2) # Unsubscribe. self.tws.reqAccountUpdates(subscribe=False, acctCode=self.account_name) self.callback.update_AccountValue = [] return data def request_tick_data(self, ticker, max_num_tries=10): """ Requests current tick data for the given ticker. """ # Create the contract to request tick data for. contract_info = self.create.create_contract(ticker, 'STK', 'SMART', 'USD') tickerId = 1004 self.callback.tick_Price = [] # Request the data. # Take a snapshot, so we don't continue to get data. self.tws.reqMktData(tickerId=tickerId, contract=contract_info, genericTickList="", snapshot=True) # Retrieve the data from the callback when it arrives. received_data = False num_tries = 0 while num_tries < max_num_tries: if len(self.callback.tick_Price) > 0: data = pd.DataFrame( self.callback.tick_Price, columns=['tickerId', 'field', 'price', 'canAutoExecute']) data["Type"] = data["field"].map(IB.tick_type) print(data) if 'LAST_PRICE' in data['Type'].values: # We have the information we need. break if 'CLOSE_PRICE' in data['Type'].values: if not received_data: # We have the close price. Wait a little longer to see if we get the last price. received_data = True time.sleep(2) continue break print( f'Waiting for tick data for ticker {ticker}, with ID {tickerId}, after try {num_tries} of {max_num_tries}.' ) num_tries += 1 time.sleep(2) self.tws.cancelMktData(tickerId) # Attach the type of each row, based on the field. # data["Type"] = data["field"].map(IB.tick_type) # Reset the tick_Price data. self.callback.tick_Price = [] if not received_data: data = None return data def request_tick_data_repeat(self, ticker, max_num_tries): """ Wrapper around request_tick_data, that makes a given number of attempts. On each attempt, a new connection is established. """ num_tries = 0 while num_tries < max_num_tries: data = self.request_tick_data(ticker) if data is not None: break num_tries += 1 return data def request_historical_data(self, ticker: str, num_days: int, last_date: datetime = None): """ https://interactivebrokers.github.io/tws-api/historical_bars.html http://interactivebrokers.github.io/tws-api/classIBApi_1_1EClient.html#aad87a15294377608e59aec1d87420594 """ # Create the contract to request tick data for. contract_info = self.create.create_contract(ticker, 'STK', 'SMART', 'USD') tickerId = 1004 self.callback.historical_Data = [] # Example: To get 3 days ending on 1/7/2013, use the last second of 1/7/2013 as the endDateTime, # and 3D as the durationStr. data_endtime = '' if last_date is None else last_date.strftime( "%Y%m%d %H:%M:%S") # Request the data. # reqHistoricalData(self, tickerId, contract, endDateTime, durationStr, barSizeSetting, whatToShow, useRTH, formatDate): self.tws.reqHistoricalData( tickerId=tickerId, contract=contract_info, endDateTime= data_endtime, # The request's end date and time (the empty string indicates current present moment). durationStr=f"{num_days} D", barSizeSetting="1 day", whatToShow= "TRADES", # Hopefully this will be a bar summarising the trades... useRTH= 1, # "Regular trading hours". For some reason, 1 chops of the most recent day. formatDate= 1, # dates applying to bars returned in the format: yyyymmdd{space}{space}hh:mm:dd #keepUpToDate=False ) # Retrieve the data from the callback when it arrives. while True: # I think the last row has "finished" in the "date" column, to indicate all data is retrieved. if len( self.callback.historical_Data ) > 0 and 'finished' in self.callback.historical_Data[-1][1]: print(self.callback.historical_Data) data = pd.DataFrame( self.callback. historical_Data[:-1], # Removed "finished" row. columns=[ "reqId", "date", "open", "high", "low", "close", "volume", "count", "WAP", "hasGaps" ]) break print(f'Waiting for historical data with ID {tickerId}.') time.sleep(2) # Reset the historical_Data data. self.callback.historical_Data = [] data['date'] = pd.to_datetime(data['date']) data.set_index('date') # print(data.to_string()) return data[[ 'date', 'open', 'high', 'low', 'close', 'volume', 'count' ]] def close(self): self.tws.eDisconnect()
def receiveFA(cls, faDataType, xml): return cls.FINANCIAL_ADVISOR + " " + EClientSocket.faMsgTypeName(faDataType) + " " + xml
def receiveFA(cls, faDataType, xml): """ generated source for method receiveFA """ return cls.FINANCIAL_ADVISOR + " " + EClientSocket.faMsgTypeName(faDataType) + " " + xml
class Wrapper(EWrapper): orders = None order_ids = [0] parameters = None connection = None _storage = None def __init__(self, storage): # Variable initialization #self.orders = orders.OrderBook() #self.price_log = data.PriceLog() #self.parameters = parameters self._storage = storage self.connection = EClientSocket(self) self.connection.eConnect('localhost', 7496, 0) # host, port, clientId tick_id = 1 symbol = "SLV" contract = self.makeContract(symbol) self.connection.reqMktData(tick_id, contract, [], False) def makeContract(self, symbol): contract = Contract() contract.m_symbol = symbol contract.m_secType = 'STK' contract.m_exchange = 'SMART' contract.m_primaryExch = 'SMART' contract.m_currency = 'USD' contract.m_localSymbol = symbol return contract def tickPrice(self, tickerId, field, price, canAutoExecute): # 1 = bid # 2 = ask # 4 = last # 6 = high # 7 = low # 9 = close priceLog = {} side = "" if field == 2: self._storage.log_price("ask", price) elif field == 1: self._storage.log_price("bid", price) if side != "": print(side, price) def openOrder(self, orderId, contract, order, state): orderId = order.m_orderId symbol = contract.m_symbol qty = order.m_totalQuantity price = order.m_lmtPrice action = order.m_action self.orders.add(orderId, symbol, qty, price, action) order = [orderId, symbol, qty, price, action] print("--> Open order:{} Status:{} Warning:{}".format(order, state.m_status, state.m_warningText)) def error(self, id=None, errorCode=None, errorMsg=None): if errorCode == 2104: print("--> {}".format(errorMsg)) elif errorCode == 502: raise Exception(errorMsg) else: showmessage('error', vars()) def nextValidId(self, orderId): self.order_ids.append(orderId) def connectionClosed(self): """ Something broke, connection lost. """ print("--> Connection closed, exiting...") sys.exit(0) def connected(self): """ Returns True of connected to TraderWorkstation, otherwise False. """ return self.connection.m_connected def tickSize(self, tickerId, field, size): pass #showmessage('tickSize', vars()) def tickGeneric(self, tickerId, tickType, value): pass #showmessage('tickGeneric', vars()) def tickString(self, tickerId, tickType, value): pass #showmessage('tickString', vars()) def tickEFP(self, tickerId, tickType, basisPoints, formattedBasisPoints, impliedFuture, holdDays, futureExpiry, dividendImpact, dividendsToExpiry): showmessage('tickEFP', vars()) def tickOptionComputation(self, tickerId, field, impliedVolatility, delta): showmessage('tickOptionComputation', vars()) def orderStatus(self, orderId, status, filled, remaining, avgFillPrice, permId, parentId, lastFillPrice, clientId, whyHeId): pass #showmessage('orderStatus', vars()) def openOrderEnd(self): showmessage('openOrderEnd', vars()) def updateAccountValue(self, key, value, currency, accountName): showmessage('updateAccountValue', vars()) def updatePortfolio(self, contract, position, marketPrice, marketValue, averageCost, unrealizedPNL, realizedPNL, accountName): showmessage('updatePortfolio', vars()) def updateAccountTime(self, timeStamp): showmessage('updateAccountTime', vars()) def accountDownloadEnd(self, accountName): showmessage('accountDownloadEnd', vars()) def contractDetails(self, contractDetails): showmessage('contractDetails', vars()) def bondContractDetails(self, contractDetails): showmessage('bondContractDetails', vars()) def contractDetailsEnd(self, reqId): showmessage('contractDetailsEnd', vars()) def execDetails(self, orderId, contract, execution): showmessage('execDetails', vars()) def execDetailsEnd(self, reqId): showmessage('execDetailsEnd', vars()) def error_0(self, strval): showmessage('error_0', vars()) def error_1(self, strval): showmessage('error_1', vars()) def updateMktDepth(self, tickerId, position, operation, side, price, size): showmessage('updateMktDepth', vars()) def updateMktDepthL2(self, tickerId, position, marketMaker, operation, side, price, size): showmessage('updateMktDepthL2', vars()) def updateNewsBulletin(self, msgId, msgType, message, origExchange): showmessage('updateNewsBulletin', vars()) def managedAccounts(self, accountsList): pass #showmessage('managedAccounts', vars()) def receiveFA(self, faDataType, xml): showmessage('receiveFA', vars()) def historicalData(self, reqId, date, open, high, low, close, volume, count, WAP, hasGaps): showmessage('historicalData', vars()) def scannerParameters(self, xml): showmessage('scannerParameters', vars()) def scannerData(self, reqId, rank, contractDetails, distance, benchmark, projection): showmessage('scannerData', vars()) def scannerDataEnd(self, reqId): showmessage('scannerDataEnd', vars()) def realtimeBar(self, reqId, time, open, high, low, close, volume, wap, count): showmessage('realtimeBar', vars()) def currentTime(self, time): showmessage('currentTime', vars()) def fundamentalData(self, reqId, data): showmessage('fundamentalData', vars()) def deltaNeutralValidation(self, reqId, underComp): showmessage('deltaNeutralValidation', vars()) def tickSnapshotEnd(self, reqId): showmessage('tickSnapshotEnd', vars()) def marketDataType(self, reqId, marketDataType): showmessage('marketDataType', vars()) def commissionReport(self, commissionReport): showmessage('commissionReport', vars())
class IBBroker(BaseBroker): def __init__(self): basicConfig() # These two variables are initialized in Connect method self._connection = None self._wrapper = None self._request_id = 0 def _get_next_request_id(self): self._request_id += 1 return self._request_id def get_security(self, symbol): contract = IBSecurity() contract.Symbol = symbol contract.symbol_id = 0 contract.Currency = 'USD' return contract def _get_next_valid_order_id(self): """ You must assign a unique order ID to each order you place. IB's servers keep track of the next available order ID you can use; this function requests that value from IB's servers, waits until IB sends a response, then returns the ID. """ last_time = self._wrapper._time_received_next_valid_order_id self._connection.reqIds(1) # Wait until IB sends the next valid ID while last_time == self._wrapper._time_received_next_valid_order_id: very_short_sleep() return self._wrapper._next_valid_order_id def _request_current_time(self): self._connection.reqCurrentTime() def connect(self): self._wrapper = ReferenceWrapper() self._connection = EClientSocket(self._wrapper) self._connection.eConnect(IB_HOST, IB_PORT, IB_CLIENT_ID) def disconnect(self): if self._connection.isConnected(): self._connection.eDisconnect() def send_pre_trade(self, trade_info): # trade info is fa profile self._connection.requestFA(self._connection.PROFILES) self._connection.replaceFA(self._connection.PROFILES, trade_info) def send_order(self, order): order.__class__ = IBOrder # casting to IBOrder order.prepare_IB_order() order_id = self._get_next_valid_order_id() contract = self.get_security(order.Symbol) #order.m_transmit = True # forces IB to transmit order straight away self._connection.placeOrder(order_id, contract, order) # places order order.Status = Order.StatusChoice.Sent.value # order status is set to SENT order.Order_Id = order_id # sets broker specific ID while not self._wrapper.isOpeningOfOrdersFinished(order_id): err = self._wrapper.getError(order_id) if err is not None: raise Exception(err) very_short_sleep() #if(self._wrapper.isError(id)): # raise Exception(self.wrapper.isError(id)) def update_orders(self, orders): requestId = self._get_next_request_id() exf = ExecutionFilter() distribution = {} self._connection.reqExecutions(requestId, exf) # while not self._wrapper.isExecutionRequestFinished(requestId): err = self._wrapper.getError(requestId) if err is not None: raise Exception(err) very_short_sleep() executions = self._wrapper.getExecutions(requestId) for order in orders: price = 0 shares = 0 if executions is not None: for execution in executions: if execution.m_shares > 0 and execution.m_orderId == order.Order_Id and not execution.m_acctNumber.startswith('DF'): price = execution.m_price if order.Symbol not in distribution: distribution[order.Symbol] = {} if execution.m_acctNumber not in distribution[order.Symbol]: distribution[order.Symbol][execution.m_acctNumber] = 0 distribution[order.Symbol][execution.m_acctNumber] += execution.m_shares shares += execution.m_shares if price != 0: order.setFills(price, shares) return distribution def get_account_info(self, broker_account): requestId = self._get_next_request_id() self._connection.reqAccountSummary(requestId, 'All', 'AccountType,TotalCashValue') while not self._wrapper.isExecutionRequestFinished(requestId): err = self._wrapper.getError(requestId) max_resp = self._wrapper.getMaxRequestFailureError() if err is not None: raise Exception(err) if max_resp: raise Exception("Maximum number of account summary requests exceeded") very_short_sleep() return self._wrapper.getAccountInfo(broker_account.ib_account)
class Wrapper(EWrapper): orders = None order_ids = [0] parameters = None connection = None def __init__(self, parameters): # Variable initialization #self.orders = orders.OrderBook() #self.price_log = data.PriceLog() #self.parameters = parameters self.connection = EClientSocket(self) self.connection.eConnect('localhost', 7496, 0) # host, port, clientId tick_id = 1 symbol = "SLV" contract = self.makeContract(symbol) self.connection.reqMktData(tick_id, contract, [], False) def makeContract(self, symbol): contract = Contract() contract.m_symbol = symbol contract.m_secType = 'STK' contract.m_exchange = 'SMART' contract.m_primaryExch = 'SMART' contract.m_currency = 'USD' contract.m_localSymbol = symbol return contract def tickPrice(self, tickerId, field, price, canAutoExecute): #showmessage('tickPrice', vars()) # 1 = bid # 2 = ask # 4 = last # 6 = high # 7 = low # 9 = close priceLog = {} side = "" if field == 2: print "a%0.2f " % price elif field == 1: print "b%0.2f " % price if side != "": print side, price def openOrder(self, orderId, contract, order, state): orderId = order.m_orderId symbol = contract.m_symbol qty = order.m_totalQuantity price = order.m_lmtPrice action = order.m_action self.orders.add(orderId, symbol, qty, price, action) order = [orderId, symbol, qty, price, action] print "--> Open order:%s Status:%s Warning:%s" % (order, state.m_status, state.m_warningText) def error(self, id=None, errorCode=None, errorMsg=None): if errorCode == 2104: print "--> %s" % errorMsg else: showmessage('error', vars()) def nextValidId(self, orderId): self.order_ids.append(orderId) def connectionClosed(self): print "--> Connection closed, exiting..." sys.exit(0) def tickSize(self, tickerId, field, size): pass #showmessage('tickSize', vars()) def tickGeneric(self, tickerId, tickType, value): pass #showmessage('tickGeneric', vars()) def tickString(self, tickerId, tickType, value): pass #showmessage('tickString', vars()) def tickEFP(self, tickerId, tickType, basisPoints, formattedBasisPoints, impliedFuture, holdDays, futureExpiry, dividendImpact, dividendsToExpiry): showmessage('tickEFP', vars()) def tickOptionComputation(self, tickerId, field, impliedVolatility, delta): showmessage('tickOptionComputation', vars()) def orderStatus(self, orderId, status, filled, remaining, avgFillPrice, permId, parentId, lastFillPrice, clientId, whyHeId): pass #showmessage('orderStatus', vars()) def openOrderEnd(self): showmessage('openOrderEnd', vars()) def updateAccountValue(self, key, value, currency, accountName): showmessage('updateAccountValue', vars()) def updatePortfolio(self, contract, position, marketPrice, marketValue, averageCost, unrealizedPNL, realizedPNL, accountName): showmessage('updatePortfolio', vars()) def updateAccountTime(self, timeStamp): showmessage('updateAccountTime', vars()) def accountDownloadEnd(self, accountName): showmessage('accountDownloadEnd', vars()) def contractDetails(self, contractDetails): showmessage('contractDetails', vars()) def bondContractDetails(self, contractDetails): showmessage('bondContractDetails', vars()) def contractDetailsEnd(self, reqId): showmessage('contractDetailsEnd', vars()) def execDetails(self, orderId, contract, execution): showmessage('execDetails', vars()) def execDetailsEnd(self, reqId): showmessage('execDetailsEnd', vars()) def error_0(self, strval): showmessage('error_0', vars()) def error_1(self, strval): showmessage('error_1', vars()) def updateMktDepth(self, tickerId, position, operation, side, price, size): showmessage('updateMktDepth', vars()) def updateMktDepthL2(self, tickerId, position, marketMaker, operation, side, price, size): showmessage('updateMktDepthL2', vars()) def updateNewsBulletin(self, msgId, msgType, message, origExchange): showmessage('updateNewsBulletin', vars()) def managedAccounts(self, accountsList): pass #showmessage('managedAccounts', vars()) def receiveFA(self, faDataType, xml): showmessage('receiveFA', vars()) def historicalData(self, reqId, date, open, high, low, close, volume, count, WAP, hasGaps): showmessage('historicalData', vars()) def scannerParameters(self, xml): showmessage('scannerParameters', vars()) def scannerData(self, reqId, rank, contractDetails, distance, benchmark, projection): showmessage('scannerData', vars()) def scannerDataEnd(self, reqId): showmessage('scannerDataEnd', vars()) def realtimeBar(self, reqId, time, open, high, low, close, volume, wap, count): showmessage('realtimeBar', vars()) def currentTime(self, time): showmessage('currentTime', vars()) def fundamentalData(self, reqId, data): showmessage('fundamentalData', vars()) def deltaNeutralValidation(self, reqId, underComp): showmessage('deltaNeutralValidation', vars()) def tickSnapshotEnd(self, reqId): showmessage('tickSnapshotEnd', vars()) def marketDataType(self, reqId, marketDataType): showmessage('marketDataType', vars()) def commissionReport(self, commissionReport): showmessage('commissionReport', vars())
# -*- coding: utf-8 -*- """ Interaction Brokers Connection Connection Demo """ from IBWrapper import IBWrapper from ib.ext.EClientSocket import EClientSocket accountName = "DU230008" callback = IBWrapper() # Instantiate IBWrapper. callback tws = EClientSocket(callback) # Instantiate EClientSocket and return data to callback host = "localhost" # For local host port = 7497 clientId = 5 tws.eConnect(host, port, clientId) # Connect to TWS tws.eDisconnect()
from IBWrapper import IBWrapper, contract from ib.ext.EClientSocket import EClientSocket import pandas as pd from datetime import datetime import os import numpy as np import pandas as pd from datetime import datetime, timedelta from pandas.lib import Timestamp import pytz #import datetime import dateutil.relativedelta import random callback = IBWrapper() tws = EClientSocket(callback) host = "" port = 7497 clientId = 100 #connect tws.eConnect(host, port, clientId) print("connected") time.sleep(2) #read function from package into a variable create = contract() #callback function to fetch data into specified similar named columns callback.initiate_variables() #fetch from TWS 1st ticker
class IBClient(object): """IB Socket client""" def __init__(self, client_name='IB', host='localhost', port=7496, client_id=0): """Constructor""" self.client_name = client_name self.host = host # host IP address in a string; e.g. '127.0.0.1', 'localhost' self.port = port # socket port; TWS default value: 7496; TWS demo account default value: 7497 self.client_id = client_id # socket client id self.connected = False # status of the socket connection self.tickerId = 0 # known as ticker ID or request ID self.ipc_msg_dict = {} # key: ticker ID or request ID; value: request and response objects; response objects ususally carrys data, Events, and Status self.order_id = 0 # current available order ID self.order_dict = {} # key: ticker ID or request ID; value: request and response objects; response objects ususally carrys data, Events, and Status self.context = None # key: ticker ID or request ID; value: request and response objects; response objects ususally carrys data, Events, and Status self.data = None self.wrapper = IBMsgWrapper(self) # the instance with IB message callback methods self.connection = EClientSocket(self.wrapper) # low layer socket client # TWS's data connection status self.hmdf_status_dict = {} for farm in IB_FARM_NAME_LS: self.hmdf_status_dict[farm] = 'unknown' # EVENTS self.conn_down_event = Event() # sock connection event self.mdf_conn_event = Event() # market data connection event self.hdf_conn_event = Event() # hist data connection event self.order_event = Event() self.account_event = Event() self.get_order_event = Event() # LOCKER self.req_id_locker = threading.Lock() # CONSTANT VALUES self.PRICE_DF_HEADER1 = ['time', 'open', 'high', 'low', 'close', 'volume'] self.PRICE_DF_HEADER2 = ['symbol', 'time', 'open', 'high', 'low', 'close', 'volume'] def connect(self): """ Connect to socket host, e.g. TWS """ self.connection.eConnect(self.host, self.port, self.client_id) timeout = 5. count = 0. while not self.connected and count < timeout: count += 0.05 sleep(0.05) if self.connected: self.order_id = self.connection.reqIds(-1) if self.context is not None: # TODO: may need to move this to a thread or at user layer self.enable_account_info_update() else: print('failed to connect.') return self.connected def close(self): """ disconnect from IB host """ self.disconnect() def disconnect(self): """ disconnect from IB host """ if self.context is not None: self.disable_account_info_update() self.connection.eDisconnect() self.connected = False def register_strategy(self, context, data): """ TBA """ self.context = context self.data = data def __get_new_request_id(self): '''' genew request ID (ticker ID) in a thread safe way ''' self.req_id_locker.acquire() self.tickerId += 1 __id = self.tickerId self.req_id_locker.release() return __id # # Tick Data Methods # def request_tick_data(self, contract): """ Subscribe tick data for a specified contract Args: contract: a legal IBPY Contract object or a string for U.S. stock only Returns: tickerId: the ID of this request. this ID could be used to cancel request later. tick_data: a reference to the tick data dictionary which will be updated with latest quote. """ if isinstance(contract, Contract): pass elif isinstance(contract, str): contract = new_stock_contract(contract) else: raise TypeError("contract must be a contract object or string (for U.S. stocks only).") if not self.connected: raise RuntimeError('IB client is not connected to TWS') __id = self.__get_new_request_id() request = RequestDetails('reqMktData', 'Snapshot', contract) response = ResponseDetails() self.ipc_msg_dict[__id] = (request, response) # False - indicating request live quotes instead of a snapshot self.connection.reqMktData(__id, contract, '', False) return __id, self.ipc_msg_dict[__id][1].tick_data def get_tick_snapshot(self, contract, max_wait_time=5): """ Get a snapshot with default tick types and corresponding tick data for a given contract Note: 1) no generic ticks can be specified. 2) only return data fields which have changed within an 11 second interval. If it is necessary for an API client to receive a certain data field, it is better to subscribe to market data until that field has been returned and then cancel the market data request. Known Issues: 1) When called outside of market hours, get_tick_snapshot request could take more than 10 sec to reach the end (tickSnapshotEnd) 2) Need to check Issue#1 during market hours Args: contract: a legal IBPY Contract object or a string for U.S. stock only Returns: a copy of tick data dictionary Raises: None """ if isinstance(contract, Contract): pass elif isinstance(contract, str): contract = new_stock_contract(contract) else: raise TypeError("contract must be a contract object or string (for U.S. stocks only).") if not self.connected: raise RuntimeError('IB client is not connected to TWS') __id = self.__get_new_request_id() request = RequestDetails('reqMktData', 'Snapshot', contract) response = ResponseDetails() self.ipc_msg_dict[__id] = (request, response) # send reqMktData req # True - indicating request live quotes instead of a snapshot # # Important: # When set it to 'True', each regulatory snapshot made will incur a fee of 0.01 USD to the account. # This applies to both live and paper accounts. self.connection.reqMktData(__id, contract, '', True) response.event.wait(max_wait_time) if response.event.is_set(): # tickPrice() and tickeSize() may write after tickSnapshotEnd() completed. sleep(0.5) snapshot = copy(response.tick_data) # remove the from dict and free the memory response.event.clear() self.ipc_msg_dict.pop(__id) else: response.event.clear() self.ipc_msg_dict.pop(__id) raise RuntimeError('reqMktData (get_tick_snapshot) is timeout. max_wait_time=%d' % (max_wait_time)) return snapshot def cancel_tick_request(self, tickerId): """ Cancel tick data request for a given ticker ID (request ID) Args: tickerId: the ticker request to cancel Returns: None Raises: None """ if not self.connected: raise RuntimeError('IB client is not connected to TWS') # TODO: check if tickerID is in the list self.connection.cancelMktData(tickerId) self.ipc_msg_dict.pop(tickerId) return def request_realtime_price(self, contract, price_type='TRADES'): """ Get real-time price/volume for a specific contract, e.g. stocks, futures and option contracts. IB API support only 5 sec duration between two real-time bar (price) records. Args: contract: one IB contract instance price_type: 'TRADES', 'MIDPOINT', 'BID', 'ASK' Returns: tickerId: the request ID; it's also the key to get response msg from ipc_msg_dict realtime_price: a reference to the real-time price (OCHL) list which will be updated with latest price (OCHL) record. Raises: None """ if isinstance(contract, Contract): pass elif isinstance(contract, str): contract = new_stock_contract(contract) else: raise TypeError("contract must be a contract object or string (for U.S. stocks only).") if not self.connected: raise RuntimeError('IB client is not connected to TWS') if price_type not in ['TRADES', 'MIDPOINT', 'BID', 'ASK']: raise TypeError("Got incorrect price_type") __id = self.__get_new_request_id() request = RequestDetails('reqHistoricalData', price_type, contract) response = ResponseDetails() self.ipc_msg_dict[__id] = (request, response) # only 5 sec duration supported # price_type: 'TRADES', 'MIDPOINT', 'BID', 'ASK' # useRTH - set to True self.connection.reqRealTimeBars(__id, contract, 5, price_type, True) return __id, self.ipc_msg_dict[__id][1].rt_price def cancel_realtime_price(self, req_id): """ Cancel realtime price/volumne request. Args: req_id: the ticker ID (or request ID) Returns: None """ if not self.connected: raise RuntimeError('IB client is not connected to TWS') self.connection.cancelRealTimeBars(req_id) # remove request/response data from ipc_msg_dict self.ipc_msg_dict.pop(req_id) return # # Historical Data Methods # def get_price_history(self, contract, ts_end, duration='1 M', frequency='daily', max_wait_time=30): """ Get price/volumne history for a specific contract, e.g. stocks, futures and option ocntract. Args: contract: one IB contract instance ts_end: a string in '%Y%m%d' or '%Y%m%d %H:%M:%S' format duration: string X S Seconds X D Day X W Week X M Month X Y Year frequency: {‘daily’, ‘minute’}, optional; Resolution of the data to be returned. max_wait_time: int; max num of sec to wait after calling reqHistoricalData Returns: pandas Panel/DataFrame/Series – The pricing data that was requested. Open High Low Close Volume Date 2017-12-15 6.96 6.96 6.86 6.90 366523000 2017-12-18 6.88 7.02 6.87 6.98 303664000 2017-12-19 7.00 7.02 6.98 7.01 299342000 Raises: None """ if not self.connected: raise RuntimeError('IB client is not connected to TWS') if isinstance(contract, Contract): pass elif isinstance(contract, str): contract = new_stock_contract(contract) else: raise TypeError("contract must be a Contract object or string") if frequency == 'daily': bar_size = '1 day' elif frequency == 'minute': bar_size = '1 min' elif frequency == 'second': bar_size = '1 sec' elif frequency == '5 seconds': bar_size = '5 secs' else: raise ValueError("get_price_history: incorrect frequency value") if len(ts_end) == 8 or len(ts_end) == 17: if len(ts_end) == 8: ts_end = ts_end + ' 23:59:59' else: print('get_price_history: incorrect ts_end format') return __id = self.__get_new_request_id() request = RequestDetails('reqHistoricalData', '', contract) response = ResponseDetails() self.ipc_msg_dict[__id] = (request, response) self.connection.reqHistoricalData(tickerId=__id, contract=contract, endDateTime=ts_end, durationStr=duration, barSizeSetting=bar_size, whatToShow='TRADES', useRTH=0, formatDate=1) df = None response.event.wait(max_wait_time) if response.event.is_set(): df = pd.DataFrame(response.price_hist, columns=self.PRICE_DF_HEADER1) # clean up the time format date = df['time'][0] if len(date) == 8: df['time'] = pd.to_datetime(df['time'], format='%Y%m%d') elif len(date) == 18: # len('20161020 23:46:00') --> 2 Spaces!!!!! # adj_date = datetime.strptime(date, "%Y%m%d %H:%M:%S") df['time'] = pd.to_datetime(df['time'], format="%Y%m%d %H:%M:%S") else: # adj_date = datetime.strptime(date, "%Y%m%d %H:%M:%S") df['time'] = pd.to_datetime(df['time'], format="%Y%m%d %H:%M:%S") # TODO: check for timezone # exchange = request.contract.m_exchange # server_timezone = pytz.timezone("Asia/Shanghai") # timezone where the server runs # mkt_timezone = pytz.timezone(IBEXCHANGE.get_timezone(exchange)) # Get Exchange's timezone # adj_date = server_timezone.localize(adj_date).astimezone( # mkt_timezone) # covert server time to Exchange's time # adj_date = adj_date.strftime("%Y%m%d %H:%M:%S") # from datetime to string df = df.set_index('time') # remove the from dict and free the memory response.event.clear() self.ipc_msg_dict.pop(__id) else: self.ipc_msg_dict.pop(__id) print('reqHistoricalData is timeout.') raise RuntimeError('reqHistoricalData is timeout.') return df def get_stock_price_history(self, security_list, ts_end, duration='1 M', frequency='daily', max_wait_time=30): """Get price/volumne history for a list of stocks. Args: security_list: a list of security symbols, .e.g. ['IBM', 'DATA'] endDateTime: a string in '%Y%m%d' or '%Y%m%d %H:%M:%S' format durationStr: see IB API doc. frequency: {‘daily’, ‘minute’}, optional; Resolution of the data to be returned. max_wait_time: int; max num of sec to wait after calling reqHistoricalData Returns: pandas Panel/DataFrame/Series – The pricing data that was requested. Raises: None """ if not self.connected: raise RuntimeError('IB client is not connected to TWS') num_secs = len(security_list) if num_secs <= 0: return if frequency == 'daily': bar_size = '1 day' elif frequency == 'minute': bar_size = '1 min' elif frequency == 'second': bar_size = '1 sec' elif frequency == '5 seconds': bar_size = '5 secs' else: print('get_stock_price_history: incorrect frequency') return if len(ts_end) == 8 or len(ts_end) == 17: if len(ts_end) == 8: ts_end = ts_end + ' 23:59:59' else: print('get_stock_price_history: incorrect ts_end format') return # ['Symbol', 'Date', 'Open', 'High', 'Low', 'Close', 'Volume'] df = pd.DataFrame(columns=self.PRICE_DF_HEADER2) for sec in security_list: __id = self.__get_new_request_id() contract = new_stock_contract(sec) request = RequestDetails('reqHistoricalData', '', contract) response = ResponseDetails() self.ipc_msg_dict[__id] = (request, response) self.connection.reqHistoricalData(tickerId=__id, contract=contract, endDateTime=ts_end, durationStr=duration, barSizeSetting=bar_size, whatToShow='TRADES', useRTH=0, formatDate=1) response.event.wait(max_wait_time) if response.event.is_set(): df_tmp = pd.DataFrame(response.price_hist, columns=self.PRICE_DF_HEADER1) # clean up the time format date = df_tmp['time'][0] if len(date) == 8: df_tmp['time'] = pd.to_datetime(df_tmp['time'], format='%Y%m%d') elif len(date) == 18: # len('20161020 23:46:00') --> 2 Spaces!!!!! # adj_date = datetime.strptime(date, "%Y%m%d %H:%M:%S") df_tmp['time'] = pd.to_datetime(df_tmp['time'], format="%Y%m%d %H:%M:%S") else: # adj_date = datetime.strptime(date, "%Y%m%d %H:%M:%S") df_tmp['time'] = pd.to_datetime(df_tmp['time'], format="%Y%m%d %H:%M:%S") # TODO: check for timezone # exchange = request.contract.m_exchange # server_timezone = pytz.timezone("Asia/Shanghai") # timezone where the server runs # mkt_timezone = pytz.timezone(IBEXCHANGE.get_timezone(exchange)) # Get Exchange's timezone # adj_date = server_timezone.localize(adj_date).astimezone( # mkt_timezone) # covert server time to Exchange's time # adj_date = adj_date.strftime("%Y%m%d %H:%M:%S") # from datetime to string df_tmp['symbol'] = pd.DataFrame([sec] * len(df_tmp)) df = df.append(df_tmp) # remove the from dict and free the memory self.ipc_msg_dict.pop(__id) response.event.clear() else: self.ipc_msg_dict.pop(__id) raise RuntimeError('reqHistoricalData is timeout.') return df def get_contract_price_history(self, contract, ts_end, duration='1 M', frequency='daily', max_wait_time=30): # Same function as get_price_history return self.get_price_history(self, contract, ts_end, duration, frequency, max_wait_time) # # Placing/Changing/Canceling Order Methods # def order_amount(self, contract, amount, style=MarketOrder()): ''' Place an order. Order X units of security Y. Warning: only mkt order and limited order work; calling stoploss/stoplimited order will result in IB disconnection. :param contract: A IB Contract object. :param amount: The integer amount of shares. Positive means buy, negative means sell. :param style: :return: ''' if amount == 0: return -1 elif amount > 0: action = 'BUY' else: action = 'SELL' if not self.connected: raise RuntimeError('IB client is not connected to TWS') if not isinstance(contract, Contract): raise TypeError("contract must be a contract object") # request next valid order ID from IB host; this request will update self.order_id self.connection.reqIds(-1) # note: input param is always ignored; sleep(0.05) order = Order() order.m_orderId = self.order_id order.m_client_id = self.client_id order.m_action = action order.m_totalQuantity = abs(amount) order.m_orderType = style.order_type if style.limit_price is not None: order.m_lmtPrice = style.limit_price if style.stop_price is not None: order.m_auxPrice = style.stop_price order.m_overridePercentageConstraints = True # override TWS order size constraints # place order self.connection.placeOrder(self.order_id, contract, order) # TODO: wait for returns from orderStatus return self.order_id def combo_order_amount(self, contract, amount, style=MarketOrder()): ''' Place an order :param contract: A security object. :param amount: The integer amount of shares. Positive means buy, negative means sell. :param style: :return: ''' if not self.connected: raise RuntimeError('IB client is not connected to TWS') if amount == 0: return -1 elif amount > 0: action = 'BUY' else: action = 'SELL' if isinstance(contract, Contract): if len(contract.m_comboLegs) == 0: raise TypeError("contract must contains combo legs") else: raise TypeError("contract must be a contract object") # request next valid order ID from IB host; this request will update self.order_id self.connection.reqIds(-1) # note: input param is always ignored; sleep(0.05) order = Order() order.m_orderId = self.order_id order.m_client_id = self.client_id order.m_action = action order.m_totalQuantity = abs(amount) order.m_orderType = style.order_type if style.limit_price is not None: order.m_lmtPrice = style.limit_price if style.stop_price is not None: order.m_auxPrice = style.stop_price order.m_overridePercentageConstraints = True # override TWS order size constraints ''' # Advanced configuration. Not tested yet. if style.is_combo_order: if style.non_guaranteed: tag = TagValue() tag.m_tag = "NonGuaranteed" tag.m_value = "1" order.m_smartComboRoutingParams = [tag] ''' self.connection.placeOrder(self.order_id, contract, order) return self.order_id def order_value(self, contract, value, style): ''' Reserve for future implementation :param contract: :param value: :param style: :return: ''' pass def order_target(self, contract, amount, style): ''' Places an order to adjust a position to a target number of shares. :param contract: :param value: :param style: :return: ''' pass def order_target_value(self, contract, amount, style): ''' Places an order to adjust a position to a target value. :param contract: :param value: :param style: :return: ''' pass def modify_order(self, order_id, contract, amount, style=MarketOrder()): ''' Change amount or order type (including limited price for limtied orders) for a existing order specified by order_id :param order_id: a existing order's order_id :param contract: A IB Contract object. supposed to be the same with the order-to-be-modified. :param amount: The integer amount of shares. Positive means buy, negative means sell. :param style: market order or limited order :return: the existing order's order_id (same as the input) ''' if amount == 0: return -1 elif amount > 0: action = 'BUY' else: action = 'SELL' if not self.connected: raise RuntimeError('IB client is not connected to TWS') if not isinstance(contract, Contract): raise TypeError("contract must be a contract object") order = Order() order.m_orderId = order_id order.m_client_id = self.client_id order.m_action = action order.m_totalQuantity = abs(amount) order.m_orderType = style.order_type if style.limit_price is not None: order.m_lmtPrice = style.limit_price if style.stop_price is not None: order.m_auxPrice = style.stop_price order.m_overridePercentageConstraints = True # override TWS order size constraints # place order self.connection.placeOrder(self.order_id, contract, order) # TODO: wait for returns from orderStatus return self.order_id def cancel_order(self, order): ''' Attempts to cancel the specified order. Cancel is attempted asynchronously. :param order: Can be the order_id as a string or the order object. :return: None ''' if not self.connected: raise RuntimeError('IB client is not connected to TWS') if isinstance(order, int): order_id = order elif isinstance(order, Order): order_id = order.m_orderId else: raise TypeError("order must be a order_id (int) or order object") self.connection.cancelOrder(order_id) def get_open_orders(self): ''' Attempts to get all open orders. :param :return: None ''' if not self.connected: raise RuntimeError('IB client is not connected to TWS') if 'reqOpenOrders' not in self.ipc_msg_dict: request = RequestDetails('reqOpenOrders', '', '') response = ResponseDetails() self.ipc_msg_dict['reqOpenOrders'] = (request, response) else: response = self.ipc_msg_dict['reqOpenOrders'][1] # self.connection.reqOpenOrders() # self.reqAutoOpenOrders(True) self.connection.reqAllOpenOrders() max_wait_time = 3. self.get_order_event.wait(max_wait_time) if self.get_order_event.is_set(): # snapshot = copy(response.tick_snapshot) # self.ipc_msg_dict.pop(self.tickerId) self.get_order_event.clear() else: # self.ipc_msg_dict.pop(self.tickerId) raise RuntimeError('get_open_orders is timeout.') return # # Account Info Methods # def enable_account_info_update(self): ''' Turn on auto account update, meaning IB socket host will push account info to IB socket client. updateAccountTime() updateAccountValue() updatePortfolio() ''' if not self.connected: raise RuntimeError('IB client is not connected to TWS') # TODO: check self.IB_acct_id before using it # request IB host (e.g. TWS) push account info to IB client (socket client) self.connection.reqAccountUpdates(True, self.context.account.account_id) return def disable_account_info_update(self): ''' Turn off auto account update, meaning IB socket host will stop pushing account info to IB socket client. ''' if not self.connected: raise RuntimeError('IB client is not connected to TWS') # TODO: check self.IB_acct_id before using it # stop IB host (e.g. TWS) to push account info to IB client (socket client) self.connection.reqAccountUpdates(False, self.context.account.account_id) return # # Fundamental Data Methods # def get_financial_statements(self, symbol, max_wait_time=20): ''' Get a company's financial statements :param: symbol: stock symbol string, e.g. 'IBM'; or a IB contract object :return: a string of financial statements ''' if not self.connected: raise RuntimeError('IB client is not connected to TWS') if isinstance(symbol, Contract): contract = symbol elif isinstance(symbol, str): contract = new_stock_contract(symbol) else: raise TypeError("contract must be a contract object or string (for U.S. stocks only).") __id = self.__get_new_request_id() request = RequestDetails('reqFundamentalData', 'ReportsFinStatements', contract) response = ResponseDetails() self.ipc_msg_dict[__id] = (request, response) self.connection.reqFundamentalData(__id, contract, 'ReportsFinStatements') response.event.wait(max_wait_time) raw_xml = None if response.event.is_set(): if response.status == ResponseDetails.STATUS_FINISHED: # covert from xml to dest. format raw_xml = copy(response.fundamental_data) else: pass # raise RuntimeError('get_financial_statements: reqFundamentalData got error. Security=%s Reason:%s' % (symbol, response.error_msg)) else: # Timeout pass # ('get_financial_statements: reqFundamentalData is timeout. Security=%s' % symbol) status = response.status self.ipc_msg_dict.pop(__id) return status, raw_xml def get_company_ownership(self, symbol, max_wait_time=60.0 * 5): ''' Get a company's ownership report :param: symbol: stock symbol string, e.g. 'IBM' max_wait_time: max number of seconds to wait before raise timeout :return: a string of ownership report ''' if not self.connected: raise RuntimeError('IB client is not connected to TWS') if isinstance(symbol, Contract): contract = symbol elif isinstance(symbol, str): # For US stock only contract = new_stock_contract(symbol) else: raise TypeError("contract must be a contract object or string (for U.S. stocks only).") __id = self.__get_new_request_id() request = RequestDetails('reqFundamentalData', 'ReportsOwnership', contract) response = ResponseDetails() self.ipc_msg_dict[__id] = (request, response) self.connection.reqFundamentalData(__id, contract, 'ReportsOwnership') response.event.wait(max_wait_time) report = None if response.event.is_set(): if response.status == ResponseDetails.STATUS_FINISHED: # covert from xml to dest. format report = parse_ownership_report(response.fundamental_data) else: pass # ('get_company_ownership: reqFundamentalData got error. Security=%s Reason:%s' % (symbol, response.error_msg)) else: pass # ('get_company_ownership: reqFundamentalData is timeout. Security=%s' % symbol) status = response.status self.ipc_msg_dict.pop(__id) return status, report def get_analyst_estimates(self, symbol, max_wait_time=20): ''' Get analyst estimates report for a company :param: symbol: stock symbol string, e.g. 'IBM'; or a IB contract object :return: a string of financial statements ''' if not self.connected: raise RuntimeError('IB client is not connected to TWS') if isinstance(symbol, Contract): contract = symbol elif isinstance(symbol, str): contract = new_stock_contract(symbol) else: raise TypeError("contract must be a contract object or string (for U.S. stocks only).") __id = self.__get_new_request_id() request = RequestDetails('reqFundamentalData', 'RESC-Analyst Estimates', contract) response = ResponseDetails() self.ipc_msg_dict[__id] = (request, response) self.connection.reqFundamentalData(__id, contract, 'RESC') response.event.wait(max_wait_time) report = None if response.event.is_set(): if response.status == ResponseDetails.STATUS_FINISHED: # covert from xml to dest. format report = parse_analyst_estimates(response.fundamental_data) else: pass # ('get_analyst_estimates: reqFundamentalData got error. Security=%s Reason:%s' % (symbol, response.error_msg)) else: pass # ('get_analyst_estimates: reqFundamentalData is timeout. Security=%s' % symbol) status = response.status self.ipc_msg_dict.pop(__id) return status, report def get_company_overview(self, symbol, max_wait_time=10): ''' Get company overview infomration :param: symbol: stock symbol string, e.g. 'IBM'; or a IB contract object :return: a string of financial statements ''' # ReportsFinSummary Financial summary if not self.connected: raise RuntimeError('IB client is not connected to TWS') if isinstance(symbol, Contract): contract = symbol elif isinstance(symbol, str): contract = new_stock_contract(symbol) else: raise TypeError("contract must be a contract object or string (for U.S. stocks only).") __id = self.__get_new_request_id() request = RequestDetails('reqFundamentalData', 'ReportSnapshot-Company overview', contract) response = ResponseDetails() self.ipc_msg_dict[__id] = (request, response) # ReportSnapshot Company's financial overview self.connection.reqFundamentalData(__id, contract, 'ReportSnapshot') response.event.wait(max_wait_time) report = None if response.event.is_set(): if response.status == ResponseDetails.STATUS_FINISHED: # TODO: covert from xml to dest. format report = response.fundamental_data else: pass # ('get_analyst_estimates: reqFundamentalData got error. Security=%s Reason:%s' % (symbol, response.error_msg)) else: pass # ('get_analyst_estimates: reqFundamentalData is timeout. Security=%s' % symbol) status = response.status self.ipc_msg_dict.pop(__id) return status, report def get_financial_summary(self, symbol, max_wait_time=10): ''' Get company finanical summary information, such as revenue history, net profit, and dividends history. :param: symbol: stock symbol string, e.g. 'IBM'; or a IB contract object :return: a string of financial statements ''' if not self.connected: raise RuntimeError('IB client is not connected to TWS') if isinstance(symbol, Contract): contract = symbol elif isinstance(symbol, str): contract = new_stock_contract(symbol) else: raise TypeError("contract must be a contract object or string (for U.S. stocks only).") __id = self.__get_new_request_id() request = RequestDetails('reqFundamentalData', 'ReportsFinSummary-Financial summary', contract) response = ResponseDetails() self.ipc_msg_dict[__id] = (request, response) self.connection.reqFundamentalData(__id, contract, 'ReportsFinSummary') response.event.wait(max_wait_time) report = None if response.event.is_set(): if response.status == ResponseDetails.STATUS_FINISHED: # TODO: covert from xml to dest. format report = response.fundamental_data else: pass else: pass status = response.status self.ipc_msg_dict.pop(__id) return status, report def get_financial_ratios(self, symbol, max_wait_time=5): ''' Get analyst estimates report for a company :param: symbol: stock symbol string, e.g. 'IBM' :return: a string of financial statements ''' if not self.connected: raise RuntimeError('IB client is not connected to TWS') if isinstance(symbol, Contract): contract = symbol elif isinstance(symbol, str): contract = new_stock_contract(symbol) else: raise TypeError("contract must be a contract object or string (for U.S. stocks only).") __id = self.__get_new_request_id() request = RequestDetails('reqFundamentalData', 'RESC-Analyst Estimates', contract) response = ResponseDetails() self.ipc_msg_dict[__id] = (request, response) # 258 - financial ratios ''' TTMNPMGN=16.1298;NLOW=80.6;TTMPRCFPS=6.26675;TTMGROSMGN=60.76731;TTMCFSHR=15.004 46;QCURRATIO=1.42071;TTMREV=259842;TTMINVTURN=5.28024;TTMOPMGN=14.22711;TTMPR2RE V=1.39703;AEPSNORM=8.55;TTMNIPEREM=144524.1;EPSCHNGYR=8.47727;TTMPRFCFPS=62.4260 6;TTMRECTURN=19.99938;TTMPTMGN=17.88125;QCSHPS=40.50882;TTMFCF=5815; LATESTADATE=2016-12-31;APTMGNPCT=17.88125;AEBTNORM=46463;TTMNIAC=33008;NetDebt_I=152080; PRYTDPCTR=-1.55563;TTMEBITD=53326;AFEEPSNTM=0;PR2TANBK=5.01599;EPSTRENDGR=- 15.53209;QTOTD2EQ=72.60778;TTMFCFSHR=1.50625;QBVPS=110.0867;NPRICE=94.1;YLD5YAVG =3.88751;REVTRENDGR=51.11774;TTMEPSXCLX=8.54981;QTANBVPS=18.75999;PRICE2BK=0.854 78;MKTCAP=363007.5;TTMPAYRAT=31.32574;TTMINTCOV=-99999.99;TTMDIVSHR=2.585;TTMREVCHG=55.81794; TTMROAPCT=4.09615;TTMROEPCT=7.73685; TTMREVPERE=896006.9;APENORM=11.00585;TTMROIPCT=5.51924;REVCHNGYR=- 6.66885;CURRENCY=HKD;DIVGRPCT=-8.33887;TTMEPSCHG=-32.80548;PEEXCLXOR=11.00609;QQUICKRATI=1.30087; TTMREVPS=67.30638;BETA=0.90979;TTMEBT=46463;ADIV5YAVG=3.1048;ANIACNORM=33008;QLTD2EQ=55.46377;NHIG=103.9 ''' report = None self.connection.reqMktData(__id, contract, "258", False) response.event.wait(max_wait_time) if response.event.is_set(): if response.status == ResponseDetails.STATUS_FINISHED: # TODO: convert the format to a table alike report = response.tick_str return report def get_dividends_info(self, symbol, max_wait_time=5): ''' Get analyst estimates report for a company :param: symbol: stock symbol string, e.g. 'IBM' :return: a string of financial statements ''' if not self.connected: raise RuntimeError('IB client is not connected to TWS') if isinstance(symbol, Contract): contract = symbol elif isinstance(symbol, str): contract = new_stock_contract(symbol) else: raise TypeError("contract must be a contract object or string (for U.S. stocks only).") __id = self.__get_new_request_id() request = RequestDetails('reqFundamentalData', 'RESC-Analyst Estimates', contract) response = ResponseDetails() self.ipc_msg_dict[__id] = (request, response) # IB Dividends ("456") # # This tick type provides four different comma-separated elements: # The sum of dividends for the past 12 months (0.83 in the example below). # The sum of dividends for the next 12 months (0.92 from the example below). # The next dividend date (20130219 in the example below). # The next single dividend amount (0.23 from the example below). # Example: 0.83,0.92,20130219,0.23 self.connection.reqMktData(__id, contract, "456", False) result = None response.event.wait(max_wait_time) if response.event.is_set(): if response.status == ResponseDetails.STATUS_FINISHED: # TODO: convert the format result = set(response.tick_str.split(',')) self.ipc_msg_dict.pop(__id) return result def get_contract_details(self, contract, max_wait_time=5): """ Get contract details for a specified contract Args: contract: a legal IBPY Contract object or a string for U.S. stock only Returns: status: a reference to the tick data dictionary which will be updated with latest quote. contract_details: a contractDetails instance """ if isinstance(contract, Contract): pass elif isinstance(contract, str): contract = new_stock_contract(contract) else: raise TypeError("contract must be a contract object or string (for U.S. stocks only).") if not self.connected: raise RuntimeError('IB client is not connected to TWS') __id = self.__get_new_request_id() request = RequestDetails('reqContractDetails', '', contract) response = ResponseDetails() self.ipc_msg_dict[__id] = (request, response) # False - indicating request live quotes instead of a snapshot self.connection.reqContractDetails(__id, contract) response.event.wait(max_wait_time) contract_details = None if response.event.is_set(): if response.status == ResponseDetails.STATUS_FINISHED: if len(response.contract_list) > 0: contract_details = copy(response.contract_list[0]) else: pass else: pass status = response.status self.ipc_msg_dict.pop(__id) return status, contract_details def get_full_contract(self, contract): """ Subscribe tick data for a specified contract Args: contract: a legal IBPY Contract object or a string for U.S. stock only Returns: tickerId: the ID of this request. this ID could be used to cancel request later. tick_data: a reference to the tick data dictionary which will be updated with latest quote. """ status, contract_details = self.get_contract_details(contract) new_contract = copy(contract_details.m_summary) return status, new_contract
###start of intializations tf1 = 1 tf2 = 1 new_entry = 'T' wdw = 25 ctime = 0 r_sflag = 0 r_qty = 0 cnt = 0 global o_type ,order_id # end of intializations callback = IBWrapper() tws = EClientSocket(callback) host = "" port = 7497 clientId = 100 create = contract() callback.initiate_variables() tws.eConnect(host, port, clientId) print("connected") ####dummy data request contract_Details1 = create.create_contract("ES" ,"FUT" ,"GLOBEX" ,"USD" ,'' ,'' ,'20170616' ,'50') tickerId = random.randint(1, 1000000) # print(datetime.now()) tws.reqRealTimeBars(tickerId, contract_Details1, 5, "TRADES",
print(df_order_status) print( "###############################################################\n" ) else: print( "\n###################### No orders to show #######################\n" ) return df_order_status # Establish connection ---------------------------------------------------------------------- #accountName = "Your Account Name Here" callback = IBWrapper() # Instantiate IBWrapper. callback tws = EClientSocket( callback) # Instantiate EClientSocket and return data to callback host = "" port = 4002 clientId = 8 tws.eConnect(host, port, clientId) # Connect to TWS create = contract() # Instantiate contract class callback.initiate_variables() tws.reqAccountUpdates(1, accountName) # Portfolio Query ------------------------------------------------------------ ib = IB() df_portfolio = ib.portfolio() count = len(df_portfolio) # Disconnect ------------------------------------------------------------------
class IbGateway(VtGateway): """IB接口""" #---------------------------------------------------------------------- def __init__(self, eventEngine, gatewayName='IB'): """Constructor""" super(IbGateway, self).__init__(eventEngine, gatewayName) self.host = EMPTY_STRING # 连接地址 self.port = EMPTY_INT # 连接端口 self.clientId = EMPTY_INT # 用户编号 self.tickerId = 0 # 订阅行情时的代码编号 self.tickDict = {} # tick快照字典,key为tickerId,value为VtTickData对象 self.orderId = 0 # 订单编号 self.orderDict = {} # 报单字典,key为orderId,value为VtOrderData对象 self.accountDict = {} # 账户字典 self.connected = False # 连接状态 self.wrapper = IbWrapper(self) # 回调接口 self.connection = EClientSocket(self.wrapper) # 主动接口 #---------------------------------------------------------------------- def connect(self): """连接""" # 载入json文件 fileName = self.gatewayName + '_connect.json' fileName = os.getcwd() + '/ibGateway/' + fileName try: f = file(fileName) except IOError: log = VtLogData() log.gatewayName = self.gatewayName log.logContent = u'读取连接配置出错,请检查' self.onLog(log) return # 解析json文件 setting = json.load(f) try: self.host = str(setting['host']) self.port = int(setting['port']) self.clientId = int(setting['clientId']) except KeyError: log = VtLogData() log.gatewayName = self.gatewayName log.logContent = u'连接配置缺少字段,请检查' self.onLog(log) return # 发起连接 self.connection.eConnect(self.host, self.port, self.clientId) # 查询服务器时间 self.connection.reqCurrentTime() # 请求账户数据主推更新 self.connection.reqAccountUpdates(True, '') #---------------------------------------------------------------------- def subscribe(self, subscribeReq): """订阅行情""" # 订阅行情 self.tickerId += 1 contract = Contract() contract.m_symbol = str(subscribeReq.symbol) contract.m_exchange = exchangeMap.get(subscribeReq.exchange, '') contract.m_secType = productClassMap.get(subscribeReq.productClass, '') contract.m_currency = currencyMap.get(subscribeReq.currency, '') contract.m_expiry = subscribeReq.expiry contract.m_strike = subscribeReq.strikePrice contract.m_right = optionTypeMap.get(subscribeReq.optionType, '') self.connection.reqMktData(self.tickerId, contract, '', False) # 创建Tick对象并保存到字典中 tick = VtTickData() tick.symbol = subscribeReq.symbol tick.exchange = subscribeReq.exchange tick.vtSymbol = '.'.join([tick.symbol, tick.exchange]) tick.gatewayName = self.gatewayName self.tickDict[self.tickerId] = tick #---------------------------------------------------------------------- def sendOrder(self, orderReq): """发单""" # 增加报单号1,最后再次进行查询 # 这里双重设计的目的是为了防止某些情况下,连续发单时,nextOrderId的回调推送速度慢导致没有更新 self.orderId += 1 # 创建合约对象 contract = Contract() contract.m_symbol = str(orderReq.symbol) contract.m_exchange = exchangeMap.get(orderReq.exchange, '') contract.m_secType = productClassMap.get(orderReq.productClass, '') contract.m_currency = currencyMap.get(orderReq.currency, '') contract.m_expiry = orderReq.expiry contract.m_strike = orderReq.strikePrice contract.m_right = optionTypeMap.get(orderReq.optionType, '') # 创建委托对象 order = Order() order.m_orderId = self.orderId order.m_clientId = self.clientId order.m_action = directionMap.get(orderReq.direction, '') order.m_lmtPrice = orderReq.price order.m_totalQuantity = orderReq.volume order.m_orderType = priceTypeMap.get(orderReq.priceType, '') # 发送委托 self.connection.placeOrder(self.orderId, contract, order) # 查询下一个有效编号 self.connection.reqIds(1) #---------------------------------------------------------------------- def cancelOrder(self, cancelOrderReq): """撤单""" self.connection.cancelOrder(cancelOrderReq.orderID) #---------------------------------------------------------------------- def qryAccount(self): """查询账户资金""" log = VtLogData() log.gatewayName = self.gatewayName log.logContent = u'IB接口账户信息提供主推更新,无需查询' self.onLog(log) #---------------------------------------------------------------------- def qryPosition(self): """查询持仓""" log = VtLogData() log.gatewayName = self.gatewayName log.logContent = u'IB接口持仓信息提供主推更新,无需查询' self.onLog(log) #---------------------------------------------------------------------- def close(self): """关闭""" self.connection.eDisconnect()
import pandas as pd from datetime import datetime, timedelta import threading from threading import Thread import multiprocessing from multiprocessing import Process # from flask import Flask, url_for, request, render_template, redirect from apscheduler.schedulers.background import BackgroundScheduler sched = BackgroundScheduler() import openpyxl ############################################### callback = IBWrapper() tws = EClientSocket(callback) host = "" port = 7496 clientId = 100 create = contract() callback.initiate_variables() acc = "DU228380" commonwealth_curr = ['GBP', 'AUD', 'NZD', 'EUR'] def conn(): status = tws.isConnected() if status == False: print(
def connect(self): self._wrapper = ReferenceWrapper() self._connection = EClientSocket(self._wrapper) self._connection.eConnect(IB_HOST, IB_PORT, IB_CLIENT_ID)
import sys import queue from wrapper import Wrapper from ib.ext.ScannerSubscription import ScannerSubscription from ib.ext.EClientSocket import EClientSocket from utils import Logger log = Logger('scanner') msgs = queue.Queue() wrapper = Wrapper({}, msgs) connection = EClientSocket(wrapper) host = "54.197.15.42" port = 7496 connection.eConnect(host, port, 1) subscription = ScannerSubscription() subscription.numberOfRows(100) subscription.instrument('STK') subscription.locationCode('STK.US') subscription.scanCode('TOP_PERC_GAIN') subscription.abovePrice(1.0) subscription.aboveVolume(1) subscription.marketCapBelow(1000000000.0) ticker_id = 10 log.operation('Requesting subscription')