Exemplo n.º 1
0
 private static void ProcessBrokerInitiatedExecutionReport(FixMessage msg)
 {
     loggerNoFlood.LogMessageFormatCheckFlood(LogEntryType.Debug,
                                                  101, 1000 * 60 * 15,
                                                  "ProcessBrokerInitiatedExecutionReport: [{0}]",
                                                  msg);
 }
Exemplo n.º 2
0
        private static void ProcessExecutionReport(FixMessage msg)
        {
            ExecutionReport execReport;
            try
            {
                // отчет об исполнении может относиться не к клиентскому ордеру,
                // а к инициированной брокером процедуре (в т.ч. - стопауту)
                if (!msg.fieldValues.ContainsKey(FixMessage.TAG_CLIENT_ORDER_ID))
                    ProcessBrokerInitiatedExecutionReport(msg);

                // промежуточный отчет по исполнению ордера, не учитывается
                var ordStatusStr = msg[FixMessage.TAG_ORDER_STATUS];
                if (ordStatusStr == FixMessage.VALUE_ORDER_NEW ||
                    ordStatusStr == FixMessage.VALUE_ORDER_PARTIALLY_FILLED ||
                    ordStatusStr == FixMessage.VALUE_ORDER_CALCULATED)
                    return;

                // получить Id запроса
                var clientIdStr = msg[FixMessage.TAG_CLIENT_ORDER_ID];
                int clientId;
                if (!int.TryParse(clientIdStr, out clientId))
                {
                    loggerNoFlood.LogMessageFormatCheckFlood(LogEntryType.Debug,
                        LogMagicIncorrectId, 1000 * 60 * 15, "Отчет брокера: поле ClientOrderId не представлено целым [{0}]",
                        clientIdStr);
                    return;
                }

                execReport = new ExecutionReport { brokerResponse = { RequestId = clientId, ValueDate = DateTime.Now } };
                if (ordStatusStr == FixMessage.VALUE_ORDER_FILLED)
                    execReport.brokerResponse.Status = OrderStatus.Исполнен;
                else
                    if (ordStatusStr == FixMessage.VALUE_ORDER_REJECTED)
                        execReport.brokerResponse.Status = OrderStatus.Отклонен;
                    else
                    {
                        // ордер может быть либо исполнен, либо не исполнен, третьего не дано
                        loggerNoFlood.LogMessageFormatCheckFlood(LogEntryType.Debug,
                        LogMagicIncorrectStatus, 1000 * 60 * 15, "Отчет брокера по запросу [{0}]: статус ({1}) не поддерживается",
                        clientId,
                        ordStatusStr);
                        return;
                    }

                var price = (decimal?)null;
                if (msg.fieldValues.ContainsKey(FixMessage.TAG_ARG_PX))
                    price = msg.GetValueDecimal(FixMessage.TAG_ARG_PX);
                else
                    if (msg.fieldValues.ContainsKey(FixMessage.TAG_PRICE))
                        price = msg.GetValueDecimal(FixMessage.TAG_PRICE);
                // нет цены в положительной квитанции
                if (!price.HasValue && execReport.brokerResponse.Status == OrderStatus.Исполнен)
                {
                    loggerNoFlood.LogMessageFormatCheckFlood(LogEntryType.Debug,
                        LogMagicNoQuoteInPositiveReport, 1000 * 60 * 15,
                        "Отчет брокера по запросу [{0}]: статус \"Исполнен\", нет цены",
                        clientId);
                    return;
                }
                execReport.brokerResponse.Price = price ?? 0;
                // сообщение об ошибке
                execReport.brokerResponse.RejectReason = OrderRejectReason.None;
                if (execReport.brokerResponse.Status != OrderStatus.Исполнен)
                {
                    if (msg.fieldValues.ContainsKey(FixMessage.TAG_ORD_REJECT_REASON))
                    {
                        var rejectReasonStr = msg[FixMessage.TAG_ORD_REJECT_REASON];
                        execReport.brokerResponse.RejectReason =
                            rejectReasonStr == FixMessage.VALUE_ORDER_REJECT_BROKER_EXCHANGE_OPT
                                ? OrderRejectReason.BrokerExchangeOption
                                : rejectReasonStr == FixMessage.VALUE_ORDER_REJECT_DUPLICATE_ORDER
                                      ? OrderRejectReason.DuplicateClOrdID
                                      : rejectReasonStr == FixMessage.VALUE_ORDER_REJECT_UNKNOWN_ORDER
                                      ? OrderRejectReason.UnknownOrder : OrderRejectReason.None;
                        execReport.brokerResponse.RejectReasonString = rejectReasonStr;
                    }
                }
            }
            catch (Exception ex)
            {
                loggerNoFlood.LogMessageFormatCheckFlood(LogEntryType.Debug,
                                                         LogMagicCommonReportParseError, 1000*60*15,
                                                         "Общая ошибка обработки ответа провайдера: {0}",
                                                         ex);
                return;
            }
            // отправить отчет FIX-дилеру
            try
            {
                execReport.SendToQueue(false);
            }
            catch (Exception ex)
            {
                loggerNoFlood.LogMessageFormatCheckFlood(LogEntryType.Debug,
                                                         LogMagicSendReportError, 1000 * 60 * 15,
                                                         "Ошибка отправки сообщения в очередь: {0}",
                                                         ex);
            }
        }
