public async Task YahooPriceHistoryHandlerTest(string firstDate, string lastDate, YahooInterval interval, int count) { var query = new YahooPriceHistoryQuery() { Symbol = "MSFT", FirstDate = DateTime.Parse(firstDate), LastDate = DateTime.Parse(lastDate), Interval = interval, }; var handler = new YahooPriceHistoryHandler(YahooPriceHistoryHelper.MockIHttpClientFactory()); var result = await handler.Handle(query, CancellationToken.None); Assert.NotNull(result); Assert.NotNull(result.Prices); Assert.True(result.Prices.Any()); Assert.Equal(count, result.Prices.Count()); }
/// <inheritdoc/> protected override async Task Handle(StockPriceUpdaterCommand request, CancellationToken cancellationToken) { YahooInterval interval = request.Frequency switch { Frequency.Daily => YahooInterval.Daily, Frequency.Weekly => YahooInterval.Weekly, Frequency.Monthly => YahooInterval.Monthly, Frequency.Quarterly => YahooInterval.Quorterly, _ => throw new NotImplementedException(), }; using var db = this.contextFactory.CreateDbContext(); // Get list of symbols to process. var stocks = await db.Stocks.AsNoTracking() .Select(s => new { s.Id, s.Symbol, }) .ToListAsync(cancellationToken); int count = 20; foreach (var item in stocks) { if (count-- <= 0) { return; } // Get the database stock price. var oldPrices = await db.StockPrices .Where(p => p.StockId == item.Id && p.Frequency == request.Frequency) .OrderBy(p => p.Period) .ToListAsync(cancellationToken: cancellationToken); DateTime?firstDate = oldPrices.Select(p => (DateTime?)p.Period).FirstOrDefault(); DateTime?lastDate = oldPrices.Select(p => (DateTime?)p.Period).LastOrDefault(); var query = new YahooPriceHistoryQuery { Symbol = item.Symbol, FirstDate = firstDate ?? DateTime.Today.AddYears(-10), LastDate = DateTime.Today, Interval = interval, }; // Get prices. var yahooResult = await this.mediator.Send(query, cancellationToken); if (yahooResult.ErrorMessage != null) { this.logger.LogWarning("{0} for {1} from {2:M-d-Y} to {3:M-d-Y} for {4}", yahooResult.ErrorMessage, query.Symbol, query.FirstDate, query.LastDate, query.Interval); } else { // Update prices if any. if (oldPrices.Any()) { List <StockPriceEntity> toUpdate = new(); var lookup = new Dictionary <DateTime, YahooPrice>(yahooResult.Prices.Select(a => new KeyValuePair <DateTime, YahooPrice>(a.Date, a))); foreach (var oldItem in oldPrices) { if (lookup.TryGetValue(oldItem.Period, out YahooPrice quote) && (oldItem.Open != quote.Open || oldItem.High != quote.High || oldItem.Low != quote.Low || oldItem.Close != quote.Close || oldItem.AdjClose != quote.AdjClose || oldItem.Volume != quote.Volume)) { oldItem.Open = quote.Open; oldItem.High = quote.High; oldItem.Low = quote.Low; oldItem.Close = quote.Close; oldItem.AdjClose = quote.AdjClose; oldItem.Volume = quote.Volume; toUpdate.Add(oldItem); } } if (toUpdate.Any()) { db.StockPrices.UpdateRange(toUpdate); await db.SaveChangesAsync(cancellationToken); } } // Add prices if any. var toAdd = yahooResult.Prices.Where(q => lastDate == null || q.Date > lastDate) .Union(yahooResult.Prices.Where(q => lastDate != null && q.Date < firstDate)) .Select(q => new StockPriceEntity() { StockId = item.Id, Frequency = request.Frequency, Period = q.Date, Open = q.Open, High = q.High, Low = q.Low, Close = q.Close, AdjClose = q.AdjClose, Volume = q.Volume, }); if (toAdd.Any()) { db.AddRange(toAdd); await db.SaveChangesAsync(cancellationToken); } } await Task.Delay(100, cancellationToken); } }