コード例 #1
0
 /// <summary>
 /// Добавить в очередь ответ на транзакцию
 /// </summary>
 /// <param name="tr"></param>
 public void PutPendingTransactionReply(QLTransactionReply tr)
 {
     using (locker.WriteLock())
     {
         pendingTransactionReplies.Add(tr);
     }
 }
コード例 #2
0
        /// <summary>
        /// Обработка неудачной транзакции
        /// </summary>
        /// <param name="message"></param>
        private void ProcessFailedTransactionReply(QLTransactionReply message)
        {
            var newOrderTransaction    = container.GetNewOrderTransaction(message.trans_id);
            var killOrderTransaction   = container.GetKillOrderTransactionByTransId(message.trans_id);
            var modifyOrderTransaction = container.GetModifyOrderTransactionByTransId(message.trans_id);

            switch (message.status)
            {
            case 13:     // отвергнута как кросс сделка
                Logger.Error().Print("Transaction failed, due to potential cross trade", LogFields.Message(message));
                break;

            default:
                Logger.Error().Print("Transaction failed", LogFields.Message(message));
                break;
            }

            OnMessageReceived(message: new TransactionReply
            {
                Success       = message.Successful,
                Message       = message.result_msg,
                TransactionId = newOrderTransaction?.TransactionId
                                ?? killOrderTransaction?.TransactionId
                                ?? modifyOrderTransaction?.TransactionId
                                ?? Guid.Empty
            });

            container.RemoveProcessedPendingReply(message);
        }
コード例 #3
0
 /// <summary>
 /// Удалить отложенный ответ на транзакцию
 /// </summary>
 public void RemoveProcessedPendingReply(QLTransactionReply tr)
 {
     log.Debug().Print("Remove processed TR", LogFields.TransactionId(tr.trans_id));
     using (locker.WriteLock())
     {
         pendingTransactionReplies.Remove(tr);
     }
 }
コード例 #4
0
        /// <summary>
        /// Обработать ответ на постановку транзакции, возвращает список QLOrderStateChange, обработка которых была отложена из-за
        /// отсутсвия trans_id
        /// </summary>
        /// <param name="message"></param>
        /// <param name="orderExchangeId"></param>
        public List <QLOrderStateChange> PutTransactionReply(QLTransactionReply message, long orderExchangeId)
        {
            // необходимо отправить на обработку отложенные OrderStateChange сообщения
            List <QLOrderStateChange> oscmToFire;

            using (locker.WriteLock())
            {
                if (!newOrderTransactionsWithoutReplies.Contains(message.trans_id))
                {
                    return(null);
                }

                var newOrderTransaction = mapQuikTransIdOnNewOrderTransaction[message.trans_id];
                newOrderTransactionsWithoutReplies.Remove(message.trans_id);

                mapOrderIdOnNewOrderTransaction[orderExchangeId] = newOrderTransaction;

                // сначала смотрим, есть ли отложенные oscm конкретно для этого номера заявки
                if (mapOrderIdOnPendingOrderStateChange.TryGetValue(orderExchangeId, out oscmToFire))
                {
                    log.Info().Print(
                        $"PutTransactionReply: to fire {oscmToFire.Count} pending OSCMs for {LogFields.ExchangeOrderId(orderExchangeId)} and {LogFields.TransactionId(message.trans_id)}"
                        );
                    mapOrderIdOnPendingOrderStateChange.Remove(orderExchangeId);

                    // проставляем trans_id в oscm, т.к. проблема заключается в том, что когда oscm приходит раньше transReply, то osc.trans_id == 0
                    foreach (var oscm in oscmToFire)
                    {
                        oscm.trans_id = message.trans_id;
                    }
                }

                // если транзакций без ответа не осталось, тогда нужно запроцессить все оставшиеся oscm, т.к. ответов на транзакции больше не последует
                if (newOrderTransactionsWithoutReplies.Count == 0 && mapOrderIdOnPendingOrderStateChange.Count != 0)
                {
                    log.Info().Print("PutTransactionReply: to fire all other pending OSCMs, since there is no more NewOrderTransaction without reply");
                    if (oscmToFire == null)
                    {
                        oscmToFire = new List <QLOrderStateChange>();
                    }

                    foreach (var pendingOscms in mapOrderIdOnPendingOrderStateChange)
                    {
                        oscmToFire.AddRange(pendingOscms.Value);
                    }
                }
            }

            if (oscmToFire == null)
            {
                return(null);
            }

            return(oscmToFire);
        }