Exemplo n.º 3
0
 public static void ProcessMessageFromBroker(Message msgOrig, FixMessage msg)
 {
     //ThreadPool.QueueUserWorkItem(ProcessMessageAsynch, new MessagePair(msgOrig, msg));
     ProcessMessageAsynch(new MessagePair(msgOrig, msg));
 }
Exemplo n.º 4
0
        private static void ProcessExecutionReport(FixMessage msg)
        {
            ExecutionReport execReport;

            try
            {
                // отчет об исполнении может относиться не к клиентскому ордеру,
                // а к инициированной брокером процедуре (в т.ч. - стопауту)
                if (!msg.fieldValues.ContainsKey(FixMessage.TAG_CLIENT_ORDER_ID))
                {
                    ProcessBrokerInitiatedExecutionReport(msg);
                }

                // промежуточный отчет по исполнению ордера, не учитывается
                var ordStatusStr = msg[FixMessage.TAG_ORDER_STATUS];
                if (ordStatusStr == FixMessage.VALUE_ORDER_NEW ||
                    ordStatusStr == FixMessage.VALUE_ORDER_PARTIALLY_FILLED ||
                    ordStatusStr == FixMessage.VALUE_ORDER_CALCULATED)
                {
                    return;
                }

                // получить Id запроса
                var clientIdStr = msg[FixMessage.TAG_CLIENT_ORDER_ID];
                int clientId;
                if (!int.TryParse(clientIdStr, out clientId))
                {
                    loggerNoFlood.LogMessageFormatCheckFlood(LogEntryType.Debug,
                                                             LogMagicIncorrectId, 1000 * 60 * 15, "Отчет брокера: поле ClientOrderId не представлено целым [{0}]",
                                                             clientIdStr);
                    return;
                }

                execReport = new ExecutionReport {
                    brokerResponse = { RequestId = clientId, ValueDate = DateTime.Now }
                };
                if (ordStatusStr == FixMessage.VALUE_ORDER_FILLED)
                {
                    execReport.brokerResponse.Status = OrderStatus.Исполнен;
                }
                else
                if (ordStatusStr == FixMessage.VALUE_ORDER_REJECTED)
                {
                    execReport.brokerResponse.Status = OrderStatus.Отклонен;
                }
                else
                {
                    // ордер может быть либо исполнен, либо не исполнен, третьего не дано
                    loggerNoFlood.LogMessageFormatCheckFlood(LogEntryType.Debug,
                                                             LogMagicIncorrectStatus, 1000 * 60 * 15, "Отчет брокера по запросу [{0}]: статус ({1}) не поддерживается",
                                                             clientId,
                                                             ordStatusStr);
                    return;
                }

                var price = (decimal?)null;
                if (msg.fieldValues.ContainsKey(FixMessage.TAG_ARG_PX))
                {
                    price = msg.GetValueDecimal(FixMessage.TAG_ARG_PX);
                }
                else
                if (msg.fieldValues.ContainsKey(FixMessage.TAG_PRICE))
                {
                    price = msg.GetValueDecimal(FixMessage.TAG_PRICE);
                }
                // нет цены в положительной квитанции
                if (!price.HasValue && execReport.brokerResponse.Status == OrderStatus.Исполнен)
                {
                    loggerNoFlood.LogMessageFormatCheckFlood(LogEntryType.Debug,
                                                             LogMagicNoQuoteInPositiveReport, 1000 * 60 * 15,
                                                             "Отчет брокера по запросу [{0}]: статус \"Исполнен\", нет цены",
                                                             clientId);
                    return;
                }
                execReport.brokerResponse.Price = price ?? 0;
                // сообщение об ошибке
                execReport.brokerResponse.RejectReason = OrderRejectReason.None;
                if (execReport.brokerResponse.Status != OrderStatus.Исполнен)
                {
                    if (msg.fieldValues.ContainsKey(FixMessage.TAG_ORD_REJECT_REASON))
                    {
                        var rejectReasonStr = msg[FixMessage.TAG_ORD_REJECT_REASON];
                        execReport.brokerResponse.RejectReason =
                            rejectReasonStr == FixMessage.VALUE_ORDER_REJECT_BROKER_EXCHANGE_OPT
                                ? OrderRejectReason.BrokerExchangeOption
                                : rejectReasonStr == FixMessage.VALUE_ORDER_REJECT_DUPLICATE_ORDER
                                      ? OrderRejectReason.DuplicateClOrdID
                                      : rejectReasonStr == FixMessage.VALUE_ORDER_REJECT_UNKNOWN_ORDER
                                      ? OrderRejectReason.UnknownOrder : OrderRejectReason.None;
                        execReport.brokerResponse.RejectReasonString = rejectReasonStr;
                    }
                }
            }
            catch (Exception ex)
            {
                loggerNoFlood.LogMessageFormatCheckFlood(LogEntryType.Debug,
                                                         LogMagicCommonReportParseError, 1000 * 60 * 15,
                                                         "Общая ошибка обработки ответа провайдера: {0}",
                                                         ex);
                return;
            }
            // отправить отчет FIX-дилеру
            try
            {
                execReport.SendToQueue(false);
            }
            catch (Exception ex)
            {
                loggerNoFlood.LogMessageFormatCheckFlood(LogEntryType.Debug,
                                                         LogMagicSendReportError, 1000 * 60 * 15,
                                                         "Ошибка отправки сообщения в очередь: {0}",
                                                         ex);
            }
        }
