From ff22dd0a9c54fe99d14672a8118cd3b208d7dc70 Mon Sep 17 00:00:00 2001 From: vlad zverzhkhovskiy Date: Mon, 15 Sep 2025 13:48:00 +0300 Subject: [PATCH] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB=D0=B5?= =?UTF-8?q?=D0=BD=D0=BE=20=D0=BB=D0=BE=D0=B3=D0=B8=D1=80=D0=BE=D0=B2=D0=B0?= =?UTF-8?q?=D0=BD=D0=B8=D0=B5=20=D1=81=D0=B4=D0=B5=D0=BB=D0=BE=D0=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Dtos/Interfaces/ITradeCommand.cs | 1 + .../Messaging/Dtos/TradeCommand.cs | 1 + KLHZ.Trader.Core.Math/Declisions/Utils/FFT.cs | 110 +++++++++++++++--- .../Declisions/Utils/SignalProcessing.cs | 57 +++++++++ KLHZ.Trader.Core.Tests/FFTTests.cs | 96 ++++++++++++--- .../SignalProcessingTests.cs | 29 +++++ KLHZ.Trader.Core/Common/BotModeSwitcher.cs | 4 +- KLHZ.Trader.Core/Exchange/Services/Trader.cs | 29 +++-- .../Services/TradingCommandsExecutor.cs | 5 + .../Controllers/PlayController.cs | 45 +++++++ 10 files changed, 333 insertions(+), 44 deletions(-) create mode 100644 KLHZ.Trader.Core.Math/Declisions/Utils/SignalProcessing.cs create mode 100644 KLHZ.Trader.Core.Tests/SignalProcessingTests.cs diff --git a/KLHZ.Trader.Core.Contracts/Messaging/Dtos/Interfaces/ITradeCommand.cs b/KLHZ.Trader.Core.Contracts/Messaging/Dtos/Interfaces/ITradeCommand.cs index d68633a..9903f42 100644 --- a/KLHZ.Trader.Core.Contracts/Messaging/Dtos/Interfaces/ITradeCommand.cs +++ b/KLHZ.Trader.Core.Contracts/Messaging/Dtos/Interfaces/ITradeCommand.cs @@ -4,6 +4,7 @@ namespace KLHZ.Trader.Core.Contracts.Messaging.Dtos.Interfaces { public interface ITradeCommand { + public Guid CommandId { get; } public TradeCommandType CommandType { get; } public string Figi { get; } public decimal? RecomendPrice { get; } diff --git a/KLHZ.Trader.Core.Contracts/Messaging/Dtos/TradeCommand.cs b/KLHZ.Trader.Core.Contracts/Messaging/Dtos/TradeCommand.cs index 4999d40..5550df0 100644 --- a/KLHZ.Trader.Core.Contracts/Messaging/Dtos/TradeCommand.cs +++ b/KLHZ.Trader.Core.Contracts/Messaging/Dtos/TradeCommand.cs @@ -5,6 +5,7 @@ namespace KLHZ.Trader.Core.Contracts.Messaging.Dtos { public class TradeCommand : ITradeCommand { + public Guid CommandId { get; init; } = Guid.NewGuid(); public TradeCommandType CommandType { get; init; } public required string Figi { get; init; } public decimal? RecomendPrice { get; init; } diff --git a/KLHZ.Trader.Core.Math/Declisions/Utils/FFT.cs b/KLHZ.Trader.Core.Math/Declisions/Utils/FFT.cs index 88c9f23..3a415b0 100644 --- a/KLHZ.Trader.Core.Math/Declisions/Utils/FFT.cs +++ b/KLHZ.Trader.Core.Math/Declisions/Utils/FFT.cs @@ -10,7 +10,28 @@ namespace KLHZ.Trader.Core.Math.Declisions.Utils var da = new List(); for (int i = 0; i < 1000; i++) { - da.Add((float)System.Math.Sin(0.01 * i)+ (float)System.Math.Cos(0.01 * i)); + da.Add((float)System.Math.Sin(0.01 * i) + (float)System.Math.Cos(0.01 * i)); + } + + var start = da.ToArray(); + var arrv = da.Select(d => new Complex32(d, 0)).ToArray(); + Fourier.Forward(arrv); + + Fourier.Inverse(arrv); + var res = arrv.Select(a => a.Real).ToArray(); + + for (int i = 0; i < 1000; i++) + { + var d = res[i] - start[i]; + } + } + + public static void А() + { + var da = new List(); + for (int i = 0; i < 1000; i++) + { + da.Add((float)System.Math.Sin(0.01 * i) + (float)System.Math.Cos(0.01 * i)); } var start = da.ToArray(); @@ -28,9 +49,9 @@ namespace KLHZ.Trader.Core.Math.Declisions.Utils public static TimeSpan CaclHarmonycPeriod(TimeSpan signalLength, int signalLengthItems, int harmonyNumber) { - var fdiscretisation = signalLengthItems /signalLength.TotalSeconds ; + var fdiscretisation = signalLengthItems / signalLength.TotalSeconds; var fharm = harmonyNumber * fdiscretisation / signalLengthItems; - return TimeSpan.FromSeconds(1/fharm); + return TimeSpan.FromSeconds(1 / fharm); } public static double CalcCurrentPhase(Complex32[] spectr, int harmonyNumber) @@ -39,32 +60,89 @@ namespace KLHZ.Trader.Core.Math.Declisions.Utils return System.Math.Atan(item.Imaginary / item.Real); } - public static (double min, double max) CalcPhaseRange(double aSin, double aCos, double initPhase, double maxLevel) + public static (double min, double max) CalcPhaseRangeFoxMax(double aSin, double aCos, double initPhase, double level) + { + return CalcPhaseRange(aSin, aCos,initPhase, level, CheckMaxValue); + } + + public static (double min, double max) CalcPhaseRangeFoxMin(double aSin, double aCos, double initPhase, double level) + { + return CalcPhaseRange(aSin, aCos, initPhase, level, CheckMinValue); + } + + internal static (double min, double max) CalcPhaseRange(double aSin, double aCos, double initPhase, double level, Func comparer) { var x = new List(); - var x2 = new List(); + var xIndexes = new List(); var y = new List(); - for (double i = 0; i <= 10 * System.Math.PI; i += 0.0001) + var max = double.MinValue; + var min = double.MaxValue; + var _2pi = 2 * System.Math.PI; + for (double i = 0; i <= 10 * System.Math.PI; i += 0.01) { - var _2pi = 2 * System.Math.PI; - var df = ((i / _2pi) % 1) * _2pi; + var df = (((i) / _2pi) % 1) * _2pi; + var val = aSin * System.Math.Sin(i + initPhase) + aCos * System.Math.Cos(i + initPhase); + if (val > max) + { + max = val; + } + if (val < min) + { + min = val; + } x.Add(df); - y.Add(aSin * System.Math.Sin(i + initPhase) + aCos * System.Math.Cos(i + initPhase)); + y.Add(val); } - var prevI = -1; - var max = y.Max(); + int start = -2; + int prevI = -2; + int end = -2; + + var drange = -2; + + var minPhaseTmp = 0d; + var maxPhaseTmp = 0d; + + var minPhase = 0d; + var maxPhase = 0d; for (int i = 0; i < y.Count; i++) { - if (y[i] > (1 - maxLevel) * max && (i - prevI == 1 || prevI < 0)) + if (comparer(y[i],min,max,level)) { - x2.Add(x[i]); - prevI = i; + if (start < 0) + { + minPhaseTmp = x[i]; + start = i; + } } + else if (start >= 0 && i - prevI == 1) + { + end = prevI; + maxPhaseTmp = x[end]; + var drangeTmp = end - start; + if (drangeTmp > drange) + { + drange = drangeTmp; + minPhase = minPhaseTmp; + maxPhase = maxPhaseTmp; + } + start = -2; + } + prevI = i; } - var minPhase = x2.Min(); - var maxPhase = x2.Max(); + + return (minPhase, maxPhase); } + + internal static bool CheckMaxValue(double val, double min, double max, double relativeValue) + { + return (val > (1 - relativeValue) * max); + } + + internal static bool CheckMinValue(double val, double min, double max, double relativeValue) + { + return (val < (1 - relativeValue) * min); + } } } diff --git a/KLHZ.Trader.Core.Math/Declisions/Utils/SignalProcessing.cs b/KLHZ.Trader.Core.Math/Declisions/Utils/SignalProcessing.cs new file mode 100644 index 0000000..a2a81fa --- /dev/null +++ b/KLHZ.Trader.Core.Math/Declisions/Utils/SignalProcessing.cs @@ -0,0 +1,57 @@ +namespace KLHZ.Trader.Core.Math.Declisions.Utils +{ + public static class SignalProcessing + { + public static (DateTime[], double[]) InterpolateData(DateTime[] timestamps, double[] values, TimeSpan timeStep) + { + var res = new List(); + var res2 = new List(); + + var startTime = new DateTime(timestamps[0].Year, timestamps[0].Month, timestamps[0].Day, 0, 0, 0, DateTimeKind.Utc); + var dt = timestamps[0] - startTime; + + var totalSteps = System.Math.Ceiling((timestamps[timestamps.Length - 1] - timestamps[0]).TotalSeconds / timeStep.TotalSeconds); + var deltaSeconds = System.Math.Floor(dt.TotalSeconds / timeStep.TotalSeconds); + startTime = startTime.AddSeconds(deltaSeconds * timeStep.TotalSeconds) ; + + var firstBound = startTime; + var secondBound = startTime + timeStep; + var bound = 0; + for (int i = 0; i < totalSteps; i++) + { + var count = 0; + var sum = 0d; + + for (int i1 = bound; i1 < timestamps.Length; i1++) + { + if (timestamps[i1]> firstBound && timestamps[i1] <= secondBound) + { + count++; + sum += values[i1]; + } + else if (count != 0) + { + bound = i1; + break; + } + } + if (count != 0) + { + res.Add(sum / count); + res2.Add(secondBound); + } + else if (bound< timestamps.Length-2) + { + res.Add(res.Last()); + res2.Add(secondBound); + } + + + firstBound += timeStep; + secondBound += timeStep; + } + + return (res2.ToArray(), res.ToArray()); + } + } +} diff --git a/KLHZ.Trader.Core.Tests/FFTTests.cs b/KLHZ.Trader.Core.Tests/FFTTests.cs index 361c7bd..52c45db 100644 --- a/KLHZ.Trader.Core.Tests/FFTTests.cs +++ b/KLHZ.Trader.Core.Tests/FFTTests.cs @@ -10,24 +10,24 @@ namespace KLHZ.Trader.Core.Tests [Test] public static void Test() { - var da = new List(); - for (int i = 0; i < 100; i++) - { - da.Add((float)System.Math.Sin(0.5 * i)+(float)System.Math.Cos(0.5 * i)); - } + //var da = new List(); + //for (int i = 0; i < 100; i++) + //{ + // da.Add((float)System.Math.Sin(0.5 * i)+(float)System.Math.Cos(0.5 * i)); + //} - var start = da.ToArray(); - var arrv = da.Select(d => new Complex32(d, 0)).ToArray(); - Fourier.Forward(arrv); - var tem = arrv.Select(a => Complex32.Abs(a)).ToArray(); - var s = tem.Sum(); - Fourier.Inverse(arrv); - var res = arrv.Select(a => a.Real).ToArray(); + //var start = da.ToArray(); + //var arrv = da.Select(d => new Complex32(d, 0)).ToArray(); + //Fourier.Forward(arrv); + //var tem = arrv.Select(a => Complex32.Abs(a)).ToArray(); + //var s = tem.Sum(); + //Fourier.Inverse(arrv); + //var res = arrv.Select(a => a.Real).ToArray(); - for (int i = 0; i < 1000; i++) - { - var d = res[i] - start[i]; - } + //for (int i = 0; i < 1000; i++) + //{ + // var d = res[i] - start[i]; + //} } [Test] @@ -41,9 +41,69 @@ namespace KLHZ.Trader.Core.Tests [Test] - public static void CalcPhaseRange() + public static void CalcPhaseRangeForMax_Sin() { - var res = FFT.CalcPhaseRange(1, 0, 0, 0.001); + var res = FFT.CalcPhaseRangeFoxMax(1, 0, 0, 0.001); + Assert.IsTrue(res.min < System.Math.PI / 2); + Assert.IsTrue(res.min > System.Math.PI / 2 * 0.9); + Assert.IsTrue(res.max > System.Math.PI / 2); + Assert.IsTrue(res.max < System.Math.PI / 2 * 1.1); + } + + [Test] + public static void CalcPhaseRangeForMin_Sin() + { + var res = FFT.CalcPhaseRangeFoxMin(1, 0, 0, 0.001); + Assert.IsTrue(res.min < 3*System.Math.PI / 2); + Assert.IsTrue(res.min > 3*System.Math.PI / 2 * 0.9); + Assert.IsTrue(res.max > 3*System.Math.PI / 2); + Assert.IsTrue(res.max < 3*System.Math.PI / 2 * 1.1); + } + + [Test] + public static void CalcPhaseRangeForMax_MinusSin() + { + var res = FFT.CalcPhaseRangeFoxMax(-1, 0, 0, 0.001); + Assert.IsTrue(res.min < 3*System.Math.PI / 2); + Assert.IsTrue(res.min > 3*System.Math.PI / 2 * 0.9); + Assert.IsTrue(res.max > 3*System.Math.PI / 2); + Assert.IsTrue(res.max < 3*System.Math.PI / 2 * 1.1); + } + + [Test] + public static void CalcPhaseRangeForMax_Cos() + { + var res = FFT.CalcPhaseRangeFoxMax(0, 1, 0, 0.001); + Assert.IsTrue(res.min < System.Math.PI * 2); + Assert.IsTrue(res.min > System.Math.PI * 2 * 0.9); + Assert.IsTrue(res.max > 0); + Assert.IsTrue(res.max < System.Math.PI * 2*0.1); + } + + [Test] + public static void CalcPhaseRangeForMin_Cos() + { + var res = FFT.CalcPhaseRangeFoxMin(0, 1, 0, 0.001); + Assert.IsTrue(res.min < System.Math.PI); + Assert.IsTrue(res.min > System.Math.PI * 0.9); + Assert.IsTrue(res.max > System.Math.PI); + Assert.IsTrue(res.max < System.Math.PI * 1.1); + } + + [Test] + public static void CalcPhaseRangeForMax_CosWithShift() + { + var res = FFT.CalcPhaseRangeFoxMax(0, 1, System.Math.PI / 2, 0.001); + Assert.IsTrue(res.min < 3 * System.Math.PI / 2); + Assert.IsTrue(res.min > 3 * System.Math.PI / 2 * 0.9); + Assert.IsTrue(res.max > 3 * System.Math.PI / 2); + Assert.IsTrue(res.max < 3 * System.Math.PI / 2 * 1.1); + } + + [Test] + public static void CalcPhaseRangeForMax_CosWithShift2() + { + var res = FFT.CalcPhaseRangeFoxMax(0, 1, - System.Math.PI / 2, 0.001); Assert.IsTrue(res.min < System.Math.PI / 2); Assert.IsTrue(res.min > System.Math.PI / 2 * 0.9); Assert.IsTrue(res.max > System.Math.PI / 2); diff --git a/KLHZ.Trader.Core.Tests/SignalProcessingTests.cs b/KLHZ.Trader.Core.Tests/SignalProcessingTests.cs new file mode 100644 index 0000000..378f811 --- /dev/null +++ b/KLHZ.Trader.Core.Tests/SignalProcessingTests.cs @@ -0,0 +1,29 @@ +using KLHZ.Trader.Core.Math.Declisions.Utils; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Security.Cryptography; +using System.Text; +using System.Threading.Tasks; + +namespace KLHZ.Trader.Core.Tests +{ + public class SignalProcessingTests + { + [Test] + public static void Test() + { + var da = new List(); + var times = new List(); + var startDt = DateTime.UtcNow; + for (int i = 0; i < 100; i++) + { + startDt = startDt.AddSeconds(((double)(RandomNumberGenerator.GetInt32(1, 100)))/100); + times.Add(startDt); + da.Add((float)System.Math.Sin(0.01 * i) + (float)System.Math.Cos(0.01 * i)); + } + + var res = SignalProcessing.InterpolateData(times.ToArray(), da.ToArray(), TimeSpan.FromSeconds(5)); + } + } +} diff --git a/KLHZ.Trader.Core/Common/BotModeSwitcher.cs b/KLHZ.Trader.Core/Common/BotModeSwitcher.cs index 5efaa4e..438658c 100644 --- a/KLHZ.Trader.Core/Common/BotModeSwitcher.cs +++ b/KLHZ.Trader.Core/Common/BotModeSwitcher.cs @@ -3,8 +3,8 @@ public static class BotModeSwitcher { private readonly static object _locker = new(); - private static bool _canSell = false; - private static bool _canPurchase = false; + private static bool _canSell = true; + private static bool _canPurchase = true; public static bool CanSell() { diff --git a/KLHZ.Trader.Core/Exchange/Services/Trader.cs b/KLHZ.Trader.Core/Exchange/Services/Trader.cs index 1ad1c36..910770d 100644 --- a/KLHZ.Trader.Core/Exchange/Services/Trader.cs +++ b/KLHZ.Trader.Core/Exchange/Services/Trader.cs @@ -19,6 +19,7 @@ using System.Collections.Concurrent; using System.Security.Cryptography; using System.Threading.Channels; using Tinkoff.InvestApi; +using static Microsoft.EntityFrameworkCore.DbLoggerCategory.Database; using AssetType = KLHZ.Trader.Core.Exchange.Models.AssetsAccounting.AssetType; namespace KLHZ.Trader.Core.Exchange.Services @@ -125,7 +126,7 @@ namespace KLHZ.Trader.Core.Exchange.Services var stoppingKey = message.Figi + asset.AccountId; if (message.Time - asset.BoughtAt > TimeSpan.FromMinutes(4) && profit < -66m && !ClosingStops.ContainsKey(stoppingKey)) { - await _dataBus.Broadcast(new TradeCommand() + var command = new TradeCommand() { AccountId = asset.AccountId, Figi = message.Figi, @@ -134,7 +135,10 @@ namespace KLHZ.Trader.Core.Exchange.Services Count = System.Math.Abs((long)asset.Count), RecomendPrice = null, EnableMargin = false, - }); + }; + await _dataBus.Broadcast(command); + _logger.LogWarning("Сброс актива {figi}! id команды {commandId} Направление сделки: {dir}; Количество активов: {count}; Разрешена ли маржиналка: {margin}", + message.Figi, command.CommandId, command.CommandType, command.Count, command.EnableMargin); OpeningStops[message.Figi] = DateTime.UtcNow.AddMinutes(10); ClosingStops[stoppingKey] = DateTime.UtcNow.AddSeconds(30); await LogDeclision(DeclisionTradeAction.CloseLong, message, profit); @@ -143,7 +147,7 @@ namespace KLHZ.Trader.Core.Exchange.Services if (message.Time - asset.BoughtAt > TimeSpan.FromHours(4) && profit > 100 && !ClosingStops.ContainsKey(stoppingKey)) { - await _dataBus.Broadcast(new TradeCommand() + var command = new TradeCommand() { AccountId = asset.AccountId, Figi = message.Figi, @@ -152,7 +156,10 @@ namespace KLHZ.Trader.Core.Exchange.Services Count = (long)asset.Count, RecomendPrice = null, EnableMargin = false, - }); + }; + await _dataBus.Broadcast(command); + _logger.LogWarning("Сброс актива {figi}! id команды {commandId} Направление сделки: {dir}; Количество активов: {count}; Разрешена ли маржиналка: {margin}", + message.Figi, command.CommandId, command.CommandType, command.Count, command.EnableMargin); ClosingStops[stoppingKey] = DateTime.UtcNow.AddSeconds(30); await LogDeclision(DeclisionTradeAction.CloseLong, message, profit); await LogDeclision(DeclisionTradeAction.CloseLongReal, message, profit); @@ -211,14 +218,17 @@ namespace KLHZ.Trader.Core.Exchange.Services { if (RandomNumberGenerator.GetInt32(100) > 50) { - await _dataBus.Broadcast(new TradeCommand() + var command = new TradeCommand() { AccountId = acc.Value.AccountId, Figi = message.Figi, CommandType = Contracts.Messaging.Dtos.Enums.TradeCommandType.MarketBuy, Count = 1, RecomendPrice = null, - }); + }; + await _dataBus.Broadcast(command); + _logger.LogWarning("Покупка актива {figi}! id команды {commandId}. Направление сделки: {dir}; Количество активов: {count}; Разрешена ли маржиналка: {margin}", + message.Figi, command.CommandId, command.CommandType, command.Count, command.EnableMargin); if (loggedDeclisions == 0) { await LogDeclision(DeclisionTradeAction.OpenLongReal, message); @@ -261,7 +271,7 @@ namespace KLHZ.Trader.Core.Exchange.Services if (profit > 0 && !ClosingStops.ContainsKey(stoppingKey)) { ClosingStops[stoppingKey] = DateTime.UtcNow.AddSeconds(30); - await _dataBus.Broadcast(new TradeCommand() + var command = new TradeCommand() { AccountId = asset.AccountId, Figi = message.Figi, @@ -269,7 +279,10 @@ namespace KLHZ.Trader.Core.Exchange.Services Count = (long)asset.Count, RecomendPrice = null, EnableMargin = false, - }); + }; + await _dataBus.Broadcast(command); + _logger.LogWarning("Продажа актива {figi}! id команды {commandId}. Направление сделки: {dir}; Количество активов: {count}; Разрешена ли маржиналка: {margin}", + message.Figi, command.CommandId, command.CommandType, command.Count, command.EnableMargin); if (loggedDeclisions == 0) { loggedDeclisions++; diff --git a/KLHZ.Trader.Core/Exchange/Services/TradingCommandsExecutor.cs b/KLHZ.Trader.Core/Exchange/Services/TradingCommandsExecutor.cs index d5e872d..5fff356 100644 --- a/KLHZ.Trader.Core/Exchange/Services/TradingCommandsExecutor.cs +++ b/KLHZ.Trader.Core/Exchange/Services/TradingCommandsExecutor.cs @@ -3,8 +3,10 @@ using KLHZ.Trader.Core.Contracts.Messaging.Interfaces; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; using System.Threading.Channels; +using Telegram.Bot.Types; using Tinkoff.InvestApi; using Tinkoff.InvestApi.V1; +using static Microsoft.EntityFrameworkCore.DbLoggerCategory.Database; namespace KLHZ.Trader.Core.Exchange.Services { @@ -69,6 +71,9 @@ namespace KLHZ.Trader.Core.Exchange.Services ConfirmMarginTrade = tradeCommand.EnableMargin, }; + _logger.LogWarning("Получена команда c id {commandId} на операцию с активом {figi}! Тип заявки сделки: {dir}; Количество активов: {count}; Разрешена ли маржиналка: {margin}", + tradeCommand.CommandId, req.InstrumentId, req.OrderType, req.Quantity, req.ConfirmMarginTrade); + var res = await _investApiClient.Orders.PostOrderAsync(req); //var result = new DealResult diff --git a/KLHZ.Trader.Service/Controllers/PlayController.cs b/KLHZ.Trader.Service/Controllers/PlayController.cs index 3ce0309..2a649e2 100644 --- a/KLHZ.Trader.Service/Controllers/PlayController.cs +++ b/KLHZ.Trader.Service/Controllers/PlayController.cs @@ -4,8 +4,10 @@ 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 Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; +using System; namespace KLHZ.Trader.Service.Controllers { @@ -200,6 +202,49 @@ namespace KLHZ.Trader.Service.Controllers } + [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 => (double)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 GetBalance(string figi) //{