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()
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)
# ---> orderStatus** # reqAutoOpenOrders ---> openOrder* # ---> orderStatus** # reqIds ---> nextValidId self.next_ValidId # ---> deltaNeutralValidation # exerciseOptions # reqGlobalCancel ###################################################################################''' print "Testing Orders Group \n" # Example 1 - placing order to buy stock tws.reqIds(1) # Need to request next valid order Id time.sleep(2) # wait for response from server order_id = callback.next_ValidId contract_info1 = create.create_contract('GOOG', 'STK', 'SMART', 'USD') order_info1 = create.create_order(accountName, 'MKT', 100, 'BUY') tws.placeOrder(order_id, contract_info1, order_info1) # Example 2 - placing order to buy FX tws.reqIds(1) time.sleep(1) order_id = callback.next_ValidId contract_info2 = create.create_contract('EUR', 'CASH', 'IDEALPRO', 'USD') order_info2 = create.create_order(accountName, 'MKT', 100000, 'BUY') tws.placeOrder(order_id, contract_info2, order_info2) #tws.cancelOrder(order_id) # Cancel example 2 order #tws.reqOpenOrders() #tws.reqAllOpenOrders() #tws.reqAutoOpenOrders(1) # clientId had to be 0 for this to work tws.reqGlobalCancel()
elif (signal != 'n'): start_time2 = timeit.default_timer() # tws.reqIds(1) order_id = callback.next_ValidId + 1 callback.next_ValidId = order_id # contract and order creation ctrct = ["ES", "FUT", "GLOBEX", "USD", '', '', '20170616', '50', "DU228380"] # contract_info = create.create_contract(ctrct[0], ctrct[1], ctrct[2], ctrct[3], ctrct[4], ctrct[5], ctrct[6], ctrct[7]) order_info = create.create_order(ctrct[8], o_type, qty, signal, price, True) print(o_type, qty, signal, price) # time.sleep(2) # place order tws.placeOrder(order_id, contract_info, order_info) # sleep for 5 seconds(discuss) time.sleep(3) # getting order confirmation### confirm = pd.DataFrame(callback.order_Status, columns=['orderId', 'status', 'filled', 'remaining', 'avgFillPrice', 'permId', 'parentId', 'lastFillPrice', 'clientId', 'whyHeld']) check = confirm.tail(1) filled = check.iloc[0]['filled'] remain = check.iloc[0]['remaining'] # filled=confirm.iloc[-1, :].filled # remain = confirm.iloc[-1, :].remaining # details to push into csv
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
False, False, None, None, None, None, None, None) tws.placeOrder(int(i), contract_info, order_info) time.sleep(0.2) loops = 1 # exitidlist = generateNumber(loops) # # while exitidlist[0] == buyidlist[0]: # exitidlist = generateNumber(loops) # # for i in exitidlist: # order_info = create.create_order(acc, "LMT", 10000, exit_signal, 10.000, False, False, None) # tws.placeOrder(int(i), contract_info, order_info) return sellidlist, buyidlist conn() id_list = [[0], [0]] curr = 'NOK.SEK' contract = create.create_contract(curr[0:3], "CASH", "IDEALPRO", curr[4:7], '', '', '', '') idlist = orderidfun(acc, contract, type, id_list, 2) rt_price = ask_bid(contract, 2000) order_info = create.create_order(acc, "LMT", 10000, "BUY", float(rt_price[0]), True, 'GTC', None, None, None, None, None, None) tws.placeOrder(int(id_list[1][0]), contract, order_info)
mylist = [] for i in range(num): mylist.append(order_id) order_id = order_id + 1 return mylist conn() order_id = generateNumber(2) contract_info = contract1("AUD", "CASH", "IDEALPRO", "NZD", "", "", "", "") #order_info = create.create_order(acc, "LMT", 10000, "BUY", 10.000, False, False, None,0,0) # 22% price # # 15% price# order_info = create.create_order(acc, "TRAIL LIMIT", 10000, "SELL", 1.0930, True, False, None, 1.09270, 0.001, True, 0.0) #trail perc # tws.placeOrder(int(order_id[0]), contract_info, order_info) # order_info = create.create_order(acc, "TRAIL LIMIT", 10000, "SELL", "0.01", True, False, None,0,0,0.02) # tws.placeOrder(int(order_id[1]), contract_info, order_info) conn() order_id = generateNumber(2) # 22% price # # 15% price# order_info = create.create_order(acc, "LMT", 10000, "SELL", 1.0930, True, False, None, "", "", True, "") tws.placeOrder(int(order_id[0]), contract_info, order_info) #trail perc # order_info = create.create_order(acc, "TRAIL LIMIT", 10000, "SELL", 1.0930, True, False, None, 1.09270, 0.001, True, 0.0) #trail perc # tws.placeOrder(int(order_id[0]), contract_info, order_info)
data = obtain_hist_data(tws, callback) short_SMA, long_SMA = SMA(data, 5, 10) print("short_SMA {}, long_SMA {}".format(short_SMA, long_SMA)) # trading decision # ideally we need a check to see if we already have a position. # Else we keep sending a buy if short_SMA > long_SMA: pos = check_existing_pos() print(pos) if not pos: print("[INFO] order placing...") tws.reqIds(1) order_id = callback.next_ValidId + 1 # placing order order_info = create.create_order(accountName, "MKT", 250, "BUY") tws.placeOrder(order_id, contract_Details, order_info) time.sleep(2) order_status_update = pd.DataFrame(callback.order_Status, columns = ['orderId', 'status', 'filled', 'remaining', 'avgFillPrice', 'permId', 'parentId', 'lastFillPrice', 'clientId', 'whyHeld']) # if the order is filled, it should be picked up by the # check_existing_pos function. The place order should not # be fired. # disconnnet tws.eDisconnect()
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
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()
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()
class IbApp(object): """IB链接器""" def __init__(self, host, port, client_id, req_dict, order_dict, tick_dict): """ TODO: 不应该维护一个巨大的类,可以考虑使用混合模式来管理这个类 :host:地址 :port:接口 :client_id:这个可以随便写一个,int :req_id:请求id :redis:redis实例 """ self.host = host self.port = port self.client_id = client_id self.warpper = IbTestWrapper(req_dict, order_dict, tick_dict) self.connection = EClientSocket(self.warpper) self.req_id = 0 self.order_id = 0 self.req_dict = req_dict self.order_dict = order_dict self.tick_dict = tick_dict def econnect(self): """docstring for connect""" # 连接TWS self.connection.eConnect(self.host, self.port, self.client_id) def subscribe(self, m_symbol, currency, m_sec_type, m_exchange, m_local_symbol): """ :return: 返回req_id和合约代码信息返回,好做好映射, """ contract = Contract() contract.m_symbol = m_symbol # 底层资产的代码 PS:CL contract.m_currency = currency # 币种 PS:USD contract.m_secType = m_sec_type # 证券类型 PS:FUT contract.m_exchange = m_exchange # 交易所NYMEX contract.m_localSymbol = m_local_symbol #合约代码 self.req_id += 1 self.connection.reqMktData(self.req_id, contract, '', False) # 订阅 self.tick_dict[self.req_id] = {} # 初始化Tick的数据 #cache_symbol cache_symbol = m_local_symbol if ' ' not in m_local_symbol else m_local_symbol.replace( ' ', '_') # 将订阅合约信息保存在字典中 self.req_dict[self.req_id] = { 'symbol': m_symbol, 'currency': currency, 'sec_type': m_sec_type, 'exchange': m_exchange, 'local_symbol': cache_symbol } def send_order(self, order_dict): """发单""" # 发单ID self.order_id += 1 contract = self._make_opt_contract(order_dict) order = self._make_opt_order(self.order_id, self.client_id, order_dict) self.connection.placeOrder(self.order_id, contract, order) self.connection.reqIds() self.order_dict[self.order_id] = order_dict return self.order_id def cancle_order(self, order_id): """撤单""" self.connection.cancelOrder(order_id) def run(self): """运行""" while True: pass def _make_opt_contract(self, order_dict): """docstring for make_opt_contract""" contract = Contract() contract.m_localSymbol = order_dict['m_local_symbol'] contract.m_secType = order_dict['m_sec_type'] contract.m_right = order_dict['m_right'] contract.m_expiry = order_dict['m_expiry'] contract.m_strike = order_dict['m_strike'] contract.m_exchange = order_dict['m_exchange'] contract.m_currency = order_dict['currency'] return contract def _make_opt_order(self, order_id, client_id, order_dict): """docstring for _make_opt_contract""" order = Order() order.m_orderId = order_id order.m_clientId = client_id order.m_action = order_dict['m_action'] order.m_lmtPrice = order_dict['price'] order.m_totalQuantity = order_dict['volume'] order.m_orderType = order_dict['m_order_type'] return order
class orderRunnerBeta: def __init__(self, datadecoder): self.accountName = "asd781820" self.callback = IBWrapper() self.tws = EClientSocket(self.callback) self.host = "" self.port = 4002 self.clientId = 8 self.tws.eConnect(self.host, self.port, self.clientId) self.dd = datadecoder self.create = contract() self.callback.initiate_variables() self.callback.initiate_variables() self.tws.reqAccountSummary(8, 'All', 'TotalCashValue') self.filedf = datadecoder.usabledf self.datadecoder = datadecoder contract_Details = self.create.create_contract('EUR', 'CASH', 'IDEALPRO', 'USD') tickerId = 10000 self.tws.reqRealTimeBars(tickerId, contract_Details, 5, "MIDPOINT", 0) self.tickerId = 9010 self.df = pd.read_csv('df.csv') self.RbinDict = bqe.makeRDf(self.df) self.column_index_value_dict, self.binDict = bqe.makeDf(self.df) def create_trailing_order(self, action, quantity, trailingPercent=0.006): order = Order() order.m_orderType = "TRAIL" order.m_totalQuantity = quantity order.m_trailingPercent = trailingPercent order.m_action = action return order def create_order(self, order_type, quantity, action): """Create an Order object (Market/Limit) to go long/short. order_type - 'MKT', 'LMT' for Market or Limit orders quantity - Integral number of assets to order action - 'BUY' or 'SELL'""" order = Order() order.m_orderType = order_type order.m_totalQuantity = quantity order.m_action = action return order def buy(self, contract_Details, limit, stop): self.tws.reqIds(-1) time.sleep(0.5) oid = self.callback.next_ValidId parent = Order() parent.orderId = oid parent.m_action = "BUY" parent.m_orderType = "MKT" parent.m_totalQuantity = 300000 parent.m_transmit = False print('|==========执行触发=================|') print('|执行时期:%s|' % datetime.now()) print('|操作: BUY |') print('|执行前总额:%s' % self.callback.account_Summary[-1][3]) print('|===================================|') self.tws.reqIds(-1) takeProfit = Order() takeProfit.orderId = parent.orderId + 1 takeProfit.m_action = "SELL" takeProfit.m_orderType = "LMT" takeProfit.m_totalQuantity = 300000 takeProfit.m_lmtPrice = limit takeProfit.m_parentId = oid takeProfit.m_transmit = False stopLoss = Order() stopLoss.orderId = parent.orderId + 2 stopLoss.m_action = "SELL" stopLoss.m_orderType = "STP" #Stop trigger price stopLoss.m_auxPrice = stop stopLoss.m_totalQuantity = 300000 stopLoss.m_parentId = oid stopLoss.m_transmit = True bracketOrder = [parent, takeProfit, stopLoss] for o in bracketOrder: self.tws.placeOrder(o.orderId, contract_Details, o) self.tws.reqIds(-1) time.sleep(1) time.sleep(2) def sell(self, contract_Details, limit, stop): self.tws.reqIds(-1) time.sleep(0.5) oid = self.callback.next_ValidId parent = Order() parent.orderId = oid parent.m_action = "SELL" parent.m_orderType = "MKT" parent.m_totalQuantity = 300000 parent.m_transmit = False print('|==========执行触发=================|') print('|执行时期:%s|' % datetime.now()) print('|操作: BUY |') print('|执行前总额:%s' % self.callback.account_Summary[-1][3]) print('|===================================|') self.tws.reqIds(-1) takeProfit = Order() takeProfit.orderId = parent.orderId + 1 takeProfit.m_action = "BUY" takeProfit.m_orderType = "LMT" takeProfit.m_totalQuantity = 300000 takeProfit.m_lmtPrice = limit takeProfit.m_parentId = oid takeProfit.m_transmit = False stopLoss = Order() stopLoss.orderId = parent.orderId + 2 stopLoss.m_action = "BUY" stopLoss.m_orderType = "STP" #Stop trigger price stopLoss.m_auxPrice = stop stopLoss.m_totalQuantity = 300000 stopLoss.m_parentId = oid stopLoss.m_transmit = True bracketOrder = [parent, takeProfit, stopLoss] for o in bracketOrder: self.tws.placeOrder(o.orderId, contract_Details, o) self.tws.reqIds(-1) time.sleep(1) time.sleep(2) def job(self): contract_Details = self.create.create_contract('EUR', 'CASH', 'IDEALPRO', 'USD') #self.tws.reqCurrentTime() #time.sleep(1) #ts = self.callback.current_Time #data_endtime = datetime.utcfromtimestamp(ts).strftime('%Y%m%d %H:%M:%S') self.tws.reqHistoricalData(tickerId=self.tickerId, contract=contract_Details, endDateTime='', durationStr="1 D", barSizeSetting="1 min", whatToShow="BID", useRTH=0, formatDate=1) time.sleep(2) data = pd.DataFrame(self.callback.historical_Data, columns=[ "reqId", "date", "open", "high", "low", "close", "volume", "count", "WAP", "hasGaps" ])[-500:-1] self.dd.findMatch(data) print(data) print(datetime.now()) for b in self.dd.reversedBucket: print('|=============================================|') print('|检测到基因:%s|' % b) print('|=============================================|') if (self.filedf.index.contains(b)): stra = bqe.get_sharpe_stra(b) if (stra > 0): threading.Thread(target=self.buy, args=[contract_Details, stra + 1]).start() elif (stra < 0): threading.Thread(target=self.sell, args=[contract_Details, abs(stra - 1)]).start() self.tws.reqIds(-1) print('|===================================|') print('|总额:%s |' % self.callback.account_Summary[-1][3]) print('|===================================|') def run(self): print('|===========================================|') print('|=======欢迎使用beta版本,祝您好运==========|') print('|====新特性:1。止损定单 2。智能订单执行====|') print('|===========================================|') print('|===========================================|') self.tickerId = 9010 while (1): now = datetime.now() if (now.second == 5): contract_Details = self.create.create_contract( 'EUR', 'CASH', 'IDEALPRO', 'USD') #self.tws.reqCurrentTime() #time.sleep(1) #ts = self.callback.current_Time #data_endtime = datetime.utcfromtimestamp(ts).strftime('%Y%m%d %H:%M:%S') self.tws.reqHistoricalData(tickerId=self.tickerId, contract=contract_Details, endDateTime='', durationStr="1 D", barSizeSetting="1 min", whatToShow="BID", useRTH=0, formatDate=1) time.sleep(2) data = pd.DataFrame(self.callback.historical_Data, columns=[ "reqId", "date", "open", "high", "low", "close", "volume", "count", "WAP", "hasGaps" ])[-500:-1] self.dd.findMatch(data) last_close = data.iloc[-1].close for b in self.dd.reversedBucket: if (self.filedf.index.contains(b)): print('usable gene: %s' % b) limit, stop, action = bqe.get_monte_carlo_stra( ast.literal_eval(b), self.column_index_value_dict, self.binDict, self.df, self.RbinDict) if (action > 0): limit_price = last_close + limit stop_price = last_close - stop threading.Thread(target=self.buy, args=[ contract_Details, limit_price, stop_price ]).start() elif (action < 0): limit_price = last_close - limit stop_price = last_close + stop threading.Thread( target=self.sell, args=[contract_Details, limit, stop]).start() self.tws.reqIds(-1) print('|===================================|') print('|总额:%s |' % self.callback.account_Summary[-1][3]) print('|===================================|') def dc(self): self.tws.eDisconnect()
class TWS_gateway(threading.Thread): # config config = None # redis connection rs = None # channel clients' requests to IB/TWS cli_request_handler = None # manage conID / contracts mapping contract_subscription_mgr = None connection = None # handler to process incoming IB/TWS messages and echo back to clients tws_event_handler = None # monitor IB connection / heart beat ibh = None tlock = None ib_conn_status = None ib_order_transmit = False def __init__(self, host, port, clientId, kafka_host, kafka_port, config): super(TWS_gateway, self).__init__() self.config = config self.host = host self.port = port self.clientId = clientId self.ib_order_transmit = config.get("tws_gateway", "tws_gateway.order_transmit").strip('"').strip("'") if \ config.get("tws_gateway", "tws_gateway.order_transmit").strip('"').strip("'") <> None\ else False logging.info('starting up TWS_gateway...') logging.info('Order straight through (no-touch) flag = %s' % ('True' if self.ib_order_transmit == True else 'False')) logging.info('connecting to Redis server...') self.initialize_redis(config) logging.info('starting up TWS_event_handler...') self.tws_event_handler = TWS_event_handler(kafka_host, kafka_port) logging.info('starting up IB EClientSocket...') self.connection = EClientSocket(self.tws_event_handler) logging.info('starting up client request handler - kafkaConsumer...') self.cli_request_handler = KafkaConsumer( *[(v,0) for v in list(TWS_Protocol.topicMethods) + list(TWS_Protocol.gatewayMethods) ], \ metadata_broker_list=['%s:%s' % (kafka_host, kafka_port)],\ group_id = 'epc.tws_gateway',\ auto_commit_enable=True,\ auto_commit_interval_ms=30 * 1000,\ auto_offset_reset='largest') # discard old ones self.reset_message_offset() if not self.eConnect(): logging.error( 'TWS_gateway: unable to establish connection to IB %s:%d' % (self.host, self.port)) sys.exit(-1) else: # start heart beat monitor logging.info('starting up IB heart beat monitor...') self.tlock = Lock() self.ibh = IbHeartBeat(config) self.ibh.register_listener([self.on_ib_conn_broken]) self.ibh.run() logging.info('starting up subscription manager...') self.initialize_subscription_mgr() def initialize_subscription_mgr(self): self.contract_subscription_mgr = SubscriptionManager(self) self.contract_subscription_mgr.register_persistence_callback( self.persist_subscriptions) key = self.config.get( "tws_gateway", "subscription_manager.subscriptions.redis_key").strip('"').strip( "'") if self.rs.get(key): #contracts = map(lambda x: ContractHelper.kvstring2contract(x), json.loads(self.rs.get(key))) def is_outstanding(c): today = time.strftime('%Y%m%d') if c.m_expiry < today: logging.info( 'initialize_subscription_mgr: ignoring expired contract %s%s%s' % (c.m_expiry, c.m_strike, c.m_right)) return False return True contracts = filter( lambda x: is_outstanding(x), map(lambda x: ContractHelper.kvstring2object(x, Contract), json.loads(self.rs.get(key)))) self.contract_subscription_mgr.load_subscription(contracts) def persist_subscriptions(self, contracts): key = self.config.get( "tws_gateway", "subscription_manager.subscriptions.redis_key").strip('"').strip( "'") #cs = json.dumps(map(lambda x: ContractHelper.contract2kvstring(x) if x <> None else None, contracts)) cs = json.dumps( map( lambda x: ContractHelper.object2kvstring(x) if x <> None else None, contracts)) logging.debug( 'Tws_gateway: updating subscription table to redis store %s' % cs) self.rs.set(key, cs) def initialize_redis(self, config): r_host = config.get("redis", "redis.server").strip('"').strip("'") r_port = config.get("redis", "redis.port") r_db = config.get("redis", "redis.db") self.rs = redis.Redis(r_host, r_port, r_db) try: self.rs.client_list() except redis.ConnectionError: logging.error( 'TWS_gateway: unable to connect to redis server using these settings: %s port:%d db:%d' % (r_host, r_port, r_db)) logging.error('aborting...') sys.exit(-1) def reset_message_offset(self): topic_offsets = map( lambda topic: (topic, self.cli_request_handler.get_partition_offsets( topic, 0, -1, 999)), TWS_Protocol.topicMethods + TWS_Protocol.gatewayMethods) topic_offsets = filter( lambda x: x <> None, map(lambda x: (x[0], x[1][1], x[1][0]) if len(x[1]) > 1 else None, topic_offsets)) logging.info('TWS_gateway set topic offset to the latest point\n%s' % (''.join('%s,%s,%s\n' % (x[0], x[1], x[2]) for x in topic_offsets))) # the set_topic_partitions method clears out all previous settings when executed # therefore it's not possible to call the function multiple times: # self.consumer.set_topic_partitions(('gw_subscriptions', 0, 114,) # self.consumer.set_topic_partitions(('tickPrice', 0, 27270,)) # as the second call will wipe out whatever was done previously self.cli_request_handler.set_topic_partitions(*topic_offsets) def run(self): for message in self.cli_request_handler: logging.info("%s:%d:%d: key=%s value=%s" % (message.topic, message.partition, message.offset, message.key, message.value)) # print ("TWS_gateway: received client request %s:%d:%d: key=%s value=%s" % (message.topic, message.partition, # message.offset, message.key, # message.value)) getattr(self, message.topic, None)(message.value) #self.cli_request_handler.task_done(message) def on_ib_conn_broken(self, msg): logging.error('TWS_gateway: detected broken IB connection!') self.ib_conn_status = 'ERROR' self.tlock.acquire() # this function may get called multiple times try: # block until another party finishes executing if self.ib_conn_status == 'OK': # check status return # if already fixed up while waiting, return self.eDisconnect() self.eConnect() while not self.connection.isConnected(): logging.error('TWS_gateway: attempt to reconnect...') self.eConnect() sleep(2) # we arrived here because the connection has been restored # resubscribe tickers again! logging.info( 'TWS_gateway: IB connection restored...resubscribe contracts') self.contract_subscription_mgr.force_resubscription() finally: self.tlock.release() def eConnect(self): logging.info('TWS_gateway - eConnect. Connecting to %s:%s App Id: %s' % (self.host, self.port, self.clientId)) self.connection.eConnect(self.host, self.port, self.clientId) return self.connection.isConnected() def reqAccountUpdates(self, value=None): logging.info('TWS_gateway - reqAccountUpdates value=%s' % value) self.connection.reqAccountUpdates(1, '') def reqAccountSummary(self, value): logging.info('TWS_gateway - reqAccountSummary value=%s' % value) vals = map( lambda x: x.encode('ascii') if isinstance(x, unicode) else x, json.loads(value)) self.connection.reqAccountSummary(vals[0], vals[1], vals[2]) def reqOpenOrders(self, value=None): self.connection.reqOpenOrders() def reqPositions(self, value=None): self.connection.reqPositions() def reqExecutions(self, value): try: filt = ExecutionFilter( ) if value == '' else ExecutionFilterHelper.kvstring2object( value, ExecutionFilter) self.connection.reqExecutions(0, filt) except: logging.error(traceback.format_exc()) def reqIds(self, value=None): self.connection.reqIds(1) def reqNewsBulletins(self): self.connection.reqNewsBulletins(1) def cancelNewsBulletins(self): self.connection.cancelNewsBulletins() def setServerLogLevel(self): self.connection.setServerLogLevel(3) def reqAutoOpenOrders(self): self.connection.reqAutoOpenOrders(1) def reqAllOpenOrders(self): self.connection.reqAllOpenOrders() def reqManagedAccts(self): self.connection.reqManagedAccts() def requestFA(self): self.connection.requestFA(1) def reqMktData(self, sm_contract): logging.info('TWS Gateway received reqMktData request: %s' % sm_contract) try: #self.contract_subscription_mgr.reqMktData(ContractHelper.kvstring2contract(sm_contract)) self.contract_subscription_mgr.reqMktData( ContractHelper.kvstring2object(sm_contract, Contract)) except: pass 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) def placeOrder(self, value=None): logging.info('TWS_gateway - placeOrder value=%s' % value) try: vals = json.loads(value) except ValueError: logging.error('TWS_gateway - placeOrder Exception %s' % traceback.format_exc()) return # c = ContractHelper.kvstring2contract(vals[1]) o = OrderHelper.kvstring2object(vals[2], Order) o.__dict__['transmit'] = self.ib_order_transmit # print c.__dict__ # print o.__dict__ # print '---------------------' #self.connection.placeOrder(vals[0], ContractHelper.kvstring2contract(vals[1]), OrderHelper.kvstring2object(vals[2], Order)) self.connection.placeOrder( vals[0], ContractHelper.kvstring2object(vals[1], Contract), OrderHelper.kvstring2object(vals[2], Order)) # self.connection.placeOrder(orderId, contract, newOptOrder) def eDisconnect(self, value=None): sleep(2) self.connection.eDisconnect() ####################################################################3 # Gateway commands def gw_req_subscriptions(self, value=None): #subm = map(lambda i: ContractHelper.contract2kvstring(self.contract_subscription_mgr.handle[i]), range(len(self.contract_subscription_mgr.handle))) #subm = map(lambda i: ContractHelper.object2kvstring(self.contract_subscription_mgr.handle[i]), range(len(self.contract_subscription_mgr.handle))) subm = map( lambda i: (i, ContractHelper.object2kvstring( self.contract_subscription_mgr.handle[i])), range(len(self.contract_subscription_mgr.handle))) print subm if subm: self.tws_event_handler.broadcast_event('gw_subscriptions', {'subscriptions': subm}, source='GW')