private void InsertNewPriceSorted(MessageFeed.Message message) { var newDisplayedPrice = new DisplayedProduct(message.Symbol, message.Price); this.cachedPriceData[message.GetIDKey()] = newDisplayedPrice; // Sequential search to find correct location to add for (int index = 0; index < displayedProducts.Count; index++) { if ((newDisplayedPrice.Symbol.CompareTo(this.displayedProducts[index].Symbol) < 0) || (newDisplayedPrice.Symbol == this.displayedProducts[index].Symbol && this.displayedProducts[index].Price < newDisplayedPrice.Price)) { this.displayedProducts.Insert(index, newDisplayedPrice); return; } } // To get here, it must be sorted after all products this.displayedProducts.Add(newDisplayedPrice); }
/// <summary> /// Used to centralize GUI updates instead of relying on external triggers. /// </summary> /// <param name="sender"></param> /// <param name="e"></param> /// <remarks> /// </remarks> void myTimer_Tick(object sender, EventArgs e) { var timeOfUpdate = DateTime.Now; // dequeue all product updates MessageFeed.Message message = null; while (this.myPendingMessages.TryDequeue(out message)) { if (this.cachedPriceData.ContainsKey(message.GetIDKey()) == false) { InsertNewPriceSorted(message); } this.cachedPriceData[message.GetIDKey()].UpdateWith(message, timeOfUpdate); List <Double> additionalPricesToAdd = this.priceWindowTracker.CheckMessage(message); if (additionalPricesToAdd.Count > 0) { foreach (double newPrice in additionalPricesToAdd) { var newMessage = new MessageFeed.Message( MessageFeed.Message.MessageTypeEnum.PRICE, message.Symbol, MessageFeed.Message.ActionEnum.ADD, MessageFeed.Message.BidAskEnum.BID, newPrice, 0 ); InsertNewPriceSorted(newMessage); } } } // Loop over all products to reset any data-changed times // **NOTE** This has O(n) complexity, which if that was undesirable... it could be restructured // to store all change times, objects, and properties in a queue where it would merely // process any that have expired and whose color must be changed back. But that is a // moderately complex solution for an evaluation project... foreach (var displayedProduct in this.displayedProducts) { displayedProduct.ClearChangedTimes(timeOfUpdate); } }
private void OnMessageReceived(MessageFeed.Message message) { this.myPendingMessages.Enqueue(message); }
/// <summary> /// Updates the state of this product with the latest message. /// </summary> /// <param name="message"></param> /// <remarks> /// I could see this needing to exist as a more formal MarketStatus system /// that takes messages and holds the whole known market state. For the /// requirements provided though, this eems sufficient. /// </remarks> internal void UpdateWith(MessageFeed.Message message, DateTime timeOfUpdate) { if (this.Symbol != message.Symbol || this.Price != message.Price) { // this should not happen, but definitely want to crash hard if it ever did throw new InvalidOperationException("Attempted to update a DisplayedProduct with the wrong Symbol or Price"); } int newValue = 0; switch (message.Action) { case MessageFeed.Message.ActionEnum.ADD: newValue = message.Quantity; break; case MessageFeed.Message.ActionEnum.MODIFY: newValue = message.Quantity; break; case MessageFeed.Message.ActionEnum.DELETE: newValue = 0; break; } if (message.MessageType == MessageFeed.Message.MessageTypeEnum.ORDER) { if (message.BidAsk == MessageFeed.Message.BidAskEnum.BID) { if (this.BidOrders != newValue) { this.BidOrders = newValue; this.BidOrders_ChangedTime = timeOfUpdate; RaisePropertyChanged("BidOrders"); RaisePropertyChanged("BidOrders_ChangedTime"); } } else { if (this.AskOrders != newValue) { this.AskOrders = newValue; this.AskOrders_ChangedTime = timeOfUpdate; RaisePropertyChanged("AskOrders"); RaisePropertyChanged("AskOrders_ChangedTime"); } } } else if (message.MessageType == MessageFeed.Message.MessageTypeEnum.PRICE) { if (message.BidAsk == MessageFeed.Message.BidAskEnum.BID) { if (this.BidSize != newValue) { this.BidSize = newValue; this.BidSize_ChangedTime = timeOfUpdate; RaisePropertyChanged("BidSize"); RaisePropertyChanged("BidSize_ChangedTime"); } } else { if (this.AskSize != newValue) { this.AskSize = newValue; this.AskSize_ChangedTime = timeOfUpdate; RaisePropertyChanged("AskSize"); RaisePropertyChanged("AskSize_ChangedTime"); } } } }
public List <Double> CheckMessage(MessageFeed.Message message) { // Update the internal state of prices that exist for each symbol if (symbolStates.ContainsKey(message.Symbol) == false) { symbolStates[message.Symbol] = new SymbolState(); } var symbolState = symbolStates[message.Symbol]; if (message.BidAsk == MessageFeed.Message.BidAskEnum.BID && message.Quantity > 0) { if (symbolState.bestBidPrice.HasValue == false || message.Price > symbolState.bestBidPrice.Value) { symbolState.bestBidPrice = (int)message.Price; } } else if (message.BidAsk == MessageFeed.Message.BidAskEnum.ASK && message.Quantity > 0) { if (symbolState.bestAskPrice.HasValue == false || message.Price < symbolState.bestAskPrice.Value) { symbolState.bestAskPrice = (int)message.Price; } } if (symbolState.highestPrice.HasValue == false || message.Price > symbolState.highestPrice) { symbolState.highestPrice = (int)message.Price; } if (symbolState.lowestPrice.HasValue == false || message.Price < symbolState.lowestPrice) { symbolState.lowestPrice = (int)message.Price; } // Last, see if there should be any prices added due to movement of the bestBid/bestAsk var newPrices = new List <Double>(); if (symbolState.highestPrice.HasValue == false || symbolState.lowestPrice.HasValue == false) { // This should not happen, but if it does, exit the algorithm now. return(newPrices); } int expectedLowest = symbolState.highestPrice.Value; int expectedHighest = symbolState.highestPrice.Value; if (symbolState.bestBidPrice.HasValue == true) { //expectedHighest = Math.Max(expectedHighest, symbolState.bestBidPrice.Value + PRICE_WINDOW); expectedLowest = Math.Min(expectedLowest, symbolState.bestBidPrice.Value - PRICE_WINDOW); } if (symbolState.bestAskPrice.HasValue == true) { expectedHighest = Math.Max(expectedHighest, symbolState.bestAskPrice.Value + PRICE_WINDOW); //expectedLowest = Math.Min(expectedLowest, symbolState.bestAskPrice.Value - PRICE_WINDOW); } expectedLowest = Math.Max(expectedLowest, 0); // Cap at 0; no negative prices. for (int price = expectedLowest; price <= expectedHighest; price += 1) { if (price < symbolState.lowestPrice || price > symbolState.highestPrice) { newPrices.Add((double)price); } } symbolState.lowestPrice = expectedLowest; symbolState.highestPrice = expectedHighest; return(newPrices); }