/// <summary>
        ///     Обновить контракт по его метаданным
        /// </summary>
        /// <param name="metadata">
        ///     Метаданные контракта
        /// </param>
        /// <param name="dependentObjectDescription">
        ///     Строковое описание объекта, который зависит от инструмента
        /// </param>
        public async Task HandleMetadataAsync(ContractMetadata metadata, string dependentObjectDescription = null)
        {
            using (LogManager.Scope())
            {
                _Log.Debug().Print(
                    "Contract metadata received",
                    LogFields.ContractId(metadata.contract_id),
                    LogFields.Symbol(metadata.contract_symbol),
                    LogFields.CorrectPriceScale(metadata.correct_price_scale),
                    LogFields.DisplayPriceScale(metadata.display_price_scale)
                    );
                using (cacheLock.Lock())
                {
                    if (cachedContracts.ContainsKey(metadata.contract_id))
                    {
                        _Log.Debug().Print("Already cached", LogFields.ContractId(metadata.contract_id));
                        return;
                    }
                }

                Instrument instrument = await InstrumentConverter.ResolveSymbolAsync(adapter, metadata.contract_symbol, dependentObjectDescription);

                if (instrument == null)
                {
                    _Log.Warn().Print("Instrument not resolved", LogFields.Symbol(metadata.contract_symbol));
                    return;
                }

                using (cacheLock.Lock())
                {
                    _Log.Debug().Print(
                        "Caching instrument",
                        LogFields.ContractId(metadata.contract_id),
                        LogFields.Symbol(metadata.contract_symbol),
                        LogFields.Instrument(instrument)
                        );

                    cachedContractIds[instrument]                   = metadata.contract_id;
                    cachedContracts[metadata.contract_id]           = instrument;
                    cachedContractPriceScales[metadata.contract_id] = (decimal)metadata.correct_price_scale;
                }

                OnInstrumentResolved(metadata.contract_id);
            }
        }
Exemple #2
0
        /// <summary>
        ///     Преобразовать цену из формата CQG
        /// </summary>
        /// <param name="contractId">
        ///     Contract ID
        /// </param>
        /// <param name="price">
        ///     Цена в нотации CQG
        /// </param>
        /// <returns>
        ///      Цена
        /// </returns>
        public decimal ConvertPriceBack(uint contractId, long price)
        {
            using (cacheLock.Lock())
            {
                decimal scale;
                if (!cachedContractPriceScales.TryGetValue(contractId, out scale))
                {
                    _Log.Error().Print(
                        "Can't find price scale for contract",
                        LogFields.ContractId(contractId),
                        LogFields.Instrument(GetInstrument(contractId)?.Code)
                        );
                    return(price);
                }

                var correctedPrice = scale != 0 ? price * scale : price;
                return(correctedPrice);
            }
        }
