public void TestBinanceOrderbookDeserializer() { //Arrange: //sample websocket message from Binance for an orderbook refresh: string sampleWebsocketMessage = "{\"u\":494630706,\"s\":\"XRPETH\",\"b\":\"0.00034050\",\"B\":\"131.00000000\",\"a\":\"0.00034350\",\"A\":\"1358.00000000\"}"; //expected deserialized orderbook: var expectedOrderbook = new BinanceOrderbook(); expectedOrderbook.Sequence = 494630706; expectedOrderbook.Symbol = "XRPETH"; expectedOrderbook.OfficialBids = new ConcurrentDictionary <decimal, decimal>(); expectedOrderbook.OfficialBids.TryAdd(0.00034050m, 131m); expectedOrderbook.OfficialAsks = new ConcurrentDictionary <decimal, decimal>(); expectedOrderbook.OfficialAsks.TryAdd(0.00034350m, 1358m); //Act: run the sample websocket message through the Huobi OrderbookConverter IOrderbook testOrderbook = (IOrderbook)JsonSerializer.Deserialize(sampleWebsocketMessage, typeof(BinanceOrderbook)); //Assert: test that the actual outcome matches the expected outcome Assert.AreEqual(expectedOrderbook.Symbol, testOrderbook.Symbol); Assert.AreEqual(expectedOrderbook.Sequence, testOrderbook.Sequence); Assert.IsTrue(expectedOrderbook.OfficialAsks.Count == testOrderbook.OfficialAsks.Count && expectedOrderbook.OfficialBids.Count == testOrderbook.OfficialBids.Count); Assert.IsTrue(CheckLayers(expectedOrderbook, testOrderbook)); }
public bool Merge(IOrderbook update) { if (this.Sequence < update.Sequence) { this.Sequence = update.Sequence; this.Timestamp = update.Timestamp; if (OfficialAsks.Count() != 0 && OfficialBids.Count() != 0) { PreviousLowestAsk = OfficialAsks.Keys.Min(); PreviousHighestBid = OfficialBids.Keys.Max(); } else { PreviousLowestAsk = 0; PreviousHighestBid = decimal.MaxValue; } OfficialAsks = update.OfficialAsks; OfficialBids = update.OfficialBids; var significantChange = SignificantChange(update); Exchange.OrderbookUpdateQueue.Enqueue(significantChange); return(significantChange.Item1); } return(false); }
public OrderManager() { this.Orderbook = new FullOrderbook(); this.Orderbook.OnTradeUpdated += WhenTradeUpdated; this.Orderbook.OnOrderbookUpdated += WhenOrderbookUpdated; this.Orderbook.OnLimitOrderUpdated += WhenLimitOrderUpdated; }
public bool Merge(IOrderbook update) { if (this.Sequence < update.Sequence) { this.Sequence = update.Sequence; this.Timestamp = update.Timestamp; if (OfficialAsks.Count() != 0 && OfficialBids.Count() != 0) { PreviousLowestAsk = OfficialAsks.Keys.Min(); PreviousHighestBid = OfficialBids.Keys.Max(); } else { PreviousLowestAsk = 0; PreviousHighestBid = decimal.MaxValue; } //Loop through update.asks and update.bids in parallel and either add them to this.asks and this.bids or update the value thats currently there. update.OfficialAsks.AsParallel().ForAll(UpdateAskLayer); update.OfficialBids.AsParallel().ForAll(UpdateBidLayer); var significantChange = SignificantChange(update); Exchange.OrderbookUpdateQueue.Enqueue(significantChange); return(significantChange.Item1); } return(false); }
public void Add_offer_queue() { var orderbook = new MarketData("btc", 8); { IOrderbook ob = orderbook as IOrderbook; ob.AddOrUpdateOrder(new Order { Exchange = "bitstamp", BidOfferEnum = BidOfferEnum.Offer, Price = 100, Quantity = 1, Symbol = "btc" }); ob.AddOrUpdateOrder(new Order { Exchange = "binance", BidOfferEnum = BidOfferEnum.Offer, Price = 99, Quantity = 2, Symbol = "btc" }); ob.AddOrUpdateOrder(new Order { Exchange = "gemini", BidOfferEnum = BidOfferEnum.Offer, Price = 100, Quantity = 90, Symbol = "btc" }); } { ITestFunctions ob = orderbook as ITestFunctions; var offers = ob.GetOffers(); Assert.AreEqual(true, offers.ContainsKey(99)); Assert.AreEqual(true, offers.ContainsKey(100)); Assert.AreEqual(true, offers[99].ContainsKey("binance")); Assert.AreEqual(2, offers[100].Values.Count); Assert.AreEqual(true, offers[100].ContainsKey("gemini")); Assert.AreEqual(true, offers[100].ContainsKey("bitstamp")); var bids = ob.GetBids(); Assert.AreEqual(0, bids.Count); } }
public IOrderbook DeepCopy() { IOrderbook deepCopy = (IOrderbook)this.MemberwiseClone(); deepCopy.OfficialAsks = new ConcurrentDictionary <decimal, decimal>(OfficialAsks); deepCopy.OfficialBids = new ConcurrentDictionary <decimal, decimal>(OfficialBids); return(deepCopy); }
public async Task UnSubscribe(IOrderbook market, IClientWebSocket client) { if (client.State == WebSocketState.Open) { await Client.SendAsync( new ArraySegment <byte>(Encoding.ASCII.GetBytes($"{{\"method\": \"unsubscribeOrderbook\",\"params\": {{ \"symbol\": \"{market.Symbol}\" }} }}")), WebSocketMessageType.Text, true, CancellationToken.None).ConfigureAwait(false); } }
public async Task UnSubscribe(IOrderbook market, IClientWebSocket client) { if (client.State == WebSocketState.Open) { await Client.SendAsync(new ArraySegment <byte>( Encoding.ASCII.GetBytes($"{{\"sub\": \"market.{market.Symbol.ToLower()}.mbp.150\",\n \"id\": \"id{ID}\"\n }}") ), WebSocketMessageType.Text, true, CancellationToken.None).ConfigureAwait(false); } }
public void Change_bid_and_offer_queue() { var orderbook = new MarketData("btc", 8); { IOrderbook ob = orderbook as IOrderbook; //add: ob.AddOrUpdateOrder(new Order { Exchange = "bitstamp", BidOfferEnum = BidOfferEnum.Bid, Price = 100, Quantity = 1, Symbol = "btc" }); ob.AddOrUpdateOrder(new Order { Exchange = "binance", BidOfferEnum = BidOfferEnum.Bid, Price = 99, Quantity = 2, Symbol = "btc" }); ob.AddOrUpdateOrder(new Order { Exchange = "gemini", BidOfferEnum = BidOfferEnum.Bid, Price = 100, Quantity = 90, Symbol = "btc" }); ob.AddOrUpdateOrder(new Order { Exchange = "bitstamp", BidOfferEnum = BidOfferEnum.Offer, Price = 100, Quantity = 1, Symbol = "btc" }); ob.AddOrUpdateOrder(new Order { Exchange = "binance", BidOfferEnum = BidOfferEnum.Offer, Price = 99, Quantity = 2, Symbol = "btc" }); ob.AddOrUpdateOrder(new Order { Exchange = "gemini", BidOfferEnum = BidOfferEnum.Offer, Price = 100, Quantity = 90, Symbol = "btc" }); //change qty from 90 to 3 ob.AddOrUpdateOrder(new Order { Exchange = "gemini", BidOfferEnum = BidOfferEnum.Bid, Price = 100, Quantity = 3, Symbol = "btc" }); ob.AddOrUpdateOrder(new Order { Exchange = "gemini", BidOfferEnum = BidOfferEnum.Offer, Price = 100, Quantity = 3, Symbol = "btc" }); } { ITestFunctions ob = orderbook as ITestFunctions; var bids = ob.GetBids(); Assert.AreEqual(true, bids.ContainsKey(99)); Assert.AreEqual(true, bids.ContainsKey(100)); Assert.AreEqual(true, bids[99].ContainsKey("binance")); Assert.AreEqual(2, bids[100].Values.Count); Assert.AreEqual(true, bids[100].ContainsKey("gemini")); Assert.AreEqual(true, bids[100].ContainsKey("bitstamp")); Assert.AreEqual(4, bids[100].Values.Sum(x => x.Quantity)); //should be 4, but not 90. var offers = ob.GetOffers(); Assert.AreEqual(true, offers.ContainsKey(99)); Assert.AreEqual(true, offers.ContainsKey(100)); Assert.AreEqual(true, offers[99].ContainsKey("binance")); Assert.AreEqual(2, offers[100].Values.Count); Assert.AreEqual(true, offers[100].ContainsKey("gemini")); Assert.AreEqual(true, offers[100].ContainsKey("bitstamp")); Assert.AreEqual(4, offers[100].Values.Sum(x => x.Quantity)); //should be 4, but not 90. } }
public Triangle(IOrderbook firstSymbolOrderbook, IOrderbook secondSymbolOrderbook, IOrderbook thirdSymbolOrderbook, Directions direction, IExchange exch) { FirstSymbolOrderbook = firstSymbolOrderbook; SecondSymbolOrderbook = secondSymbolOrderbook; ThirdSymbolOrderbook = thirdSymbolOrderbook; FirstSymbol = firstSymbolOrderbook.Symbol; SecondSymbol = secondSymbolOrderbook.Symbol; ThirdSymbol = thirdSymbolOrderbook.Symbol; Direction = direction; _logger = new LoggerFactory().CreateLogger <Triangle>(); Exchange = exch; }
public async Task SubscribeViaQueue(IOrderbook market) { market.OfficialAsks.Clear(); market.OfficialBids.Clear(); if (Client.State == WebSocketState.Open) //given the amount of time it takes to complete this for loop, a client could be aborted in process. { await Client.SendAsync( new ArraySegment <byte>(Encoding.ASCII.GetBytes($"{{\"method\": \"subscribeOrderbook\",\"params\": {{ \"symbol\": \"{market.Symbol}\" }} }}")), WebSocketMessageType.Text, true, CancellationToken.None).ConfigureAwait(false); Client.Markets.Add(market); } }
public async Task SubscribeViaQueue(IOrderbook market) { if (Client.State == WebSocketState.Open) { await Client.SendAsync(new ArraySegment <byte>( Encoding.ASCII.GetBytes($"{{\"method\": \"SUBSCRIBE\",\"params\": [\"{market.Symbol.ToLower()}@bookTicker\"], \"id\": {ID} }}") ), WebSocketMessageType.Text, true, CancellationToken.None).ConfigureAwait(false); ID++; Client.Markets.Add(market); } await Task.Delay(250); }
public void TestHuobiOrderbookDeserializer() { //Arrange: //sample websocket message from Huobi for an orderbook refresh: string sampleWebsocketMessage = "{\"ch\":\"market.btcusdt.mbp.refresh.10\",\"ts\":1611504245560,\"tick\":{\"seqNum\":119437040567,\"bids\":[[32115.28,0.02],[32115.13,0.15],[32114.05,0.072382],[32113.94,0.066716],[32113.61,0.02],[32113.52,2.521393],[32113.48,0.117404],[32109.42,0.139009],[32108.76,0.123],[32108.25,0.11175]],\"asks\":[[32115.29,0.005],[32115.69,0.03],[32116.2,0.01759],[32118.17,0.03],[32118.86,0.03],[32119.61,0.12],[32120.41,0.08],[32121.51,0.125775],[32121.64,0.03563],[32124.4,0.001]]}}"; //expected deserialized orderbook: var expectedOrderbook = new HuobiOrderbook(); expectedOrderbook.Sequence = 119437040567; expectedOrderbook.Symbol = "BTCUSDT"; expectedOrderbook.OfficialBids = new ConcurrentDictionary <decimal, decimal>(); expectedOrderbook.OfficialBids.TryAdd(32115.28m, 0.02m); expectedOrderbook.OfficialBids.TryAdd(32115.13m, 0.15m); expectedOrderbook.OfficialBids.TryAdd(32114.05m, 0.072382m); expectedOrderbook.OfficialBids.TryAdd(32113.94m, 0.066716m); expectedOrderbook.OfficialBids.TryAdd(32113.61m, 0.02m); expectedOrderbook.OfficialBids.TryAdd(32113.52m, 2.521393m); expectedOrderbook.OfficialBids.TryAdd(32113.48m, 0.117404m); expectedOrderbook.OfficialBids.TryAdd(32109.42m, 0.139009m); expectedOrderbook.OfficialBids.TryAdd(32108.76m, 0.123m); expectedOrderbook.OfficialBids.TryAdd(32108.25m, 0.11175m); expectedOrderbook.OfficialAsks = new ConcurrentDictionary <decimal, decimal>(); expectedOrderbook.OfficialAsks.TryAdd(32115.29m, 0.005m); expectedOrderbook.OfficialAsks.TryAdd(32115.69m, 0.03m); expectedOrderbook.OfficialAsks.TryAdd(32116.2m, 0.01759m); expectedOrderbook.OfficialAsks.TryAdd(32118.17m, 0.03m); expectedOrderbook.OfficialAsks.TryAdd(32118.86m, 0.03m); expectedOrderbook.OfficialAsks.TryAdd(32119.61m, 0.12m); expectedOrderbook.OfficialAsks.TryAdd(32120.41m, 0.08m); expectedOrderbook.OfficialAsks.TryAdd(32121.51m, 0.125775m); expectedOrderbook.OfficialAsks.TryAdd(32121.64m, 0.03563m); expectedOrderbook.OfficialAsks.TryAdd(32124.4m, 0.001m); //Act: run the sample websocket message through the Huobi OrderbookConverter IOrderbook testOrderbook = (IOrderbook)JsonSerializer.Deserialize(sampleWebsocketMessage, typeof(HuobiOrderbook)); //Assert: test that the actual outcome matches the expected outcome Assert.AreEqual(expectedOrderbook.Symbol, testOrderbook.Symbol); Assert.AreEqual(expectedOrderbook.Sequence, testOrderbook.Sequence); Assert.IsTrue(expectedOrderbook.OfficialAsks.Count == testOrderbook.OfficialAsks.Count && expectedOrderbook.OfficialBids.Count == testOrderbook.OfficialBids.Count); Assert.IsTrue(CheckLayers(expectedOrderbook, testOrderbook)); }
public async Task SendPong(IOrderbook orderbook) //sends a 'pong' message back to the server if required to maintain connection. Only Huobi (so far) uses this methodology { if (Client.State == WebSocketState.Open) { try { var cts = new CancellationToken(); await Client.SendAsync(new ArraySegment <byte>(Encoding.ASCII.GetBytes($"{{\"pong\": {orderbook.PongValue}}}")), WebSocketMessageType.Text, true, cts); //_logger.LogDebug($"took {(DateTime.UtcNow - orderbook.Timestamp).TotalMilliseconds}ms to send pong"); orderbook.Pong = false; //set the flag back to false } catch (Exception ex) { _logger.LogError($"problem sending pong: {ex.Message}"); } } }
public async Task SubscribeViaQueue(IOrderbook market) { if (Client.State == WebSocketState.Open) { try { await Client.SendAsync(new ArraySegment <byte>( Encoding.ASCII.GetBytes($"{{\"sub\": \"market.{market.Symbol.ToLower()}.mbp.refresh.10\",\n \"id\": \"id{ID}\"\n }}") ), WebSocketMessageType.Text, true, CancellationToken.None).ConfigureAwait(false); ID++; Client.Markets.Add(market); } catch (Exception ex) { Console.WriteLine(ex.Message); } } await Task.Delay(100); }
public bool CheckLayers(IOrderbook expected, IOrderbook actual) { bool layersMatch = true; foreach (var layer in expected.OfficialBids) { if (actual.OfficialBids.TryGetValue(layer.Key, out var testValue)) { if (layer.Value != testValue) { layersMatch = false; Assert.Fail($"Expected value {layer.Value} was actually {testValue}"); return(layersMatch); } } else { layersMatch = false; Assert.Fail($"Expected key {layer.Key} not present"); return(layersMatch); } } foreach (var layer in expected.OfficialAsks) { if (actual.OfficialAsks.TryGetValue(layer.Key, out var testValue)) { if (layer.Value != testValue) { layersMatch = false; Assert.Fail($"Expected value {layer.Value} was actually {testValue}"); return(layersMatch); } } else { layersMatch = false; Assert.Fail($"Expected key {layer.Key} not present"); return(layersMatch); } } return(layersMatch); }
public (bool, string) SignificantChange(IOrderbook updatedOrderbook) { if (Exchange.ProfitableSymbolMapping.TryGetValue(Symbol, out var lastProfitable)) { if (DateTime.UtcNow - lastProfitable < TimeSpan.FromMinutes(1)) //if the symbol had a profitable triangle in the last minute { return(true, "Symbol had a profitable triangle within the last minute"); } else { if (OfficialAsks.Keys.Min() < PreviousLowestAsk || OfficialBids.Keys.Max() > PreviousHighestBid) { return(true, "Price improved"); } else //price got worse or did not change { return(false, "Price worsened"); } } } else //symbol is not mapped as profitable - update is only significant if the bottom bid/ask layers changed, and the price improved { if (OfficialAsks.Count < 1 || OfficialBids.Count < 1) { return(false, "No OfficialAsks/Bids"); } if (OfficialAsks.Keys.Min() < PreviousLowestAsk || OfficialBids.Keys.Max() > PreviousHighestBid) //if the lowest ask price got lower, or the highest bid got higher, this is a universally better price that will always improve profitability { return(true, "Price improved"); } else //price got worse or did not change { return(false, "Price worsened"); } } }
public void Place_Single_New_Sell_Order() { orderbook = new FullOrderbook(); var orderId = "order1"; var isBid = false; var quantity = 10; var price = 100; var timestamp = DateTimeOffset.Now; var order = new LimitOrder( orderId, isBid, quantity, price, timestamp); orderbook.PlaceOrder(order); Assert.IsNotNull(orderbook.AskLevels); Assert.IsNotNull(orderbook.BestAskLevel); Assert.AreEqual(quantity, orderbook.BestAskLevel.TotalLevelQuantity); Assert.AreEqual(price, orderbook.BestAskLevel.LimitPrice); Assert.AreEqual(isBid, orderbook.BestAskLevel.IsBid); Assert.AreEqual(1, orderbook.BestAskLevel.NumberOfOrders); Assert.IsNotNull(orderbook.BestAskLevel.Orders); Assert.IsNotNull(orderbook.BestAskLevel.Orders.List); Assert.AreEqual(1, orderbook.BestAskLevel.Orders.List.Count); Assert.AreEqual(orderId, orderbook.BestAskLevel.Orders.List.First.Value.OrderId); Assert.AreEqual(isBid, orderbook.BestAskLevel.Orders.List.First.Value.IsBid); Assert.AreEqual(quantity, orderbook.BestAskLevel.Orders.List.First.Value.Quantity); Assert.AreEqual(price, orderbook.BestAskLevel.Orders.List.First.Value.LimitPrice); Assert.AreEqual(timestamp, orderbook.BestAskLevel.Orders.List.First.Value.ReceivedTimestamp); Assert.IsNotNull(orderbook.BestAskLevel.Orders.Table); Assert.AreEqual(1, orderbook.BestAskLevel.Orders.Table.Count); Assert.AreSame(orderbook.BestAskLevel.Orders.Table[orderId], orderbook.BestAskLevel.Orders.List.First); }
public OrderbookEventArgs(IOrderbook orderbook) { Orderbook = orderbook; }
public void Place_New_Sell_Order_And_Match_Exist_Buy_Orders() { orderbook = new FullOrderbook(); var bid_orderId_1 = "bid_order_1"; var bid_isBid_1 = true; var bid_quantity_1 = 5; var bid_price_1 = 23; var bid_timestamp_1 = DateTimeOffset.Now; var bid_order_1 = new LimitOrder( bid_orderId_1, bid_isBid_1, bid_quantity_1, bid_price_1, bid_timestamp_1); var bid_orderId_2 = "bid_order_2"; var bid_isBid_2 = true; var bid_quantity_2 = 5; var bid_price_2 = 22; var bid_timestamp_2 = DateTimeOffset.Now; var bid_order_2 = new LimitOrder( bid_orderId_2, bid_isBid_2, bid_quantity_2, bid_price_2, bid_timestamp_2); var bid_orderId_3 = "bid_order_3"; var bid_isBid_3 = true; var bid_quantity_3 = 5; var bid_price_3 = 21; var bid_timestamp_3 = DateTimeOffset.Now; var bid_order_3 = new LimitOrder( bid_orderId_3, bid_isBid_3, bid_quantity_3, bid_price_3, bid_timestamp_3); var bid_orderId_4 = "bid_order_4"; var bid_isBid_4 = true; var bid_quantity_4 = 5; var bid_price_4 = 21; var bid_timestamp_4 = DateTimeOffset.Now; var bid_order_4 = new LimitOrder( bid_orderId_4, bid_isBid_4, bid_quantity_4, bid_price_4, bid_timestamp_4); var bid_orderId_5 = "bid_order_5"; var bid_isBid_5 = true; var bid_quantity_5 = 5; var bid_price_5 = 21; var bid_timestamp_5 = DateTimeOffset.Now; var bid_order_5 = new LimitOrder( bid_orderId_5, bid_isBid_5, bid_quantity_5, bid_price_5, bid_timestamp_5); var ask_orderId = "ask_order"; var ask_isBid = false; var ask_quantity = 16; var ask_price = 21; var ask_timestamp = DateTimeOffset.Now; var ask_order = new LimitOrder( ask_orderId, ask_isBid, ask_quantity, ask_price, ask_timestamp); orderbook.PlaceOrder(bid_order_1); orderbook.PlaceOrder(bid_order_2); orderbook.PlaceOrder(bid_order_3); orderbook.PlaceOrder(bid_order_4); orderbook.PlaceOrder(bid_order_5); orderbook.PlaceOrder(ask_order); //<1>. Should have only one bid level at $21 left. //<2>. Two orders at this level, with Quantity 4 and 5. //<3>. TotalLevelQuantity at this level should be 9. Assert.IsNull(orderbook.BestAskLevel); //Assert <1> Assert.IsNotNull(orderbook.BestBidLevel); var bestBidLevels = orderbook.GetBestBidLevels(3); Assert.AreEqual(1, bestBidLevels.Count); Assert.AreEqual(21, orderbook.BestBidLevel.LimitPrice); //Assert <2> Assert.AreEqual(2, orderbook.BestBidLevel.NumberOfOrders); var firstBidOrderNode = orderbook.BestBidLevel.Orders.List.First; Assert.IsNotNull(firstBidOrderNode); Assert.AreEqual(4, firstBidOrderNode.Value.Quantity); var secondBidOrderNode = firstBidOrderNode.Next; Assert.IsNotNull(secondBidOrderNode); Assert.AreEqual(5, secondBidOrderNode.Value.Quantity); //Assert <3> Assert.AreEqual(9, orderbook.BestBidLevel.TotalLevelQuantity); }
public void Place_Mutiple_Buy_Orders_With_Same_Price() { orderbook = new FullOrderbook(); var limitPrice = 100; var isBid = true; var orderId_1 = "order1"; var isBid_1 = isBid; var quantity_1 = 10; var price_1 = limitPrice; var timestamp_1 = DateTimeOffset.Now; var order_1 = new LimitOrder( orderId_1, isBid_1, quantity_1, price_1, timestamp_1); var orderId_2 = "order2"; var isBid_2 = isBid; var quantity_2 = 20; var price_2 = limitPrice; var timestamp_2 = DateTimeOffset.Now; var order_2 = new LimitOrder( orderId_2, isBid_2, quantity_2, price_2, timestamp_2); var orderId_3 = "order3"; var isBid_3 = isBid; var quantity_3 = 30; var price_3 = limitPrice; var timestamp_3 = DateTimeOffset.Now; var order_3 = new LimitOrder( orderId_3, isBid_3, quantity_3, price_3, timestamp_3); orderbook.PlaceOrder(order_1); orderbook.PlaceOrder(order_2); orderbook.PlaceOrder(order_3); Assert.IsNotNull(orderbook.BidLevels); Assert.IsNotNull(orderbook.BestBidLevel); Assert.AreEqual(quantity_1 + quantity_2 + quantity_3, orderbook.BestBidLevel.TotalLevelQuantity); Assert.AreEqual(limitPrice, orderbook.BestBidLevel.LimitPrice); Assert.AreEqual(isBid, orderbook.BestBidLevel.IsBid); Assert.AreEqual(3, orderbook.BestBidLevel.NumberOfOrders); Assert.IsNotNull(orderbook.BestBidLevel.Orders); Assert.IsNotNull(orderbook.BestBidLevel.Orders.List); Assert.AreEqual(3, orderbook.BestBidLevel.Orders.List.Count); Assert.IsTrue(orderbook.BestBidLevel.Orders.List.Contains(order_1)); Assert.IsTrue(orderbook.BestBidLevel.Orders.List.Contains(order_2)); Assert.IsTrue(orderbook.BestBidLevel.Orders.List.Contains(order_3)); Assert.AreEqual(3, orderbook.BestBidLevel.Orders.Table.Count); }
public void Place_Mutiple_Sell_Orders_With_Different_Prices() { orderbook = new FullOrderbook(); var isBid = false; var orderId_1 = "order1"; var isBid_1 = isBid; var quantity_1 = 10; var price_1 = 100; var timestamp_1 = DateTimeOffset.Now; var order_1 = new LimitOrder( orderId_1, isBid_1, quantity_1, price_1, timestamp_1); var orderId_2 = "order2"; var isBid_2 = isBid; var quantity_2 = 20; var price_2 = 200; var timestamp_2 = DateTimeOffset.Now; var order_2 = new LimitOrder( orderId_2, isBid_2, quantity_2, price_2, timestamp_2); var orderId_3 = "order3"; var isBid_3 = isBid; var quantity_3 = 30; var price_3 = 300; var timestamp_3 = DateTimeOffset.Now; var order_3 = new LimitOrder( orderId_3, isBid_3, quantity_3, price_3, timestamp_3); List <LimitOrder> testOrders = new List <LimitOrder>() { order_1, order_2, order_3 }; orderbook.PlaceOrder(order_1); orderbook.PlaceOrder(order_2); orderbook.PlaceOrder(order_3); Assert.IsNotNull(orderbook.AskLevels); Assert.IsNotNull(orderbook.BestAskLevel); var bestAsksFromGetBestAskLevels = orderbook.GetBestAskLevels(3); Assert.IsNotNull(bestAsksFromGetBestAskLevels); Assert.AreEqual(3, bestAsksFromGetBestAskLevels.Count); var askLevelEnumerator = orderbook.AskLevels.GetEnumerator(); for (int i = 0; i < 3; i++) { Assert.IsTrue(askLevelEnumerator.MoveNext()); var askLevel = askLevelEnumerator.Current; var testOrder = testOrders[i]; Assert.AreEqual(testOrder.Quantity, askLevel.TotalLevelQuantity); Assert.AreEqual(testOrder.LimitPrice, askLevel.LimitPrice); Assert.AreEqual(1, askLevel.NumberOfOrders); Assert.IsNotNull(askLevel.Orders); Assert.IsNotNull(askLevel.Orders.List); Assert.AreEqual(1, askLevel.Orders.List.Count); Assert.AreEqual(testOrder.OrderId, askLevel.Orders.List.First.Value.OrderId); Assert.AreEqual(isBid, askLevel.Orders.List.First.Value.IsBid); Assert.AreEqual(testOrder.Quantity, askLevel.Orders.List.First.Value.Quantity); Assert.AreEqual(testOrder.LimitPrice, askLevel.Orders.List.First.Value.LimitPrice); Assert.AreEqual(testOrder.ReceivedTimestamp, askLevel.Orders.List.First.Value.ReceivedTimestamp); Assert.IsNotNull(askLevel.Orders.Table); Assert.AreEqual(1, askLevel.Orders.Table.Count); Assert.AreSame(askLevel.Orders.Table[testOrder.OrderId], askLevel.Orders.List.First); Assert.AreSame(askLevel, bestAsksFromGetBestAskLevels[i]); } }
public void Remove_Order_By_OrderId() { orderbook = new FullOrderbook(); var bid_orderId_1 = "bid_order_1"; var bid_isBid_1 = true; var bid_quantity_1 = 5; var bid_price_1 = 23; var bid_timestamp_1 = DateTimeOffset.Now; var bid_order_1 = new LimitOrder( bid_orderId_1, bid_isBid_1, bid_quantity_1, bid_price_1, bid_timestamp_1); var bid_orderId_2 = "bid_order_2"; var bid_isBid_2 = true; var bid_quantity_2 = 5; var bid_price_2 = 22; var bid_timestamp_2 = DateTimeOffset.Now; var bid_order_2 = new LimitOrder( bid_orderId_2, bid_isBid_2, bid_quantity_2, bid_price_2, bid_timestamp_2); var bid_orderId_3 = "bid_order_3"; var bid_isBid_3 = true; var bid_quantity_3 = 5; var bid_price_3 = 21; var bid_timestamp_3 = DateTimeOffset.Now; var bid_order_3 = new LimitOrder( bid_orderId_3, bid_isBid_3, bid_quantity_3, bid_price_3, bid_timestamp_3); var bid_orderId_4 = "bid_order_4"; var bid_isBid_4 = true; var bid_quantity_4 = 5; var bid_price_4 = 21; var bid_timestamp_4 = DateTimeOffset.Now; var bid_order_4 = new LimitOrder( bid_orderId_4, bid_isBid_4, bid_quantity_4, bid_price_4, bid_timestamp_4); var bid_orderId_5 = "bid_order_5"; var bid_isBid_5 = true; var bid_quantity_5 = 5; var bid_price_5 = 21; var bid_timestamp_5 = DateTimeOffset.Now; var bid_order_5 = new LimitOrder( bid_orderId_5, bid_isBid_5, bid_quantity_5, bid_price_5, bid_timestamp_5); orderbook.PlaceOrder(bid_order_1); orderbook.PlaceOrder(bid_order_2); orderbook.PlaceOrder(bid_order_3); orderbook.PlaceOrder(bid_order_4); orderbook.PlaceOrder(bid_order_5); var orderToRemove = new LimitOrder(); orderToRemove.SetOrderId(bid_order_3.OrderId); orderbook.RemoveOrderByOrderId(orderToRemove); //<1>. Should have 3 level of Bids, $23, $22, $21 //<2>. At level $21, there are two orders left, with Quantity 5 and 5. //<3>. TotalLevelQuantity at this level should be 10. //Assert <1> Assert.IsNotNull(orderbook.BestBidLevel); var bestBidLevels = orderbook.GetBestBidLevels(3); Assert.AreEqual(3, bestBidLevels.Count); Assert.AreEqual(23, bestBidLevels[0].LimitPrice); Assert.AreEqual(22, bestBidLevels[1].LimitPrice); Assert.AreEqual(21, bestBidLevels[2].LimitPrice); //Assert <2> var targetLevel = bestBidLevels[2]; Assert.AreEqual(2, targetLevel.NumberOfOrders); var firstBidOrderNode = targetLevel.Orders.List.First; Assert.IsNotNull(firstBidOrderNode); Assert.AreEqual(5, firstBidOrderNode.Value.Quantity); var secondBidOrderNode = firstBidOrderNode.Next; Assert.IsNotNull(secondBidOrderNode); Assert.AreEqual(5, secondBidOrderNode.Value.Quantity); //Assert <3> Assert.AreEqual(10, targetLevel.TotalLevelQuantity); }
public static void MapHelper(string firstDirection, string secondDirection, string thirdDirection, IOrderbook firstMarket, IOrderbook secondMarket, IOrderbook thirdMarket, IExchange Exchange) { var direction = (Triangle.Directions)Enum.Parse(typeof(Triangle.Directions), $"{firstDirection}{secondDirection}{thirdDirection}"); var newTriangle = new Triangle(firstMarket, secondMarket, thirdMarket, direction, Exchange); //Console.WriteLine($"{exchangeName}: {firstDirection} {firstMarket.symbol}, {secondDirection} {secondMarket.symbol}, {thirdDirection} {thirdMarket.symbol}"); UniqueTriangleCount++; TriarbEligibleMarkets.Add(firstMarket.Symbol); TriarbEligibleMarkets.Add(secondMarket.Symbol); TriarbEligibleMarkets.Add(thirdMarket.Symbol); Exchange.OfficialOrderbooks.TryAdd(firstMarket.Symbol, firstMarket); Exchange.OfficialOrderbooks.TryAdd(secondMarket.Symbol, secondMarket); Exchange.OfficialOrderbooks.TryAdd(thirdMarket.Symbol, thirdMarket); foreach (var marketName in new List <string> { firstMarket.Symbol, secondMarket.Symbol, thirdMarket.Symbol }) { if (!Exchange.TriarbMarketMapping.Keys.Contains(marketName)) { Exchange.TriarbMarketMapping.AddOrUpdate(marketName, new List <Triangle>() { newTriangle }, (key, triangleList) => { if (key == marketName && !triangleList.Contains(newTriangle)) { triangleList.Add(newTriangle); } return(triangleList); }); } else { Exchange.TriarbMarketMapping.TryGetValue(marketName, out var triangles); if (!triangles.Contains(newTriangle)) { Exchange.TriarbMarketMapping.AddOrUpdate(marketName, new List <Triangle>() { newTriangle }, (key, triangleList) => { if (key == marketName && !triangleList.Contains(newTriangle)) { triangleList.Add(newTriangle); } return(triangleList); }); } } } }
public void Update_Order_Price() { orderbook = new FullOrderbook(); decimal newPrice = 22; var bid_orderId_1 = "bid_order_1"; var bid_isBid_1 = true; var bid_quantity_1 = 5; var bid_price_1 = 23; var bid_timestamp_1 = DateTimeOffset.Now; var bid_order_1 = new LimitOrder( bid_orderId_1, bid_isBid_1, bid_quantity_1, bid_price_1, bid_timestamp_1); var bid_orderId_2 = "bid_order_2"; var bid_isBid_2 = true; var bid_quantity_2 = 5; var bid_price_2 = 22; var bid_timestamp_2 = DateTimeOffset.Now; var bid_order_2 = new LimitOrder( bid_orderId_2, bid_isBid_2, bid_quantity_2, bid_price_2, bid_timestamp_2); var bid_orderId_3 = "bid_order_3"; var bid_isBid_3 = true; var bid_quantity_3 = 4; var bid_price_3 = 21; var bid_timestamp_3 = DateTimeOffset.Now; var bid_order_3 = new LimitOrder( bid_orderId_3, bid_isBid_3, bid_quantity_3, bid_price_3, bid_timestamp_3); var bid_orderId_4 = "bid_order_4"; var bid_isBid_4 = true; var bid_quantity_4 = 5; var bid_price_4 = 21; var bid_timestamp_4 = DateTimeOffset.Now; var bid_order_4 = new LimitOrder( bid_orderId_4, bid_isBid_4, bid_quantity_4, bid_price_4, bid_timestamp_4); var bid_orderId_5 = "bid_order_5"; var bid_isBid_5 = true; var bid_quantity_5 = 3; var bid_price_5 = 21; var bid_timestamp_5 = DateTimeOffset.Now; var bid_order_5 = new LimitOrder( bid_orderId_5, bid_isBid_5, bid_quantity_5, bid_price_5, bid_timestamp_5); orderbook.PlaceOrder(bid_order_1); orderbook.PlaceOrder(bid_order_2); orderbook.PlaceOrder(bid_order_3); orderbook.PlaceOrder(bid_order_4); orderbook.PlaceOrder(bid_order_5); var updated_bid_order_3 = new LimitOrder( bid_order_3.OrderId, bid_order_3.IsBid, bid_order_3.Quantity, newPrice, DateTimeOffset.Now ); orderbook.PlaceOrder(updated_bid_order_3); //<1>. Should have 3 level of Bids, $23, $22, $21 //<2>. At level $22, there are two orders left, with Quantity 5 and 4. //<3>. At level $21, there is two order left, with Quantity 5 and 3. //Assert <1> Assert.IsNotNull(orderbook.BestBidLevel); var bestBidLevels = orderbook.GetBestBidLevels(3); Assert.AreEqual(3, bestBidLevels.Count); Assert.AreEqual(23, bestBidLevels[0].LimitPrice); Assert.AreEqual(22, bestBidLevels[1].LimitPrice); Assert.AreEqual(21, bestBidLevels[2].LimitPrice); //Assert <2> var targetLevel_22 = bestBidLevels[1]; Assert.AreEqual(2, targetLevel_22.NumberOfOrders); var firstBidOrderNode_22 = targetLevel_22.Orders.List.First; Assert.IsNotNull(firstBidOrderNode_22); Assert.AreEqual(5, firstBidOrderNode_22.Value.Quantity); var secondBidOrderNode_22 = firstBidOrderNode_22.Next; Assert.IsNotNull(secondBidOrderNode_22); Assert.AreEqual(4, secondBidOrderNode_22.Value.Quantity); Assert.AreEqual(9, targetLevel_22.TotalLevelQuantity); //Assert <3> var targetLevel_21 = bestBidLevels[2]; Assert.AreEqual(2, targetLevel_21.NumberOfOrders); var firstBidOrderNode_21 = targetLevel_21.Orders.List.First; Assert.IsNotNull(firstBidOrderNode_21); Assert.AreEqual(5, firstBidOrderNode_21.Value.Quantity); var secondBidOrderNode_21 = firstBidOrderNode_21.Next; Assert.IsNotNull(secondBidOrderNode_21); Assert.AreEqual(3, secondBidOrderNode_21.Value.Quantity); Assert.AreEqual(8, targetLevel_21.TotalLevelQuantity); }
public void Place_New_Buy_Order_And_Match_Exist_Sell_Orders() { orderbook = new FullOrderbook(); var ask_orderId_1 = "ask_order_1"; var ask_isBid_1 = false; var ask_quantity_1 = 5; var ask_price_1 = 10; var ask_timestamp_1 = DateTimeOffset.Now; var ask_order_1 = new LimitOrder( ask_orderId_1, ask_isBid_1, ask_quantity_1, ask_price_1, ask_timestamp_1); var ask_orderId_2 = "ask_order_2"; var ask_isBid_2 = false; var ask_quantity_2 = 5; var ask_price_2 = 11; var ask_timestamp_2 = DateTimeOffset.Now; var ask_order_2 = new LimitOrder( ask_orderId_2, ask_isBid_2, ask_quantity_2, ask_price_2, ask_timestamp_2); var ask_orderId_3 = "ask_order_3"; var ask_isBid_3 = false; var ask_quantity_3 = 5; var ask_price_3 = 11; var ask_timestamp_3 = DateTimeOffset.Now; var ask_order_3 = new LimitOrder( ask_orderId_3, ask_isBid_3, ask_quantity_3, ask_price_3, ask_timestamp_3); var ask_orderId_4 = "ask_order_4"; var ask_isBid_4 = false; var ask_quantity_4 = 5; var ask_price_4 = 12; var ask_timestamp_4 = DateTimeOffset.Now; var ask_order_4 = new LimitOrder( ask_orderId_4, ask_isBid_4, ask_quantity_4, ask_price_4, ask_timestamp_4); var bid_orderId = "bid_order"; var bid_isBid = true; var bid_quantity = 17; var bid_price = 12; var bid_timestamp = DateTimeOffset.Now; var bid_order = new LimitOrder( bid_orderId, bid_isBid, bid_quantity, bid_price, bid_timestamp); orderbook.PlaceOrder(ask_order_1); orderbook.PlaceOrder(ask_order_2); orderbook.PlaceOrder(ask_order_3); orderbook.PlaceOrder(ask_order_4); orderbook.PlaceOrder(bid_order); //<1>. Should have only one ask level at $12 left. //<2>. Only one order at this level. //<3>. TotalLevelQuantity at this level should be 3. Assert.IsNull(orderbook.BestBidLevel); //Assert <1> Assert.IsNotNull(orderbook.BestAskLevel); var bestAskLevels = orderbook.GetBestAskLevels(3); Assert.AreEqual(1, bestAskLevels.Count); Assert.AreEqual(12, orderbook.BestAskLevel.LimitPrice); //Assert <2> Assert.AreEqual(1, orderbook.BestAskLevel.NumberOfOrders); //Assert <3> Assert.AreEqual(3, orderbook.BestAskLevel.TotalLevelQuantity); }
private async Task BackgroundProcessing(CancellationToken stoppingToken) { while (Client.State == WebSocketState.Open && !stoppingToken.IsCancellationRequested) { var buffer = WebSocket.CreateClientBuffer(1024 * 64, 1024); using (var ms = new MemoryStream()) { string payload = string.Empty; WebSocketReceiveResult result = null; var receiptTask = Task.Run(() => { result = Client.ReceiveAsync(ms, buffer, CancellationToken.None).Result; }); bool successfulReceiveAsync = receiptTask.Wait(TimeSpan.FromMilliseconds(10000)); if (!successfulReceiveAsync) { _logger.LogError($"ReceiveAsync Timed Out"); continue; } if (result.MessageType == WebSocketMessageType.Text) //hitbtc, binance { var reader = new StreamReader(ms, Encoding.UTF8); payload = await reader.ReadToEndAsync(); } else if (result.MessageType == WebSocketMessageType.Binary) //huobi global sends all data in a compressed GZIP format { payload = ReadBinarySocketMessage(ms); } else if (result.MessageType == WebSocketMessageType.Close) { _logger.LogError($"{Exchange.ExchangeName}: Socket connection closed because {result.CloseStatusDescription}"); await HandleWebsocketClose(); continue; } IOrderbook orderbook = (IOrderbook)JsonSerializer.Deserialize(payload, OrderbookType); if (orderbook.Pong == true) { SendPong(orderbook); //do not await this task; the listener should move on in reading other websocket messages continue; } if (Exchange.OfficialOrderbooks.TryGetValue(orderbook.Symbol, out IOrderbook OfficialOrderbook)) { bool shouldRecalculate = false; lock (OfficialOrderbook.OrderbookLock) //lock the orderbook when the orderbook is being modified { shouldRecalculate = OfficialOrderbook.Merge(orderbook); } if (shouldRecalculate) { if (Exchange.TriarbMarketMapping.TryGetValue(orderbook.Symbol, out List <Triangle> impactedTriangles)) //get all of the impacted triangles { foreach (var impactedTriangle in impactedTriangles) { var triangleSnapshot = CreateTriangleSnapshot(impactedTriangle); Exchange.TrianglesToRecalculate.Enqueue(triangleSnapshot); } } } } } } await HandleWebsocketClose(); }
public void Update_Order_Quantity() { orderbook = new FullOrderbook(); decimal newQty = 10; var bid_orderId_1 = "bid_order_1"; var bid_isBid_1 = true; var bid_quantity_1 = 5; var bid_price_1 = 23; var bid_timestamp_1 = DateTimeOffset.Now; var bid_order_1 = new LimitOrder( bid_orderId_1, bid_isBid_1, bid_quantity_1, bid_price_1, bid_timestamp_1); var bid_orderId_2 = "bid_order_2"; var bid_isBid_2 = true; var bid_quantity_2 = 5; var bid_price_2 = 23; var bid_timestamp_2 = DateTimeOffset.Now; var bid_order_2 = new LimitOrder( bid_orderId_2, bid_isBid_2, bid_quantity_2, bid_price_2, bid_timestamp_2); var bid_orderId_3 = "bid_order_3"; var bid_isBid_3 = true; var bid_quantity_3 = 5; var bid_price_3 = 23; var bid_timestamp_3 = DateTimeOffset.Now; var bid_order_3 = new LimitOrder( bid_orderId_3, bid_isBid_3, bid_quantity_3, bid_price_3, bid_timestamp_3); orderbook.PlaceOrder(bid_order_1); orderbook.PlaceOrder(bid_order_2); orderbook.PlaceOrder(bid_order_3); var updated_bid_order_2 = bid_order_2.DeepCopyWithQtyChanged(newQty); orderbook.PlaceOrder(updated_bid_order_2); //<1>. Should have only one bid level. //<2>. At this level, the Quantities are 5, 10, 5. //Assert <1> Assert.IsNotNull(orderbook.BestBidLevel); var bestBidLevels = orderbook.GetBestBidLevels(3); Assert.AreSame(orderbook.BestBidLevel, bestBidLevels[0]); Assert.AreEqual(1, bestBidLevels.Count); Assert.AreEqual(23, bestBidLevels[0].LimitPrice); //Assert <2> Assert.AreEqual(3, orderbook.BestBidLevel.NumberOfOrders); var firstBidOrderNode = orderbook.BestBidLevel.Orders.List.First; Assert.IsNotNull(firstBidOrderNode); Assert.AreEqual(5, firstBidOrderNode.Value.Quantity); var secondBidOrderNode = firstBidOrderNode.Next; Assert.IsNotNull(secondBidOrderNode); Assert.AreEqual(newQty, secondBidOrderNode.Value.Quantity); var thirdBidOrderNode = secondBidOrderNode.Next; Assert.IsNotNull(thirdBidOrderNode); Assert.AreEqual(5, thirdBidOrderNode.Value.Quantity); Assert.AreEqual(20, orderbook.BestBidLevel.TotalLevelQuantity); }