Limit_order_is_accepted_by_empty_exchange (LimitOrder limitOrder, Market market) { foreach (var exchange in BasicTests.CreateExchangesOfDifferentTypes()) { var side = limitOrder.OrderInfo.Side; // first make sure exchange's orderbook is empty var someOrderBook = exchange[market]; Assert.That(someOrderBook[Side.Bid].Count(), Is.EqualTo(0), "initial exchange state should be zero orders (buy)"); Assert.That(someOrderBook[Side.Ask].Count(), Is.EqualTo(0), "initial exchange state should be zero orders (sell)"); SendOrder(exchange, limitOrder, market); var someOrderBookAgain = exchange[market]; Assert.That(someOrderBookAgain[side].Count(), Is.EqualTo(1)); var uniqueLimitOrder = someOrderBookAgain[side].Tip.Value; Assert.That(uniqueLimitOrder.OrderInfo.Side, Is.EqualTo(side)); Assert.That(uniqueLimitOrder.Price, Is.EqualTo(limitOrder.Price)); Assert.That(uniqueLimitOrder.OrderInfo.Quantity, Is.EqualTo(limitOrder.OrderInfo.Quantity)); Assert.That(someOrderBookAgain[side.Other()].Count(), Is.EqualTo(0)); yield return(exchange); } }
private static void MakerOnly_order_can_not_cross_another_limit_order_of_same_amount_and_same_price_and_should_be_rejected (Side side) { var quantity = 1; var price = 10000; var market = new Market(Currency.BTC, Currency.USD); foreach (var exchange in BasicTests.CreateExchangesOfDifferentTypes()) { var orderBook = exchange[market]; var firstLimitOrder = new LimitOrder(new OrderInfo(Guid.NewGuid(), side, quantity), price); SendOrder(exchange, firstLimitOrder, market); var secondLimitMatchingOrder = new LimitOrder(new OrderInfo(Guid.NewGuid(), side.Other(), quantity), price); Assert.Throws <MatchExpectationsUnmet>(() => { SendOrder(exchange, secondLimitMatchingOrder, market); }); var orderBookAgain = exchange[market]; Assert.That(orderBookAgain[side].Count(), Is.EqualTo(1)); Assert.That(orderBookAgain[side.Other()].Count(), Is.EqualTo(0)); } }
private static void Limit_orders_of_different_sides_and_different_price_can_match (Side side) { var quantity = 1; var price = 1000; var opposingPrice = side == Side.Bid ? price - 1 : price + 1; var market = new Market(Currency.BTC, Currency.USD); foreach (var exchange in BasicTests.CreateExchangesOfDifferentTypes()) { var orderBook = exchange[market]; var firstLimitOrder = new LimitOrder(new OrderInfo(Guid.NewGuid(), side, quantity), price); SendOrder(exchange, firstLimitOrder, market); var secondLimitOrder = new LimitOrder(new OrderInfo(Guid.NewGuid(), side.Other(), quantity), opposingPrice); var maybeMatch = SendOrder(exchange, secondLimitOrder, market); var orderBookAgain = exchange[market]; Assert.That(orderBookAgain[side].Count(), Is.EqualTo(0)); Assert.That(orderBookAgain[side.Other()].Count(), Is.EqualTo(0)); Assert.That(maybeMatch, Is.Not.Null); Assert.That(maybeMatch.Value.IsFull); } }
private static void Limit_orders_of_same_side_never_match(Side side) { var quantity = 1; var price = 10000; var secondAndThirdPrice = price + 1; var market = new Market(Currency.BTC, Currency.USD); foreach (var exchange in BasicTests.CreateExchangesOfDifferentTypes()) { var orderBook = exchange[market]; var firstLimitOrder = new LimitOrder(new OrderInfo(Guid.NewGuid(), side, quantity), price); var maybeMatch = SendOrder(exchange, firstLimitOrder, market); Assert.That(maybeMatch, Is.Null); var secondLimitOrder = new LimitOrder(new OrderInfo(Guid.NewGuid(), side, quantity), secondAndThirdPrice); maybeMatch = SendOrder(exchange, secondLimitOrder, market); Assert.That(maybeMatch, Is.Null); var thirdLimitOrder = new LimitOrder(new OrderInfo(Guid.NewGuid(), side, quantity), secondAndThirdPrice); maybeMatch = SendOrder(exchange, thirdLimitOrder, market); Assert.That(maybeMatch, Is.Null); var allLimitOrdersSent = new List <LimitOrder> { firstLimitOrder, secondLimitOrder, thirdLimitOrder }; var orderBookAgain = exchange[market]; Assert.That(orderBookAgain[side.Other()].Count(), Is.EqualTo(0)); var ourSide = new List <LimitOrder>(); var orderBookThisSide = orderBookAgain[side]; while (true) { try { var tip = orderBookThisSide.Tip.Value; ourSide.Add(tip); orderBookThisSide = orderBookThisSide.Tail.Value; } catch { break; } } AssertAreSameOrdersRegardlessOfOrder(ourSide, allLimitOrdersSent); } }
CreateNewExchangeAndSendTheseOrdersToIt (IEnumerable <LimitOrder> orders) { foreach (var exchange in BasicTests.CreateExchangesOfDifferentTypes()) { var someMarket = new Market(Currency.BTC, Currency.USD); foreach (var order in orders) { SendOrder(exchange, order, someMarket); } yield return(exchange[someMarket]); } }
private static void Limit_order_crosses_one_limit_order_and_stays_partially_after_no_more_liquidity_left_in_one_side(Side side) { var quantityOfThePreviouslySittingOrder = 1; var quantityOfIncomingOrder = quantityOfThePreviouslySittingOrder + 1; var price = 10000; var market = new Market(Currency.BTC, Currency.USD); foreach (var exchange in BasicTests.CreateExchangesOfDifferentTypes()) { var orderBook = exchange[market]; var firstLimitOrder = new LimitOrder(new OrderInfo(Guid.NewGuid(), side, quantityOfThePreviouslySittingOrder), price); var maybeMatch = SendOrder(exchange, firstLimitOrder, market); Assert.That(maybeMatch, Is.Null); var incomingLimitMatchingOrder = new LimitOrder(new OrderInfo(Guid.NewGuid(), side.Other(), quantityOfIncomingOrder), price); maybeMatch = SendOrder(exchange, incomingLimitMatchingOrder, market); Assert.That(maybeMatch, Is.Not.Null); var orderBookAgain = exchange[market]; Assert.That(orderBookAgain[side].Count(), Is.EqualTo(0)); Assert.That(orderBookAgain[side.Other()].Count(), Is.EqualTo(1)); var leftOverLimitOrder = orderBookAgain[side.Other()].Tip.Value; Assert.That(leftOverLimitOrder.OrderInfo.Side, Is.EqualTo(incomingLimitMatchingOrder.OrderInfo.Side)); Assert.That(leftOverLimitOrder.Price, Is.EqualTo(price)); Assert.That(leftOverLimitOrder.OrderInfo.Quantity, Is.EqualTo(quantityOfIncomingOrder - quantityOfThePreviouslySittingOrder)); Assert.That(maybeMatch.Value.IsPartial, Is.True); var amount = ((Match.Partial)maybeMatch.Value).Item; Assert.That(amount, Is.EqualTo(quantityOfThePreviouslySittingOrder)); } }
private static void Limit_order_that_matches_when_inserted_always_chooses_the_best_price_even_if_trader_was_stupid_to_choose_a_worse_price(Side side) { var quantity = 1; var tipPrice = 10000; var notTipPrice = side == Side.Bid ? tipPrice / 2 : tipPrice * 2; var market = new Market(Currency.BTC, Currency.USD); foreach (var exchange in BasicTests.CreateExchangesOfDifferentTypes()) { var tipLimitOrder = new LimitOrder(new OrderInfo(Guid.NewGuid(), side, quantity), tipPrice); var maybeMatch = SendOrder(exchange, tipLimitOrder, market); Assert.That(maybeMatch, Is.Null); var nonTipLimitOrder = new LimitOrder(new OrderInfo(Guid.NewGuid(), side, quantity), notTipPrice); maybeMatch = SendOrder(exchange, nonTipLimitOrder, market); Assert.That(maybeMatch, Is.Null); var incomingLimitMatchingOrder = new LimitOrder(new OrderInfo(Guid.NewGuid(), side.Other(), quantity), notTipPrice); maybeMatch = SendOrder(exchange, incomingLimitMatchingOrder, market); var orderBook = exchange[market]; Assert.That(orderBook[side.Other()].Count(), Is.EqualTo(0)); Assert.That(orderBook[side].Count(), Is.EqualTo(1)); var leftOverLimitOrder = orderBook[side].Tip.Value; Assert.That(leftOverLimitOrder.OrderInfo.Side, Is.EqualTo(side)); Assert.That(leftOverLimitOrder.OrderInfo.Quantity, Is.EqualTo(quantity)); Assert.That(leftOverLimitOrder.Price, Is.EqualTo(notTipPrice)); Assert.That(maybeMatch, Is.Not.Null); Assert.That(maybeMatch.Value.IsFull, Is.True); } }
private static void Limit_order_crosses_two_limit_orders_of_same_price(Side side) { var quantityOfEachOfTheSittingOrders = 1; var quantityOfIncomingOrder = quantityOfEachOfTheSittingOrders * 2; var price = 10000; var market = new Market(Currency.BTC, Currency.USD); foreach (var exchange in BasicTests.CreateExchangesOfDifferentTypes()) { var orderBook = exchange[market]; var firstLimitOrder = new LimitOrder(new OrderInfo(Guid.NewGuid(), side, quantityOfEachOfTheSittingOrders), price); var maybeMatch = SendOrder(exchange, firstLimitOrder, market); Assert.That(maybeMatch, Is.Null); var secondLimitMatchingOrder = new LimitOrder(new OrderInfo(Guid.NewGuid(), side, quantityOfEachOfTheSittingOrders), price); maybeMatch = SendOrder(exchange, secondLimitMatchingOrder, market); Assert.That(maybeMatch, Is.Null); var incomingLimitMatchingOrder = new LimitOrder(new OrderInfo(Guid.NewGuid(), side.Other(), quantityOfIncomingOrder), price); maybeMatch = SendOrder(exchange, incomingLimitMatchingOrder, market); var orderBookAgain = exchange[market]; Assert.That(orderBookAgain[side.Other()].Count(), Is.EqualTo(0)); Assert.That(orderBookAgain[side].Count(), Is.EqualTo(0)); Assert.That(maybeMatch, Is.Not.Null); Assert.That(maybeMatch.Value.IsFull); } }
private static void MakerOnly_orders_of_different_sides_but_different_price_dont_match (Side side) { var quantity = 1; var price = 10000; var opposingPrice = side == Side.Bid ? price + 1 : price - 1; var market = new Market(Currency.BTC, Currency.USD); foreach (var exchange in BasicTests.CreateExchangesOfDifferentTypes()) { var orderBook = exchange[market]; var firstMakerOnlyOrder = new LimitOrder(new OrderInfo(Guid.NewGuid(), side, quantity), price); SendOrder(exchange, firstMakerOnlyOrder, market); var secondMakerOnlyOrder = new LimitOrder(new OrderInfo(Guid.NewGuid(), side.Other(), quantity), opposingPrice); SendOrder(exchange, secondMakerOnlyOrder, market); var orderBookAgain = exchange[market]; Assert.That(orderBookAgain[side].Count(), Is.EqualTo(1)); var aLimitOrder = orderBookAgain[side].Tip.Value; Assert.That(aLimitOrder.OrderInfo.Side, Is.EqualTo(firstMakerOnlyOrder.OrderInfo.Side)); Assert.That(aLimitOrder.Price, Is.EqualTo(firstMakerOnlyOrder.Price)); Assert.That(aLimitOrder.OrderInfo.Quantity, Is.EqualTo(firstMakerOnlyOrder.OrderInfo.Quantity)); Assert.That(orderBookAgain[side.Other()].Count(), Is.EqualTo(1)); var anotherLimitOrder = orderBookAgain[side.Other()].Tip.Value; Assert.That(anotherLimitOrder.OrderInfo.Side, Is.EqualTo(secondMakerOnlyOrder.OrderInfo.Side)); Assert.That(anotherLimitOrder.Price, Is.EqualTo(secondMakerOnlyOrder.Price)); Assert.That(anotherLimitOrder.OrderInfo.Quantity, Is.EqualTo(secondMakerOnlyOrder.OrderInfo.Quantity)); } }
private static void Limit_order_half_crosses_another_limit_order_of_same_price (Side side) { var quantityOfFirstOrder = 2; var quantityOfSecondOrder = quantityOfFirstOrder - 1; var price = 10000; var market = new Market(Currency.BTC, Currency.USD); foreach (var exchange in BasicTests.CreateExchangesOfDifferentTypes()) { var orderBook = exchange[market]; var firstLimitOrder = new LimitOrder(new OrderInfo(Guid.NewGuid(), side, quantityOfFirstOrder), price); var maybeMatch = SendOrder(exchange, firstLimitOrder, market); Assert.That(maybeMatch, Is.Null); var secondLimitMatchingOrder = new LimitOrder(new OrderInfo(Guid.NewGuid(), side.Other(), quantityOfSecondOrder), price); maybeMatch = SendOrder(exchange, secondLimitMatchingOrder, market); var orderBookAgain = exchange[market]; Assert.That(orderBookAgain[side.Other()].Count(), Is.EqualTo(0)); Assert.That(orderBookAgain[side].Count(), Is.EqualTo(1)); var leftOverLimitOrder = orderBookAgain[side].Tip.Value; Assert.That(leftOverLimitOrder.OrderInfo.Side, Is.EqualTo(side)); Assert.That(leftOverLimitOrder.Price, Is.EqualTo(price)); Assert.That(leftOverLimitOrder.OrderInfo.Quantity, Is.EqualTo(quantityOfFirstOrder - quantityOfSecondOrder)); Assert.That(maybeMatch, Is.Not.Null); Assert.That(maybeMatch.Value.IsFull, Is.True); } }
private void Market_order_throws_on_exchange_with_no_limit_orders_and_orderbooks_are_left_intact(Side side) { var quantityForMarketOrder = 1; var market = new Market(Currency.BTC, Currency.USD); foreach (var exchange in BasicTests.CreateExchangesOfDifferentTypes()) { var marketOrder = new OrderInfo(Guid.NewGuid(), side, quantityForMarketOrder); Assert.Throws <LiquidityProblem>(() => { exchange.SendMarketOrder(marketOrder, market); }); var btcUsdOrderBookAfterException = exchange[market]; Assert.That(btcUsdOrderBookAfterException[Side.Ask].Count(), Is.EqualTo(0)); Assert.That(btcUsdOrderBookAfterException[Side.Bid].Count(), Is.EqualTo(0)); } }