Beispiel #1
0
        /// <summary>
        ///     Обработчик события по статусу заявки
        /// </summary>
        private async void OrderStatusReceived(AdapterEventArgs <OrderStatus> args)
        {
            using (LogManager.Scope())
            {
                try
                {
                    Logger.Debug().Print(
                        "Order status received",
                        LogFields.ExchangeOrderId(args.Message.order_id),
                        LogFields.ChainOrderId(args.Message.chain_order_id),
                        LogFields.State(ConvertionHelper.GetOrderState(args.Message)),
                        LogFields.AccountId(args.Message.account_id),
                        LogFields.ExecOrderId(args.Message.exec_order_id)
                        );

                    args.MarkHandled();

                    // Обрабатываем метаданные контрактов
                    if (args.Message.contract_metadata != null)
                    {
                        foreach (var metadata in args.Message.contract_metadata)
                        {
                            await instrumentResolver.HandleMetadataAsync(metadata);
                        }
                    }

                    // Пытаемся выбрать заявку из контейнера
                    Order order;
                    using (ordersLock.Lock())
                        ordersByOrderExchangeId.TryGetValue(args.Message.chain_order_id, out order);

                    Message message;
                    // Обрабатываем изменение статуса заявки
                    if (order != null)
                    {
                        message = HandleOrderStateAsOrderStateChange(order, args.Message);
                    }
                    // Обрабатываем заявку как новую
                    else
                    {
                        message = HandleOrderStateAsNewOrder(args.Message, out order);
                    }

                    // Отправляем сообщение и TransactionReply
                    if (message != null)
                    {
                        OnMessageReceived(message);
                        TryEmitTransactionReplies(args.Message);

                        // Обрабатываем сделки
                        TryEmitFills(args.Message, order);
                    }
                }
                catch (Exception e)
                {
                    Logger.Error().Print(e, $"Failed to process {args.Message}");
                }
            }
        }
Beispiel #2
0
        /// <summary>
        ///     Обработка заявки как уже существующей
        /// </summary>
        private OrderStateChangeMessage HandleOrderStateAsOrderStateChange(Order order, OrderStatus message)
        {
            Logger.Debug().Print(
                "Order state change received",
                LogFields.ExchangeOrderId(message.order_id),
                LogFields.ChainOrderId(message.chain_order_id),
                LogFields.State(ConvertionHelper.GetOrderState(message)),
                LogFields.AccountId(message.account_id),
                LogFields.ExecOrderId(message.exec_order_id)
                );

            using (ordersLock.Lock())
            {
                Guid transactionId;
                if (!Guid.TryParse(message.order.cl_order_id, out transactionId))
                {
                    Logger.Debug().Print(
                        $"Unable to parse {LogFieldNames.TransactionId} from {LogFields.ClOrderId(message.order.cl_order_id)}. Use order's {LogFieldNames.TransactionId}"
                        );
                    transactionId = order.TransactionId;
                }

                var change = new OrderStateChangeMessage
                {
                    OrderExchangeId = message.chain_order_id,
                    TransactionId   = transactionId,
                    ActiveQuantity  = message.remaining_qty,
                    FilledQuantity  = message.fill_qty,
                    Price           = instrumentResolver.ConvertPriceBack(order.Instrument, message.order.limit_price),
                    ChangeTime      = adapter.ResolveDateTime(message.status_utc_time),
                    Quantity        = message.remaining_qty + message.fill_qty,
                    State           = ConvertionHelper.GetOrderState(message)
                };

                // Обработка изменения order_id (происходит при модификации заявки)
                if (order.OrderExchangeId != change.OrderExchangeId)
                {
                    ordersByOrderExchangeId.Remove(order.OrderExchangeId);
                    ordersByOrderExchangeId.Add(change.OrderExchangeId, order);
                }

                orderStatusByChainOrderId[message.chain_order_id] = message;

                //order.AcceptStateMessage(change);
                return(change);
            }
        }
