def publishMarketData(self):
        self.logger.info("FixServer: publishMarketData %s", self.subscriptions)
        for subscription in self.subscriptions:
            if not subscription.hasSessions():
                self.logger.info(
                    "FixServer:No session subscribed, skip publish symbol %s",
                    subscription.symbol)
                continue

            message = self.fixVersion.MarketDataSnapshotFullRefresh()
            message.setField(quickfix.Symbol(subscription.symbol))
            message.setField(quickfix.MDReqID(self.idGen.reqID()))

            group = self.fixVersion.MarketDataSnapshotFullRefresh(
            ).NoMDEntries()

            subscription.createOrderBook()
            for quote in subscription.orderbook:
                self.logger.info('FixServer:add quote to fix message %s',
                                 str(quote))

                group.setField(quickfix.MDEntryType(quote.side))
                group.setField(quickfix.MDEntryPx(quote.price))
                group.setField(quickfix.MDEntrySize(quote.size))
                group.setField(quickfix.QuoteEntryID(quote.id))
                group.setField(quickfix.Currency(subscription.currency))
                group.setField(
                    quickfix.QuoteCondition(
                        quickfix.QuoteCondition_OPEN_ACTIVE))
                message.addGroup(group)

            for sessionID in subscription:
                self.sendToTarget(message, sessionID)
    def parse_MassQuote(self, message, sending_time):

        if self.verbose:
            print(self._server_str + ' MassQuote!')

        #################################
        # Enter Tick Storage Logic here.

        # we could have multiple QuoteSets for multiple symbols
        num_sets = extract_message_field_value(fix.NoQuoteSets(), message,
                                               'int')  # 296
        # print('num_sets:', num_sets)

        for i in range(num_sets):

            NoQuoteSets_Group = fix44.MassQuote.NoQuoteSets()

            # Groups have indexes in FIX messages starting at 1
            message.getGroup(i + 1, NoQuoteSets_Group)
            reqid = extract_message_field_value(fix.QuoteSetID(),
                                                NoQuoteSets_Group)
            # print(self._id_to_symbol)
            _symbol = self._id_to_symbol[reqid]

            # Bid/Offer data is inside NoQuoteEntries group, inside _NoQuoteSets group.
            # num_sets = extract_message_field_value(fix.NoQuoteEntries(), message, 'int')  # 295, should always be 1.
            NoQuoteEntries_Group = fix44.MassQuote.NoQuoteSets.NoQuoteEntries()
            NoQuoteSets_Group.getGroup(1, NoQuoteEntries_Group)

            depth = extract_message_field_value(fix.QuoteEntryID(),
                                                NoQuoteEntries_Group,
                                                'int')  # 299
            bid = extract_message_field_value(fix.BidSpotRate(),
                                              NoQuoteEntries_Group,
                                              'float')  # 188
            ask = extract_message_field_value(fix.OfferSpotRate(),
                                              NoQuoteEntries_Group,
                                              'float')  # 190
            bid_size = extract_message_field_value(fix.BidSize(),
                                                   NoQuoteEntries_Group,
                                                   'int')  # 134
            ask_size = extract_message_field_value(fix.OfferSize(),
                                                   NoQuoteEntries_Group,
                                                   'int')  # 135

            self.update_asset(sending_time, _symbol, depth, bid, ask, bid_size,
                              ask_size)

        #################################

        # If QuoteID is set the client has to respond immediately with a MassQuoteAcknowledgement.
        if message.isSetField(fix.QuoteID()):
            self.sender.send_MassQuoteAcknowledgement(message)
    def parse_MarketDataSnapshotFullRefresh(self, message, sending_time):

        if self.verbose:
            print(self._server_str + ' {MD} Full refresh!')

        symbol = extract_message_field_value(fix.Symbol(), message)

        if symbol in self.history_dict:

            num_entries = extract_message_field_value(fix.NoMDEntries(),
                                                      message, 'int')  # 268

            # MarketDataSnapshotFullRefresh message contains multiple NoQuoteSets group
            # Groups have indexes in FIX messages starting at 1
            NoMDEntries_Group = fix44.MarketDataSnapshotFullRefresh.NoMDEntries(
            )
            # NoMDEntries_Group = fix44.MarketDataIncrementalRefresh.NoMDEntries()

            for i in range(num_entries):
                message.getGroup(i + 1, NoMDEntries_Group)

                bid, ask, bid_size, ask_size = None, None, None, None

                _type = extract_message_field_value(
                    fix.MDEntryType(), NoMDEntries_Group,
                    'str')  # 269 (0: bid, 1: ask)
                price = extract_message_field_value(fix.MDEntryPx(),
                                                    NoMDEntries_Group,
                                                    'float')  # 270
                size = extract_message_field_value(fix.MDEntrySize(),
                                                   NoMDEntries_Group,
                                                   'float')  # 271
                depth = extract_message_field_value(fix.QuoteEntryID(),
                                                    NoMDEntries_Group,
                                                    'int')  # 299

                if _type == '0':
                    bid = price
                    bid_size = size
                elif _type == '1':
                    ask = price
                    ask_size = size

                if self.verbose:
                    print(
                        f'symbol: {symbol} | bid: {bid} | ask: {ask} | bid_size: {bid_size} | ask_size: {ask_size}'
                    )

                self.update_asset(sending_time, symbol, depth, bid, ask,
                                  bid_size, ask_size)
    def onMarketDataSnapshotFullRefresh(self, message, sessionID):
        skip_chance = random.choice(range(1, 101))
        if self.skipSnapshotChance > skip_chance:
            self.logger.info(
                "FIXSIM-CLIENT onMarketDataSnapshotFullRefresh skip making trade with random choice %d",
                skip_chance)
            return

        fix_symbol = quickfix.Symbol()
        message.getField(fix_symbol)
        symbol = fix_symbol.getValue()

        snapshot = Snapshot(symbol)

        group = self.fixVersion.MarketDataSnapshotFullRefresh.NoMDEntries()
        fix_no_entries = quickfix.NoMDEntries()
        message.getField(fix_no_entries)
        no_entries = fix_no_entries.getValue()

        for i in range(1, no_entries + 1):
            message.getGroup(i, group)
            price = quickfix.MDEntryPx()
            size = quickfix.MDEntrySize()
            currency = quickfix.Currency()
            quote_id = quickfix.QuoteEntryID()

            group.getField(quote_id)
            group.getField(currency)
            group.getField(price)
            group.getField(size)

            quote = Quote()
            quote.price = price.getValue()
            quote.size = size.getValue()
            quote.currency = currency.getValue()
            quote.id = quote_id.getValue()

            fix_entry_type = quickfix.MDEntryType()
            group.getField(fix_entry_type)
            entry_type = fix_entry_type.getValue()

            if entry_type == quickfix.MDEntryType_BID:
                snapshot.addBid(quote)
            elif entry_type == quickfix.MDEntryType_OFFER:
                snapshot.addAsk(quote)
            else:
                raise RuntimeError("Unknown entry type %s" % str(entry_type))

        self.makeOrder(snapshot)