Example #1
0
        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);
        }
Example #2
0
        /// <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);
            }
        }
Example #3
0
 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);
        }