Beispiel #3
0
        /// <summary>
        ///     Сгенерировать <see cref="FillMessage"/>
        /// </summary>
        private void TryEmitFills(OrderStatus orderStatus, Order order)
        {
            if (orderStatus.transaction_status != null)
            {
                foreach (var transactionStatus in orderStatus.transaction_status)
                {
                    switch ((TransactionStatus.Status)transactionStatus.status)
                    {
                    case TransactionStatus.Status.FILL:
                        if (transactionStatus.trade == null)
                        {
                            continue;
                        }

                        foreach (var trade in transactionStatus.trade)
                        {
                            if (trade != null)
                            {
                                using (processedFillsLock.Lock())
                                {
                                    if (!processedFillIds.Add(trade.trade_id))
                                    {
                                        // Сделка уже обработана
                                        continue;
                                    }
                                }

                                var fill = new FillMessage
                                {
                                    Instrument      = order.Instrument,
                                    DateTime        = adapter.ResolveDateTime(trade.trade_utc_time),
                                    Account         = order.Account,
                                    Operation       = ConvertionHelper.GetOrderOperation((WebAPI.Order.Side)trade.side),
                                    Price           = instrumentResolver.ConvertPriceBack(trade.contract_id, trade.price),
                                    Quantity        = trade.qty,
                                    ExchangeId      = trade.trade_id,
                                    ExchangeOrderId = order.OrderExchangeId
                                };

                                OnMessageReceived(fill);
                            }
                        }
                        break;
                    }
                }
            }
        }
Beispiel #4
0
        /// <summary>
        ///     Постановка заявки
        /// </summary>
        private async void SendTransactionInternal(NewOrderTransaction transaction)
        {
            using (LogManager.Scope())
            {
                try
                {
                    // Получаем счет для транзакции
                    int accountId;
                    var hasAccountId = true;
                    using (accountsLock.ReadLock())
                    {
                        if (!accountIdsByCode.TryGetValue(transaction.Account, out accountId))
                        {
                            hasAccountId = false;
                        }
                    }

                    if (!hasAccountId)
                    {
                        OnMessageReceived(TransactionReply.Rejected(
                                              transaction,
                                              $"Account \"{transaction.Account}\" is unknown"));
                        return;
                    }

                    // Получаем инструмент для транзации
                    uint contractId;
                    try
                    {
                        contractId = await instrumentResolver.GetContractIdAsync(transaction.Instrument);

                        if (contractId == uint.MaxValue)
                        {
                            return;
                        }
                    }
                    catch (OperationCanceledException)
                    {
                        OnMessageReceived(TransactionReply.Rejected(
                                              transaction,
                                              $"Instrument \"{transaction.Instrument.Code}\" is unknown"));
                        return;
                    }

                    // Пребразовываем цену
                    var price = instrumentResolver.ConvertPrice(contractId, transaction.Price);
                    if (price == null)
                    {
                        OnMessageReceived(TransactionReply.Rejected(transaction, "Unable to convert price"));
                        return;
                    }

                    // Формируем запрос
                    var msg = new OrderRequest
                    {
                        new_order = new NewOrder
                        {
                            order = new WebAPI.Order
                            {
                                cl_order_id = transaction.TransactionId.ToString("N"),
                                account_id  = accountId,
                                contract_id = contractId,
                                side        = (uint)ConvertionHelper.GetSide(transaction.Operation),
                                order_type  = (uint)ConvertionHelper.GetOrderType(transaction.Type),
                                duration    = (uint)ConvertionHelper.GetDuration(transaction.ExecutionCondition),
                                limit_price = price.Value,
                                qty         = transaction.Quantity,
                                is_manual   = transaction.IsManual,

                                user_attribute =
                                {
                                    new UserAttribute
                                    {
                                        name  = CommentAttributeName,
                                        value = transaction.Comment
                                    }
                                }
                            },
                            suspend = false
                        },
                        request_id = adapter.GetNextRequestId()
                    };

                    // Запоминаем заявку
                    using (ordersLock.Lock())
                    {
                        var order = new Order(transaction);
                        ordersByTransactionId.Add(transaction.TransactionId, order);
                    }

                    // Запоминаем транзакцию
                    StoreTransaction(msg.request_id, transaction);

                    Logger.Debug().PrintFormat("Sending {0}", transaction);
                    adapter.SendMessage(msg);
                }
                catch (Exception e)
                {
                    Logger.Error().Print(e, $"Failed to send {transaction}");
                    OnMessageReceived(TransactionReply.Rejected(transaction, "Unable to send order"));
                }
            }
        }
