private Order GetTip(Order order, Iceberg iceberg) { var quantity = iceberg.TipQuantity < iceberg.TotalQuantity ? iceberg.TipQuantity : iceberg.TotalQuantity; iceberg.TotalQuantity -= quantity; if (iceberg.TotalQuantity == 0) { _currentIcebergOrders.Remove(order.OrderId); } return(new Order { IsBuy = order.IsBuy, Price = order.Price, OrderId = order.OrderId, IsTip = true, OpenQuantity = quantity, CancelOn = order.CancelOn, Cost = iceberg.Cost }); }
public OrderMatchingResult AddOrder(OrderWrapper orderWrapper, bool isOrderTriggered = false) { var incomingOrder = orderWrapper.Order; if (incomingOrder == null) { throw new ArgumentNullException(nameof(incomingOrder)); } incomingOrder.IsTip = false; if (incomingOrder.Price < 0 || (incomingOrder.OpenQuantity <= 0 && orderWrapper.OrderAmount == 0) || (incomingOrder.OpenQuantity == 0 && orderWrapper.OrderAmount <= 0) || orderWrapper.StopPrice < 0 || orderWrapper.TotalQuantity < 0) { return(OrderMatchingResult.InvalidPriceQuantityStopPriceOrderAmountOrTotalQuantity); } if (orderWrapper.OrderCondition == OrderCondition.BookOrCancel && (incomingOrder.Price == 0 || orderWrapper.StopPrice != 0)) { return(OrderMatchingResult.BookOrCancelCannotBeMarketOrStopOrder); } if (incomingOrder.OpenQuantity % _stepSize != 0 || orderWrapper.TotalQuantity % _stepSize != 0) { return(OrderMatchingResult.QuantityAndTotalQuantityShouldBeMultipleOfStepSize); } if (orderWrapper.OrderCondition == OrderCondition.ImmediateOrCancel && (orderWrapper.StopPrice != 0)) { return(OrderMatchingResult.ImmediateOrCancelCannotBeStopOrder); } if (orderWrapper.OrderCondition == OrderCondition.FillOrKill && orderWrapper.StopPrice != 0) { return(OrderMatchingResult.FillOrKillCannotBeStopOrder); } if (incomingOrder.CancelOn < 0) { return(OrderMatchingResult.InvalidCancelOnForGTD); } if (incomingOrder.CancelOn > 0 && (orderWrapper.OrderCondition == OrderCondition.FillOrKill || orderWrapper.OrderCondition == OrderCondition.ImmediateOrCancel)) { return(OrderMatchingResult.GoodTillDateCannotBeIOCorFOK); } if (incomingOrder.Price == 0 && orderWrapper.OrderAmount != 0 && incomingOrder.OpenQuantity != 0) { return(OrderMatchingResult.MarketOrderOnlySupportedOrderAmountOrQuantityNoBoth); } if (orderWrapper.OrderAmount != 0 && (incomingOrder.Price != 0 || !incomingOrder.IsBuy)) { return(OrderMatchingResult.OrderAmountOnlySupportedForMarketBuyOrder); } if (orderWrapper.TotalQuantity > 0) { incomingOrder.OpenQuantity = orderWrapper.TipQuantity; if (orderWrapper.OrderCondition == OrderCondition.FillOrKill || orderWrapper.OrderCondition == OrderCondition.ImmediateOrCancel) { return(OrderMatchingResult.IcebergOrderCannotBeFOKorIOC); } if (orderWrapper.StopPrice != 0 || incomingOrder.Price == 0) { return(OrderMatchingResult.IcebergOrderCannotBeStopOrMarketOrder); } if (orderWrapper.TotalQuantity <= orderWrapper.TipQuantity) { return(OrderMatchingResult.InvalidIcebergOrderTotalQuantity); } } if (_acceptedOrders.Contains(incomingOrder.OrderId)) { return(OrderMatchingResult.DuplicateOrder); } _acceptedOrders.Add(incomingOrder.OrderId); _tradeListener?.OnAccept(incomingOrder.OrderId); if (orderWrapper.OrderAmount != 0 && orderWrapper.StopPrice != 0) { _orderAmount.Add(orderWrapper.Order.OrderId, orderWrapper.OrderAmount); } Quantity?quantity = null; bool canBeFilled = false; if (orderWrapper.Order.IsBuy && orderWrapper.Order.OpenQuantity == 0 && orderWrapper.StopPrice == 0) { var quantityAndFill = GetQuantity(orderWrapper.OrderAmount); if (quantityAndFill.Quantity.HasValue) { quantity = quantityAndFill.Quantity.Value; canBeFilled = quantityAndFill.CanFill; } } var timeNow = _timeProvider.GetSecondsFromEpoch(); CancelExpiredOrders(timeNow); if (orderWrapper.OrderCondition == OrderCondition.BookOrCancel && ((incomingOrder.IsBuy && _book.BestAskPrice <= incomingOrder.Price) || (!incomingOrder.IsBuy && incomingOrder.Price <= _book.BestBidPrice))) { if (orderWrapper.TotalQuantity == 0) { _tradeListener?.OnCancel(incomingOrder.OrderId, incomingOrder.OpenQuantity, incomingOrder.Cost, CancelReason.BookOrCancel); } else { _tradeListener?.OnCancel(incomingOrder.OrderId, orderWrapper.TotalQuantity, incomingOrder.Cost, CancelReason.BookOrCancel); } } else if (orderWrapper.OrderCondition == OrderCondition.FillOrKill && orderWrapper.OrderAmount == 0 && !_book.CheckCanFillOrder(incomingOrder.IsBuy, incomingOrder.OpenQuantity, incomingOrder.Price)) { _tradeListener?.OnCancel(incomingOrder.OrderId, incomingOrder.OpenQuantity, incomingOrder.Cost, CancelReason.FillOrKill); } else if (orderWrapper.OrderCondition == OrderCondition.FillOrKill && orderWrapper.OrderAmount != 0 && canBeFilled == false) { _tradeListener?.OnCancel(incomingOrder.OrderId, 0, 0, CancelReason.FillOrKill); } else if (incomingOrder.CancelOn > 0 && incomingOrder.CancelOn <= timeNow) { if (orderWrapper.TotalQuantity == 0) { _tradeListener?.OnCancel(incomingOrder.OrderId, incomingOrder.OpenQuantity, incomingOrder.Cost, CancelReason.ValidityExpired); } else { _tradeListener?.OnCancel(incomingOrder.OrderId, orderWrapper.TotalQuantity, incomingOrder.Cost, CancelReason.ValidityExpired); } } else { if (orderWrapper.TotalQuantity > 0) { var iceberg = new Iceberg() { TipQuantity = orderWrapper.TipQuantity, TotalQuantity = orderWrapper.TotalQuantity }; _currentIcebergOrders.Add(incomingOrder.OrderId, iceberg); incomingOrder = GetTip(incomingOrder, iceberg); } if (incomingOrder.CancelOn > 0) { AddGoodTillDateOrder(incomingOrder.CancelOn, incomingOrder.OrderId); } _currentOrders.Add(incomingOrder.OrderId, incomingOrder); if (orderWrapper.StopPrice != 0 && !isOrderTriggered && ((incomingOrder.IsBuy && orderWrapper.StopPrice > _marketPrice) || (!incomingOrder.IsBuy && (orderWrapper.StopPrice < _marketPrice || _marketPrice == 0)))) { _book.AddStopOrder(incomingOrder, orderWrapper.StopPrice); } else { if (orderWrapper.Order.IsBuy && orderWrapper.Order.OpenQuantity == 0) { if (quantity.HasValue) { orderWrapper.Order.OpenQuantity = quantity.Value; MatchAndAddOrder(incomingOrder, orderWrapper.OrderCondition); } else { _currentOrders.Remove(orderWrapper.Order.OrderId); _tradeListener?.OnCancel(orderWrapper.Order.OrderId, 0, 0, CancelReason.MarketOrderNoLiquidity); } } else { MatchAndAddOrder(incomingOrder, orderWrapper.OrderCondition); } } } return(OrderMatchingResult.OrderAccepted); }