示例#1
0
        private bool ProcessOrders(Order order, MatchedOrderCollection matchedOrders)
        {
            if (!matchedOrders.Any())
            {
                order.CloseDate        = DateTime.UtcNow;
                order.Status           = OrderStatus.Rejected;
                order.RejectReason     = OrderRejectReason.NoLiquidity;
                order.RejectReasonText = "No orders to match";
                return(false);
            }

            if (matchedOrders.SummaryVolume < Math.Abs(order.Volume) && order.FillType == OrderFillType.FillOrKill)
            {
                order.CloseDate        = DateTime.UtcNow;
                order.Status           = OrderStatus.Rejected;
                order.RejectReason     = OrderRejectReason.NoLiquidity;
                order.RejectReasonText = "No orders to match or not fully matched";
                return(false);
            }

            //if (!CheckIfWeCanOpenPosition(order, matchedOrders))
            //{
            //    order.Status = OrderStatus.Rejected;
            //    order.RejectReason = OrderRejectReason.AccountStopOut;
            //    order.RejectReasonText = "Opening the position will lead to account Stop Out level";
            //    return false;
            //}

            order.MatchedOrders = matchedOrders;
            order.OpenPrice     = Math.Round(order.MatchedOrders.WeightedAveragePrice, order.AssetAccuracy);
            order.OpenDate      = DateTime.UtcNow;
            order.Status        = OrderStatus.Active;
            return(true);
        }
示例#2
0
        private void MakeOrderClosed(Order order, MatchedOrderCollection matchedOrders)
        {
            order.MatchedCloseOrders.AddRange(matchedOrders);

            if (order.GetIsCloseFullfilled())
            {
                order.Status    = OrderStatus.Closed;
                order.CloseDate = DateTime.UtcNow;
                _ordersCache.ClosingOrders.Remove(order);
                _orderClosedEventChannel.SendEvent(this, new OrderClosedEventArgs(order));
            }
        }
        public Task <MatchedOrderCollection> MatchOrderAsync(Order order, bool shouldOpenNewPosition,
                                                             OrderModality modality = OrderModality.Regular)
        {
            var col = new MatchedOrderCollection(new [] { new MatchedOrder
                                                          {
                                                              OrderId       = _externalOrderId,
                                                              MarketMakerId = _marketMakerId,
                                                              Volume        = Math.Abs(order.Volume),
                                                              Price         = _price,
                                                              MatchedDate   = _externalExecutionTime,
                                                              IsExternal    = true,
                                                          } });

            return(Task.FromResult(col));
        }
        public void Test_Serialization()
        {
            var list = new List <MatchedOrder> {
                new MatchedOrder()
            };
            var collection = new MatchedOrderCollection(list);

            var listJson       = list.ToJson();
            var collectionJson = collection.ToJson();

            var newList       = collectionJson.DeserializeJson <List <MatchedOrder> >();
            var newCollection = listJson.DeserializeJson <MatchedOrderCollection>();

            Assert.AreEqual(list.Count, newList.Count);
            Assert.AreEqual(collection.Count, newCollection.Count);
        }
