using KLHZ.Trader.Core.Contracts.Messaging.Dtos; using KLHZ.Trader.Core.Contracts.Messaging.Interfaces; using KLHZ.Trader.Core.DataLayer; using KLHZ.Trader.Core.DataLayer.Entities.Orders; using KLHZ.Trader.Core.DataLayer.Entities.Prices; using KLHZ.Trader.Core.Exchange.Services; using KLHZ.Trader.Core.Math.Declisions.Utils; using KLHZ.Trader.Service.Models; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; namespace KLHZ.Trader.Service.Controllers { [ApiController] [Route("[controller]/[action]")] public class PlayController : ControllerBase { private readonly TraderDataProvider _traderDataProvider; private readonly IDataBus _dataBus; private readonly IDbContextFactory _dbContextFactory; public PlayController(IDataBus dataBus, IDbContextFactory dbContextFactory, TraderDataProvider traderDataProvider) { _dbContextFactory = dbContextFactory; _dataBus = dataBus; _traderDataProvider = traderDataProvider; } [HttpGet] public async Task Run(double? shift = null) { try { var figi1 = "FUTIMOEXF000"; //var figi1 = "BBG004730N88"; var figi2 = "BBG004730N88"; //var figi2 = "FUTIMOEXF000"; var time1 = DateTime.UtcNow.AddDays(-shift ?? -7).Date; //var time1 = new DateTime(2025, 9, 24, 11, 00, 0, DateTimeKind.Utc); //var time2 = DateTime.UtcNow.AddMinutes(18); using var context1 = await _dbContextFactory.CreateDbContextAsync(); context1.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking; while (time1 < DateTime.UtcNow) { var data = new List(); var data2 = new List(); var time2 = time1.AddHours(1); var orderbooks = await context1.OrderbookItems .Where(oi => (oi.Figi == figi1 || oi.Figi == figi2) && oi.Time >= time1 && oi.Time < time2) .OrderBy(c => c.Time) .ToArrayAsync(); var prices = await context1.PriceChanges .Where(c => (c.Figi == figi1 || c.Figi == figi2) && c.Time >= time1 && c.Time < time2) .OrderBy(c => c.Time) .Select(c => new NewPriceMessage() { Figi = c.Figi, Ticker = c.Ticker, Time = c.Time, Value = c.Value, IsHistoricalData = true, Direction = c.Direction, Count = c.Count, }) .ToArrayAsync(); var lookupImoexf = orderbooks.Where(o => o.Figi == "FUTIMOEXF000").ToLookup(o => o.Time); var lookupSber = orderbooks.Where(o => o.Figi == "BBG004730N88").ToLookup(o => o.Time); foreach (var item in lookupImoexf) { var asks = item .Where(i => i.ItemType == Core.DataLayer.Entities.Orders.Enums.OrderbookItemType.Ask) .OrderBy(o => o.Price) .ToList(); var bids = item .Where(i => i.ItemType == Core.DataLayer.Entities.Orders.Enums.OrderbookItemType.Bid) .OrderByDescending(o => o.Price) .ToList(); var forRemove = new List(); if (asks.Count > 4 || bids.Count > 4) { } foreach (var bid in bids) { var bids2 = bids.Where(b => b.Price == bid.Price).ToList(); var summCount = bids2.Sum(b => b.Count); var b = bids2.First(); b.Count = summCount; bids2.Remove(b); forRemove.AddRange(bids2); } foreach (var ask in asks) { var asks2 = asks.Where(b => b.Price == ask.Price).ToList(); var summCount = asks2.Sum(b => b.Count); var b = asks2.First(); b.Count = summCount; asks2.Remove(b); forRemove.AddRange(asks2); } asks.RemoveAll(a => forRemove.Contains(a)); bids.RemoveAll(a => forRemove.Contains(a)); var orderbook = new NewOrderbookMessage() { Figi = "FUTIMOEXF000", Ticker = "IMOEXF", Asks = asks.ToArray(), Bids = bids.ToArray(), AsksCount = asks.Sum(a => a.Count), BidsCount = bids.Sum(a => a.Count), Time = item.Key }; var wrapper = new TimeSeriesData() { Figi = orderbook.Figi, Orderbook = orderbook, Time = item.Key, }; data.Add(wrapper); } foreach (var item in lookupSber) { var asks = item .Where(i => i.ItemType == Core.DataLayer.Entities.Orders.Enums.OrderbookItemType.Ask) .OrderBy(o => o.Price) .ToList(); var bids = item .Where(i => i.ItemType == Core.DataLayer.Entities.Orders.Enums.OrderbookItemType.Bid) .OrderByDescending(o => o.Price) .ToList(); var forRemove = new List(); if (asks.Count > 4 || bids.Count > 4) { } foreach (var bid in bids) { var bids2 = bids.Where(b => b.Price == bid.Price).ToList(); var summCount = bids2.Sum(b => b.Count); var b = bids2.First(); b.Count = summCount; bids2.Remove(b); forRemove.AddRange(bids2); } foreach (var ask in asks) { var asks2 = asks.Where(b => b.Price == ask.Price).ToList(); var summCount = asks2.Sum(b => b.Count); var b = asks2.First(); b.Count = summCount; asks2.Remove(b); forRemove.AddRange(asks2); } asks.RemoveAll(a => forRemove.Contains(a)); bids.RemoveAll(a => forRemove.Contains(a)); var orderbook = new NewOrderbookMessage() { Figi = "BBG004730N88", Ticker = "SBER", Asks = asks.ToArray(), Bids = bids.ToArray(), AsksCount = asks.Sum(a => a.Count), BidsCount = bids.Sum(a => a.Count), Time = item.Key }; var wrapper = new TimeSeriesData() { Figi = orderbook.Figi, Orderbook = orderbook, Time = item.Key, }; data.Add(wrapper); } time1 = time2; foreach (var price in prices) { var wrapper = new TimeSeriesData() { Figi = price.Figi, NewPrice = price, Time = price.Time, }; data.Add(wrapper); } data = data.OrderBy(d => d.Time).ToList(); for (int i = 0; i < data.Count; i++) { if (data[i].NewPrice != null && i > 0) { for (int i1 = i - 1; i1 >= 0; i1--) { var ob = data[i1].Orderbook; if (data[i1].Figi == data[i].Figi && ob != null) { var d = new TimeSeriesData() { Figi = ob.Figi, Orderbook = ob, Time = data[i].Time, NewPrice = data[i].NewPrice, }; data2.Add(d); break; } } } else { var ob = data[i].Orderbook; if (ob != null) { var d = new TimeSeriesData() { Figi = ob.Figi, Orderbook = ob, Time = ob.Time, }; data2.Add(d); } } } foreach (var mess in data2) { if (mess.Orderbook != null) { await _dataBus.Broadcast(mess.Orderbook); } if (mess.NewPrice != null) { //await _traderDataProvider.AddData(mess.NewPrice, TimeSpan.FromHours(6)); await _dataBus.Broadcast(mess.NewPrice); } } data.Clear(); data2.Clear(); } } catch (Exception ex) { } } [HttpGet] public async Task CalcOrderbookMeanav(string figi) { try { var t = DateTime.UtcNow.AddHours(-4); using var context1 = await _dbContextFactory.CreateDbContextAsync(); context1.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking; var data = await context1.OrderbookItems .Where(i => i.Time > t && i.Figi == figi && (i.ItemType == Core.DataLayer.Entities.Orders.Enums.OrderbookItemType.BidsSummary4 || i.ItemType == Core.DataLayer.Entities.Orders.Enums.OrderbookItemType.AsksSummary4)) .OrderBy(i => i.Time) .ToArrayAsync(); var bids = new LinkedList(); var asks = new LinkedList(); var buffer = new List(); var dt = TimeSpan.FromMinutes(1); var q = data.ToLookup(d => d.Time); foreach (var d in q) { var pair = d.DistinctBy(www => www.ItemType).OrderBy(www => www.ItemType).ToArray(); if (pair.Length == 2) { bids.AddLast(pair[1]); if (bids.Last().Time - bids.First().Time > dt) { bids.RemoveFirst(); } if (pair[0].Count != 0) { pair[1].Price = ((decimal)pair[1].Count) / ((decimal)pair[0].Count); buffer.Add(new OrderbookItem() { Figi = pair[1].Figi, Ticker = pair[1].Ticker, Count = 1, ItemType = Core.DataLayer.Entities.Orders.Enums.OrderbookItemType.BidsAsksSummary4_2min, Price = bids.Sum(b => b.Price) / bids.Count, Time = pair[1].Time, }); } if (buffer.Count > 10000) { await context1.OrderbookItems.AddRangeAsync(buffer); await context1.SaveChangesAsync(); buffer.Clear(); } } else { } } if (buffer.Count > 0) { await context1.OrderbookItems.AddRangeAsync(buffer); await context1.SaveChangesAsync(); buffer.Clear(); } } catch (Exception ex) { } } [HttpGet] public async Task CalcTradesMeanav(string figi) { try { var t = DateTime.UtcNow.AddHours(-4); using var context1 = await _dbContextFactory.CreateDbContextAsync(); context1.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking; var data = await context1.PriceChanges .Where(i => i.Time > t && i.Figi == figi) .OrderBy(i => i.Time) .ToArrayAsync(); var sells = new LinkedList(); var buys = new LinkedList(); var buffer = new List(); var dt = TimeSpan.FromMinutes(1); foreach (var d in data) { if (d.Direction == 1) { if (buys.Last().Time - buys.First().Time > dt) { buys.RemoveFirst(); } buys.AddLast(d); } if (d.Direction == 2) { sells.AddLast(d); if (sells.Last().Time - sells.First().Time > dt) { sells.RemoveFirst(); } sells.AddLast(d); } if (sells.Count > 0 && buys.Count > 0) { var meanS = ((decimal)sells.Sum(s => s.Count)) / sells.Count; var meanB = ((decimal)buys.Sum(s => s.Count)) / buys.Count; if (meanS != 0) { buffer.Add(new ProcessedPrice() { Figi = d.Figi, Processor = "tradesbalance", Ticker = d.Ticker, Count = 1, Direction = 0, Time = d.Time, Value = meanB / meanS }); } } if (buffer.Count > 10000) { await context1.ProcessedPrices.AddRangeAsync(buffer); await context1.SaveChangesAsync(); buffer.Clear(); } } if (buffer.Count > 0) { await context1.ProcessedPrices.AddRangeAsync(buffer); await context1.SaveChangesAsync(); buffer.Clear(); } } catch (Exception ex) { } } [HttpGet] public async Task TestInmterpolate(string figi) { try { var t1 = DateTime.UtcNow.AddHours(-4); var t2 = DateTime.UtcNow.AddHours(1); //t1 = new DateTime(2025, 9, 15, 10, 1, 0, DateTimeKind.Utc); //t2 = new DateTime(2025, 9, 15, 10, 1, 50, DateTimeKind.Utc); using var context1 = await _dbContextFactory.CreateDbContextAsync(); context1.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking; var data = await context1.PriceChanges .Where(i => i.Time >= t1 && i.Time <= t2 && i.Figi == figi) .OrderBy(i => i.Time) .ToArrayAsync(); var buffer = new List(); var res = SignalProcessing.InterpolateData(data.Select(d => d.Time).ToArray(), data.Select(d => d.Value).ToArray(), TimeSpan.FromSeconds(1)); for (int i = 0; i < res.Item1.Length; i++) { buffer.Add(new ProcessedPrice() { Figi = figi, Processor = "1secinterpol", Ticker = data[0].Ticker, Count = 1, Direction = 0, Time = res.Item1[i], Value = (decimal)res.Item2[i] }); } await context1.ProcessedPrices.AddRangeAsync(buffer); await context1.SaveChangesAsync(); } catch (Exception ex) { } } [HttpGet] public async Task TestFFT(string figi) { try { var t1 = new DateTime(2025, 9, 15, 6, 30, 0, DateTimeKind.Utc); var t2 = new DateTime(2025, 9, 15, 7, 50, 50, DateTimeKind.Utc); using var context1 = await _dbContextFactory.CreateDbContextAsync(); context1.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking; var data = await context1.PriceChanges .Where(i => i.Time >= t1 && i.Time <= t2 && i.Figi == figi) .OrderBy(i => i.Time) .ToArrayAsync(); var buffer = new List(); var values = data.Select(d => d.Value).ToArray(); var times = data.Select(d => d.Time).ToArray(); var res = SignalProcessing.InterpolateData(times, values, TimeSpan.FromSeconds(10)); //FFT.GetMainHarmonictPeriod(res.Item2, res.Item1.Last() - res.Item1.First()); } catch (Exception ex) { } } //[HttpGet] //public async Task GetBalance(string figi) //{ // try // { // var time1 = DateTime.UtcNow.AddDays(-7); // using var context1 = await _dbContextFactory.CreateDbContextAsync(); // context1.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking; // var data = await context1.PriceChanges // .Where(c => c.Figi == figi && c.Time >= time1) // .OrderBy(c => c.Time) // .Select(c => new NewPriceMessage() // { // Figi = figi, // Ticker = c.Ticker, // Time = c.Time, // Value = c.Value, // IsHistoricalData = true // }) // .ToArrayAsync(); // var buffer = new List(); // var list = new LinkedList<(DateTime time, double value)>(); // var previous = -1000d; // foreach (var mess in data) // { // await _traderDataProvider.AddData(mess, TimeSpan.FromHours(6)); // var dataFromCache = await _traderDataProvider.GetData(figi, TimeSpan.FromMinutes(15)); // if (dataFromCache.isFullIntervalExists) // { // if (ShapeAreaCalculator.TryGetAreasRelation(dataFromCache.timestamps, dataFromCache.prices,mess.Value,out var res)) // { // if (list.Count > 0 && mess.Time - list.Last().time > TimeSpan.FromMinutes(5)) // { // list.Clear(); // } // list.AddLast((mess.Time, res)); // if (list.Last().time - list.First().time > TimeSpan.FromMinutes(1)) // { // list.RemoveFirst(); // } // var newRes = (decimal)(list.Sum(i => i.value) / list.Count); // if (list.Count > 0) // { // try // { // buffer.Add(new ProcessedPrice() // { // Figi = figi, // Processor = "balancescalc30min", // Ticker = mess.Ticker, // Time = mess.Time, // Value = newRes, // //Value = (decimal)res, // }); // } // catch(Exception ex) // { // } // } // } // } // else // { // previous = -1d; // } // if (buffer.Count > 10000) // { // await context1.ProcessedPrices.AddRangeAsync(buffer); // await context1.SaveChangesAsync(); // buffer.Clear(); // } // } // if (buffer.Count > 0) // { // await context1.ProcessedPrices.AddRangeAsync(buffer); // await context1.SaveChangesAsync(); // buffer.Clear(); // } // } // catch (Exception ex) // { // } //} ////[HttpGet] //public async Task LoadTradesToHistory(string figi) //{ // try // { // using var context1 = await _dbContextFactory.CreateDbContextAsync(); // context1.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking; // var data = await context1.InstrumentTrades // .Where(c => c.Figi == figi) // .OrderBy(c => c.BoughtAt) // .Select(c => new PriceChange() // { // Figi = figi, // Ticker = c.Ticker, // Time = c.BoughtAt, // Value = c.Price, // IsHistoricalData = true // }) // .ToArrayAsync(); // await context1.PriceChanges.Where(p => p.Figi == figi).ExecuteDeleteAsync(); // await context1.PriceChanges.AddRangeAsync(data); // await context1.SaveChangesAsync(); // } // catch (Exception ex) // { // } //} } }