コード例 #5
0
 /// <summary>
 /// Предварительная обработка ответа на транзакцию
 /// </summary>
 /// <param name="message"></param>
 private void PreprocessTransactionReply(QLTransactionReply message)
 {
     if (!string.IsNullOrEmpty(message.result_msg) &&
         (
             message.result_msg.Contains("Вы не можете снять данную заявку") ||
             message.result_msg.Contains("Не найдена заявка для удаления")
         ))
     {
         Logger.Warn().Print(
             $"Failed kill transaction will be treated as successfull, status will be changed from {message.status} to 3",
             LogFields.Message(message)
             );
         message.status = 3;
     }
 }
コード例 #6
0
        private void Handle(QLTransactionReply message)
        {
            Logger.Debug().PrintFormat("Handle: {0}", message);

            var orderExchangeId = ParseOrderIdFromTransactionReply(message);

            if (orderExchangeId > 0)
            {
                container.PutOrderExchangeId(orderExchangeId);
            }

            var pendingOscmsToProcess = container.PutTransactionReply(message, orderExchangeId);

            if (pendingOscmsToProcess != null)
            {
                Logger.Debug().Print($"Handle(TR) fires {pendingOscmsToProcess.Count} pending OSCMs to process", LogFields.Message(message));
                foreach (var oscm in pendingOscmsToProcess)
                {
                    // если у нас был получен oscm по данному номеру заявки, но с нулевым trans_id (да да, такое бывает), то тут
                    // у нас есть прекрасная возможность проставить oscm.trans_id и запроцессить oscm по нормальному алгоритму
                    if (oscm.order_num == orderExchangeId && oscm.trans_id == 0)
                    {
                        oscm.trans_id = message.trans_id;
                    }

                    Handle(oscm);
                }
            }

            PreprocessTransactionReply(message);

            if (message.Successful)
            {
                ProcessSuccessfulTransactionReply(message);
            }
            else
            {
                ProcessFailedTransactionReply(message);
            }
        }
コード例 #7
0
        /// <summary>
        /// Возвращает последний полученный статус заявки, связанный с транзакцией с идентификатором transId
        /// </summary>
        /// <param name="reply">Квиковый идентификатор транзакции</param>
        /// <returns></returns>
        public QLOrderStateChange GetLastOrderStateChangeForTransactionId(QLTransactionReply reply)
        {
            QLOrderStateChange rValue;
            long orderId;

            using (locker.ReadLock())
            {
                if (!mapQuikTransIdOnOrderExchangeId.TryGetValue(reply.trans_id, out orderId))
                {
                    return(null);
                }

                List <QLOrderStateChange> orderStates;
                if (!orderStateChanges.TryGetValue(orderId, out orderStates))
                {
                    return(null);
                }

                rValue = orderStates.LastOrDefault();
            }

            return(rValue);
        }
コード例 #8
0
        /// <summary>
        /// Парсинг не снятого количества из ответа на kill транзакцию
        /// </summary>
        /// <param name="message"></param>
        /// <returns></returns>
        private int ParseUnfilledQuantityFromTransactionReply(QLTransactionReply message)
        {
            if (string.IsNullOrEmpty(message.result_msg))
            {
                return(-1);
            }

            var match = (message.result_msg.Contains("Неисполненный остаток") ? quantityFromKillTransReplRegex1 : quantityFromKillTransReplRegex2).Match(message.result_msg);

            // должено быть ровно одно совпадение
            if (match.Length == 0 || match.Groups.Count != 2)
            {
                return(-1);
            }

            int rValue = -1;

            int.TryParse(match.Groups["quantity"].Value, out rValue);

            Logger.Debug().PrintFormat("Parsed unfilled quantity is: {0}", LogFields.ActiveQuantity(rValue));

            return(rValue);
        }