示例#5
0
        private void CheckIfWeCanOpenPosition(Order order, MatchedOrderCollection matchedOrders)
        {
            var accountAsset = _accountAssetsCacheService.GetAccountAsset(order.TradingConditionId, order.AccountAssetId, order.Instrument);

            _validateOrderService.ValidateInstrumentPositionVolume(accountAsset, order);

            order.MatchedOrders.AddRange(matchedOrders);
            order.OpenPrice = Math.Round(order.MatchedOrders.WeightedAveragePrice, order.AssetAccuracy);

            var defaultMatchingEngine = _meRouter.GetMatchingEngineForClose(order);

            var closePrice = defaultMatchingEngine.GetPriceForClose(order);

            if (!closePrice.HasValue)
            {
                throw new ValidateOrderException(OrderRejectReason.NoLiquidity, "No orders to match for close");
            }

            order.UpdateClosePrice(Math.Round(closePrice.Value, order.AssetAccuracy));

            //TODO: very strange check.. think about it one more time
            var guessAccount      = _accountUpdateService.GuessAccountWithNewActiveOrder(order);
            var guessAccountLevel = guessAccount.GetAccountLevel();

            if (guessAccountLevel != AccountLevel.None)
            {
                order.OpenPrice = 0;
                order.UpdateClosePrice(0);
                order.MatchedOrders = new MatchedOrderCollection();
            }

            if (guessAccountLevel == AccountLevel.MarginCall)
            {
                throw new ValidateOrderException(OrderRejectReason.AccountInvalidState,
                                                 "Opening the position will lead to account Margin Call level");
            }

            if (guessAccountLevel == AccountLevel.StopOUt)
            {
                throw new ValidateOrderException(OrderRejectReason.AccountInvalidState,
                                                 "Opening the position will lead to account Stop Out level");
            }
        }
示例#6
0
        //TODO: remove orderProcessed function and make all validations before match
        public void MatchMarketOrderForOpen(Order order, Func <MatchedOrderCollection, bool> orderProcessed)
        {
            var prices = _externalOrderBooksList.GetPricesForOpen(order);

            if (prices == null)
            {
                orderProcessed(new MatchedOrderCollection());
                return;
            }

            prices = order.GetOrderType() == OrderDirection.Buy
                ? prices.OrderBy(tuple => tuple.price).ToList()
                : prices.OrderByDescending(tuple => tuple.price).ToList();

            var assetPair         = _assetPairsCache.GetAssetPairByIdOrDefault(order.Instrument);
            var externalAssetPair = assetPair?.BasePairId ?? order.Instrument;

            foreach (var sourcePrice in prices)
            {
                var externalOrderModel = new OrderModel();

                try
                {
                    externalOrderModel = new OrderModel(
                        order.GetOrderType().ToType <TradeType>(),
                        OrderType.Market,
                        TimeInForce.FillOrKill,
                        (double)Math.Abs(order.Volume),
                        _dateService.Now(),
                        sourcePrice.source,
                        externalAssetPair);

                    var executionResult = _exchangeConnectorService.CreateOrderAsync(externalOrderModel).GetAwaiter()
                                          .GetResult();

                    var executedPrice = Math.Abs(executionResult.Price) > 0
                        ? (decimal)executionResult.Price
                        : sourcePrice.price.Value;

                    var matchedOrders = new MatchedOrderCollection
                    {
                        new MatchedOrder
                        {
                            ClientId      = order.ClientId,
                            MarketMakerId = sourcePrice.source,
                            MatchedDate   = _dateService.Now(),
                            OrderId       = executionResult.ClientOrderId,
                            Price         = CalculatePriceWithMarkups(assetPair, order.GetOrderType(), executedPrice),
                            Volume        = (decimal)executionResult.Volume
                        }
                    };

                    order.OpenExternalProviderId = sourcePrice.source;
                    order.OpenExternalOrderId    = executionResult.ClientOrderId;

                    _rabbitMqNotifyService.ExternalOrder(executionResult).GetAwaiter().GetResult();

                    if (orderProcessed(matchedOrders))
                    {
                        return;
                    }
                    else
                    {
                        var cancelOrderModel = new OrderModel(
                            order.GetCloseType().ToType <TradeType>(),
                            OrderType.Market,
                            TimeInForce.FillOrKill,
                            (double)Math.Abs(order.Volume),
                            _dateService.Now(),
                            sourcePrice.source,
                            externalAssetPair);

                        var cancelOrderResult = _exchangeConnectorService.CreateOrderAsync(cancelOrderModel).GetAwaiter().GetResult();

                        _rabbitMqNotifyService.ExternalOrder(cancelOrderResult).GetAwaiter().GetResult();
                    }

                    return;
                }
                catch (Exception e)
                {
                    _log.WriteErrorAsync(nameof(StpMatchingEngine), nameof(MatchMarketOrderForOpen),
                                         $"Internal order: {order.ToJson()}, External order model: {externalOrderModel.ToJson()}", e);
                }
            }

            if (string.IsNullOrEmpty(order.OpenExternalProviderId) ||
                string.IsNullOrEmpty(order.OpenExternalOrderId))
            {
                order.Status           = OrderStatus.Rejected;
                order.RejectReason     = OrderRejectReason.NoLiquidity;
                order.RejectReasonText = "Error executing external order";
            }
        }
