def parse_ExecutionReport(self, message, sending_time): # Extract fields from the message here and pass to an upper layer if self.verbose: print('[fromApp] Execution Report received!') print(f'[fromApp] {read_FIX_message(message)}') # Tag 11 (client order ID, the one we sent) # must be the same type (int) as in open_orders. ClOrdID = extract_message_field_value(fix.ClOrdID(), message, 'int') # print('ClOrdID:', ClOrdID) # Tag 15 # _Currency = extract_message_field_value(fix.Currency(), message) # print('_Currency:', _Currency) # Tag 17 # _ExecID = extract_message_field_value(fix.ExecID(), message) # print('_ExecID:', _ExecID) # Tag 37 # _OrderID = extract_message_field_value(fix.OrderID(), message) # print('_OrderID:', _OrderID) # Tag 39 OrderStatus: 0 = New, 1 = Partially filled, 2 = Filled, 3 = Done for day, 4 = Canceled, # 6 = Pending Cancel (e.g. result of Order Cancel Request <F>), 7 = Stopped, # 8 = Rejected, 9 = Suspended, A = Pending New, B = Calculated, C = Expired, # D = Accepted for bidding, E = Pending Replace (e.g. result of Order Cancel/Replace Request <G>) # maybe also check 3=Done for day, 7=Stopped, 9=Suspended, B=Calculated and C=Expired, but it seems that Stopped means it can still be filled? # https://www.onixs.biz/fix-dictionary/4.4/tagNum_39.html ordStatus = extract_message_field_value(fix.OrdStatus(), message, 'str') # print('ordStatus:', ordStatus) # Tag 150 Execution type # 0 = New, 4 = Canceled, F = Trade (partial fill or fill), I = Order Status, ... _ExecType = extract_message_field_value(fix.ExecType(), message) # print('_ExecType:', _ExecType) # if the exection report is a response to an OrderStatusRequest, # fields other than OrdStatus might not be set. if _ExecType == 'I': if ClOrdID in self.open_orders.keys(): self.open_orders[ClOrdID].status = ordStatus else: print(f'Order {ClOrdID} not found! Order status: {ordStatus}') return # Tag 40 OrderType: 1 = Market, 2 = Limit, 3 = Stop ordType = extract_message_field_value(fix.OrdType(), message, 'str') # print('ordType:', ordType) # Tag 44 price = extract_message_field_value(fix.Price(), message, 'float') # print('price:', price) # Tag 54 side = extract_message_field_value(fix.Side(), message, 'str') # print('side:', side) # Tag 55 symbol = extract_message_field_value(fix.Symbol(), message, 'str') # print('symbol:', symbol) # canceled or rejected: here a few fields are not defined, which would # prevent further parsing of the message. therefore, exiting earlier. # https://www.onixs.biz/fix-dictionary/4.4/tagNum_39.html # tags not defined: 60, 18, 100 # [fromApp] 8=FIX.4.4, 9=164, 35=8, 34=33, 49=XCD17, 52=20201020-10:04:25.518, # 56=T008, 11=51515, 14=0.0, 17=0, 37=0, 38=1000, 39=8, 40=2, 44=1.17, 54=1, # 55=EUR/USD, 58=reject: duplicate clOrdID, 150=8, 151=0.0, 10=073 if ordStatus == '4' or ordStatus == '6' or ordStatus == '8': action = 'canceled' if ordStatus == '8': action = 'rejected' if ClOrdID in self.open_orders.keys( ): # the report might be sent after restarting the FIX algo. if ordStatus == '4' or ordStatus == '6': if self.verbose: print(f'Order {action}: {self.open_orders[ClOrdID]}') if ordStatus == '8': if self.verbose: print(f'Order {action}: {self.open_orders[ClOrdID]}') del self.open_orders[ClOrdID] elif self.verbose: print(f'Order {action}, but not found in open_orders.') report = execution_report(ClOrdID, symbol, side, price, ordType, ordStatus, 0, 0, 0, 0) self.execution_history.append(report) if self.verbose: print(report) transactTime = datetime_to_str(datetime.datetime.utcnow()) log( self.execution_logger, '{},{},{},{},{},{},{},{},{},{},{}'.format( transactTime, ClOrdID, symbol, side, price, ordType, ordStatus, 0, 0, 0, 0)) if self.read_positions_from_file: self.save_positions_to_file() self.lock.acquire() self.tick_processor.on_execution_report(report, self) self.lock.release() return # Tag 60 (how to make it a datetime object?) # here without extract_message_field_value() because we want to call getString() and not getValue(). transactTime = fix.TransactTime() message.getField(transactTime) transactTime = transactTime.getString() # print('transactTime:', transactTime) # Tag 18 orderQty = extract_message_field_value(fix.OrderQty(), message, 'int') # print('orderQty:', orderQty) # Tag 110 minQty = extract_message_field_value(fix.MinQty(), message, 'int') # print('minQty:', minQty) # Tag 14 CumQty: Total quantity filled. cumQty = extract_message_field_value(fix.CumQty(), message, 'int') # print('cumQty:', cumQty) # Tag 151 LeavesQty: Quantity open for further execution. 0 if 'Canceled', 'DoneForTheDay', # 'Expired', 'Calculated', or' Rejected', else LeavesQty <151> = OrderQty <38> - CumQty <14>. leavesQty = extract_message_field_value(fix.LeavesQty(), message, 'int') # print('leavesQty:', leavesQty) if not ClOrdID in self.open_orders.keys(): log(self.execution_logger, f'[ERROR] ClOrdID {ClOrdID} not found in open_orders:', True) for o in self.open_orders: log(self.execution_logger, o) report = execution_report(ClOrdID, symbol, side, price, ordType, ordStatus, orderQty, minQty, cumQty, leavesQty) self.execution_history.append(report) print(report) # maybe better exit if the ID was not found? # but it could happen on the start of a session with PersistMessages=Y. return self.open_orders[ClOrdID].status = ordStatus if ordStatus == '0': # new self.open_orders[ClOrdID].openTime = transactTime elif ordStatus == '1' and leavesQty > 0: # partially filled if leavesQty == 0: if ClOrdID in self.open_orders.keys(): del self.open_orders[ClOrdID] else: self.open_orders[ClOrdID].leaves_quantity = leavesQty # orderQty is the ordered quantity, cumQty the one filled. self.add_position(symbol, side, cumQty) # for consistency check. canceled quantity is orderQty-cumQty. self.add_canceled_quantity(symbol, side, orderQty - cumQty) elif ordStatus == '2': # filled if ClOrdID in self.open_orders.keys(): del self.open_orders[ClOrdID] self.add_position(symbol, side, cumQty) # if there was a partial fill before, a following canceled order can have a non-zero cumQty. elif ordStatus == '3': if ClOrdID in self.open_orders.keys(): del self.open_orders[ClOrdID] report = execution_report(ClOrdID, symbol, side, price, ordType, ordStatus, orderQty, minQty, cumQty, leavesQty) self.execution_history.append(report) if self.verbose: print(report) log( self.execution_logger, '{},{},{},{},{},{},{},{},{},{},{}'.format( transactTime, ClOrdID, symbol, side, price, ordType, ordStatus, orderQty, minQty, cumQty, leavesQty)) if self.read_positions_from_file: self.save_positions_to_file() self.lock.acquire() self.tick_processor.on_execution_report(report, self) self.lock.release()
def onMessage(self, message, sessionID): print("OnMessage %s" % message) msgType = fix.MsgType() message.getHeader().getField(msgType) if (msgType.getValue() == "X"): print("MarketDataIncrementalRefresh %s" % message) noMDEntries = fix.NoMDEntries() message.getField(noMDEntries) if (noMDEntries.getValue() != 1): print("NoMDEntries in MarketDataIncrementalRefresh is not 1!") return group = fix44.MarketDataIncrementalRefresh.NoMDEntries() message.getGroup(1, group) entryID = fix.MDEntryID() group.getField(entryID) action = fix.MDUpdateAction() group.getField(action) actionvalue = action.getValue() # 0=New, 1=Update, 2=Delete) if (actionvalue == '2'): # delete if entryID.getValue() in securities: del securities[entryID.getValue()] return security = SECURITY() security.MDEntryID = entryID.getValue() security.MDUpdateAction = action.getValue() symbol = fix.Symbol() if (group.isSetField(symbol)): group.getField(symbol) security.Symbol = symbol.getValue() entryType = fix.MDEntryType() if (group.isSetField(entryType)): group.getField(entryType) security.MDEntryType = entryType.getValue() price = fix.MDEntryPx() if (group.isSetField(price)): group.getField(price) security.MDEntryPx = price.getValue() size = fix.MDEntrySize() if (group.isSetField(size)): group.getField(size) security.MDEntrySize = size.getValue() qty = fix.MinQty() if (group.isSetField(qty)): group.getField(qty) security.MinQty = qty.getValue() inc = MinInc() if (message.isSetField(inc)): message.getField(inc) security.MinInc = inc.getValue() br = MinBr() if (message.isSetField(br)): message.getField(br) security.MinBR = br.getValue() ytm = YTM() if (message.isSetField(ytm)): message.getField(ytm) security.YTM = ytm.getValue() ytw = YTW() if (message.isSetField(ytw)): message.getField(ytw) security.YTW = ytw.getValue() print(security) securities[entryID.getValue()] = security
def onMessage(self, message, sessionID): # print("OnMessage %s" % message) msgType = fix.MsgType() message.getHeader().getField(msgType) if msgType.getValue() == "X": # print("MarketDataIncrementalRefresh %s" % message) noMDEntries = fix.NoMDEntries() message.getField(noMDEntries) if (noMDEntries.getValue() != 1): # print("NoMDEntries in MarketDataIncrementalRefresh is not 1!") return group = fix44.MarketDataIncrementalRefresh.NoMDEntries() message.getGroup(1, group) entryID = fix.MDEntryID() group.getField(entryID) action = fix.MDUpdateAction() group.getField(action) security = LAST_TRADE() security.MDEntryID = entryID.getValue() security.MDUpdateAction = action.getValue() symbol = fix.Symbol() if (group.isSetField(symbol)): group.getField(symbol) security.Symbol = symbol.getValue() entryType = fix.MDEntryType() if (group.isSetField(entryType)): group.getField(entryType) security.MDEntryType = entryType.getValue() price = fix.MDEntryPx() if (group.isSetField(price)): group.getField(price) security.MDEntryPx = price.getValue() size = fix.MDEntrySize() if (group.isSetField(size)): group.getField(size) security.MDEntrySize = size.getValue() qty = fix.MinQty() if (group.isSetField(qty)): group.getField(qty) security.MinQty = qty.getValue() fire(self.callback, "OnTradeUpdated", **{"trade": security}) if msgType.getValue() == 'W': book = BOOK() Symbol = fix.Symbol() message.getField(Symbol) book.symbol = Symbol.getValue() noMDEntries = fix.NoMDEntries() message.getField(noMDEntries) group = fix44.MarketDataSnapshotFullRefresh.NoMDEntries() MDEntryType = fix.MDEntryType() MDEntryPx = fix.MDEntryPx() MDEntrySize = fix.MDEntrySize() for i in range(1, noMDEntries.getValue()): message.getGroup(i, group) group.getField(MDEntryType) group.getField(MDEntryPx) group.getField(MDEntrySize) if MDEntryType.getValue() == '0': book.bid.append(MDEntryPx.getValue()) book.bid_size.append(MDEntrySize.getValue()) if MDEntryType.getValue() == '1': book.ask.append(MDEntryPx.getValue()) book.ask_size.append(MDEntrySize.getValue()) fire(self.callback, "OnBookUpdated", **{"book": book})