From d0ec2af4880fdc836f9d546436f334240742ba6c Mon Sep 17 00:00:00 2001 From: vlad zverzhkhovskiy Date: Mon, 15 Sep 2025 16:30:21 +0300 Subject: [PATCH] =?UTF-8?q?=D0=B8=D0=B7=D0=BC=D0=B5=D0=BD=D0=B5=D0=BD?= =?UTF-8?q?=D0=B0=20=D1=81=D0=B8=D0=BD=D1=85=D1=80=D0=BE=D0=BD=D0=B8=D0=B7?= =?UTF-8?q?=D0=B0=D1=86=D0=B8=D1=8F=20=D0=BF=D0=BE=D1=80=D1=82=D1=84=D0=B5?= =?UTF-8?q?=D0=BB=D1=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Declisions/Dtos/Harmonic.cs | 17 ++++ KLHZ.Trader.Core.Math/Declisions/Utils/FFT.cs | 89 +++++++++++++++---- KLHZ.Trader.Core.Tests/FFTTests.cs | 30 +++++-- .../Exchange/Services/TraderDataProvider.cs | 60 ++++++------- .../Controllers/PlayController.cs | 30 +++++++ 5 files changed, 167 insertions(+), 59 deletions(-) create mode 100644 KLHZ.Trader.Core.Math/Declisions/Dtos/Harmonic.cs diff --git a/KLHZ.Trader.Core.Math/Declisions/Dtos/Harmonic.cs b/KLHZ.Trader.Core.Math/Declisions/Dtos/Harmonic.cs new file mode 100644 index 0000000..e60f333 --- /dev/null +++ b/KLHZ.Trader.Core.Math/Declisions/Dtos/Harmonic.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace KLHZ.Trader.Core.Math.Declisions.Dtos +{ + public class Harmonic + { + public TimeSpan Period { get; set; } + public float Magnitude { get; set; } + public float Real { get; set; } + public float Imaginary { get; set; } + public float Phase { get; set; } + } +} diff --git a/KLHZ.Trader.Core.Math/Declisions/Utils/FFT.cs b/KLHZ.Trader.Core.Math/Declisions/Utils/FFT.cs index 3a415b0..30ece8c 100644 --- a/KLHZ.Trader.Core.Math/Declisions/Utils/FFT.cs +++ b/KLHZ.Trader.Core.Math/Declisions/Utils/FFT.cs @@ -1,4 +1,5 @@ -using MathNet.Numerics; +using KLHZ.Trader.Core.Math.Declisions.Dtos; +using MathNet.Numerics; using MathNet.Numerics.IntegralTransforms; namespace KLHZ.Trader.Core.Math.Declisions.Utils @@ -26,25 +27,81 @@ namespace KLHZ.Trader.Core.Math.Declisions.Utils } } - public static void А() + public static TimeSpan GetMainHarmonictPeriod(double[] data, TimeSpan period) { - 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(); - var arrv = da.Select(d => new Complex32(d, 0)).ToArray(); + var arrv = data.Select(d => new Complex32((float)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 res = new List(); + var times = new TimeSpan[arrv.Length]; + var mags = arrv.Select(a => a.Magnitude).ToArray(); + var phases = arrv.Select(a => a.Phase).ToArray(); + var max = 0f; + var kmax = 1; + var sum = 0f; + for (int i=0;i< arrv.Length / 2; i++) { - var d = res[i] - start[i]; + if (i == 0) + { + res.Add(new Harmonic() + { + Magnitude = arrv[i].Magnitude, + Period = TimeSpan.MaxValue, + Phase = arrv[i].Phase, + }); + } + else + { + times[i] = CaclHarmonycPeriod(period, data.Length, i); + res.Add(new Harmonic() + { + Magnitude = arrv[i].Magnitude, + Period = times[i], + Phase = arrv[i].Phase, + }); + var mag = arrv[i].Magnitude; + sum += mag; + if (mag > max) + { + max = mag; + kmax = i; + } + } } + return CaclHarmonycPeriod(period, data.Length, kmax); + } + + public static Harmonic[] GetHarmonics(double[] data, TimeSpan period, int count) + { + var arrv = data.Select(d => new Complex32((float)d, 0)).ToArray(); + Fourier.Forward(arrv); + var res = new List(); + + for (int i = 1; i < count; i++) + { + res.Add(new Harmonic() + { + Imaginary = arrv[i].Imaginary, + Real = arrv[i].Real, + Magnitude = arrv[i].Magnitude, + Period = CaclHarmonycPeriod(period, data.Length, i), + Phase = arrv[i].Phase, + }); + } + + return res.ToArray(); + } + + public static double Calc(Harmonic[] harmonics, DateTime startTime, DateTime endTime, DateTime currentTime) + { + var sum = 0d; + var timeSpan = currentTime - startTime; + for (int i = 0; i < harmonics.Length; i++) + { + var currentPhase = System.Math.PI * 2 * timeSpan.TotalSeconds * (endTime - startTime).TotalSeconds / harmonics[i].Period.TotalSeconds / harmonics[i].Period.TotalSeconds + harmonics[i].Phase; + var value = harmonics[i].Real * System.Math.Cos(currentPhase) + harmonics[i].Imaginary * System.Math.Sign(currentPhase); + sum += value; + } + return sum; } public static TimeSpan CaclHarmonycPeriod(TimeSpan signalLength, int signalLengthItems, int harmonyNumber) diff --git a/KLHZ.Trader.Core.Tests/FFTTests.cs b/KLHZ.Trader.Core.Tests/FFTTests.cs index 52c45db..d3f2a48 100644 --- a/KLHZ.Trader.Core.Tests/FFTTests.cs +++ b/KLHZ.Trader.Core.Tests/FFTTests.cs @@ -10,16 +10,28 @@ 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(); + var da2 = new List(); + var dates = new List(); + var dt = DateTime.UtcNow; + for (int i = 0; i < 1000; i++) + { + dt = dt.AddSeconds(1); + da.Add((float)System.Math.Sin(0.01 * i) + (float)System.Math.Cos(0.02 * i)); + dates.Add(dt); + } - //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 start = da.ToArray(); + var arrv = da.Select(d => new Complex32(d, 0)).ToArray(); + Fourier.Forward(arrv); + var harms = FFT.GetHarmonics(da.Select(d => (double)d).ToArray(), dates.Last() - dates.First(), 20); + + + foreach(var d in dates) + { + da2.Add((float)FFT.Calc(harms, dates.First(), dates.Last(), d)); + } + //var tem = arrv.Select(a => Complex32.Abs(a)).ToArray(); //var s = tem.Sum(); //Fourier.Inverse(arrv); //var res = arrv.Select(a => a.Real).ToArray(); diff --git a/KLHZ.Trader.Core/Exchange/Services/TraderDataProvider.cs b/KLHZ.Trader.Core/Exchange/Services/TraderDataProvider.cs index b261498..14d9a00 100644 --- a/KLHZ.Trader.Core/Exchange/Services/TraderDataProvider.cs +++ b/KLHZ.Trader.Core/Exchange/Services/TraderDataProvider.cs @@ -192,6 +192,7 @@ namespace KLHZ.Trader.Core.Exchange.Services } } + _ = SyncPortfolioWorker(); _ = WritePricesTask(); } catch (Exception ex) @@ -241,8 +242,12 @@ namespace KLHZ.Trader.Core.Exchange.Services if (trade != null) { + trade.Count = position.Quantity; + trade.Position = position.Quantity > 0 ? DataLayer.Entities.Trades.Enums.PositionType.Long : DataLayer.Entities.Trades.Enums.PositionType.Short; trades.Remove(trade); price = trade.Price; + context.Trades.Update(trade); + await context.SaveChangesAsync(); } else { @@ -351,40 +356,8 @@ namespace KLHZ.Trader.Core.Exchange.Services { trade.Price = (oldAmount + newAmount) / trade.Count; } - } - - if (Accounts.TryGetValue(dealResult.AccountId, out var account)) - { - if (account.Assets.TryGetValue(dealResult.Figi, out var asset)) - { - if (trade.Count == 0) - { - await context.Trades.Where(t => t.Id == trade.Id && t.ArchiveStatus == 0) - .ExecuteUpdateAsync(t => t.SetProperty(tr => tr.ArchiveStatus, 1)); - account.Assets.TryRemove(dealResult.Figi, out _); - return; - } - else - { - context.Trades.Update(trade); - await context.SaveChangesAsync(); - var newAsset = new Asset() - { - AccountId = asset.AccountId, - Figi = asset.Figi, - Ticker = asset.Ticker, - BlockedItems = asset.BlockedItems, - BoughtAt = DateTime.UtcNow, - BoughtPrice = trade.Price, - Count = sign * trade.Count, - Position = trade.Count > 0 ? PositionType.Long : PositionType.Short, - Type = asset.Type, - TradeId = asset.TradeId, - }; - account.Assets[dealResult.Figi] = newAsset; - return; - } - } + context.Trades.Update(trade); + await context.SaveChangesAsync(); } } } @@ -419,6 +392,25 @@ namespace KLHZ.Trader.Core.Exchange.Services } } + private async Task SyncPortfolioWorker() + { + while (true) + { + try + { + await Task.Delay(20000); + foreach (var acc in Accounts) + { + await SyncPortfolio(acc.Value); + } + } + catch (Exception ex) + { + _logger.LogError(ex, "Ошибка при цикличном обновлении портфеля"); + } + } + } + private async Task WritePricesTask() { var buffer1 = new List(); diff --git a/KLHZ.Trader.Service/Controllers/PlayController.cs b/KLHZ.Trader.Service/Controllers/PlayController.cs index 2a649e2..4414838 100644 --- a/KLHZ.Trader.Service/Controllers/PlayController.cs +++ b/KLHZ.Trader.Service/Controllers/PlayController.cs @@ -245,6 +245,36 @@ namespace KLHZ.Trader.Service.Controllers } } + + [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 => (double)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) //{