示例#7
0
        //TODO: remove orderProcessed function and make all validations before match
        public void MatchMarketOrderForClose(Order order, Func <MatchedOrderCollection, bool> orderProcessed)
        {
            var closePrice = _externalOrderBooksList.GetPriceForClose(order);

            //TODO: rework!
            if (!closePrice.HasValue)
            {
                orderProcessed(new MatchedOrderCollection());
                return;
            }

            var closeLp           = order.OpenExternalProviderId;
            var assetPair         = _assetPairsCache.GetAssetPairByIdOrDefault(order.Instrument);
            var externalAssetPair = assetPair?.BasePairId ?? order.Instrument;

            var externalOrderModel = new OrderModel();

            try
            {
                externalOrderModel = new OrderModel(
                    order.GetCloseType().ToType <TradeType>(),
                    OrderType.Market,
                    TimeInForce.FillOrKill,
                    (double)Math.Abs(order.Volume),
                    _dateService.Now(),
                    closeLp,
                    externalAssetPair);

                var executionResult = _exchangeConnectorService.CreateOrderAsync(externalOrderModel).GetAwaiter()
                                      .GetResult();

                var executedPrice = Math.Abs(executionResult.Price) > 0
                    ? (decimal)executionResult.Price
                    : closePrice.Value;

                var price = CalculatePriceWithMarkups(assetPair, order.GetCloseType(), executedPrice);

                order.CloseExternalProviderId = closeLp;
                order.CloseExternalOrderId    = executionResult.ClientOrderId;
                order.UpdateClosePrice(price);

                _rabbitMqNotifyService.ExternalOrder(executionResult).GetAwaiter().GetResult();

                var matchedOrders = new MatchedOrderCollection
                {
                    new MatchedOrder
                    {
                        ClientId      = order.ClientId,
                        MarketMakerId = closeLp,
                        MatchedDate   = _dateService.Now(),
                        OrderId       = executionResult.ClientOrderId,
                        Price         = order.ClosePrice,
                        Volume        = Math.Abs(order.Volume)
                    }
                };

                orderProcessed(matchedOrders);
            }
            catch (Exception e)
            {
                _log.WriteErrorAsync(nameof(StpMatchingEngine), nameof(MatchMarketOrderForClose),
                                     $"Internal order: {order.ToJson()}, External order model: {externalOrderModel.ToJson()}", e);
            }
        }
