private async Task HandleOrderEventAsync(string productId, long orderEventSequenceNumber, OrderBookEventType eventType, OrderBookItem orderBookItem) { // Queue this item if the order book is not fully populated yet var orderBookExists = _symbolsLastSequenceNumbers.TryGetValue(productId, out long seqNumberInOrderBook) && TryGetOrderBookSnapshot(productId, out var _); if (!orderBookExists) { QueueItem(productId, new GdaxQueueOrderItem(orderEventSequenceNumber, eventType, orderBookItem)); return; } // Dequeue all items in the order events queue foreach (var queuedItem in DequeuOrderItems(productId) .Where(q => q.SequenceNumber > seqNumberInOrderBook)) { await HandleOrdersEventsAsync(productId, queuedItem.OrderBookEventType, new[] { queuedItem.OrderBookItem }); } // Handle the current item if (orderEventSequenceNumber > seqNumberInOrderBook) { await HandleOrdersEventsAsync(productId, eventType, new[] { orderBookItem }); } }
private async Task HandleTableResponse(MarketDataSnapshotFullRefresh snapshot) { var symbol = snapshot.Symbol.Obj; var equFun = new Func <OrderBookItem, OrderBookItem, bool>((item1, item2) => item1.Id == item2.Id); var hashFunc = new Func <OrderBookItem, int>(item => item.Id.GetHashCode()); var orders = new List <OrderBookItem>(); for (var j = 1; j <= snapshot.NoMDEntries.Obj; j++) { var ob = snapshot.GetGroup(j, new MarketDataSnapshotFullRefresh.NoMDEntriesGroup()); var dir = ob.GetField(new MDEntryType()).Obj; var price = ob.GetField(new MDEntryPx()).Obj; var size = ob.GetField(new MDEntrySize()).Obj; var id = long.Parse(ob.GetField(new QuoteEntryID()).Obj); var ordeItem = new OrderBookItem(equFun, hashFunc) { Id = id.ToString(), Symbol = symbol, IsBuy = dir == MDEntryType.BID, Price = price, Size = size }; orders.Add(ordeItem); } await HandleOrderBookSnapshotAsync(symbol, DateTime.UtcNow, orders); }
public GdaxQueueOrderItem(long sequenceNumber, OrderBookEventType orderBookEventType, OrderBookItem orderBookItem) { SequenceNumber = sequenceNumber; OrderBookItem = orderBookItem; OrderBookEventType = orderBookEventType; }
private void RegisterOrderBookChannel(Pusher pusher, CurrencyPair pair) { var orderBookChannel = pusher.Subscribe($"order_book{GetSubscriptionName(pair)}"); orderBookChannel.Subscribed += _chatChannel_Subscribed; orderBookChannel.Bind("data", (dynamic data) => { _dateLastUpdated = DateTime.UtcNow; var orderBook = new OrderBook(); orderBook.CurrencyPair = pair; foreach (var bid in data.bids) { var orderBookItem = new OrderBookItem { Volume = bid[1], Price = bid[0] }; orderBook.Bids.Add(orderBookItem); } foreach (var ask in data.asks) { var orderBookItem = new OrderBookItem { Volume = ask[1], Price = ask[0] }; orderBook.Asks.Add(orderBookItem); } _orderBookDict[pair] = orderBook; OrderBookChanged?.Invoke(pair, orderBook); }); }
private void Snapshot(dynamic data) { var currencyPair = _currencyMapper.GetCurrency((string)data.product_id); var orderBook = new OrderBook { CurrencyPair = currencyPair }; foreach (var bid in data.bids) { var bookItem = new OrderBookItem() { Price = DecimalHelper.Get((string)bid[0]), Volume = DecimalHelper.Get((string)bid[1]), }; orderBook.Bids.Add(bookItem); } foreach (var ask in data.asks) { var bookItem = new OrderBookItem() { Price = DecimalHelper.Get((string)ask[0]), Volume = DecimalHelper.Get((string)ask[1]), }; orderBook.Asks.Add(bookItem); } SortBids(orderBook); SortAsks(orderBook); _orderBookDict[currencyPair] = orderBook; OrderBookChanged?.Invoke(orderBook.CurrencyPair, orderBook); }
public IActionResult Get() { var observations = _observationService.GetObservations(); var list = new List <WatchModel>(); foreach (var item in observations) { var orderBook = new WatchOrderBookModel(); var sell_book = _exchangeDataService.GetOrderBook(item.ToExchangeName, item.CurrencyPair); var buy_book = _exchangeDataService.GetOrderBook(item.FromExchangeName, item.CurrencyPair); var bid1 = new OrderBookItem(); var ask1 = new OrderBookItem(); if (sell_book.Bids.Count > 0) { bid1 = sell_book.Bids[0]; orderBook.Bid1 = bid1.ToString(); orderBook.Bid2 = sell_book.Bids[1].ToString(); orderBook.Bid3 = sell_book.Bids[2].ToString(); } else { orderBook.Bid1 = ""; orderBook.Bid2 = ""; orderBook.Bid3 = ""; } if (buy_book.Asks.Count > 0) { ask1 = buy_book.Asks[0]; orderBook.Ask1 = ask1.ToString(); orderBook.Ask2 = buy_book.Asks[1].ToString(); orderBook.Ask3 = buy_book.Asks[2].ToString(); } else { orderBook.Ask1 = ""; orderBook.Ask2 = ""; orderBook.Ask3 = ""; } orderBook.SpreadValue = (bid1.Price - ask1.Price).ToString("f2"); orderBook.SpreadVolume = Math.Min(bid1.Volume, ask1.Volume).ToString("f4"); var model = new WatchModel() { Observation = item, OrderBook = orderBook }; list.Add(model); } return(Ok(list)); }
/// <summary> /// Парсинг стакана /// </summary> private void ParseBook(IStompFrame frame, string subscription) { if (!obIdToInstrument.TryGetValue(subscription, out var instrument)) { return; } var json = JObject.Parse(frame.Body); var columns = (JArray)json["columns"]; var allData = (JArray)json["data"]; var ob = new OrderBook(allData.Count) { Instrument = instrument }; var ip = GetInstrumentParams(instrument); OrderBookItem prev = null, curr; for (var i = allData.Count - 1; i >= 0; i--) { var data = (JArray)allData[i]; var dict = new JArrayDictionary(columns, data); ob.Items.Add(curr = new OrderBookItem { Operation = dict["BUYSELL"].ToString() == "B" ? OrderOperation.Buy : OrderOperation.Sell, Price = (decimal)dict["PRICE"][0], Quantity = (int)dict["QUANTITY"], }); if (prev?.Operation == OrderOperation.Sell && curr.Operation == OrderOperation.Buy) { ip.BestOfferPrice = prev.Price; ip.BestOfferQuantity = prev.Quantity; ip.BestBidPrice = curr.Price; ip.BestBidQuantity = curr.Quantity; } prev = curr; } OnMessageReceived(ip); OnMessageReceived(ob); }
private List <IOrderBookItem> ParseOrders(List <string[]> orders) { var _result = new List <IOrderBookItem>(); foreach (var _order in orders) { var _o = new OrderBookItem { price = decimal.Parse(_order[0], NumberStyles.Float), quantity = decimal.Parse(_order[1], NumberStyles.Float), count = 1 }; _o.amount = _o.quantity * _o.price; _result.Add(_o); } return(_result); }
private OrderBookItem[] EnumerateBook(OrderOperation operation) { var sorted = operation == OrderOperation.Buy ? bids : asks; var size = Math.Min(Capacity, sorted.Count); var rArray = new OrderBookItem[size]; var c = 0; foreach (var pair in sorted) { var price = pair.Key; var list = pair.Value; rArray[c] = new OrderBookItem(operation, price, list.Sum(_ => _.Quantity)); if (++c >= Capacity) { break; } } return(rArray); }
/// <summary> /// Обработка Quote-ов. Могут приходить в разных сообщениях /// </summary> private SendMessageFlags HandleQuotes(List <Quote> quotes) { var result = SendMessageFlags.None; if (quotes == null || quotes.Count == 0) { return(result); } foreach (var quote in quotes) { // marketData.contract_id var price = instrumentResolver.ConvertPriceBack(ContractId, quote.price); var volume = (long)quote.volume; switch ((Quote.Type)quote.type) { case Quote.Type.BESTBID: if (InstrumentParams.BestBidPrice != price || InstrumentParams.BestBidQuantity != volume) { InstrumentParams.BestBidPrice = price; InstrumentParams.BestBidQuantity = volume; if (!level2QuoteReceived) { domBids.Clear(); domBids[price] = new OrderBookItem(OrderOperation.Buy, price, volume); result |= SendMessageFlags.OrderBook; } result |= SendMessageFlags.InstrumentParams; } break; case Quote.Type.BESTASK: if (InstrumentParams.BestOfferPrice != price || InstrumentParams.BestOfferQuantity != volume) { InstrumentParams.BestOfferPrice = price; InstrumentParams.BestOfferQuantity = volume; if (!level2QuoteReceived) { domAsks.Clear(); domAsks[-price] = new OrderBookItem(OrderOperation.Sell, price, volume); result |= SendMessageFlags.OrderBook; } result |= SendMessageFlags.InstrumentParams; } break; case Quote.Type.BID: level2QuoteReceived = true; if (volume == 0) { domBids.Remove(price); } else { domBids[price] = new OrderBookItem(OrderOperation.Buy, price, volume); } result |= SendMessageFlags.OrderBook; break; case Quote.Type.ASK: level2QuoteReceived = true; // Цена с минусом, чтобы был правильный порядок сортировки if (volume == 0) { domAsks.Remove(-price); } else { domAsks[-price] = new OrderBookItem(OrderOperation.Sell, price, volume); } result |= SendMessageFlags.OrderBook; break; case Quote.Type.SETTLEMENT: if (InstrumentParams.Settlement != price) { InstrumentParams.Settlement = price; if (InstrumentParams.LastPrice == 0) { InstrumentParams.LastPrice = price; } UpdatePriceStep(price); result |= SendMessageFlags.InstrumentParams; } break; case Quote.Type.TRADE: if (InstrumentParams.LastPrice != price) { InstrumentParams.LastPrice = price; UpdatePriceStep(price); result |= SendMessageFlags.InstrumentParams; } break; } } return(result); }
/// <summary> /// Вставляет с сортировкой запись в стакан. /// </summary> /// <param name="replId">replId записи из plaza2.</param> /// <param name="p2Ob">Мэп replId на OrderBookItem.</param> /// <param name="ob">Объект стакана.</param> /// <param name="obi">Запись в стакане.</param> private void InsertOrderBookItemSorted(long replId, Dictionary <long, OrderBookItem> p2Ob, OrderBook ob, OrderBookItem obi) { p2Ob.Add(replId, obi); var indexToInsert = 0; if (ob.Items.Count > 0) { for (indexToInsert = 0; indexToInsert < ob.Items.Count; indexToInsert++) { if (obi.Price > ob.Items[indexToInsert].Price) { break; } } } ob.Items.Insert(indexToInsert, obi); }
private void ProcessOrderBookUpdates() { const int numberOfCyclesToFireEvent = 10; // каждые 10 циклов нижлежащего while мы райзим эвент об обновлении стаканов var cyclesCount = 0; while (!cancellationTokenSource.Token.WaitHandle.WaitOne(10)) { if (++cyclesCount > numberOfCyclesToFireEvent) { cyclesCount = 0; FireUpdatedOrderBooks(); } CGateOrderBookUpdate record; while (orderBooksUdatesQueue.TryDequeue(out record)) { // вызываем событие обновления стаканов если в потоке пришло сообщение об окончании блока обновлений if (record.Type == CGateOrderBookUpdateType.StreamDataEnd) { FireUpdatedOrderBooks(); continue; } if (record.Type == CGateOrderBookUpdateType.ClearTable) { ClearOrderBooks(record); FireUpdatedOrderBooks(); continue; } //revisionsDictionary[record.ReplId] = record.ReplRev; RememberUpdatedInstrument(record.IsinId); int obItemIsinId; var instrumentChanged = false; if (!obRowsOnInstruments.TryGetValue(record.ReplId, out obItemIsinId)) { obRowsOnInstruments.Add(record.ReplId, record.IsinId); } else if (record.IsinId != obItemIsinId) { RememberUpdatedInstrument(obItemIsinId); obRowsOnInstruments[record.ReplId] = record.IsinId; instrumentChanged = true; } Dictionary <long, OrderBookItem> p2ob; OrderBook ob; if (!plaza2OrderBooks.TryGetValue(record.IsinId, out p2ob)) { p2ob = new Dictionary <long, OrderBookItem>(); plaza2OrderBooks.Add(record.IsinId, p2ob); } if (!orderBooks.TryGetValue(record.IsinId, out ob)) { var instrument = instrumentResolver.GetInstrument(record.InstrumentCode); ob = new OrderBook { Instrument = instrument.Instrument }; orderBooks.Add(record.IsinId, ob); } // если произошла смена инструмента у записи, то её нужно удалить из того стакана, в котором она была раньше if (instrumentChanged) { Dictionary <long, OrderBookItem> p2obToDeleteRecord; OrderBook obToDeleteRecord; if (plaza2OrderBooks.TryGetValue(obItemIsinId, out p2obToDeleteRecord) && orderBooks.TryGetValue(obItemIsinId, out obToDeleteRecord)) { if (p2obToDeleteRecord.ContainsKey(record.ReplId)) { obToDeleteRecord.Items.Remove(p2obToDeleteRecord[record.ReplId]); p2obToDeleteRecord.Remove(record.ReplId); } } } OrderBookItem obi; if (!p2ob.TryGetValue(record.ReplId, out obi) && record.Price != 0m && record.Quantity != 0 && record.ReplAct == 0) { obi = new OrderBookItem { Price = record.Price, Quantity = record.Quantity, Operation = record.Operation }; // ишем место, куда приткнуть новую котировку InsertOrderBookItemSorted(record.ReplId, p2ob, ob, obi); } else if (obi != null) { if (record.Price == 0 || record.Quantity == 0 || record.ReplAct > 0) { RemoveOrderBookItem(record.ReplId, p2ob, ob); } else { var needSort = obi.Price != record.Price || obi.Operation != record.Operation; obi.Price = record.Price; obi.Operation = record.Operation; obi.Quantity = record.Quantity; if (needSort) { RemoveOrderBookItem(record.ReplId, p2ob, ob); InsertOrderBookItemSorted(record.ReplId, p2ob, ob, obi); } } } } } }
private void L2Update(dynamic data) { try { var currencyPair = _currencyMapper.GetCurrency((string)data.product_id); var orderBook = _orderBookDict[currencyPair]; foreach (var change in data.changes) { var tradeType = (string)change[0]; var price = DecimalHelper.Get((string)change[1]); var volume = DecimalHelper.Get((string)change[2]); if (tradeType == "buy") { var bid = orderBook.Bids.FirstOrDefault(it => it.Price == price); if (volume == 0) { if (bid != null) { orderBook.Bids.Remove(bid); SortBids(orderBook); } } else { if (bid != null) { bid.Volume = volume; } else { bid = new OrderBookItem() { Price = price, Volume = volume }; orderBook.Bids.Add(bid); SortBids(orderBook); } } } else { var ask = orderBook.Asks.FirstOrDefault(it => it.Price == price); if (volume == 0) { if (ask != null) { orderBook.Asks.Remove(ask); SortAsks(orderBook); } } if (ask != null) { ask.Volume = volume; } else { ask = new OrderBookItem() { Price = price, Volume = volume }; orderBook.Asks.Add(ask); SortAsks(orderBook); } } OrderBookChanged?.Invoke(currencyPair, orderBook); } } catch (Exception) { //Todo:log data format error } }