Exemplo n.º 5
0
 public MessagePair(Message msgOrig, FixMessage msg)
 {
     this.msgOrig = msgOrig;
     this.msg = msg;
 }
Exemplo n.º 6
0
 public MessagePair(Message msgOrig, FixMessage msg)
 {
     this.msgOrig = msgOrig;
     this.msg     = msg;
 }
Exemplo n.º 7
0
 public static void ProcessMessageFromBroker(Message msgOrig, FixMessage msg)
 {
     //ThreadPool.QueueUserWorkItem(ProcessMessageAsynch, new MessagePair(msgOrig, msg));
     ProcessMessageAsynch(new MessagePair(msgOrig, msg));
 }
Exemplo n.º 8
0
        public static void ProcessMsgMarketDataIncrRefresh(Message msgOrig, FixMessage msg)
        {
            loggerNoFlood.LogMessageFormatCheckFlood(LogEntryType.Info,
                                                     LogMagicMarketData, 1000 * 60 * 60 * 2, "Quote incr: {0}", msg.ToString());

            try
            {
                // информация по тикерам разбита на группы, каждому тикеру - по группе
                var groupHeader = new NoMDEntries();
                msgOrig.getField(groupHeader);
                int numGroups = groupHeader.getValue();

                var group = FixMessage.FixVersion == FixVersion.Fix42
                                ? new QuickFix42.MarketDataIncrementalRefresh.NoMDEntries()
                                : FixMessage.FixVersion == FixVersion.Fix43
                                      ? (Group) new QuickFix43.MarketDataIncrementalRefresh.NoMDEntries()
                                      : new QuickFix44.MarketDataIncrementalRefresh.NoMDEntries();

                var lastQuotes = new System.Collections.Generic.Dictionary <string, QuoteData>();

                for (var i = 1; i <= numGroups; i++)
                {
                    var groupTicker = msgOrig.getGroup((uint)i, group);
                    var symbol      = groupTicker.getField(new Symbol()).getValue();

                    if (!ConvertFromProviderSymbolNaming(ref symbol))
                    {
                        return;
                    }

                    var entType     = new MDEntryType();
                    var entTypeChar = groupTicker.getField(entType).getValue();
                    var priceType   = entTypeChar == FixMessage.VALUE_MD_ENTRY_TYPE_BID[0]
                                        ? Contract.Entity.QuoteType.Bid
                                        : entTypeChar == FixMessage.VALUE_MD_ENTRY_TYPE_OFFER[0]
                                              ? Contract.Entity.QuoteType.Ask
                                              : Contract.Entity.QuoteType.NonSpecified;
                    if (priceType == Contract.Entity.QuoteType.NonSpecified)
                    {
                        continue;
                    }
                    var price = msg.GetValueFloat(FixMessage.TAG_MD_ENTRY_PX) ?? 0;
                    if (price == 0)
                    {
                        continue;
                    }

                    if (lastQuotes.ContainsKey(symbol))
                    {
                        var quote = lastQuotes[symbol];
                        if (priceType == Contract.Entity.QuoteType.Bid)
                        {
                            quote.bid = price;
                        }
                        else
                        {
                            quote.ask = price;
                        }
                    }
                    else
                    {
                        lastQuotes.Add(symbol, new QuoteData(
                                           priceType == Contract.Entity.QuoteType.Bid ? price : 0,
                                           priceType == Contract.Entity.QuoteType.Ask ? price : 0, DateTime.Now));
                    }
                }

                foreach (var quote in lastQuotes)
                {
                    loggerNoFlood.LogMessageFormatCheckFlood(LogEntryType.Info,
                                                             LogMagicQuote, 1000 * 60 * 15,
                                                             "Котировка {0}: {1} - {2}",
                                                             quote.Key, quote.Value.bid, quote.Value.ask);

                    QuoteDistributor.Instance.UpdateQuote(quote.Key,
                                                          quote.Value.bid == 0 ? (float?)null : quote.Value.bid,
                                                          quote.Value.ask == 0 ? (float?)null : quote.Value.ask);
                }

                loggerNoFlood.LogMessageFormatCheckFlood(LogEntryType.Info,
                                                         LogMagicParseMarketDataSuccess,
                                                         1000 * 60 * 10, "Обработан пакет MarketDataIncrRefresh");
            }
            catch (Exception ex)
            {
                loggerNoFlood.LogMessageFormatCheckFlood(LogEntryType.Info,
                                                         LogMagicParseMarketDataError,
                                                         1000 * 60 * 10, "Ошибка разбора MarketDataIncrRefresh: {0}", ex);
            }
        }