示例#8
0
        public async Task <MatchedOrderCollection> MatchOrderAsync(Order order, bool shouldOpenNewPosition,
                                                                   OrderModality modality = OrderModality.Regular)
        {
            List <(string source, decimal?price)> prices = null;

            if (!string.IsNullOrEmpty(_marginTradingSettings.DefaultExternalExchangeId))
            {
                var quote = _quoteCacheService.GetQuote(order.AssetPairId);

                if (quote.GetVolumeForOrderDirection(order.Direction) >= Math.Abs(order.Volume))
                {
                    prices = new List <(string source, decimal?price)>
                    {
                        (_marginTradingSettings
                         .DefaultExternalExchangeId, quote.GetPriceForOrderDirection(order.Direction))
                    };
                }
            }

            if (prices == null)
            {
                prices = _externalOrderbookService.GetOrderedPricesForExecution(order.AssetPairId, order.Volume, shouldOpenNewPosition);

                if (prices == null || !prices.Any())
                {
                    return(new MatchedOrderCollection());
                }
            }

            var assetPair         = _assetPairsCache.GetAssetPairByIdOrDefault(order.AssetPairId);
            var externalAssetPair = assetPair?.BasePairId ?? order.AssetPairId;

            foreach (var(source, price) in prices
                     .Where(x => string.IsNullOrEmpty(order.ExternalProviderId) || x.source == order.ExternalProviderId))
            {
                var externalOrderModel = new OrderModel();

                var orderType = order.OrderType == Core.Orders.OrderType.Limit ||
                                order.OrderType == Core.Orders.OrderType.TakeProfit
                    ? Core.Orders.OrderType.Limit
                    : Core.Orders.OrderType.Market;

                var isCancellationTrade = order.AdditionalInfo.IsCancellationTrade(out var cancellationTradeExternalId);

                var targetPrice = order.OrderType != Core.Orders.OrderType.Market || isCancellationTrade
                    ? (double?)order.Price
                    : (double?)price;

                try
                {
                    externalOrderModel = new OrderModel(
                        tradeType: order.Direction.ToType <TradeType>(),
                        orderType: orderType.ToType <OrderType>(),
                        timeInForce: TimeInForce.FillOrKill,
                        volume: (double)Math.Abs(order.Volume),
                        dateTime: _dateService.Now(),
                        exchangeName: source,
                        instrument: externalAssetPair,
                        price: targetPrice,
                        orderId: order.Id,
                        modality: modality.ToType <TradeRequestModality>(),
                        isCancellationTrade: isCancellationTrade,
                        cancellationTradeExternalId: cancellationTradeExternalId);

                    var cts = new CancellationTokenSource();
                    cts.CancelAfter(_marginTradingSettings.GavelTimeout);

                    var executionResult = await _exchangeConnectorClient.ExecuteOrder(externalOrderModel, cts.Token);

                    if (!executionResult.Success)
                    {
                        var ex = new Exception(
                            $"External order was not executed. Status: {executionResult.ExecutionStatus}. Failure: {executionResult.FailureType}");
                        LogOrderExecutionException(order, externalOrderModel, ex);
                    }
                    else
                    {
                        var executedPrice = Math.Abs(executionResult.Price) > 0
                            ? (decimal)executionResult.Price
                            : price.Value;

                        if (executedPrice.EqualsZero())
                        {
                            var ex = new Exception($"Have got execution price from Gavel equal to 0. Ignoring.");
                            LogOrderExecutionException(order, externalOrderModel, ex);
                        }
                        else
                        {
                            var matchedOrders = new MatchedOrderCollection
                            {
                                new MatchedOrder
                                {
                                    MarketMakerId = source,
                                    MatchedDate   = _dateService.Now(),
                                    OrderId       = executionResult.ExchangeOrderId,
                                    Price         = CalculatePriceWithMarkups(assetPair, order.Direction, executedPrice),
                                    Volume        = (decimal)executionResult.Volume,
                                    IsExternal    = true
                                }
                            };

                            await _rabbitMqNotifyService.ExternalOrder(executionResult);

                            _operationsLogService.AddLog("external order executed", order.AccountId,
                                                         externalOrderModel.ToJson(), executionResult.ToJson());

                            return(matchedOrders);
                        }
                    }
                }
                catch (Exception ex)
                {
                    LogOrderExecutionException(order, externalOrderModel, ex);
                    throw new OrderExecutionTechnicalException();
                }
            }

            return(new MatchedOrderCollection());
        }