Exemple #3
0
        /// <summary>
        ///     Обновить контракт по его метаданным
        /// </summary>
        /// <param name="metadata">
        ///     Метаданные контракта
        /// </param>
        public void HandleMetadata(ContractMetadata metadata)
        {
            _Log.Debug().Print(
                "Contract metadata received",
                LogFields.ContractId(metadata.contract_id),
                LogFields.Symbol(metadata.contract_symbol),
                LogFields.CorrectPriceScale(metadata.correct_price_scale),
                LogFields.DisplayPriceScale(metadata.display_price_scale)
                );
            using (cacheLock.Lock())
            {
                if (cachedContracts.ContainsKey(metadata.contract_id))
                {
                    _Log.Debug().Print("Already cached", LogFields.ContractId(metadata.contract_id));
                    return;
                }
            }

            var instrument = InstrumentConverter.ResolveSymbolAsync(adapter, metadata.contract_symbol).Result;

            if (instrument == null)
            {
                _Log.Warn().Print("Instrument not resolved", LogFields.Symbol(metadata.contract_symbol));
                return;
            }

            using (cacheLock.Lock())
            {
                _Log.Debug().Print(
                    "Caching instrument",
                    LogFields.ContractId(metadata.contract_id),
                    LogFields.Symbol(metadata.contract_symbol),
                    LogFields.Instrument(instrument)
                    );

                cachedContractIds[instrument]                   = metadata.contract_id;
                cachedContracts[metadata.contract_id]           = instrument;
                cachedContractPriceScales[metadata.contract_id] = (decimal)metadata.correct_price_scale;
            }

            OnInstrumentResolved(metadata.contract_id);
        }
        /// <summary>
        ///     Подписаться на исторические данные
        /// </summary>
        /// <param name="consumer">
        ///     Потребитель исторических данных
        /// </param>
        /// <param name="instrument">
        ///     Инструмент
        /// </param>
        /// <param name="begin">
        ///     Начало диапазона
        /// </param>
        /// <param name="span">
        ///     Интервал свечей для исторических данных
        /// </param>
        /// <returns>
        ///     Подписка на исторические данные
        /// </returns>
        /// <remarks>
        ///     Провайдер вправе переопределить параметры исторических графиков - диапазон, интервал,
        ///     если он не в состоянии предоставить запрошенные данные.
        /// </remarks>
        public async Task <IHistoryDataSubscription> SubscribeToHistoryDataAsync(
            IHistoryDataConsumer consumer,
            Instrument instrument,
            DateTime begin,
            HistoryProviderSpan span)
        {
            using (LogManager.Scope())
            {
                var message = await PrepareTimeBarRequestAsync(instrument, begin, DateTime.Now, span, TimeBarRequest.RequestType.SUBSCRIBE);

                if (message == null)
                {
                    throw new ArgumentException($"Unable to resolve instrument {instrument}");
                }

                message.time_bar_parameters.to_utc_time = 0;

                var request = new HistoryDataSubscription(
                    this,
                    new HistoryData(instrument, begin, DateTime.Now, span),
                    consumer,
                    message.time_bar_parameters.contract_id,
                    message.request_id);

                using (requestsLock.Lock())
                {
                    requests[message.request_id] = request;
                }

                CQGCAdapter.Log.Debug().Print(
                    "Requesting history data stream",
                    LogFields.RequestId(message.request_id),
                    LogFields.ContractId(message.time_bar_parameters.contract_id));
                adapter.SendMessage(message);

                return(request);
            }
        }
        /// <summary>
        ///     Получить исторические данные
        /// </summary>
        /// <param name="consumer">
        ///     Потребитель исторических данных
        /// </param>
        /// <param name="instrument">
        ///     Инструмент
        /// </param>
        /// <param name="begin">
        ///     Начало диапазона
        /// </param>
        /// <param name="end">
        ///     Конец диапазона
        /// </param>
        /// <param name="span">
        ///     Интервал свечей для исторических данных
        /// </param>
        /// <param name="cancellationToken">
        ///     Токен отмены
        /// </param>
        /// <returns>
        ///     Исторические данные
        /// </returns>
        /// <remarks>
        ///     Провайдер вправе переопределить параметры исторических графиков - диапазон, интервал,
        ///     если он не в состоянии предоставить запрошенные данные.
        /// </remarks>
        /// <exception cref="NoHistoryDataException">
        ///     Бросается, если исторические данные за указанный период недоступны
        /// </exception>
        public async Task GetHistoryDataAsync(
            IHistoryDataConsumer consumer,
            Instrument instrument,
            DateTime begin,
            DateTime end,
            HistoryProviderSpan span,
            CancellationToken cancellationToken = new CancellationToken())
        {
            var message =
                await PrepareTimeBarRequestAsync(instrument, begin, end, span, TimeBarRequest.RequestType.GET);

            if (message == null)
            {
                throw new ArgumentException($"Unable to resolve instrument {instrument}");
            }

            var request = new HistoryDataRequest(this, consumer, instrument, begin, end, span, message);

            using (requestsLock.Lock())
            {
                requests[message.request_id] = request;
            }

            CQGCAdapter.Log.Debug().Print(
                "Requesting history data block",
                LogFields.RequestId(message.request_id),
                LogFields.ContractId(message.time_bar_parameters.contract_id));
            adapter.SendMessage(message);

            // Поддержка отмены запроса
            cancellationToken.RegisterSafe(() => request.TrySetCanceled());

            var data = await request.Task;

            consumer.Update(data, HistoryDataUpdateType.Batch);
        }
Exemple #6
0
        private void MarketDataResolved(AdapterEventArgs <InformationReport> args)
        {
            var contractId = args.Message.symbol_resolution_report.contract_metadata.contract_id;

            ResolutionRequest request;

            using (resolutionRequestsLock.Lock())
            {
                if (!resolutionRequestsById.TryGetValue(args.Message.id, out request))
                {
                    return;
                }

                using (cacheLock.Lock())
                {
                    cachedContractIds[request.Instrument] = contractId;
                    cachedContracts[contractId]           = request.Instrument;
                    cachedContractPriceScales[contractId] = (decimal)args.Message.symbol_resolution_report.contract_metadata.correct_price_scale;
                }

                resolutionRequestsById.Remove(request.Id);
                resolutionRequestsByInstrument.Remove(request.Instrument);
            }

            request.Resolve(contractId);
            args.MarkHandled();
            OnInstrumentResolved(contractId);
            _Log.Debug().Print("Instrument is resolved", LogFields.Instrument(request.Instrument), LogFields.ContractId(contractId), LogFields.RequestId(request.Id));
        }
Exemple #7
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
                });
            }
        }