def check(self, messages: [ReconMessage], *args, **kwargs) -> Event: logger.info( f"RULE '{self.get_name()}': CHECK: input_messages: {messages}") ignore_fields = [ 'CheckSum', 'BodyLength', 'SendingTime', 'TargetCompID', 'PartyID', 'OrderID', 'CumQty', 'OrderQty', 'ExecID', 'LeavesQty', 'MsgSeqNum', 'Price', 'TimeInForce', 'Side', 'Text', 'OrdStatus', 'ClOrdID' ] verification_component = self.message_comparator.compare_messages( messages, ignore_fields) info_for_name = dict() for message in messages: info_for_name.update(message.hash_info) body = EventUtils.create_event_body(verification_component) attach_ids = [msg.proto_message.metadata.id for msg in messages] return EventUtils.create_event( name=f"Match by '{ReconMessage.get_info(info_for_name)}'", status=verification_component.status, attached_message_ids=attach_ids, body=body)
def store_no_match_within_timeout(self, rule_event_id: EventID, recon_message: ReconMessage, actual_timestamp: int, timeout: int): name = f'{recon_message.all_info}' units = ['sec', 'ms', 'mcs', 'ns'] factor_units = [1_000_000_000, 1_000_000, 1_000, 1] for unit, factor_unit in zip(units, factor_units): if not any((actual_timestamp % factor_unit, recon_message.timestamp % factor_unit, timeout % factor_unit)): break actual_timestamp = int(actual_timestamp / factor_unit) message_timestamp = int(recon_message.timestamp / factor_unit) timeout = int(timeout / factor_unit) event_message = f"Timestamp of the last received message: '{actual_timestamp:,}' {unit}\n" \ f"Timestamp this message: '{message_timestamp:,}' {unit}\n" \ f"Timeout: '{timeout:,}' {unit}" body = EventUtils.create_event_body(MessageComponent(event_message)) attached_message_ids = self._get_attached_message_ids(recon_message) event = EventUtils.create_event(name=name, body=body, attached_message_ids=attached_message_ids, type=EventUtils.EventType.EVENT) logger.debug("Create '%s' Event for rule Event '%s'", self.NO_MATCH_WITHIN_TIMEOUT, rule_event_id) self.send_event(event, rule_event_id, self.NO_MATCH_WITHIN_TIMEOUT)
def __init__(self, recon: Recon, cache_size: int, match_timeout: int, autoremove_timeout: Optional[int], configuration) -> None: self.recon = recon self.configure(configuration) self.name = self.get_name() logger.info("Rule '%s' initializing...", self.name) self.event_store = recon.event_store self.message_comparator: Optional[ MessageComparator] = recon.message_comparator self.match_timeout = match_timeout self.autoremove_timeout = autoremove_timeout self.reprocess_queue = list() self.rule_event: Event = \ EventUtils.create_event(name=self.name, parent_id=recon.event_store.root_event.id, body=EventUtils.create_event_body(MessageComponent(message=self.get_description())), type=EventUtils.EventType.RULE) logger.debug( "Created report Event for Rule '%s': %s", self.name, text_format.MessageToString(self.rule_event, as_one_line=True)) self.event_store.send_parent_event(self.rule_event) self.__cache = Cache(self, cache_size) self.RULE_PROCESSING_TIME = Histogram( f"th2_recon_{re.sub('[^a-zA-Z0-9_: ]', '', self.name).lower().replace(' ', '_')}_rule_processing_time", 'Time of the message processing with a rule', buckets=common_metrics.DEFAULT_BUCKETS) logger.info("Rule '%s' initialized", self.name)
def check(self, messages: [ReconMessage], *args, **kwargs) -> Event: logger.info(f"RULE '{self.get_name()}': CHECK: ") table_component = TableComponent([ 'Session alias', 'MessageType', 'ExecType', 'ClOrdID', 'Group ID' ]) for msg in messages: msg_type = msg.proto_message.metadata.message_type exec_type = msg.proto_message.fields['ExecType'].simple_value cl_ord_id = msg.proto_message.fields['ClOrdID'].simple_value session_alias = msg.proto_message.metadata.id.connection_id.session_alias table_component.add_row(session_alias, msg_type, exec_type, cl_ord_id, msg.group_id) info_for_name = dict() for message in messages: info_for_name.update(message.hash_info) body = EventUtils.create_event_body(table_component) attach_ids = [msg.proto_message.metadata.id for msg in messages] return EventUtils.create_event( name= f"Match by '{ReconMessage.get_info(info_for_name)}' from 3 group", attached_message_ids=attach_ids, body=body)
def store_no_match_within_timeout(self, rule_event_id: EventID, recon_message: ReconMessage, actual_timestamp: int, timeout: int): name = f'{recon_message.get_all_info()}' message_timestamp = MessageUtils.get_timestamp_ns( recon_message.proto_message) units = ['sec', 'ms', 'mcs', 'ns'] factor_units = [1_000_000_000, 1_000_000, 1_000, 1] units_idx = 0 while actual_timestamp % factor_units[units_idx] != 0 \ or message_timestamp % factor_units[units_idx] != 0 \ or timeout % factor_units[units_idx] != 0: units_idx += 1 unit = units[units_idx] actual_timestamp = int(actual_timestamp / factor_units[units_idx]) message_timestamp = int(message_timestamp / factor_units[units_idx]) timeout = int(timeout / factor_units[units_idx]) event_message = f"Timestamp of the last received message: '{actual_timestamp:,}' {unit}\n" \ f"Timestamp this message: '{message_timestamp:,}' {unit}\n" \ f"Timeout: '{timeout:,}' {unit}" body = EventUtils.create_event_body(MessageComponent(event_message)) attached_message_ids = [recon_message.proto_message.metadata.id] event = EventUtils.create_event( name=name, body=body, attached_message_ids=attached_message_ids) logger.info( f"Create '{self.NO_MATCH_WITHIN_TIMEOUT}' Event for rule Event '{rule_event_id}'" ) self.send_event(event, rule_event_id, self.NO_MATCH_WITHIN_TIMEOUT)
def store_error(self, rule_event_id: EventID, event_name: str, error_message: str, messages: List[ReconMessage] = None): body = EventUtils.create_event_body(MessageComponent(error_message)) attached_message_ids = self._get_attached_message_ids(*messages) event = EventUtils.create_event(name=event_name, status=EventStatus.FAILED, attached_message_ids=attached_message_ids, body=body, type=EventUtils.EventType.EVENT) logger.debug("Create '%s' Event for rule Event '%s'", self.ERRORS, rule_event_id) self.send_event(event, rule_event_id, self.ERRORS)
def store_message_removed(self, rule_event_id: EventID, message: ReconMessage, event_message: str): name = f"Remove {message.all_info}" event_message += f"\n Message {'not' if not message.is_matched else ''} matched" body = EventUtils.create_event_body(MessageComponent(event_message)) attached_message_ids = self._get_attached_message_ids(message) event = EventUtils.create_event(name=name, body=body, status=EventStatus.SUCCESS if message.is_matched else EventStatus.FAILED, attached_message_ids=attached_message_ids, type=EventUtils.EventType.EVENT) logger.debug("Create '%s' Event for rule Event '%s'", self.NO_MATCH, rule_event_id) self.send_event(event, rule_event_id, self.NO_MATCH)
def store_no_match(self, rule_event_id: EventID, message: ReconMessage, event_message: str): name = f"Remove '{message.proto_message.metadata.message_type}' {message.get_all_info()}" event_message += f"\n Message {'not' if not message.is_matched else ''} matched" body = EventUtils.create_event_body(MessageComponent(event_message)) attached_message_ids = [message.proto_message.metadata.id] event = EventUtils.create_event( name=name, body=body, status=EventStatus.SUCCESS if message.is_matched else EventStatus.FAILED, attached_message_ids=attached_message_ids) logger.info( f"Create '{self.NO_MATCH}' Event for rule Event '{rule_event_id}'") self.send_event(event, rule_event_id, self.NO_MATCH)
def check(self, messages: [ReconMessage]) -> Event: logger.info(f"RULE '{self.get_name()}': CHECK: input_messages: {messages}") ignore_fields = ['CheckSum', 'BodyLength', 'SendingTime', 'TransactTime', 'MsgSeqNum', 'ClOrdID'] verification_component = self.message_comparator.compare_messages(messages, ignore_fields) info_for_name = dict() for message in messages: info_for_name.update(message.hash_info) body = EventUtils.create_event_body(verification_component) attach_ids = [msg.proto_message.metadata.id for msg in messages] return EventUtils.create_event(name=f"Match by '{ReconMessage.get_info(info_for_name)}'", status=verification_component.status, attached_message_ids=attach_ids, body=body)
def store_error(self, rule_event_id: EventID, event_name: str, error_message: str, messages: [ReconMessage] = None): body = EventUtils.create_event_body(MessageComponent(error_message)) attached_message_ids = [ message.proto_message.metadata.id for message in messages ] event = EventUtils.create_event( name=event_name, status=EventStatus.FAILED, attached_message_ids=attached_message_ids, body=body) logger.info( f"Create '{self.ERRORS}' Event for rule Event '{rule_event_id}'") self.send_event(event, rule_event_id, self.ERRORS)
def send_event(self, event: Event, rule_event_id: EventID, group_event_name: str): try: if rule_event_id.id not in self.__group_event_by_rule_id: self.__group_event_by_rule_id[rule_event_id.id] = dict() if group_event_name not in self.__group_event_by_rule_id[rule_event_id.id]: group_event = EventUtils.create_event(parent_id=rule_event_id, name=group_event_name, type=EventUtils.EventType.STATUS) logger.debug(f"Create group Event '%s' for rule Event '%s'", group_event_name, rule_event_id) self.__group_event_by_rule_id[rule_event_id.id][group_event_name] = group_event self.send_parent_event(group_event) group_event = self.__group_event_by_rule_id[rule_event_id.id][group_event_name] event.id.CopyFrom(EventUtils.new_event_id()) event.parent_id.CopyFrom(group_event.id) self.__events_batch_collector.put_event(event) except Exception: logger.exception(f'Error while sending event')
def __init__(self, event_router: EventBatchRouter, report_name: str, event_batch_max_size: int, event_batch_send_interval: int) -> None: self.event_router = event_router self.__events_batch_collector = EventsBatchCollector(event_router, event_batch_max_size, event_batch_send_interval) self.__group_event_by_rule_id = dict() self.root_event: Event = EventUtils.create_event(name='Recon: ' + report_name, type=EventUtils.EventType.ROOT) logger.debug('Created root report Event for Recon: %s', text_format.MessageToString(self.root_event, as_one_line=True)) self.send_parent_event(self.root_event)
def __init__(self, event_router: EventBatchRouter, report_name: str, event_batch_max_size: int, event_batch_send_interval: int) -> None: self.event_router = event_router self.__events_batch_collector = EventsBatchCollector( event_router, event_batch_max_size, event_batch_send_interval) self.__group_event_by_rule_id = dict() self.root_event: Event = EventUtils.create_event(name='Recon: ' + report_name) logger.info(f'Created root report Event for Recon: {self.root_event}') self.send_parent_event(self.root_event)
def send_event(self, event: Event, rule_event_id: EventID, group_event_name: str): try: if not self.__group_event_by_rule_id.__contains__( rule_event_id.id): self.__group_event_by_rule_id[rule_event_id.id] = dict() if not self.__group_event_by_rule_id[ rule_event_id.id].__contains__(group_event_name): group_event = EventUtils.create_event(parent_id=rule_event_id, name=group_event_name) logger.info( f"Create group Event '{group_event_name}' for rule Event '{rule_event_id}'" ) self.__group_event_by_rule_id[ rule_event_id.id][group_event_name] = group_event self.send_parent_event(group_event) group_event = self.__group_event_by_rule_id[ rule_event_id.id][group_event_name] event.id.CopyFrom(EventUtils.new_event_id()) event.parent_id.CopyFrom(group_event.id) self.__events_batch_collector.put_event(event) except Exception: logger.exception(f'Error while sending event')
def check(self, messages: [ReconMessage], *args, **kwargs) -> Event: message_types = [] cl_order_id = messages[0].proto_message.fields['ClOrdID'].simple_value latency_type = 'Unknown' recv_msg: Message = None send_msg: Message = None recv_msg_type: str = '' send_msg_type: str = '' explanation = None msg: ReconMessage for msg in messages: message = msg.proto_message message_type = message.metadata.message_type message_types.append(message_type) if msg.group_id == Group.RESPONSE: recv_msg = msg.proto_message recv_msg_type = message_type else: send_msg = msg.proto_message send_msg_type = message_type if recv_msg_type == 'ExecutionReport': exec_type = recv_msg.fields['ExecType'].simple_value ord_status = recv_msg.fields['OrdStatus'].simple_value logger.info(f"RULE '{self.get_name()}': " f"CHECK: messageER: [ClOrdID:{cl_order_id}, " f"ExecType: {exec_type}, " f"OrdStatus: {ord_status}]") if send_msg_type == 'NewOrderSingle': if exec_type == 'A' and ord_status == 'A': latency_type = 'PendingNew' elif exec_type == '0' and ord_status == '0': latency_type = 'New' elif exec_type == 'F' and ord_status in ['1', '2'] and \ recv_msg.fields['LastLiquidityInd'].simple_value == '2': latency_type = 'Trade' elif exec_type == '8' and ord_status == '8': latency_type = 'NewReject' elif send_msg_type == 'OrderCancelRequest': if exec_type == '6' and ord_status == '6': latency_type = 'PendingCancel' elif (exec_type == '4' and ord_status == '4') or (exec_type == 'C' and ord_status == 'C'): latency_type = 'Cancel' elif send_msg_type == 'OrderCancelReplaceRequest': if exec_type == 'E' and ord_status == 'E': latency_type = 'PendingReplace' elif exec_type == '5' and ord_status in ['0', '1']: latency_type = 'Replace' # Should be always in the end. if latency_type == 'Unknown': explanation = MessageComponent(f"Attention! Unknown messages combination. \n" f"ER[ExecType]: {exec_type}, ER[OrdStatus]: {ord_status}\n\n" f" --------------- Recv msg --------------- \n {recv_msg}\n" f" --------------- Send msg --------------- \n {send_msg}") elif recv_msg_type == 'OrderCancelReject': if send_msg_type == 'OrderCancelReplaceRequest': latency_type = 'ReplaceReject' elif send_msg_type == 'OrderCancelRequest': latency_type = 'CancelReject' else: logger.error(f"RULE '{self.get_name()}': " f"CHECK: Unknown message received. " f"Msg types: {message_types}\n" f"Recv msg: {recv_msg}\n" f"Send msg: {send_msg}") type1, type2, latency = latency_check(recv_msg, send_msg) table = TableComponent(['Name', 'Value']) table.add_row('ClOrdId', cl_order_id) table.add_row('Message Response', type1) table.add_row('Message Request', type2) table.add_row('Latency type', latency_type) table.add_row('Latency', latency) event_message = f'{type1} and {type2} ' \ f'Latency_type: {latency_type} ' \ f'Latency = {latency}' logger.info( f"RULE '{self.get_name()}': Thread: {threading.current_thread().name}: " f"EventMessage={event_message}. Latency was calculated for {cl_order_id} between {message_types[0]} " f"and {message_types[1]}") if explanation is None: body = EventUtils.create_event_body(table) else: body = EventUtils.create_event_body([table, explanation]) attach_ids = [msg.proto_message.metadata.id for msg in messages] status = EventStatus.SUCCESS if latency < self.LATENCY_LIMIT else EventStatus.FAILED return EventUtils.create_event(name=f"Match by ClOrdID: '{cl_order_id}'", status=status, attached_message_ids=attach_ids, body=body)