Exemplo n.º 9
0
        public static void ProcessMsgMarketDataFullRefresh(Message msgOrig, FixMessage msg)
        {
            loggerNoFlood.LogMessageFormatCheckFlood(
                LogEntryType.Info,
                LogMagicMarketData,
                1000 * 60 * 60 * 2, "Quote full: {0}", msg.ToString());

            // 8=FIX.4.4#9=128#35=W#34=2#49=XTB#52=20141125-16:12:33.269#56=PARTNER1#55=USDJPY..#262=USDJPY-25-16-12#268=2#269=0#270=117.906#10=097#

            try
            {
                var reqIdField = msgOrig.getField(new MDReqID());
                if (reqIdField == null)
                {
                    throw new Exception("Поле MDReqID не прочитано");
                }
                // инструмент указан в самом запросе
                var requestedSymbol = msg.GetValueString(FixMessage.TAG_SYMBOL);
                //requestedSymbol = TickerCodeDictionary.Instance.GetClearTickerName(requestedSymbol);
                // котировка будет исправлена в дальнейшем

                var groupHeader = new NoMDEntries();
                msgOrig.getField(groupHeader);
                var numGroups = groupHeader.getValue();

                if (numGroups == 0)
                {
                    throw new Exception("Поле NoMDEntries не прочитано");
                }

                var group = FixMessage.FixVersion == FixVersion.Fix42
                                ? new QuickFix42.MarketDataIncrementalRefresh.NoMDEntries()
                                : FixMessage.FixVersion == FixVersion.Fix43
                                      ? (Group) new QuickFix43.MarketDataIncrementalRefresh.NoMDEntries()
                                      : new QuickFix44.MarketDataIncrementalRefresh.NoMDEntries();

                float bid = 0, ask = 0;
                for (var i = 1; i <= numGroups; i++)
                {
                    var groupTicker = msgOrig.getGroup((uint)i, group);
                    // тип (бид или аск)
                    var isBid     = true;
                    var typeField = groupTicker.getField(new MDEntryType());
                    if (typeField == null)
                    {
                        throw new Exception("Поле MDEntryType не прочитано");
                    }
                    isBid = typeField.getValue() == '0';
                    // цена
                    var priceField = group.getField(new MDEntryPx());
                    if (priceField == null)
                    {
                        throw new Exception("Поле MDEntryPx не прочитано");
                    }
                    var price = priceField.getValue();
                    if (isBid)
                    {
                        bid = (float)price;
                    }
                    else
                    {
                        ask = (float)price;
                    }
                }
                // цены прочитаны?
                if (bid == 0 || ask == 0)
                {
                    loggerNoFlood.LogMessageFormatCheckFlood(LogEntryType.Info,
                                                             LogMagicLackPrice,
                                                             1000 * 60 * 10, "MarketDataFullRefresh - нет цены (" +
                                                             bid.ToStringUniformPriceFormat() + ", " + ask.ToStringUniformPriceFormat() + ")");
                    return;
                }

                // обновить котировку
                QuoteDistributor.Instance.UpdateQuote(requestedSymbol, bid, ask);

                loggerNoFlood.LogMessageFormatCheckFlood(LogEntryType.Info,
                                                         LogMagicParseMarketDataSuccess,
                                                         1000 * 60 * 10,
                                                         "Обработан пакет MarketDataFullRefresh - {0}: {1}/{2}",
                                                         requestedSymbol, bid, ask);
            }
            catch (Exception ex)
            {
                loggerNoFlood.LogMessageFormatCheckFlood(LogEntryType.Info,
                                                         LogMagicParseMarketDataError,
                                                         1000 * 60 * 10, "Ошибка разбора MarketDataFullRefresh: {0}", ex);
            }
        }