コード例 #9
0
        /// <summary>
        /// Разбор биржевого ID заявки из ответа на транзакцию
        /// </summary>
        /// <param name="message"></param>
        /// <returns></returns>
        private long ParseOrderIdFromTransactionReply(QLTransactionReply message)
        {
            if (string.IsNullOrEmpty(message.result_msg))
            {
                return(-1);
            }

            var match = orderIdFromTransReplRegex.Match(message.result_msg);

            // должено быть ровно одно совпадение
            if (match.Length == 0 || match.Groups.Count != 2)
            {
                return(-1);
            }

            long rValue = -1;

            long.TryParse(match.Groups[1].Value, out rValue);

            Logger.Debug().PrintFormat("Parsed order exchange id is: {0}", LogFields.ExchangeOrderId(rValue));

            return(rValue);
        }
コード例 #10
0
        /// <summary>
        /// Обработка успешной транзакции
        /// </summary>
        private void ProcessSuccessfulTransactionReply(QLTransactionReply message)
        {
            var newOrderTransaction    = container.GetNewOrderTransaction(message.trans_id);
            var killOrderTransaction   = container.GetKillOrderTransactionByTransId(message.trans_id);
            var modifyOrderTransaction = container.GetModifyOrderTransactionByTransId(message.trans_id);

            if (newOrderTransaction == null && killOrderTransaction == null && modifyOrderTransaction == null)
            {
                Logger.Warn().Print("TRANS_REPL received for transaction which wasn't sent from application", LogFields.Message(message));
                container.RemoveProcessedPendingReply(message);
                return;
            }

            QLOrderStateChange lastState;

            if ((lastState = container.GetLastOrderStateChangeForTransactionId(message)) == null)
            {
                Logger.Debug().Print("Postpone TrRepl processing, no last OSCM found", LogFields.Message(message));
                container.PutPendingTransactionReply(message);
                return;
            }

            // проверка соответствия последнего статуса типу транзакции
            // kill транзакция считатется завершённой, если заявка находится в одном из статусов ниже
            if (killOrderTransaction != null && lastState.State == OrderState.New)
            {
                Logger.Debug().Print($"Postpone KillTrRepl processing. Last ord state is {lastState.State}", LogFields.Message(message));
                container.PutPendingTransactionReply(message);
                return;
            }

            // new транзакция считатется завершённой, если заявка не находится в одном из статусов ниже
            if (newOrderTransaction != null &&
                (lastState.State == OrderState.New || lastState.State == OrderState.Undefined))
            {
                Logger.Debug().Print($"Postpone NewTrRepl processing. Last ord state is {lastState.State}", LogFields.Message(message));
                container.PutPendingTransactionReply(message);
                return;
            }

            if (modifyOrderTransaction != null &&
                (lastState.State == OrderState.New || lastState.State == OrderState.Undefined ||
                 lastState.trans_id != message.trans_id))
            {
                Logger.Debug().Print($"Postpone ModifyTrRepl processing. Last ord state is {lastState.State}", LogFields.Message(message));
                Logger.Error().Print("Handle(TR): for modify transaction not implemented");
                container.PutPendingTransactionReply(message);
                return;
            }

            if (killOrderTransaction != null)
            {
                var unfilledQuantity = ParseUnfilledQuantityFromTransactionReply(message);
                Logger.Debug().Print($"Handle(TR)[{message.trans_id}]: Create artifitial OSCM from successful kill transaction reply", LogFields.Message(message));

                OnMessageReceived(new OrderStateChangeMessage
                {
                    TransactionId   = killOrderTransaction.TransactionId,
                    ActiveQuantity  = (uint)unfilledQuantity,
                    OrderExchangeId = message.order_num.ToString(),
                    //FilledQuantity = (uint)(message.filled),
                    State    = OrderState.Cancelled,
                    Price    = message.price,
                    Quantity = (uint?)message.quantity
                });
            }

            OnMessageReceived(new TransactionReply
            {
                Success       = message.Successful,
                Message       = message.result_msg,
                TransactionId = newOrderTransaction?.TransactionId
                                ?? killOrderTransaction?.TransactionId
                                ?? modifyOrderTransaction?.TransactionId
                                ?? Guid.Empty
            });

            container.RemoveProcessedPendingReply(message);
        }