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);
        }
        public OrderMatchingResult AddOrder(Order incomingOrder, bool isOrderTriggered = false)
        {
            if (incomingOrder == null)
            {
                throw new ArgumentNullException(nameof(incomingOrder));
            }

            incomingOrder.OpenQuantity = incomingOrder.Quantity;
            incomingOrder.IsTip        = false;

            if (incomingOrder.Price < 0 || (incomingOrder.Quantity <= 0 && incomingOrder.OrderAmount == 0) || (incomingOrder.Quantity == 0 && incomingOrder.OrderAmount <= 0) || incomingOrder.StopPrice < 0 || incomingOrder.TotalQuantity < 0)
            {
                return(OrderMatchingResult.InvalidPriceQuantityStopPriceOrderAmountOrTotalQuantity);
            }

            if (incomingOrder.OrderCondition == OrderCondition.BookOrCancel && (incomingOrder.Price == 0 || incomingOrder.StopPrice != 0))
            {
                return(OrderMatchingResult.BookOrCancelCannotBeMarketOrStopOrder);
            }

            if (incomingOrder.Quantity % _stepSize != 0 || incomingOrder.TotalQuantity % _stepSize != 0)
            {
                return(OrderMatchingResult.QuantityAndTotalQuantityShouldBeMultipleOfStepSize);
            }

            if (incomingOrder.OrderCondition == OrderCondition.ImmediateOrCancel && (incomingOrder.StopPrice != 0))
            {
                return(OrderMatchingResult.ImmediateOrCancelCannotBeStopOrder);
            }

            if (incomingOrder.OrderCondition == OrderCondition.FillOrKill && incomingOrder.StopPrice != 0)
            {
                return(OrderMatchingResult.FillOrKillCannotBeStopOrder);
            }

            if (incomingOrder.CancelOn < 0)
            {
                return(OrderMatchingResult.InvalidCancelOnForGTD);
            }

            if (incomingOrder.CancelOn > 0 && (incomingOrder.OrderCondition == OrderCondition.FillOrKill || incomingOrder.OrderCondition == OrderCondition.ImmediateOrCancel))
            {
                return(OrderMatchingResult.GoodTillDateCannotBeIOCorFOK);
            }

            if (incomingOrder.Price == 0 && incomingOrder.OrderAmount != 0 && incomingOrder.OpenQuantity != 0)
            {
                return(OrderMatchingResult.MarketOrderOnlySupportedOrderAmountOrQuantityNoBoth);
            }

            if (incomingOrder.OrderAmount != 0 && (incomingOrder.Price != 0 || !incomingOrder.IsBuy))
            {
                return(OrderMatchingResult.OrderAmountOnlySupportedForMarketBuyOrder);
            }

            if (incomingOrder.TotalQuantity > 0)
            {
                incomingOrder.OpenQuantity = incomingOrder.TotalQuantity;
                if (incomingOrder.OrderCondition == OrderCondition.FillOrKill || incomingOrder.OrderCondition == OrderCondition.ImmediateOrCancel)
                {
                    return(OrderMatchingResult.IcebergOrderCannotBeFOKorIOC);
                }
                if (incomingOrder.StopPrice != 0 || incomingOrder.Price == 0)
                {
                    return(OrderMatchingResult.IcebergOrderCannotBeStopOrMarketOrder);
                }
                if (incomingOrder.TotalQuantity <= incomingOrder.Quantity)
                {
                    return(OrderMatchingResult.InvalidIcebergOrderTotalQuantity);
                }
            }

            if (_acceptedOrders.Contains(incomingOrder.OrderId))
            {
                return(OrderMatchingResult.DuplicateOrder);
            }
            _acceptedOrders.Add(incomingOrder.OrderId);
            var timeNow = _timeProvider.GetUpochMilliseconds();

            CancelExpiredOrders(timeNow);
            if (incomingOrder.OrderCondition == OrderCondition.BookOrCancel && ((incomingOrder.IsBuy && _book.BestAskPrice <= incomingOrder.Price) || (!incomingOrder.IsBuy && incomingOrder.Price <= _book.BestBidPrice)))
            {
                _tradeListener?.OnCancel(incomingOrder.OrderId, incomingOrder.OpenQuantity, incomingOrder.OrderAmount, CancelReason.BookOrCancel);
            }
            else if (incomingOrder.OrderCondition == OrderCondition.FillOrKill && incomingOrder.OrderAmount == 0 && !_book.CheckCanFillOrder(incomingOrder.IsBuy, incomingOrder.OpenQuantity, incomingOrder.Price))
            {
                _tradeListener?.OnCancel(incomingOrder.OrderId, incomingOrder.OpenQuantity, incomingOrder.OrderAmount, CancelReason.FillOrKill);
            }
            else if (incomingOrder.OrderCondition == OrderCondition.FillOrKill && incomingOrder.OrderAmount != 0 && !_book.CheckCanFillMarketOrderAmount(incomingOrder.IsBuy, incomingOrder.OrderAmount))
            {
                _tradeListener?.OnCancel(incomingOrder.OrderId, incomingOrder.OpenQuantity, incomingOrder.OrderAmount, CancelReason.FillOrKill);
            }
            else if (incomingOrder.CancelOn > 0 && incomingOrder.CancelOn <= timeNow)
            {
                _tradeListener?.OnCancel(incomingOrder.OrderId, incomingOrder.OpenQuantity, incomingOrder.OrderAmount, CancelReason.ValidityExpired);
            }
            else
            {
                if (incomingOrder.TotalQuantity > 0)
                {
                    _currentIcebergOrders.Add(incomingOrder.OrderId, incomingOrder);
                    incomingOrder = GetTip(incomingOrder);
                }
                if (incomingOrder.CancelOn > 0)
                {
                    AddGoodTillDateOrder(incomingOrder.CancelOn, incomingOrder.OrderId);
                }
                _currentOrders.Add(incomingOrder.OrderId, incomingOrder);

                if (incomingOrder.StopPrice != 0 && !isOrderTriggered && ((incomingOrder.IsBuy && incomingOrder.StopPrice > _marketPrice) || (!incomingOrder.IsBuy && (incomingOrder.StopPrice < _marketPrice || _marketPrice == 0))))
                {
                    _book.AddStopOrder(incomingOrder);
                }
                else
                {
                    MatchAndAddOrder(incomingOrder);
                }
            }

            return(OrderMatchingResult.OrderAccepted);
        }