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); }
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); }
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"); } }
//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"; } }
//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); } }
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()); }
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()); }