def run(self): try: logger.debug("EReader thread started") buf = b"" while self.conn.isConnected(): data = self.conn.recvMsg() logger.debug("reader loop, recvd size %d", len(data)) buf += data while len(buf) > 0: (size, msg, buf) = comm.read_msg(buf) #logger.debug("resp %s", buf.decode('ascii')) logger.debug("size:%d msg.size:%d msg:|%s| buf:%s|", size, len(msg), buf, "|") if msg: self.msg_queue.put(msg) else: logger.debug("more incoming packet(s) are needed ") break logger.debug("EReader thread finished") except: logger.exception('unhandled exception in EReader thread')
def run(self): while self.conn.isConnected(): buf = self.prevBuf + self.conn.recvMsg() logging.debug("reader loop, prevBuf.size: %d recvd size: %d buf %s", len(self.prevBuf), len(buf), buf) while len(buf) > 0: (size, msg, buf) = comm.read_msg(buf) #logging.debug("resp %s", buf.decode('ascii')) logging.debug("size:%d msg.size:%d msg:|%s| buf:%s|", size, len(msg), buf, "|") if len(msg) == size: self.msg_queue.put(msg) self.prevBuf = b"" elif len(msg) < size: logging.debug("more incoming packet(s) are needed ") self.prevBuf = buf break else: logging.error("recvd bigger msg (%d) than expected (%d)", len(msg), size) logging.debug("EReader thread finished")
def test_read_msg(self): text = "ABCD" msg = comm.make_msg(text) (size, text2, rest) = comm.read_msg(msg) self.assertEqual(size, len(text), "msg size not good") self.assertEqual(text2.decode(), text, "msg payload not good") self.assertEqual(len(rest), 0, "there should be no remainder msg")
def test_readFields(self): text1 = "ABCD" text2 = "123" msg = comm.make_msg(comm.make_field(text1) + comm.make_field(text2)) (size, text, rest) = comm.read_msg(msg) fields = comm.read_fields(text) self.assertEqual(len(fields), 2, "incorrect number of fields") self.assertEqual(fields[0].decode(), text1) self.assertEqual(fields[1].decode(), text2)
def run(self): try: buf = b"" while self.conn.isConnected(): try: data = self.conn.recvMsg() logger.debug("reader loop, recvd size %d", len(data)) buf += data except OSError as err: #If connection is disconnected, Windows will generate error 10038 if err.errno == 10038: #Wait up to 1 second for disconnect confirmation waitUntil = time.time() + 1 while time.time() < waitUntil: if not self.conn.isConnected(): break time.sleep(.1) if not self.conn.isConnected(): logger.debug("Ignoring OSError: {0}".format(err)) break #Disconnect wasn't received or error != 10038 raise while len(buf) > 0: (size, msg, buf) = comm.read_msg(buf) #logger.debug("resp %s", buf.decode('ascii')) logger.debug("size:%d msg.size:%d msg:|%s| buf:%s|", size, len(msg), buf, "|") if msg: self.msg_queue.put(msg) else: logger.debug("more incoming packet(s) are needed ") break logger.debug("EReader thread finished") except: logger.exception('unhandled exception in EReader thread')
def send_msg(self, msg): """Mock send to ib by interpreting and placing on receive queue. Args: msg: message to be sent (i.e., interpreted and queued) """ logger.debug('entered with message %s', msg) (size, msg2, buf) = read_msg(msg) logger.debug('size, msg, buf: %d, %s, %s ', size, msg2, buf) fields = read_fields(msg2) logger.debug('fields: %s', fields) recv_msg = b'' ####################################################################### # get version and connect time (special case - not decode able) ####################################################################### if msg == b'API\x00\x00\x00\x00\tv100..157': current_dt = datetime.now( tz=timezone(offset=timedelta(hours=5))).\ strftime('%Y%m%d %H:%M:%S') # recv_msg = b'\x00\x00\x00\x1a157\x0020210301 23:43:23 EST\x00' recv_msg = b'\x00\x00\x00\x1a157\x00' \ + current_dt.encode('utf-8') + b' EST\x00' ####################################################################### # reqId (get next valid requestID) # b'\x00\x00\x00\x0871\x002\x000\x00\x00' ####################################################################### elif int(fields[0]) == OUT.START_API: logger.info('startAPI detected') # recv_msg = b'\x00\x00\x00\x069\x001\x001\x00' if self.reqId_timeout: # if testing timeout case recv_msg = make_msg('0') # simulate timeout else: # build the normal next valid id message recv_msg = make_msg( make_field(IN.NEXT_VALID_ID) + make_field('1') + make_field('1')) logger.debug('recv_msg: %s', recv_msg) ####################################################################### # Handle special test cases for request disconnect and timeout ####################################################################### elif self.simulate_request_disconnect: # if testing timeout case time.sleep(2) # allow some time for request to get into wait loop recv_msg = b'' # simulate disconnect elif self.simulate_request_timeout: recv_msg = make_msg('0') # simulate timeout ####################################################################### # reqMatchingSymbols ####################################################################### elif int(fields[0]) == OUT.REQ_MATCHING_SYMBOLS: logger.info('reqMatchingSymbols detected') reqId = int(fields[1]) pattern = fields[2].decode(errors='backslashreplace') logger.debug('pattern: %s', pattern) # construct start of receive message for wrapper build_msg = make_field(IN.SYMBOL_SAMPLES) + make_field(reqId) # find pattern matches in mock contract descriptions symbol_starts_with_pattern = \ self.contract_descriptions['symbol'].map( lambda symbol: symbol.startswith(pattern)) match_descs = \ self.contract_descriptions[symbol_starts_with_pattern] # match_descs = self.contract_descriptions.loc[ # self.contract_descriptions['symbol'].str. # startswith(pattern)] # limit the number found as ib does num_found = min(self.MAX_CONTRACT_DESCS_RETURNED, match_descs.shape[0]) # add the number of descriptions to the receive message build_msg = build_msg + make_field(num_found) for i in range(num_found): build_msg = build_msg \ + make_field(match_descs.iloc[i].conId) \ + make_field(match_descs.iloc[i].symbol) \ + make_field(match_descs.iloc[i].secType) \ + make_field(match_descs.iloc[i].primaryExchange) \ + make_field(match_descs.iloc[i].currency) \ + make_field(len(match_descs.iloc[i].derivativeSecTypes)) for dvt in match_descs.iloc[i].derivativeSecTypes: build_msg = build_msg + make_field(dvt) recv_msg = make_msg(build_msg) ####################################################################### # reqContractDetails ####################################################################### elif int(fields[0]) == OUT.REQ_CONTRACT_DATA: logger.info('reqContractDetails detected') version = int(fields[1]) reqId = int(fields[2]) conId = int(fields[3]) # construct start of receive message for wrapper start_msg = make_field(IN.CONTRACT_DATA) \ + make_field(version) \ + make_field(reqId) # find pattern matches in mock contract descriptions # fow now, just conId match_descs = self.contract_descriptions.loc[ self.contract_descriptions['conId'] == conId] for i in range(len(match_descs)): build_msg = start_msg \ + make_field(match_descs.iloc[i].symbol) \ + make_field(match_descs.iloc[i].secType) \ + make_field(match_descs.iloc[i]. lastTradeDateOrContractMonth) \ + make_field(match_descs.iloc[i].strike) \ + make_field(match_descs.iloc[i].right) \ + make_field(match_descs.iloc[i].exchange) \ + make_field(match_descs.iloc[i].currency) \ + make_field(match_descs.iloc[i].localSymbol) \ + make_field(match_descs.iloc[i].marketName) \ + make_field(match_descs.iloc[i].tradingClass) \ + make_field(match_descs.iloc[i].conId) \ + make_field(match_descs.iloc[i].minTick) \ + make_field(match_descs.iloc[i].mdSizeMultiplier) \ + make_field(match_descs.iloc[i].multiplier) \ + make_field(match_descs.iloc[i].orderTypes) \ + make_field(match_descs.iloc[i].validExchanges) \ + make_field(match_descs.iloc[i].priceMagnifier) \ + make_field(match_descs.iloc[i].underConId) \ + make_field(match_descs.iloc[i].longName) \ + make_field(match_descs.iloc[i].primaryExchange) \ + make_field(match_descs.iloc[i].contractMonth) \ + make_field(match_descs.iloc[i].industry) \ + make_field(match_descs.iloc[i].category) \ + make_field(match_descs.iloc[i].subcategory) \ + make_field(match_descs.iloc[i].timeZoneId) \ + make_field(match_descs.iloc[i].tradingHours) \ + make_field(match_descs.iloc[i].liquidHours) \ + make_field(match_descs.iloc[i].evRule) \ + make_field(match_descs.iloc[i].evMultiplier) \ + make_field(match_descs.iloc[i].secIdListCount) for tv in match_descs.iloc[i].secIdList: build_msg += make_field(tv) build_msg += make_field(match_descs.iloc[i].aggGroup) \ + make_field(match_descs.iloc[i].underSymbol) \ + make_field(match_descs.iloc[i].underSecType) \ + make_field(match_descs.iloc[i].marketRuleIds) \ + make_field(match_descs.iloc[i].realExpirationDate) \ + make_field(match_descs.iloc[i].stockType) recv_msg = make_msg(build_msg) self.msg_rcv_q.put(recv_msg, timeout=5) build_msg = make_field(IN.CONTRACT_DATA_END) \ + make_field(version) \ + make_field(reqId) recv_msg = make_msg(build_msg) ####################################################################### # queue the message to be received ####################################################################### self.msg_rcv_q.put(recv_msg, timeout=5)