public async Task MarketCandlesTest() { const string figi = Figi; const string from = "2019-10-17T18:38:33.1316420Z"; const string to = "2019-10-17T18:39:33.1316420Z"; _handler.Expect(HttpMethod.Get, $"{BaseUri}market/candles") .WithQueryString(new Dictionary <string, string> { ["figi"] = figi, ["from"] = from, ["to"] = to, ["interval"] = "1min" }) .WithoutContent() .RespondJsonFromFile("market-candles-response"); var candles = await _context.MarketCandlesAsync(figi, DateTime.Parse(from).ToUniversalTime(), DateTime.Parse(to).ToUniversalTime(), CandleInterval.Minute); var expected = new CandleList(figi, CandleInterval.Minute, new List <CandlePayload> { new CandlePayload(299.5m, 298.87m, 299.59m, 298.8m, 18887, DateTime.Parse("2019-10-17T15:39Z").ToUniversalTime(), CandleInterval.Minute, figi) }); candles.Should().BeEquivalentTo(expected); }
/// <summary> /// 親専用 /// Tickerでローソクを更新する /// </summary> /// <param name="ticker">Ticker</param> public void Update(Ticker ticker) { // 現在のTickerに設定 CurrentTicker = ticker; if (CurrentCandle == null) { // 起動してから初回の動作 if (SystemConstants.IsFirstDeleteCandle) { // この板の全ローソクデータを削除 Logger.LogInformation($"ローソクデータ全削除:{Board.Name}"); var delList = DbContext.Candles.Include(x => x.MBoard).Where(d => d.MBoard.MBoardId == Board.MBoardId).ToList(); DbContext.Candles.RemoveRange(delList); DbContext.SaveChanges(); } // 新しいローソクの準備 CurrentCandle = new Candle(Board, TimeScale, CurrentTicker); // 子要素を新しいローソクで更新 UpdateChildren(CurrentCandle); } else // 初回ではない場合 { // ローソクを更新 var newCandles = CurrentCandle.UpdateByTicker(CurrentTicker); foreach (var item in newCandles) { // 新しいローソクがある場合 // 現在のローソクでDB更新 Logger.LogDebug($"DB:Candles更新:{Board.Name} {TimeScale.DisplayName}"); DbContext.Candles.Add(CurrentCandle); // 今まで作成したローソクに追加 CandleList.Add(CurrentCandle); // 子要素を新しいローソクで更新 UpdateChildren(item); // 新しいローソクをセット CurrentCandle = item; } if (newCandles.Count > 0) { // 更新があった場合 // 最大データ数を超えていたら、古いデータを1件削除 DeleteOldData(); // 子要素も更新が終わったらコミット DbContext.SaveChanges(SystemConstants.SystemName); } } }
public async Task <decimal> GetPrice(string figi, DateTime moment = default) { //по стакану if (moment == default) { return(await GetPriceByOrderbook(figi)); } //по истории свечей CandleList candleList = await _context.MarketCandlesAsync(figi, moment.AddDays(-7), moment, CandleInterval.Week); if (candleList.Candles.Any()) { return(candleList.Candles.OrderBy(candle => candle.Time).Last().Close); } throw new Exception($"Не удалось найти цену на {moment.ToShortDateString()} с FIGI {figi}"); }
public async Task AddOrAppendCandles(CurrencyCode currency, AssetFIGI figi, CandleList candles) { var model = TryGetModel(figi); var candleModels = candles.Candles .Select(c => new CandleModel(new DateTimeOffset(c.Time), c.Open, c.Close, c.Low, c.High)) .ToList(); if (model != null) { _logger.LogTrace($"Add {candleModels.Count} candles to existing model '{currency}' with FIGI '{figi}'"); model.Candles.AddRange(candleModels); await _repository.Update(model); } else { _logger.LogTrace($"Creating new model '{currency}' with FIGI '{figi}'"); var newModel = new CurrencyPriceModel(currency, candles.Figi, candleModels); await _repository.Add(newModel); } }
/// <summary> /// 子専用 /// 親から送られてきたローソクで更新する /// </summary> /// <param name="candle">ローソクデータ</param> public void UpdateByCandle(Candle candle) { Logger.LogDebug($"DB:Candles更新:{Board.Name} {TimeScale.DisplayName}"); if (CurrentCandle == null) { // 初回 // 新しいローソクの準備 CurrentCandle = new Candle(Board, TimeScale, candle); // 子要素を更新 UpdateChildren(candle); } else { // 2回目以降 var newCandles = CurrentCandle.UpdateByCandle(candle); foreach (var item in newCandles) { // 新しいローソクがある場合 // 現在のローソクでDB更新 DbContext.Candles.Add(CurrentCandle); // 今まで作成したローソクに追加 CandleList.Add(CurrentCandle); // 子要素を新しいローソクで更新 UpdateChildren(item); // 新しいローソクをセット CurrentCandle = item; } if (newCandles.Count > 0) { // 更新があった場合 // 最大データ数を超えていたら、古いデータを1件削除 DeleteOldData(); } } }
public async Task MarketCandlesTest() { var handler = new HttpMessageHandlerStub(HttpStatusCode.OK, "{\"trackingId\":\"QBASTAN\",\"payload\":{\"candles\":[{\"o\":299.5,\"c\":298.87,\"h\":299.59,\"l\":298.8,\"v\":18887,\"time\":\"2019-10-17T15:39Z\",\"interval\":\"1min\",\"figi\":\"BBG000CL9VN6\"}],\"interval\":\"1min\",\"figi\":\"BBG000CL9VN6\"},\"status\":\"Ok\"}"); var connection = new Connection(BaseUri, WebSocketBaseUri, Token, new HttpClient(handler)); var context = connection.Context; var candles = await context.MarketCandlesAsync("BBG000CL9VN6", DateTime.Parse("2019-10-17T18:38:33.1316420+03:00"), DateTime.Parse("2019-10-17T18:39:33.1316420+03:00"), CandleInterval.Minute); Assert.NotNull(handler.RequestMessage); Assert.Equal(HttpMethod.Get, handler.RequestMessage.Method); Assert.Equal(new Uri($"{BaseUri}market/candles?figi=BBG000CL9VN6&from={HttpUtility.UrlEncode("2019-10-17T18:38:33.1316420+03:00")}&to={HttpUtility.UrlEncode("2019-10-17T18:39:33.1316420+03:00")}&interval=1min"), handler.RequestMessage.RequestUri); Assert.Null(handler.RequestMessage.Content); Assert.NotNull(candles); var expected = new CandleList("BBG000CL9VN6", CandleInterval.Minute, new List <CandlePayload> { new CandlePayload(299.5m, 298.87m, 299.59m, 298.8m, 18887, DateTime.Parse("2019-10-17T15:39Z").ToUniversalTime(), CandleInterval.Minute, "BBG000CL9VN6") }); Assert.Equal(expected.Figi, candles.Figi); Assert.Equal(expected.Interval, candles.Interval); Assert.Equal(expected.Candles.Count, candles.Candles.Count); for (var i = 0; i < expected.Candles.Count; ++i) { var expectedCandle = expected.Candles[i]; var actualCandle = candles.Candles[i]; Assert.Equal(expectedCandle.Time, actualCandle.Time); Assert.Equal(expectedCandle.Interval, actualCandle.Interval); Assert.Equal(expectedCandle.Figi, actualCandle.Figi); Assert.Equal(expectedCandle.Open, actualCandle.Open); Assert.Equal(expectedCandle.Close, actualCandle.Close); Assert.Equal(expectedCandle.High, actualCandle.High); Assert.Equal(expectedCandle.Low, actualCandle.Low); Assert.Equal(expectedCandle.Volume, actualCandle.Volume); } }
/// <summary> /// 親子共通 /// 最大データ数を超えていたら古いデータを削除する /// 1件だけ /// </summary> private void DeleteOldData() { // コミットしていない分は数えられないことに注意 // 今は起動のたびに消しているから良いが、メモリ内ローソクとDBのローソクの数が異なる場合があることも注意 // →コミット済みに関して、メモリローソクの数を上回った分を削除するようにする if (CandleList.Count > SystemConstants.MaxCandle) { // 今まで作成したローソクから削除 int delCount = CandleList.Count - SystemConstants.MaxCandle; for (int i = 0; i < delCount; i++) { CandleList.RemoveAt(0); } } var count = DbContext.Candles.Include(x => x.MTimeScale).Include(x => x.MBoard).Where(d => d.MTimeScale.Id == TimeScale.Id && d.MBoard.MBoardId == Board.MBoardId).Count(); // DBの、メモリのローソクよりも多い分を削除(今回コミットされてないものは除くので、1件ズレたりする) // ※遅いようだったらOrderByの使用をやめておく if (count > CandleList.Count) { int delCount = count - CandleList.Count; Logger.LogDebug($"Candle{delCount}件削除:{Board.Name} {TimeScale.DisplayName}"); var delList = new List <Candle>(delCount); var dataList = DbContext.Candles.Where(d => d.MTimeScale.Id == TimeScale.Id && d.MBoard.MBoardId == Board.MBoardId).OrderBy(d => d.Id).ToList(); for (int i = 0; i < delCount; i++) { delList.Add(dataList[i]); } foreach (var item in delList) { DbContext.Candles.Remove(item); } } }
private async Task <bool> GetMonthStats(IStockModel stock) { if (!stock.MonthStatsExpired) { return(true); } _apiCount++; //Debug.WriteLine($"API Request {++_apiCount} for {stock.Ticker} {stock.PriceF} {stock.DayChangeF} {DateTime.Now}"); CandleList prices = null; try { prices = await CommonConnection.Context.MarketCandlesAsync(stock.Figi, DateTime.Now.Date.AddMonths(-1), DateTime.Now.Date.AddDays(1), CandleInterval.Day); stock.LastMonthDataUpdate = DateTime.Now; } catch { return(false); } if (prices.Candles.Count == 0) { return(false); } decimal monthVolume = 0, monthHigh = 0, monthLow = 0, monthAvgPrice = 0, avgDayVolumePerMonth = 0, avgDayPricePerMonth = 0, monthOpen = -1, yesterdayVolume = 0, yesterdayMin = 0, yesterdayMax = 0, yesterdayAvgPrice = 0; var todayCandle = prices.Candles[prices.Candles.Count - 1]; foreach (var candle in prices.Candles) { if (monthOpen == -1) { monthOpen = candle.Open; } monthLow = monthLow == 0 ? candle.Low : Math.Min(monthLow, candle.Low); monthHigh = monthHigh == 0 ? candle.High : Math.Min(monthHigh, candle.High); monthVolume += candle.Volume; avgDayPricePerMonth += (candle.High + candle.Low) / 2; yesterdayVolume = candle.Volume; yesterdayMin = candle.Low; yesterdayMax = candle.High; AddCandleToStock(Tuple.Create(stock, candle)); } monthAvgPrice = (monthLow + monthHigh) / 2; yesterdayAvgPrice = (yesterdayMin + yesterdayMax) / 2; avgDayPricePerMonth /= prices.Candles.Count; avgDayVolumePerMonth = monthVolume / prices.Candles.Count; stock.MonthOpen = monthOpen; stock.MonthHigh = monthHigh; stock.MonthLow = monthLow; stock.MonthVolume = monthVolume; stock.MonthVolumeCost = monthVolume * monthAvgPrice * stock.Lot; stock.AvgDayVolumePerMonth = Math.Round(avgDayVolumePerMonth); stock.AvgDayPricePerMonth = avgDayPricePerMonth; stock.AvgDayVolumePerMonthCost = avgDayPricePerMonth * avgDayVolumePerMonth * stock.Lot; stock.DayVolChgOfAvg = stock.DayVolume / stock.AvgDayVolumePerMonth; stock.YesterdayAvgPrice = yesterdayAvgPrice; stock.YesterdayVolume = yesterdayVolume; stock.YesterdayVolumeCost = yesterdayVolume * yesterdayAvgPrice * stock.Lot; return(true); }