public void FindLevel_ShouldReturnCorrectValue()
        {
            var pair1    = "BTC/USD";
            var pair2    = "ETH/BTC";
            var data1    = GetOrderBookSnapshotMockData(pair1, 500);
            var data2    = GetOrderBookSnapshotMockData(pair2, 200);
            var data     = data2.Concat(data1).ToArray();
            var snapshot = new OrderBookLevelBulk(OrderBookAction.Insert, data, CryptoOrderBookType.L2);
            var source   = new OrderBookSourceMock(snapshot);

            ICryptoOrderBook orderBook = new CryptoOrderBook(pair1, source);

            source.StreamSnapshot();

            Assert.Equal(1000, orderBook.FindBidLevelByPrice(0)?.Amount);
            Assert.Equal(1000, orderBook.FindBidLevelById("0-bid")?.Amount);

            Assert.Equal(3000, orderBook.FindAskLevelByPrice(1000)?.Amount);
            Assert.Equal(3000, orderBook.FindAskLevelById("1000-ask")?.Amount);
        }
        public async Task StreamingDiff_TwoPairs_ShouldHandleCorrectly()
        {
            var pair1 = "BTC/USD";
            var pair2 = "ETH/USD";

            var data1    = GetOrderBookSnapshotMockData(pair1, 500);
            var data2    = GetOrderBookSnapshotMockData(pair2, 200);
            var data     = data2.Concat(data1).ToArray();
            var snapshot = new OrderBookLevelBulk(OrderBookAction.Insert, data, CryptoOrderBookType.L2);
            var source   = new OrderBookSourceMock(snapshot);

            ICryptoOrderBook orderBook1 = new CryptoOrderBook(pair1, source)
            {
                DebugEnabled = true
            };
            ICryptoOrderBook orderBook2 = new CryptoOrderBook(pair2, source)
            {
                DebugEnabled = true
            };

            source.StreamSnapshot();

            source.StreamBulk(GetInsertBulkL2(
                                  CreateLevel(pair2, 199.4, 50, CryptoOrderSide.Bid),
                                  CreateLevel(pair2, 198.3, 600, CryptoOrderSide.Bid),
                                  CreateLevel(pair2, 50.33, 3350, CryptoOrderSide.Bid),

                                  CreateLevel(pair1, 500.2, 400, CryptoOrderSide.Ask),
                                  CreateLevel(pair1, 503.1, 3000, CryptoOrderSide.Ask),
                                  CreateLevel(pair1, 800.123, 1234, CryptoOrderSide.Ask),

                                  CreateLevel(null, 101.1, null, CryptoOrderSide.Bid),
                                  CreateLevel(null, 901.1, null, CryptoOrderSide.Ask)
                                  ));

            source.StreamBulk(GetInsertBulkL2(
                                  CreateLevel(pair1, 499.4, 50, CryptoOrderSide.Bid),
                                  CreateLevel(pair1, 498.3, 600, CryptoOrderSide.Bid),
                                  CreateLevel(pair1, 300.33, 3350, CryptoOrderSide.Bid),

                                  CreateLevel(pair2, 200.2, 400, CryptoOrderSide.Ask),
                                  CreateLevel(pair2, 203.1, 3000, CryptoOrderSide.Ask),
                                  CreateLevel(pair2, 250.123, 1234, CryptoOrderSide.Ask)
                                  ));

            source.StreamBulk(GetUpdateBulkL2(
                                  CreateLevel(pair1, 499, 33, CryptoOrderSide.Bid),
                                  CreateLevel(pair1, 450, 33, CryptoOrderSide.Bid),
                                  CreateLevel(pair1, 501, 32, CryptoOrderSide.Ask),
                                  CreateLevel(pair1, 503.1, 32, CryptoOrderSide.Ask),

                                  CreateLevel(pair1, 100, null, CryptoOrderSide.Bid),
                                  CreateLevel(pair1, 900, null, CryptoOrderSide.Ask)
                                  ));

            source.StreamBulk(GetDeleteBulkL2(
                                  CreateLevel(pair1, 0, CryptoOrderSide.Bid),
                                  CreateLevel(pair1, 1, CryptoOrderSide.Bid),

                                  CreateLevel(pair2, 0, CryptoOrderSide.Bid),
                                  CreateLevel(pair2, 1, CryptoOrderSide.Bid)
                                  ));

            source.StreamBulk(GetDeleteBulkL2(
                                  CreateLevel(pair2, 400, CryptoOrderSide.Ask),
                                  CreateLevel(pair2, 399, CryptoOrderSide.Ask),

                                  CreateLevel(pair1, 1000, CryptoOrderSide.Ask),
                                  CreateLevel(pair1, 999, CryptoOrderSide.Ask)
                                  ));

            await Task.Delay(500);

            Assert.NotEmpty(orderBook1.BidLevels);
            Assert.NotEmpty(orderBook1.AskLevels);

            Assert.Equal(501, orderBook1.BidLevels.Length);
            Assert.Equal(501, orderBook1.AskLevels.Length);

            Assert.Equal(201, orderBook2.BidLevels.Length);
            Assert.Equal(201, orderBook2.AskLevels.Length);

            Assert.Equal(499.4, orderBook1.BidPrice);
            Assert.Equal(500.2, orderBook1.AskPrice);

            Assert.Equal(199.4, orderBook2.BidPrice);
            Assert.Equal(200.2, orderBook2.AskPrice);

            Assert.Equal(33, orderBook1.FindBidLevelByPrice(499)?.Amount);
            Assert.Equal(33, orderBook1.FindBidLevelByPrice(450)?.Amount);

            Assert.Equal(32, orderBook1.FindAskLevelByPrice(501)?.Amount);
            Assert.Equal(32, orderBook1.FindAskLevelByPrice(503.1)?.Amount);

            var notCompleteBid = orderBook1.FindBidLevelByPrice(100);

            Assert.Equal(CryptoPairsHelper.Clean(pair1), notCompleteBid.Pair);
            Assert.Equal(1100, notCompleteBid.Amount);
            Assert.Equal(3, notCompleteBid.Count);

            var notCompleteAsk = orderBook1.FindAskLevelByPrice(900);

            Assert.Equal(CryptoPairsHelper.Clean(pair1), notCompleteAsk.Pair);
            Assert.Equal(2900, notCompleteAsk.Amount);
            Assert.Equal(3, notCompleteAsk.Count);

            Assert.Null(orderBook1.FindBidLevelByPrice(0));
            Assert.Null(orderBook1.FindBidLevelByPrice(1));
            Assert.Null(orderBook1.FindAskLevelByPrice(1000));
            Assert.Null(orderBook1.FindAskLevelByPrice(999));

            Assert.Null(orderBook1.FindBidLevelByPrice(101.1));
            Assert.Null(orderBook1.FindAskLevelByPrice(901.1));

            Assert.Null(orderBook2.FindBidLevelByPrice(101.1));
            Assert.Null(orderBook2.FindAskLevelByPrice(901.1));
        }
        public async Task StreamingDiff_ShouldHandleCorrectly()
        {
            var pair     = "BTC/USD";
            var data     = GetOrderBookSnapshotMockData(pair, 500);
            var snapshot = new OrderBookLevelBulk(OrderBookAction.Insert, data, CryptoOrderBookType.L2);
            var source   = new OrderBookSourceMock(snapshot);

            ICryptoOrderBook orderBook = new CryptoOrderBook(pair, source);

            source.StreamSnapshot();

            source.StreamBulk(GetInsertBulkL2(
                                  CreateLevel(pair, 499.4, 50, CryptoOrderSide.Bid),
                                  CreateLevel(pair, 498.3, 600, CryptoOrderSide.Bid),
                                  CreateLevel(pair, 300.33, 3350, CryptoOrderSide.Bid),
                                  CreateLevel(pair, 500.2, 400, CryptoOrderSide.Ask),
                                  CreateLevel(pair, 503.1, 3000, CryptoOrderSide.Ask),
                                  CreateLevel(pair, 800.123, 1234, CryptoOrderSide.Ask),

                                  CreateLevel(null, 101.1, null, CryptoOrderSide.Bid),
                                  CreateLevel(null, 901.1, null, CryptoOrderSide.Ask)
                                  ));

            source.StreamBulk(GetUpdateBulkL2(
                                  CreateLevel(pair, 499, 33, CryptoOrderSide.Bid),
                                  CreateLevel(pair, 450, 33, CryptoOrderSide.Bid),
                                  CreateLevel(pair, 501, 32, CryptoOrderSide.Ask),
                                  CreateLevel(pair, 503.1, 32, CryptoOrderSide.Ask),

                                  CreateLevel(pair, 100, null, CryptoOrderSide.Bid),
                                  CreateLevel(pair, 900, null, CryptoOrderSide.Ask)
                                  ));

            source.StreamBulk(GetDeleteBulkL2(
                                  CreateLevel(pair, 0, CryptoOrderSide.Bid),
                                  CreateLevel(pair, 1, CryptoOrderSide.Bid),
                                  CreateLevel(pair, 1000, CryptoOrderSide.Ask),
                                  CreateLevel(pair, 999, CryptoOrderSide.Ask)
                                  ));

            await Task.Delay(500);

            Assert.NotEmpty(orderBook.BidLevels);
            Assert.NotEmpty(orderBook.AskLevels);

            Assert.Equal(501, orderBook.BidLevels.Length);
            Assert.Equal(501, orderBook.AskLevels.Length);

            Assert.Equal(499.4, orderBook.BidPrice);
            Assert.Equal(500.2, orderBook.AskPrice);

            Assert.Equal(33, orderBook.FindBidLevelByPrice(499)?.Amount);
            Assert.Equal(33, orderBook.FindBidLevelByPrice(450)?.Amount);

            Assert.Equal(32, orderBook.FindAskLevelByPrice(501)?.Amount);
            Assert.Equal(32, orderBook.FindAskLevelByPrice(503.1)?.Amount);

            var notCompleteBid = orderBook.FindBidLevelByPrice(100);

            Assert.Equal(CryptoPairsHelper.Clean(pair), notCompleteBid.Pair);
            Assert.Equal(1100, notCompleteBid.Amount);
            Assert.Equal(3, notCompleteBid.Count);

            var notCompleteAsk = orderBook.FindAskLevelByPrice(900);

            Assert.Equal(CryptoPairsHelper.Clean(pair), notCompleteAsk.Pair);
            Assert.Equal(2900, notCompleteAsk.Amount);
            Assert.Equal(3, notCompleteAsk.Count);

            Assert.Null(orderBook.FindBidLevelByPrice(0));
            Assert.Null(orderBook.FindBidLevelByPrice(1));
            Assert.Null(orderBook.FindAskLevelByPrice(1000));
            Assert.Null(orderBook.FindAskLevelByPrice(999));

            Assert.Null(orderBook.FindBidLevelByPrice(101.1));
            Assert.Null(orderBook.FindAskLevelByPrice(901.1));
        }