示例#9
0
        public async Task <MatchedOrderCollection> MatchOrderAsync(Order order, bool shouldOpenNewPosition,
                                                                   OrderModality modality = OrderModality.Regular)
        {
            List <(string source, decimal?price)> prices = null;

            if (!string.IsNullOrEmpty(_marginTradingSettings.DefaultExternalExchangeId))
            {
                var quote = _quoteCacheService.GetQuote(order.AssetPairId);

                if (quote.GetVolumeForOrderDirection(order.Direction) >= Math.Abs(order.Volume))
                {
                    prices = new List <(string source, decimal?price)>
                    {
                        (_marginTradingSettings
                         .DefaultExternalExchangeId, quote.GetPriceForOrderDirection(order.Direction))
                    };
                }
            }

            if (prices == null)
            {
                prices = _externalOrderbookService.GetOrderedPricesForExecution(order.AssetPairId, order.Volume, shouldOpenNewPosition);

                if (prices == null || !prices.Any())
                {
                    return(new MatchedOrderCollection());
                }
            }

            var assetPair         = _assetPairsCache.GetAssetPairByIdOrDefault(order.AssetPairId);
            var externalAssetPair = assetPair?.BasePairId ?? order.AssetPairId;

            foreach (var(source, price) in prices
                     .Where(x => string.IsNullOrEmpty(order.ExternalProviderId) || x.source == order.ExternalProviderId))
            {
                var externalOrderModel = new OrderModel();

                var orderType = order.OrderType == Core.Orders.OrderType.Limit ||
                                order.OrderType == Core.Orders.OrderType.TakeProfit
                    ? OrderType.Limit
                    : OrderType.Market;

                var targetPrice = order.OrderType == Core.Orders.OrderType.Market
                    ? (double?)price
                    : (double?)order.Price;

                try
                {
                    externalOrderModel = new OrderModel(
                        tradeType: order.Direction.ToType <TradeType>(),
                        orderType: orderType,
                        timeInForce: TimeInForce.FillOrKill,
                        volume: (double)Math.Abs(order.Volume),
                        dateTime: _dateService.Now(),
                        exchangeName: source,
                        instrument: externalAssetPair,
                        price: targetPrice,
                        orderId: order.Id,
                        modality: modality.ToType <TradeRequestModality>());

                    var cts = new CancellationTokenSource();
                    cts.CancelAfter(_marginTradingSettings.GavelTimeout);
                    var executionResult = await _exchangeConnectorService.CreateOrderAsync(externalOrderModel, cts.Token);

                    if (!executionResult.Success)
                    {
                        throw new Exception(
                                  $"External order was not executed. Status: {executionResult.ExecutionStatus}. Failure: {executionResult.FailureType}");
                    }

                    var executedPrice = Math.Abs(executionResult.Price) > 0
                        ? (decimal)executionResult.Price
                        : price.Value;

                    var matchedOrders = new MatchedOrderCollection
                    {
                        new MatchedOrder
                        {
                            MarketMakerId = source,
                            MatchedDate   = _dateService.Now(),
                            OrderId       = executionResult.ExchangeOrderId,
                            Price         = CalculatePriceWithMarkups(assetPair, order.Direction, executedPrice),
                            Volume        = (decimal)executionResult.Volume,
                            IsExternal    = true
                        }
                    };

                    await _rabbitMqNotifyService.ExternalOrder(executionResult);

                    _operationsLogService.AddLog("external order executed", order.AccountId,
                                                 externalOrderModel.ToJson(), executionResult.ToJson());

                    return(matchedOrders);
                }
                catch (Exception e)
                {
                    var connector =
                        _marginTradingSettings.ExchangeConnector == ExchangeConnectorType.FakeExchangeConnector
                            ? "Fake"
                            : _exchangeConnectorService.BaseUri.OriginalString;

                    _log.WriteError(
                        $"{nameof(StpMatchingEngine)}:{nameof(MatchOrderAsync)}:{connector}",
                        $"Internal order: {order.ToJson()}, External order model: {externalOrderModel.ToJson()}", e);
                }
            }

            return(new MatchedOrderCollection());
        }