예제 #1
0
        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);
            }
        }
예제 #2
0
        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));
            }
        }
예제 #3
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);
            }
        }
예제 #4
0
        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);
            }
        }
예제 #5
0
 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]);
     }
 }
예제 #6
0
        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));
            }
        }
예제 #7
0
        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);
            }
        }
예제 #8
0
        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);
            }
        }
예제 #9
0
        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));
            }
        }
예제 #10
0
        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);
            }
        }
예제 #11
0
        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));
            }
        }