Exemplo n.º 10
0
        public static void ProcessMsgMarketDataFullRefresh(Message msgOrig, FixMessage msg)
        {
            loggerNoFlood.LogMessageFormatCheckFlood(
                LogEntryType.Info,
                LogMagicMarketData,
                1000 * 60 * 60 * 2, "Quote full: {0}", msg.ToString());

            // 8=FIX.4.4#9=128#35=W#34=2#49=XTB#52=20141125-16:12:33.269#56=PARTNER1#55=USDJPY..#262=USDJPY-25-16-12#268=2#269=0#270=117.906#10=097#

            try
            {
                var reqIdField = msgOrig.getField(new MDReqID());
                if (reqIdField == null)
                    throw new Exception("Поле MDReqID не прочитано");
                // инструмент указан в самом запросе
                var requestedSymbol = msg.GetValueString(FixMessage.TAG_SYMBOL);
                //requestedSymbol = TickerCodeDictionary.Instance.GetClearTickerName(requestedSymbol);
                // котировка будет исправлена в дальнейшем

                var groupHeader = new NoMDEntries();
                msgOrig.getField(groupHeader);
                var numGroups = groupHeader.getValue();

                if (numGroups == 0)
                    throw new Exception("Поле NoMDEntries не прочитано");

                var group = FixMessage.FixVersion == FixVersion.Fix42
                                ? new QuickFix42.MarketDataIncrementalRefresh.NoMDEntries()
                                : FixMessage.FixVersion == FixVersion.Fix43
                                      ? (Group)new QuickFix43.MarketDataIncrementalRefresh.NoMDEntries()
                                      : new QuickFix44.MarketDataIncrementalRefresh.NoMDEntries();

                float bid = 0, ask = 0;
                for (var i = 1; i <= numGroups; i++)
                {
                    var groupTicker = msgOrig.getGroup((uint)i, group);
                    // тип (бид или аск)
                    var isBid = true;
                    var typeField = groupTicker.getField(new MDEntryType());
                    if (typeField == null)
                        throw new Exception("Поле MDEntryType не прочитано");
                    isBid = typeField.getValue() == '0';
                    // цена
                    var priceField = group.getField(new MDEntryPx());
                    if (priceField == null)
                        throw new Exception("Поле MDEntryPx не прочитано");
                    var price = priceField.getValue();
                    if (isBid)
                        bid = (float)price;
                    else
                        ask = (float)price;
                }
                // цены прочитаны?
                if (bid == 0 || ask == 0)
                {
                    loggerNoFlood.LogMessageFormatCheckFlood(LogEntryType.Info,
                                                             LogMagicLackPrice,
                                                             1000 * 60 * 10, "MarketDataFullRefresh - нет цены (" +
                                                             bid.ToStringUniformPriceFormat() + ", " + ask.ToStringUniformPriceFormat() + ")");
                    return;
                }

                // обновить котировку
                QuoteDistributor.Instance.UpdateQuote(requestedSymbol, bid, ask);

                loggerNoFlood.LogMessageFormatCheckFlood(LogEntryType.Info,
                                                         LogMagicParseMarketDataSuccess,
                                                         1000 * 60 * 10,
                                                         "Обработан пакет MarketDataFullRefresh - {0}: {1}/{2}",
                                                         requestedSymbol, bid, ask);
            }
            catch (Exception ex)
            {
                loggerNoFlood.LogMessageFormatCheckFlood(LogEntryType.Info,
                                                         LogMagicParseMarketDataError,
                                                         1000 * 60 * 10, "Ошибка разбора MarketDataFullRefresh: {0}", ex);
            }
        }
