def parse_order_book(message: simplefix.FixMessage) -> dict: order_count = int(message.get(268)) buy_orders = [] sell_orders = [] orders = {} for i in range(1, order_count): side = 'SELL' if message.get( simplefix.TAG_SYMBOL).decode('utf-8') == '1' else 'BUY' order = { 'time': int(time.time()), # No timestamp available 'side': side, 'contract_symbol': message.get(simplefix.TAG_SYMBOL).decode('utf-8'), 'qty': message.get(271).decode('utf-8'), 'price': message.get(270).decode('utf-8') } if side == 'SELL': sell_orders.append(order) else: buy_orders.append(order) buy_orders.sort(key=lambda o: float(o['price'])) sell_orders.sort(key=lambda o: float(o['price'])) orders['buy'] = buy_orders orders['sell'] = sell_orders return orders
def _handle_application_message(self, message: simplefix.FixMessage): """ If application message is an order processing message, add it to the order_book keyed by the messages client_order_id. Otherwise, add marketdata messages to application_messages queue. """ assert isinstance(message, simplefix.FixMessage) # handle ORDER MASS STATUS REQUEST messages if message.get(b'584') != None: self.application_messages.put(message) return order_processing_types = [simplefix.MSGTYPE_EXECUTION_REPORT, simplefix.MSGTYPE_ORDER_CANCEL_REJECT] # handle order processing messages if message.get(simplefix.TAG_MSGTYPE) in order_processing_types: # ensure the correct client id is used, depending on order execution type use_origclordid_types = [simplefix.EXECTYPE_CANCELED, simplefix.EXECTYPE_REPLACE, simplefix.EXECTYPE_PENDING_CANCEL, simplefix.EXECTYPE_PENDING_REPLACE] if message.get(simplefix.TAG_EXECTYPE) in use_origclordid_types: client_order_id = f"{message.get(simplefix.TAG_ORIGCLORDID).decode('utf-8')}" else: client_order_id = f"{message.get(simplefix.TAG_CLORDID).decode('utf-8')}" if client_order_id not in self.order_book: self.order_book[client_order_id] = queue.Queue() self.order_book[client_order_id].put(message) # keep marketdata messages in application queue else: self.application_messages.put(message)
def parse_security_list(m: simplefix.FixMessage) -> dict: security_count = int(m.get(146)) securities = {} for i in range(1, security_count): # Required fields symbol = m.get(simplefix.TAG_SYMBOL, i).decode('utf-8') securities[symbol] = { "Product": m.get(460, i).decode('utf-8'), "MinPriceIncrement": float(m.get(969, i).decode('utf-8')), "SecurityDesc": m.get(simplefix.TAG_SECURITYDESC, i).decode('utf-8'), "Currency": m.get(simplefix.TAG_CURRENCY, i).decode('utf-8') } # Optional fields min_trade_vol = m.get(562, i) if min_trade_vol: securities[symbol]["MinTradeVol"] = float( min_trade_vol.decode('utf-8')) max_trade_vol = m.get(1140, i) if max_trade_vol: securities[symbol]["MaxTradeVol"] = float( max_trade_vol.decode('utf-8')) round_lot = m.get(561, i) if round_lot: securities[symbol]["RoundLot"] = float( round_lot.decode('utf-8')) return securities
def test_get_repeating(self): pkt = FixMessage() pkt.append_pair(42, "a") pkt.append_pair(42, "b") pkt.append_pair(42, "c") self.assertEqual("a", pkt.get(42)) self.assertEqual("b", pkt.get(42, 2)) self.assertEqual("c", pkt.get(42, 3)) self.assertEqual("a", pkt.get(42, 1)) self.assertIsNone(pkt.get(42, 4)) return
def test_get_repeating(self): """Test retrieval of repeating field's value""" pkt = FixMessage() pkt.append_pair(42, "a") pkt.append_pair(42, "b") pkt.append_pair(42, "c") self.assertEqual(fix_str("a"), pkt.get(42)) self.assertEqual(fix_str("b"), pkt.get(42, 2)) self.assertEqual(fix_str("c"), pkt.get(42, 3)) self.assertEqual(fix_str("a"), pkt.get(42, 1)) self.assertIsNone(pkt.get(42, 4)) return
def test_utcto_datetime(self): """Test UTCTimeOnly with datetime timestamp values""" msg = FixMessage() t = 1484581872.933458 dt = datetime.datetime.utcfromtimestamp(t) msg.append_utc_time_only(273, dt) self.assertEqual(fix_str("15:51:12.933"), msg.get(273))
def test_utcts_datetime(self): """Test UTCTimestamp with datetime timestamp values""" msg = FixMessage() t = 1484581872.933458 dt = datetime.datetime.utcfromtimestamp(t) msg.append_utc_timestamp(52, dt) self.assertEqual(fix_str("20170116-15:51:12.933"), msg.get(52))
def send(self, values: dict) -> None: with self._send_lock: msg = FixMessage() msg.append_pair(simplefix.TAG_BEGINSTRING, 'FIX.4.2') msg.append_pair(simplefix.TAG_SENDER_COMPID, self._sender_id) msg.append_pair(simplefix.TAG_TARGET_COMPID, self._target_id) msg.append_pair(simplefix.TAG_MSGSEQNUM, self._next_send_seq_num) for key, value in values.items(): if isinstance(value, datetime): msg.append_utc_timestamp(key, value) else: msg.append_pair(key, value) if not msg.get(simplefix.TAG_SENDING_TIME): msg.append_utc_timestamp(simplefix.TAG_SENDING_TIME) encoded = msg.encode() self._last_send_time = time.time() self._next_send_seq_num += 1 try: print('send', encoded.replace(b'\x01', b'|')) self._sock.sendall(encoded) except OSError: self.close(clean=False) return if msg.message_type == simplefix.MSGTYPE_LOGON: self._has_session = True
def test_time_datetime(self): """Test use of built-in datetime timestamp values""" msg = FixMessage() t = 1484581872.933458 dt = datetime.datetime.utcfromtimestamp(t) msg.append_time(52, dt) self.assertEqual(fix_str("20170116-15:51:12.933"), msg.get(52))
def test_tzts_seconds_only(self): """Test formatting of TZTimestamp values with seconds only""" msg = FixMessage() t = 1484581872.933458 msg.append_tz_timestamp(1253, t, 0) test = time.localtime(t) s = "%04u%02u%02u-%02u:%02u:%02u" % \ (test.tm_year, test.tm_mon, test.tm_mday, test.tm_hour, test.tm_min, test.tm_sec) offset = int( (datetime.datetime.fromtimestamp(t) - datetime.datetime.utcfromtimestamp(t)).total_seconds() / 60) if offset == 0: s += "Z" else: offset_hours = abs(offset) / 60 offset_mins = abs(offset) % 60 s += "%c%02u" % ("+" if offset > 0 else "-", offset_hours) if offset_mins > 0: s += ":%02u" % offset_mins self.assertEqual(fix_str(s), msg.get(1253)) return
def test_time_microseconds(self): msg = FixMessage() t = 1484581872.933458 msg.append_time(52, t, 6) self.assertEqual("2017-01-16-15:51:12.933458", msg.get(52)) return
def test_time_float(self): msg = FixMessage() t = 1484581872.933458 msg.append_time(52, t) self.assertEqual("2017-01-16-15:51:12.933", msg.get(52)) return
def test_tzto_parts_15_51_12_933_458_150(self): """Test TZTimeOnly with h, m, s, ms, and us components, partial hour offset.""" msg = FixMessage() msg.append_tz_time_only_parts(1, 15, 51, 12, 933, 458, offset=150) self.assertEqual(fix_str("15:51:12.933458+02:30"), msg.get(1)) return
def test_tzto_parts_15_51_12_270(self): """Test TZTimeOnly with hour, minute and second components, partial hour offset.""" msg = FixMessage() msg.append_tz_time_only_parts(1, 15, 51, 12, offset=-270) self.assertEqual(fix_str("15:51:12-04:30"), msg.get(1)) return
def test_append_tzts_datetime(self): msg = FixMessage() t = 1484581872.933458 local = datetime.datetime.fromtimestamp(t) msg.append_tz_timestamp(1132, local) test = time.localtime(t) s = "%04u%02u%02u-%02u:%02u:%02u.%03u" % \ (test.tm_year, test.tm_mon, test.tm_mday, test.tm_hour, test.tm_min, test.tm_sec, int((t - int(t)) * 1000)) offset = int((datetime.datetime.fromtimestamp(t) - datetime.datetime.utcfromtimestamp(t)).total_seconds() / 60) if offset == 0: s += "Z" else: offset_hours = abs(offset) / 60 offset_mins = abs(offset) % 60 s += "%c%02u" % ("+" if offset > 0 else "-", offset_hours) if offset_mins > 0: s += ":%02u" % offset_mins self.assertEqual(fix_str(s), msg.get(1132)) return
def test_tzto_parts_15_51_240(self): """Test TZTimeOnly with hour and minute components, full hour offset""" msg = FixMessage() msg.append_tz_time_only_parts(1, 15, 51, offset=-240) self.assertEqual(fix_str("15:51-04"), msg.get(1)) return
def test_utcto_seconds_only(self): """Test UTCTimeOnly formatting of seconds only""" msg = FixMessage() t = 1484581872.933458 msg.append_utc_time_only(273, t, 0) self.assertEqual(fix_str("15:51:12"), msg.get(273)) return
def test_utcto_float(self): """Test UTCTimeOnly with floating point value""" msg = FixMessage() t = 1484581872.933458 msg.append_utc_time_only(273, t) self.assertEqual(fix_str("15:51:12.933"), msg.get(273)) return
def simplefix_init(): msg = FixMessage() msg.append_string("8=FIX.4.2") msg.append_string("35=O") mohamed = users[0] schema = UserSchema() print(msg.get("35")) return jsonify(schema.dump(mohamed))
def test_utcts_seconds_only(self): """Test UTCTimestamp formatting of seconds only""" msg = FixMessage() t = 1484581872.933458 msg.append_utc_timestamp(52, t, 0) self.assertEqual(fix_str("20170116-15:51:12"), msg.get(52)) return
def test_utcts_float(self): """Test UTCTimestamp with floating point value""" msg = FixMessage() t = 1484581872.933458 msg.append_utc_timestamp(52, t) self.assertEqual(fix_str("20170116-15:51:12.933"), msg.get(52)) return
def test_time_seconds_only(self): """Test formatting of time values with no decimal component""" msg = FixMessage() t = 1484581872.933458 msg.append_time(52, t, 0) self.assertEqual(fix_str("20170116-15:51:12"), msg.get(52)) return
def test_time_microseconds(self): """Test formatting of time values with microseconds""" msg = FixMessage() t = 1484581872.933458 msg.append_time(52, t, 6) self.assertEqual(fix_str("20170116-15:51:12.933458"), msg.get(52)) return
def test_time_float(self): """Test floating point timestamp values""" msg = FixMessage() t = 1484581872.933458 msg.append_time(52, t) self.assertEqual(fix_str("20170116-15:51:12.933"), msg.get(52)) return
def test_time_datetime(self): msg = FixMessage() t = 1484581872.933458 dt = datetime.datetime.utcfromtimestamp(t) msg.append_time(52, dt) self.assertEqual("2017-01-16-15:51:12.933", msg.get(52)) return
def test_time_localtime(self): msg = FixMessage() t = 1484581872.933458 msg.append_time(52, t, utc=False) test = time.localtime(t) s = "%04u-%02u-%02u-%02u:%02u:%02u.%03u" % (test.tm_year, test.tm_mon, test.tm_mday, test.tm_hour, test.tm_min, test.tm_sec, int((t - int(t)) * 1000)) self.assertEqual(s, msg.get(52)) return
def _handle_session_message(self, message: simplefix.FixMessage) -> bool: assert isinstance(message, simplefix.FixMessage) is_session_message = False if message.get(simplefix.TAG_MSGTYPE) == simplefix.MSGTYPE_LOGON: is_session_message = True self.connection_state = FixConnectionState.LOGGED_IN elif message.get(simplefix.TAG_MSGTYPE) == simplefix.MSGTYPE_TEST_REQUEST: is_session_message = True m = self.create_message(simplefix.MSGTYPE_HEARTBEAT) m.append_pair(simplefix.TAG_TESTREQID, message.get(simplefix.TAG_TESTREQID)) self.write(m) if message.get(simplefix.TAG_RESETSEQNUMFLAG) == simplefix.RESETSEQNUMFLAG_YES: logging.info("resetting sequence number to 1") self.sequenceNum = 1 return is_session_message
def test_time_localtime(self): """Test non-UTC supplied time values""" msg = FixMessage() t = 1484581872.933458 msg.append_time(52, t, utc=False) test = time.localtime(t) s = "%04u%02u%02u-%02u:%02u:%02u.%03u" % \ (test.tm_year, test.tm_mon, test.tm_mday, test.tm_hour, test.tm_min, test.tm_sec, int((t - int(t)) * 1000)) self.assertEqual(fix_str(s), msg.get(52))
def test_remove_nth(self): """Test removal of nth field.""" msg = FixMessage() msg.append_pair(99999, 1) msg.append_pair(99999, 99999) msg.append_pair(99999, 2) self.assertEqual(3, msg.count()) result = msg.remove(99999, 2) self.assertEqual(b'99999', result) self.assertEqual(2, msg.count()) self.assertEqual(b'1', msg.get(99999, 1)) self.assertEqual(b'2', msg.get(99999, 2))
def reject_message(self, msg: FixMessage, reason: str, *, tag_id: Optional[Union[bytes, int]] = None, error_code: Union[bytes, int]) -> None: params = { simplefix.TAG_MSGTYPE: simplefix.MSGTYPE_REJECT, simplefix.TAG_REFSEQNUM: msg.get(simplefix.TAG_MSGSEQNUM), 372: msg.message_type, simplefix.TAG_TEXT: reason, simplefix.TAG_SESSIONREJECTREASON: error_code, } if tag_id is not None: params[371] = tag_id self.send(params)