Beispiel #5
0
        /// <summary>
        ///     Обработка заявки как новой
        /// </summary>
        private Message HandleOrderStateAsNewOrder(OrderStatus message, out Order order)
        {
            Logger.Debug().Print(
                "New order received",
                LogFields.ExchangeOrderId(message.order_id),
                LogFields.ChainOrderId(message.chain_order_id),
                LogFields.State(ConvertionHelper.GetOrderState(message)),
                LogFields.AccountId(message.account_id),
                LogFields.ExecOrderId(message.exec_order_id)
                );

            // Ищем счет для заявки
            string accountCode;

            using (accountsLock.ReadLock())
            {
                if (!accountCodesById.TryGetValue(message.account_id, out accountCode))
                {
                    Logger.Error().Print(
                        "Unable to process order: account is unknown",
                        LogFields.ExchangeOrderId(message.order_id),
                        LogFields.AccountId(message.account_id)
                        );
                    order = null;
                    return(null);
                }
            }

            // Ищем инструмент для заявки
            var instrument = instrumentResolver.GetInstrument(message.order.contract_id);

            if (instrument == null)
            {
                // Пришла заявка по неизвестному инструменту
                Logger.Error().Print(
                    "Received an order but no matching instrument is found. Put to pending orders",
                    LogFields.ExchangeOrderId(message.order_id),
                    LogFields.ContractId(message.order.contract_id),
                    LogFields.Account(accountCode)
                    );

                using (ordersLock.Lock())
                {
                    pendingOrders.Enqueue(message);
                    pendingOrderAddedEvent.Set();
                }

                order = null;
                return(null);
            }

            using (ordersLock.Lock())
            {
                if (ordersByOrderExchangeId.TryGetValue(message.chain_order_id, out order))
                {
                    // Race condition, такая заявка уже есть, надобно обработать ее через OSCM
                    return(HandleOrderStateAsOrderStateChange(order, message));
                }

                Guid transactionId;
                if (Guid.TryParse(message.order.cl_order_id, out transactionId))
                {
                    if (ordersByTransactionId.TryGetValue(transactionId, out order))
                    {
                        // Такая заявка уже есть, надобно обработать ее через OSCM
                        order.OrderExchangeId = message.chain_order_id;
                        ordersByOrderExchangeId.Add(message.chain_order_id, order);

                        return(HandleOrderStateAsOrderStateChange(order, message));
                    }
                }
                else
                {
                    transactionId = Guid.Empty;
                }

                // Создаем новую внешнюю заявку
                order = new Order
                {
                    OrderExchangeId = message.chain_order_id,
                    Instrument      = instrument,
                    Type            = ConvertionHelper.GetOrderType(message),
                    Account         = accountCode,
                    Price           = instrumentResolver.ConvertPriceBack(message.order.contract_id, message.order.limit_price),
                    Quantity        = message.order.qty,
                    ActiveQuantity  = message.remaining_qty,
                    Operation       = message.order.side == (uint)WebAPI.Order.Side.BUY ? OrderOperation.Buy : OrderOperation.Sell,
                    State           = ConvertionHelper.GetOrderState(message),
                    DateTime        = adapter.ResolveDateTime(message.status_utc_time),
                    Comment         = ConvertionHelper.GetComment(message),
                    TransactionId   = transactionId
                };

                ordersByOrderExchangeId.Add(message.chain_order_id, order);
                orderStatusByChainOrderId[message.chain_order_id] = message;

                return(new ExternalOrderMessage {
                    Order = order
                });
            }
        }