private bool MatchWithOpenOrders(Order incomingOrder) { bool anyMatchHappend = false; while (true) { Order restingOrder = _book.GetBestBuyOrderToMatch(!incomingOrder.IsBuy); if (restingOrder == null) { break; } if ((incomingOrder.IsBuy && (restingOrder.Price <= incomingOrder.Price || incomingOrder.Price == 0)) || (!incomingOrder.IsBuy && (restingOrder.Price >= incomingOrder.Price))) { Price matchPrice = restingOrder.Price; Quantity maxQuantity = 0; if (incomingOrder.OpenQuantity > 0) { maxQuantity = incomingOrder.OpenQuantity >= restingOrder.OpenQuantity ? restingOrder.OpenQuantity : incomingOrder.OpenQuantity; incomingOrder.OpenQuantity -= maxQuantity; } else { throw new Exception("not expected"); } var cost = Math.Round(maxQuantity * matchPrice, _quoteCurrencyDecimalPlaces); restingOrder.Cost += cost; incomingOrder.Cost += cost; bool orderFilled = _book.FillOrder(restingOrder, maxQuantity); bool isRestingTipAdded = false; if (orderFilled) { _currentOrders.Remove(restingOrder.OrderId); if (restingOrder.CancelOn > 0) { RemoveGoodTillDateOrder(restingOrder.CancelOn, restingOrder.OrderId); } if (restingOrder.IsTip) { isRestingTipAdded = AddTip(restingOrder, restingOrder.Cost); } } bool isIncomingOrderFilled = incomingOrder.IsFilled; if (incomingOrder.IsTip == true) { isIncomingOrderFilled = !_currentIcebergOrders.ContainsKey(incomingOrder.OrderId); } bool isRestingOrderFilled = restingOrder.IsFilled && !isRestingTipAdded; Quantity?askRemainingQuanity = null; Quantity?bidCost = null; if (incomingOrder.IsBuy) { if (isIncomingOrderFilled) { bidCost = incomingOrder.Cost; } if (isRestingOrderFilled) { askRemainingQuanity = restingOrder.OpenQuantity; } } else { if (isRestingOrderFilled) { bidCost = restingOrder.Cost; } if (isIncomingOrderFilled) { askRemainingQuanity = incomingOrder.OpenQuantity; } } _tradeListener?.OnTrade(incomingOrder.OrderId, restingOrder.OrderId, matchPrice, maxQuantity, askRemainingQuanity, bidCost); _marketPrice = matchPrice; anyMatchHappend = true; } else { break; } if (incomingOrder.IsFilled) { break; } } return(anyMatchHappend); }
public static byte[] Serialize(OrderId orderId, Quantity remainingQuantity, Quantity cost, Quantity fee, CancelReason cancelReason, int timeStamp) { byte[] msg = new byte[sizeOfMessage]; Write(msg, messageLengthOffset, sizeOfMessage); msg[messageTypeOffset] = (byte)MessageType.Cancel; Write(msg, versionOffset, version); Write(msg, orderIdOffset, orderId); Write(msg, remainingQuantityOffset, remainingQuantity); msg[cancelReasonOffset] = (byte)cancelReason; Write(msg, timestampOffset, timeStamp); Write(msg, costOffset, cost); Write(msg, feeOffset, fee); return(msg); }
internal void AddOrder(Order order) { _quantity += order.OpenQuantity; _orders.Add(order); }
internal bool RemoveOrder(Order order) { _quantity -= order.OpenQuantity; return(_orders.Remove(order)); }
public static byte[] Serialize(ulong makerOrderId, ulong takerOrderId, Price matchRate, Quantity matchQuantity, long timeStamp, bool incomingOrderFilled) { byte[] msg = new byte[sizeOfMessage]; Write(msg, messageLengthOffset, sizeOfMessage); msg[messageTypeOffset] = (byte)MessageType.Fill; Write(msg, versionOffset, version); Write(msg, makerOrderIdOffset, makerOrderId); Write(msg, takerOrderIdOffset, takerOrderId); Write(msg, matchRateOffset, matchRate); Write(msg, matchQuantityOffset, matchQuantity); Write(msg, timestampOffset, timeStamp); Write(msg, incomingOrderFilledOffset, incomingOrderFilled); return(msg); }
public QuantityTrackingPriceLevel(Price price) { _price = price; _quantity = 0; _orders = new SortedSet <Order>(_orderSequenceComparer); }
private bool MatchWithOpenOrders(Order incomingOrder) { bool anyMatchHappend = false; while (true) { Order?restingOrder = _book.GetBestBuyOrderToMatch(!incomingOrder.IsBuy); if (restingOrder == null) { break; } if ((incomingOrder.IsBuy && (restingOrder.Price <= incomingOrder.Price || incomingOrder.Price == 0)) || (!incomingOrder.IsBuy && (restingOrder.Price >= incomingOrder.Price))) { Price matchPrice = restingOrder.Price; Quantity maxQuantity = 0; if (incomingOrder.OpenQuantity > 0) { maxQuantity = incomingOrder.OpenQuantity >= restingOrder.OpenQuantity ? restingOrder.OpenQuantity : incomingOrder.OpenQuantity; incomingOrder.OpenQuantity -= maxQuantity; } else { throw new Exception(Constant.NOT_EXPECTED); } var cost = Math.Round(maxQuantity * matchPrice, _quoteCurrencyDecimalPlaces); restingOrder.Cost += cost; incomingOrder.Cost += cost; var incomingFee = _feeProvider.GetFee(incomingOrder.FeeId); var restingFee = _feeProvider.GetFee(restingOrder.FeeId); restingOrder.Fee += Math.Round((cost * restingFee.MakerFee) / 100, _quoteCurrencyDecimalPlaces); incomingOrder.Fee += Math.Round((cost * incomingFee.TakerFee) / 100, _quoteCurrencyDecimalPlaces); bool orderFilled = _book.FillOrder(restingOrder, maxQuantity); bool isRestingTipAdded = false; if (orderFilled) { _currentOrders.Remove(restingOrder.OrderId); if (restingOrder.CancelOn > 0) { RemoveGoodTillDateOrder(restingOrder.CancelOn, restingOrder.OrderId); } if (restingOrder.IsTip) { isRestingTipAdded = AddTip(restingOrder); } } bool isIncomingOrderFilled = incomingOrder.IsFilled; if (incomingOrder.IsTip == true) { isIncomingOrderFilled = incomingOrder.TotalQuantity == 0; } bool isRestingOrderFilled = restingOrder.IsFilled && !isRestingTipAdded; Quantity?askRemainingQuanity = null; Quantity?askFee = null; Quantity?bidCost = null; Quantity?bidFee = null; if (incomingOrder.IsBuy) { if (isIncomingOrderFilled) { bidCost = incomingOrder.Cost; bidFee = incomingOrder.Fee; } if (isRestingOrderFilled) { askRemainingQuanity = restingOrder.OpenQuantity; askFee = restingOrder.Fee; } } else { if (isRestingOrderFilled) { bidCost = restingOrder.Cost; bidFee = restingOrder.Fee; } if (isIncomingOrderFilled) { askRemainingQuanity = incomingOrder.OpenQuantity; askFee = incomingOrder.Fee; } } _tradeListener?.OnTrade(incomingOrder.OrderId, restingOrder.OrderId, matchPrice, maxQuantity, askRemainingQuanity, askFee, bidCost, bidFee); _marketPrice = matchPrice; anyMatchHappend = true; } else { break; } if (incomingOrder.IsFilled) { break; } } return(anyMatchHappend); }
public MatchingEngine(ITradeListener tradeListener, IFeeProvider feeProvider, Quantity stepSize, int quoteCurrencyDecimalPlaces = 0) { if (quoteCurrencyDecimalPlaces < 0) { throw new NotSupportedException($"Invalid value of {nameof(quoteCurrencyDecimalPlaces)}"); } if (stepSize < 0) { throw new NotSupportedException($"Invalid value of {nameof(stepSize)}"); } _book = new Book(); _currentOrders = new Dictionary <OrderId, Order>(); _goodTillDateOrders = new SortedDictionary <int, HashSet <OrderId> >(); _acceptedOrders = new HashSet <OrderId>(); _tradeListener = tradeListener; _feeProvider = feeProvider; _quoteCurrencyDecimalPlaces = quoteCurrencyDecimalPlaces; _power = (decimal)Math.Pow(10, _quoteCurrencyDecimalPlaces); _stepSize = stepSize; }
public bool CheckCanFillMarketOrderAmount(bool isBuy, Quantity orderAmount) { return(isBuy ? CheckMarketOrderAmountCanBeFilled(orderAmount, _askSide) : CheckMarketOrderAmountCanBeFilled(orderAmount, _bidSide)); }
public bool CheckCanFillOrder(bool isBuy, Quantity requestedQuantity, Price limitPrice) { return(isBuy ? CheckBuyOrderCanBeFilled(requestedQuantity, limitPrice) : CheckSellOrderCanBeFilled(requestedQuantity, limitPrice)); }
private (bool anyMatch, bool isMarketOrderLessThanStepSize) MatchWithOpenOrders(Order incomingOrder) { bool anyMatchHappend = false; bool isMarketOrderLessThanStepSize = false; while (true) { Order restingOrder = _book.GetBestBuyOrderToMatch(!incomingOrder.IsBuy); if (restingOrder == null) { break; } if ((incomingOrder.IsBuy && (restingOrder.Price <= incomingOrder.Price || incomingOrder.Price == 0)) || (!incomingOrder.IsBuy && (restingOrder.Price >= incomingOrder.Price))) { Price matchPrice = restingOrder.Price; Quantity maxQuantity = 0; if (incomingOrder.Price == 0 && incomingOrder.IsBuy == true && incomingOrder.OpenQuantity == 0 && incomingOrder.OrderAmount != 0) { Quantity quantity = incomingOrder.OrderAmount / matchPrice; quantity = quantity - (quantity % _stepSize); if (quantity == 0) { isMarketOrderLessThanStepSize = true; break; } maxQuantity = quantity >= restingOrder.OpenQuantity ? restingOrder.OpenQuantity : quantity; var tradeAmount = decimal.Round(maxQuantity * matchPrice * _power) / _power; if (tradeAmount == 0) { isMarketOrderLessThanStepSize = true; break; } incomingOrder.OrderAmount -= tradeAmount; } else if (incomingOrder.OpenQuantity != 0) { maxQuantity = incomingOrder.OpenQuantity >= restingOrder.OpenQuantity ? restingOrder.OpenQuantity : incomingOrder.OpenQuantity; incomingOrder.OpenQuantity -= maxQuantity; } else { throw new Exception("not expected"); } bool orderFilled = _book.FillOrder(restingOrder, maxQuantity); if (orderFilled) { _currentOrders.Remove(restingOrder.OrderId); if (restingOrder.CancelOn > 0) { RemoveGoodTillDateOrder(restingOrder.CancelOn, restingOrder.OrderId); } if (restingOrder.IsTip) { AddTip(restingOrder); } } bool isIncomingOrderFilled = incomingOrder.IsFilled; if (incomingOrder.IsTip == true) { isIncomingOrderFilled = !_currentIcebergOrders.ContainsKey(incomingOrder.OrderId); } _tradeListener?.OnTrade(incomingOrder.OrderId, restingOrder.OrderId, matchPrice, maxQuantity, isIncomingOrderFilled); _marketPrice = matchPrice; anyMatchHappend = true; } else { break; } if (incomingOrder.IsFilled) { break; } } return(anyMatchHappend, isMarketOrderLessThanStepSize); }