public void AddOrUpdateQuote(T quote)
        {
            // Typically this would be in a mapper/transformer/etc. likely, but for now simply adding some formating here
            quote.Price  = Math.Round(quote.Price, 8);
            quote.Symbol = quote.Symbol.Trim();

            if (quote.ExpirationTimestamp <= 0)
            {
                quote.ExpirationTimestamp = quote.ExpirationDate.ToUnixTimestamp();
            }

            // Intent write is essentially a single writer/many readers lock mode - only one thread can enter intent-write at a time, but readers
            // are not blocked at all (which is the functionality we're looking for here). Note that we never actually enter a write-lock mode, which
            // would allow a single writer and nothing else to occur...
            _lockManager.EnterIntentWrite(quote.Symbol);

            try
            {
                StorageService.AddOrUpdate(quote);
            }
            finally
            {
                _lockManager.ExitIntentWrite(quote.Symbol);
            }

            _observer.OnAddOrUpdate(quote);
        }
Example #2
0
        public ITradeResult ExecuteTrade(string symbol, uint volumeRequested, ILockManager lockManager, IQuoteStorageService <T> quoteStorageService)
        {
            var response = new TradeResult
            {
                Id              = Guid.NewGuid(),
                Symbol          = symbol,
                VolumeRequested = volumeRequested
            };

            double weightedSum = 0;

            var originalQuotes = new List <T>();

            // Intent write is essentially a single writer/many readers lock mode - only one thread can enter intent-write at a time, but readers
            // are not blocked at all (which is the functionality we're looking for here). Note that we never actually enter a write-lock mode, which
            // would allow a single writer and nothing else to occur...
            lockManager.EnterIntentWrite(symbol);

            try
            {
                var quotes = quoteStorageService.GetQuotesByPrice(symbol);

                if (quotes == null || quotes.Count == 0)
                {
                    return(response);
                }

                foreach (var quote in quotes)
                { // Candidate quote, has to pass validation predicates to be considered
                    if (!_quoteValidationPredicates.All(p => p(quote)))
                    {
                        continue;
                    }

                    var volumeTaken = quote.AvailableVolume >= (response.VolumeRequested - response.VolumeExecuted)
                                          ? (response.VolumeRequested - response.VolumeExecuted)
                                          : quote.AvailableVolume;

                    originalQuotes.Add((T)quote.Clone());

                    quote.AvailableVolume -= volumeTaken;

                    // Mostly here to mimic what would be needed if a non-reference based storage service were to be used...
                    quoteStorageService.Update(quote);

                    weightedSum += volumeTaken * quote.Price;

                    response.VolumeExecuted += volumeTaken;

                    if (response.VolumeExecuted >= response.VolumeRequested)
                    {
                        break;
                    }
                }
            }
            catch when(RevertQuotesAndReturnFalse(originalQuotes, quoteStorageService))
            { // Unreachable throw - the above when filter will revert any modified quotes and return false, just rethrowing like an unhandled exception
                // without unwinding the call stack
                throw;
            }
            finally
            {
                lockManager.ExitIntentWrite(symbol);
            }

            if (response.VolumeExecuted > 0)
            {
                response.VolumeWeightedAveragePrice = weightedSum / response.VolumeExecuted;
            }

            return(response);
        }