//callback from the background websocket thread. Checks and updates any matching topics. void _api_OnDataUpdate(object sender, MarketDataSnapshot snap) { //update data cache _cache.MarketData[snap.Product] = snap; //no current subscription for this product if (!_topics.ContainsKey(snap.Product)) { return; } Dictionary <Tuple <DataPoint, int>, Topic> productTopics = _topics[snap.Product].TopicItems; //iterate down the depth on the bid side, updating any currently subscribed topics for (int level = 0; level < snap.BidDepth.Count; level++) { //update bid Tuple <DataPoint, int> key = Tuple.Create(DataPoint.Bid, level); if (productTopics.ContainsKey(key)) { productTopics[key].UpdateValue(snap.BidDepth[level].Price); } //update bidvol key = Tuple.Create(DataPoint.BidVol, level); if (productTopics.ContainsKey(key)) { productTopics[key].UpdateValue(snap.BidDepth[level].Qty); } } //iterate down the depth on the ask side, updating any currently subscribed topics for (int level = 0; level < snap.AskDepth.Count; level++) { //update ask Tuple <DataPoint, int> key = Tuple.Create(DataPoint.Ask, level); if (productTopics.ContainsKey(key)) { productTopics[key].UpdateValue(snap.AskDepth[level].Price); } //update askvol key = Tuple.Create(DataPoint.AskVol, level); if (productTopics.ContainsKey(key)) { productTopics[key].UpdateValue(snap.AskDepth[level].Qty); } } }
private void ProcessDepthSnapshot(string data) { try { BitMexStreamDepthSnap depth = JSON.ToObject <BitMexStreamDepthSnap>(data); if (depth.data.Count != 1) { Logging.Log("BitMexHandler missing depth snap data"); return; } BitMexDepthCache info = depth.data[0]; string symbol = info.symbol; //parse into snapshot MarketDataSnapshot snap = new MarketDataSnapshot(symbol); foreach (object[] bid in info.bids) { decimal price = Convert.ToDecimal(bid[0]); int rawQty = Convert.ToInt32(bid[1]); snap.BidDepth.Add(new MarketDepth(price, rawQty)); } foreach (object[] ask in info.asks) { decimal price = Convert.ToDecimal(ask[0]); int rawQty = Convert.ToInt32(ask[1]); snap.AskDepth.Add(new MarketDepth(price, rawQty)); } //notify listeners if (OnDepthUpdate != null) { OnDepthUpdate(null, snap); } } catch (Exception ex) { Logging.Log("BitMexHandler Depth {0}", ex.Message); } }
//New RTD subscription callback protected override object ConnectData(Topic topic, IList <string> topicInfo, ref bool newValues) { object result; int topicId = GetTopicId(topic); Logging.Log("ConnectData: {0} - {{{1}}}", topicId, string.Join(", ", topicInfo)); //parse and validate request //--- //count and default parameters if (topicInfo.Count < 2) { return(ExcelErrorUtil.ToComError(ExcelError.ExcelErrorNA)); //need at least a product and a datapoint } else if (topicInfo.Count == 2) { topicInfo.Add("0"); //default to top level of book } //parse parameters string product = topicInfo[0]; DataPoint dataPoint; bool dataPointOk = Enum.TryParse <DataPoint>(topicInfo[1], out dataPoint); int level; bool levelOk = Int32.TryParse(topicInfo[2], out level); //return error if parameters are invalid if (!dataPointOk || !levelOk) { return(ExcelErrorUtil.ToComError(ExcelError.ExcelErrorNA)); //incorrect level or datapoint request } if (level > 9 || level < 0) { return(ExcelErrorUtil.ToComError(ExcelError.ExcelErrorNA)); //level out of range } //store subscription request //--- //if product has not yet been subscribed, create subscription container if (!_topics.ContainsKey(product)) { _topics[product] = new TopicCollection(); } //store subscription request _topics[product].TopicItems[Tuple.Create(dataPoint, level)] = topic; if (_topicDetails.ContainsKey(topicId)) { Logging.Log("ERROR: duplicate topicId: {0} {1}", topicId, _topicDetails[topicId]); } _topicDetails.Add(topicId, new TopicSubscriptionDetails(product, dataPoint, level)); //return data //-- if (dataPoint == DataPoint.Last) { if (!_cache.Instruments.ContainsKey(product)) { //may be empty when sheet is first loaded result = ExcelErrorUtil.ToComError(ExcelError.ExcelErrorGettingData); //return "pending data" to Excel } else { //get data from cache BitMexInstrument inst = _cache.Instruments[product]; switch (dataPoint) { case DataPoint.Last: result = inst.lastPrice; break; default: result = ExcelErrorUtil.ToComError(ExcelError.ExcelErrorNA); break; } } } else //bid/ask etc { if (!_cache.MarketData.ContainsKey(product)) { //if product is not yet in cache, request snapshot //this can happen if a product doesnt update very frequently _api.GetSnapshot(product); result = ExcelErrorUtil.ToComError(ExcelError.ExcelErrorGettingData); //return "pending data" to Excel } else { //get data from cache MarketDataSnapshot snap = _cache.MarketData[product]; switch (dataPoint) { case DataPoint.Bid: result = snap.BidDepth[level].Price; break; case DataPoint.BidVol: result = snap.BidDepth[level].Qty; break; case DataPoint.Ask: result = snap.AskDepth[level].Price; break; case DataPoint.AskVol: result = snap.AskDepth[level].Qty; break; default: result = ExcelErrorUtil.ToComError(ExcelError.ExcelErrorNA); break; } } } return(result); }
//callback from the background websocket thread. Checks and updates any matching topics. void _api_OnDataUpdate(object sender, MarketDataSnapshot snap) { //update data cache _cache.MarketData[snap.Product] = snap; //no current subscription for this product if (!_topics.ContainsKey(snap.Product)) return; Dictionary<Tuple<DataPoint, int>, Topic> productTopics = _topics[snap.Product].TopicItems; //iterate down the depth on the bid side, updating any currently subscribed topics for (int level = 0; level < snap.BidDepth.Count; level++) { //update bid Tuple<DataPoint, int> key = Tuple.Create(DataPoint.Bid, level); if (productTopics.ContainsKey(key)) productTopics[key].UpdateValue(snap.BidDepth[level].Price); //update bidvol key = Tuple.Create(DataPoint.BidVol, level); if (productTopics.ContainsKey(key)) productTopics[key].UpdateValue(snap.BidDepth[level].Qty); } //iterate down the depth on the ask side, updating any currently subscribed topics for (int level = 0; level < snap.AskDepth.Count; level++) { //update ask Tuple<DataPoint, int> key = Tuple.Create(DataPoint.Ask, level); if (productTopics.ContainsKey(key)) productTopics[key].UpdateValue(snap.AskDepth[level].Price); //update askvol key = Tuple.Create(DataPoint.AskVol, level); if (productTopics.ContainsKey(key)) productTopics[key].UpdateValue(snap.AskDepth[level].Qty); } }
private void ProcessDepth(string data) { try { BitMexStreamDepth depth = JSON.ToObject<BitMexStreamDepth>(data); if (depth.data.Count <= 0) return; string symbol = depth.data[0].symbol; //create cache container if not already available MarketDataSnapshot snap = new MarketDataSnapshot(symbol); //normally returns "partial" on response to getSymbol if( depth.action.Equals("partial")) { foreach (BitMexDepth info in depth.data) { int level = info.level; //extend depth if new levels added if ((info.bidPrice.HasValue || info.bidSize.HasValue) && snap.BidDepth.Count < level + 1) for (int i = snap.BidDepth.Count; i < level + 1; i++) snap.BidDepth.Add(new MarketDepth()); if ((info.askPrice.HasValue || info.askSize.HasValue) && snap.AskDepth.Count < level + 1) for (int i = snap.AskDepth.Count; i < level + 1; i++) snap.AskDepth.Add(new MarketDepth()); //update values, or blank out if values are null if (info.bidPrice.HasValue || info.bidSize.HasValue) { if (info.bidPrice.HasValue) snap.BidDepth[level].Price = info.bidPrice.Value; if (info.bidSize.HasValue) snap.BidDepth[level].Qty = info.bidSize.Value; } if (info.askPrice.HasValue || info.askSize.HasValue) { if (info.askPrice.HasValue) snap.AskDepth[level].Price = info.askPrice.Value; if (info.askSize.HasValue) snap.AskDepth[level].Qty = info.askSize.Value; } } //notify listeners if (OnDepthUpdate != null) OnDepthUpdate(null, snap); } } catch (Exception ex) { Logging.Log("BitMexHandler Depth", ex); } }
private void ProcessDepthSnapshot(string data) { try { BitMexStreamDepthSnap depth = JSON.ToObject<BitMexStreamDepthSnap>(data); if (depth.data.Count != 1) { Logging.Log("BitMexHandler missing depth snap data"); return; } BitMexDepthCache info = depth.data[0]; string symbol = info.symbol; //parse into snapshot MarketDataSnapshot snap = new MarketDataSnapshot(symbol); foreach (object[] bid in info.bids) { decimal price = Convert.ToDecimal(bid[0]); int rawQty = Convert.ToInt32(bid[1]); snap.BidDepth.Add(new MarketDepth(price, rawQty)); } foreach (object[] ask in info.asks) { decimal price = Convert.ToDecimal(ask[0]); int rawQty = Convert.ToInt32(ask[1]); snap.AskDepth.Add(new MarketDepth(price, rawQty)); } //notify listeners if(OnDepthUpdate != null) OnDepthUpdate(null, snap); } catch (Exception ex) { Logging.Log("BitMexHandler Depth {0}", ex.Message); } }
private void ProcessDepth(string data) { try { BitMexStreamDepth depth = JSON.ToObject <BitMexStreamDepth>(data); if (depth.data.Count <= 0) { return; } string symbol = depth.data[0].symbol; //create cache container if not already available MarketDataSnapshot snap = new MarketDataSnapshot(symbol); //normally returns "partial" on response to getSymbol if (depth.action.Equals("partial")) { foreach (BitMexDepth info in depth.data) { int level = info.level; //extend depth if new levels added if ((info.bidPrice.HasValue || info.bidSize.HasValue) && snap.BidDepth.Count < level + 1) { for (int i = snap.BidDepth.Count; i < level + 1; i++) { snap.BidDepth.Add(new MarketDepth()); } } if ((info.askPrice.HasValue || info.askSize.HasValue) && snap.AskDepth.Count < level + 1) { for (int i = snap.AskDepth.Count; i < level + 1; i++) { snap.AskDepth.Add(new MarketDepth()); } } //update values, or blank out if values are null if (info.bidPrice.HasValue || info.bidSize.HasValue) { if (info.bidPrice.HasValue) { snap.BidDepth[level].Price = info.bidPrice.Value; } if (info.bidSize.HasValue) { snap.BidDepth[level].Qty = info.bidSize.Value; } } if (info.askPrice.HasValue || info.askSize.HasValue) { if (info.askPrice.HasValue) { snap.AskDepth[level].Price = info.askPrice.Value; } if (info.askSize.HasValue) { snap.AskDepth[level].Qty = info.askSize.Value; } } } //notify listeners if (OnDepthUpdate != null) { OnDepthUpdate(null, snap); } } } catch (Exception ex) { Logging.Log("BitMexHandler Depth", ex); } }