Exemplo n.º 11
0
        /// <summary>
        /// сообщение содержит котировку: одну цену либо бид и аск сразу
        /// </summary>        
        public static void ProcessMsgQuote(FixMessage msg)
        {
            loggerNoFlood.LogMessageFormatCheckFlood(LogEntryType.Info,
                LogMagicMarketData, 1000 * 60 * 60 * 2, "Quote: {0}", msg.ToString());

            string symbol = msg.GetValueString(FixMessage.TAG_SYMBOL);
            if (string.IsNullOrEmpty(symbol))
            {
                Logger.Debug("Quote msg: no symbol tag");
                return;
            }

            if (!ConvertFromProviderSymbolNaming(ref symbol)) return;

            var bid = msg.GetValueFloat(FixMessage.TAG_BID_PRICE);
            var ask = msg.GetValueFloat(FixMessage.TAG_OFFER_PRICE);
            if (!bid.HasValue && ask.HasValue == false)
            {
                Logger.Debug("Quote msg: no bid - ask");
                return;
            }
            QuoteDistributor.Instance.UpdateQuote(symbol, bid, ask);
        }
Exemplo n.º 12
0
        public static void ProcessMsgMarketDataIncrRefresh(Message msgOrig, FixMessage msg)
        {
            loggerNoFlood.LogMessageFormatCheckFlood(LogEntryType.Info,
                LogMagicMarketData, 1000 * 60 * 60 * 2, "Quote incr: {0}", msg.ToString());

            try
            {
                // информация по тикерам разбита на группы, каждому тикеру - по группе
                var groupHeader = new NoMDEntries();
                msgOrig.getField(groupHeader);
                int numGroups = groupHeader.getValue();

                var group = FixMessage.FixVersion == FixVersion.Fix42
                                ? new QuickFix42.MarketDataIncrementalRefresh.NoMDEntries()
                                : FixMessage.FixVersion == FixVersion.Fix43
                                      ? (Group)new QuickFix43.MarketDataIncrementalRefresh.NoMDEntries()
                                      : new QuickFix44.MarketDataIncrementalRefresh.NoMDEntries();

                var lastQuotes = new System.Collections.Generic.Dictionary<string, QuoteData>();

                for (var i = 1; i <= numGroups; i++)
                {
                    var groupTicker = msgOrig.getGroup((uint)i, group);
                    var symbol = groupTicker.getField(new Symbol()).getValue();

                    if (!ConvertFromProviderSymbolNaming(ref symbol)) return;

                    var entType = new MDEntryType();
                    var entTypeChar = groupTicker.getField(entType).getValue();
                    var priceType = entTypeChar == FixMessage.VALUE_MD_ENTRY_TYPE_BID[0]
                                        ? Contract.Entity.QuoteType.Bid
                                        : entTypeChar == FixMessage.VALUE_MD_ENTRY_TYPE_OFFER[0]
                                              ? Contract.Entity.QuoteType.Ask
                                              : Contract.Entity.QuoteType.NonSpecified;
                    if (priceType == Contract.Entity.QuoteType.NonSpecified) continue;
                    var price = msg.GetValueFloat(FixMessage.TAG_MD_ENTRY_PX) ?? 0;
                    if (price == 0) continue;

                    if (lastQuotes.ContainsKey(symbol))
                    {
                        var quote = lastQuotes[symbol];
                        if (priceType == Contract.Entity.QuoteType.Bid)
                            quote.bid = price;
                        else
                            quote.ask = price;
                    }
                    else
                    {
                        lastQuotes.Add(symbol, new QuoteData(
                            priceType == Contract.Entity.QuoteType.Bid ? price : 0,
                            priceType == Contract.Entity.QuoteType.Ask ? price : 0, DateTime.Now));
                    }
                }

                foreach (var quote in lastQuotes)
                {
                    loggerNoFlood.LogMessageFormatCheckFlood(LogEntryType.Info,
                                                             LogMagicQuote, 1000 * 60 * 15,
                                                             "Котировка {0}: {1} - {2}",
                                                             quote.Key, quote.Value.bid, quote.Value.ask);

                    QuoteDistributor.Instance.UpdateQuote(quote.Key,
                        quote.Value.bid == 0 ? (float?)null : quote.Value.bid,
                        quote.Value.ask == 0 ? (float?)null : quote.Value.ask);
                }

                loggerNoFlood.LogMessageFormatCheckFlood(LogEntryType.Info,
                                                         LogMagicParseMarketDataSuccess,
                                                         1000 * 60 * 10, "Обработан пакет MarketDataIncrRefresh");
            }
            catch (Exception ex)
            {
                loggerNoFlood.LogMessageFormatCheckFlood(LogEntryType.Info,
                                                         LogMagicParseMarketDataError,
                                                         1000 * 60 * 10, "Ошибка разбора MarketDataIncrRefresh: {0}", ex);
            }
        }