From cdc179b3a526adaf101dc514702d62f4c9bf481b Mon Sep 17 00:00:00 2001 From: vlad zverzhkhovskiy Date: Thu, 25 Sep 2025 10:03:04 +0300 Subject: [PATCH] =?UTF-8?q?=D1=84=D0=B8=D0=BA=D1=81=D0=B0=D1=86=D0=B8?= =?UTF-8?q?=D1=8F=20=D1=8D=D0=BA=D1=81=D0=BF=D0=B5=D1=80=D0=B8=D0=BC=D0=B5?= =?UTF-8?q?=D0=BD=D1=82=D0=BE=D0=B2=20=D1=81=20FFT?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Dtos/FFT/Enums/ValueAmplitudePosition.cs | 6 +- .../Declisions/Dtos/FFT/FFTAnalyzeResult.cs | 2 +- KLHZ.Trader.Core.Math/Declisions/Utils/FFT.cs | 100 +++++++++++++++++- KLHZ.Trader.Core/Exchange/Services/Trader.cs | 80 +++++++++++--- .../Controllers/PlayController.cs | 1 + 5 files changed, 168 insertions(+), 21 deletions(-) diff --git a/KLHZ.Trader.Core.Math/Declisions/Dtos/FFT/Enums/ValueAmplitudePosition.cs b/KLHZ.Trader.Core.Math/Declisions/Dtos/FFT/Enums/ValueAmplitudePosition.cs index d5ce25b..bb2ad13 100644 --- a/KLHZ.Trader.Core.Math/Declisions/Dtos/FFT/Enums/ValueAmplitudePosition.cs +++ b/KLHZ.Trader.Core.Math/Declisions/Dtos/FFT/Enums/ValueAmplitudePosition.cs @@ -5,6 +5,10 @@ None = 0, Middle = 1, UpperThen30Decil = 10, - LowerThenMediana = -50, + LowerThen30Decil = -0, + LowerThenMedianaGrowing = -50, + LowerThenMedianaFalling = -51, + Growing = -512, + Falling = -513, } } diff --git a/KLHZ.Trader.Core.Math/Declisions/Dtos/FFT/FFTAnalyzeResult.cs b/KLHZ.Trader.Core.Math/Declisions/Dtos/FFT/FFTAnalyzeResult.cs index 9ff6bcc..96c656a 100644 --- a/KLHZ.Trader.Core.Math/Declisions/Dtos/FFT/FFTAnalyzeResult.cs +++ b/KLHZ.Trader.Core.Math/Declisions/Dtos/FFT/FFTAnalyzeResult.cs @@ -8,7 +8,7 @@ public decimal Min { get; init; } public decimal Mediana { get; init; } public decimal Upper30Decil { get; init; } - public decimal Lower20Decil { get; init; } + public decimal Lower30Decil { get; init; } public int Length { get; init; } public DateTime LastTime { get; init; } public DateTime StartTime { get; init; } diff --git a/KLHZ.Trader.Core.Math/Declisions/Utils/FFT.cs b/KLHZ.Trader.Core.Math/Declisions/Utils/FFT.cs index 496d16d..1301c70 100644 --- a/KLHZ.Trader.Core.Math/Declisions/Utils/FFT.cs +++ b/KLHZ.Trader.Core.Math/Declisions/Utils/FFT.cs @@ -2,7 +2,6 @@ using KLHZ.Trader.Core.Math.Declisions.Dtos.FFT.Enums; using MathNet.Numerics; using MathNet.Numerics.IntegralTransforms; -using static System.Runtime.InteropServices.JavaScript.JSType; namespace KLHZ.Trader.Core.Math.Declisions.Utils { @@ -18,7 +17,47 @@ namespace KLHZ.Trader.Core.Math.Declisions.Utils } else if (value < fftData.Mediana && System.Math.Sign(value2) >= 0) { - return ValueAmplitudePosition.LowerThenMediana; + return ValueAmplitudePosition.LowerThenMedianaGrowing; + } + else if (value < fftData.Mediana && System.Math.Sign(value2) < 0) + { + return ValueAmplitudePosition.LowerThenMedianaFalling; + } + else + { + return ValueAmplitudePosition.Middle; + } + } + + public static ValueAmplitudePosition CheckExtremums(FFTAnalyzeResult fftData, DateTime timestamp) + { + var value = (decimal)CalcAmplitude(fftData.Harmonics, fftData.StartTime, timestamp); + var value2 = (decimal)CalcExtremum(fftData.Harmonics, fftData.StartTime, timestamp); + if (value > fftData.Upper30Decil && System.Math.Sign(value2) <= 0) + { + return ValueAmplitudePosition.UpperThen30Decil; + } + else if (value < fftData.Lower30Decil && System.Math.Sign(value2) >= 0) + { + return ValueAmplitudePosition.LowerThen30Decil; + } + else + { + return ValueAmplitudePosition.Middle; + } + } + + public static ValueAmplitudePosition CheckSign(FFTAnalyzeResult fftData, DateTime timestamp) + { + var value = (decimal)CalcAmplitude(fftData.Harmonics, fftData.StartTime, timestamp); + var value2 = (decimal)CalcExtremum(fftData.Harmonics, fftData.StartTime, timestamp); + if (System.Math.Sign(value2) <= 0) + { + return ValueAmplitudePosition.Falling; + } + else if (System.Math.Sign(value2) >= 0) + { + return ValueAmplitudePosition.Growing; } else { @@ -49,7 +88,7 @@ namespace KLHZ.Trader.Core.Math.Declisions.Utils StartTime = startTime, Mediana = newValues[newValues.Length / 2], Upper30Decil = newValues[(int)(newValues.Length * 0.7)], - Lower20Decil = newValues[(int)(newValues.Length * 0.2)], + Lower30Decil = newValues[(int)(newValues.Length * 0.3)], Max = newValues.Max(), Min = newValues.Min(), Length = values.Length, @@ -91,7 +130,60 @@ namespace KLHZ.Trader.Core.Math.Declisions.Utils StartTime = result.StartTime, Mediana = newValues[newValues.Length / 2], Upper30Decil = newValues[(int)(newValues.Length * 0.7)], - Lower20Decil = newValues[(int)(newValues.Length * 0.2)], + Lower30Decil = newValues[(int)(newValues.Length * 0.3)], + Max = newValues.Max(), + Min = newValues.Min(), + }; + } + + public static FFTAnalyzeResult ReAnalyze(FFTAnalyzeResult result, string key, float energyPart, bool include) + { + var tmp = new List(); + var symmEnergy = result.Harmonics.Sum(h => h.Magnitude); + var tmpSumEnergy = 0f; + for (int i = 0; i < result.Harmonics.Length; i++) + { + tmpSumEnergy += result.Harmonics[i].Magnitude; + if (include) + { + if (tmpSumEnergy/ symmEnergy < energyPart) + { + tmp.Add(result.Harmonics[i]); + } + } + else + { + if (tmpSumEnergy / symmEnergy >= energyPart) + { + tmp.Add(result.Harmonics[i]); + } + } + } + + var harms = tmp.ToArray(); + var newValues = new decimal[result.Length]; + var newValues2 = new decimal[result.Length]; + var time = result.StartTime; + var dt = (result.LastTime - result.StartTime).TotalSeconds / result.Length; + for (int i = 0; i < result.Length; i++) + { + var currentTime = time.AddSeconds(i * dt); + newValues[i] = (decimal)CalcAmplitude(harms, result.StartTime, currentTime); + newValues2[i] = (decimal)CalcExtremum(harms, result.StartTime, currentTime); + } + + newValues = newValues.Order().ToArray(); + var ma = newValues2.Max(); + var mi = newValues2.Min(); + return new FFTAnalyzeResult() + { + Key = key, + Harmonics = harms, + LastTime = result.LastTime, + StartTime = result.StartTime, + Mediana = newValues[newValues.Length / 2], + Upper30Decil = newValues[(int)(newValues.Length * 0.7)], + Lower30Decil = newValues[(int)(newValues.Length * 0.3)], Max = newValues.Max(), Min = newValues.Min(), }; diff --git a/KLHZ.Trader.Core/Exchange/Services/Trader.cs b/KLHZ.Trader.Core/Exchange/Services/Trader.cs index f4b9955..c123785 100644 --- a/KLHZ.Trader.Core/Exchange/Services/Trader.cs +++ b/KLHZ.Trader.Core/Exchange/Services/Trader.cs @@ -92,9 +92,9 @@ namespace KLHZ.Trader.Core.Exchange.Services return Task.CompletedTask; } - public async ValueTask<(DateTime[] timestamps, decimal[] prices, bool isFullIntervalExists)> GetData(INewPrice message) + public async ValueTask<(DateTime[] timestamps, decimal[] prices, bool isFullIntervalExists)> GetData(INewPrice message, TimeSpan? windowSize = null) { - var data2 = await _tradeDataProvider.GetData(message.Figi, TimeSpan.FromHours(1.5)); + var data2 = await _tradeDataProvider.GetData(message.Figi, windowSize??TimeSpan.FromHours(1.5)); if (!data2.isFullIntervalExists) { data2 = await _tradeDataProvider.GetData(message.Figi, TimeSpan.FromHours(1)); @@ -111,30 +111,80 @@ namespace KLHZ.Trader.Core.Exchange.Services var currentTime = message.IsHistoricalData ? message.Time : DateTime.UtcNow; var position = ValueAmplitudePosition.None; var fft = await _tradeDataProvider.GetFFtResult(message.Figi); + var fftFull = await _tradeDataProvider.GetFFtResult(message.Figi + "_full"); + //var highFreq = await _tradeDataProvider.GetFFtResult(message.Figi + "_high_freq"); + //var lowFreq = await _tradeDataProvider.GetFFtResult(message.Figi + "_low_freq"); var step = message.IsHistoricalData ? 5 : 5; if (fft.IsEmpty || (currentTime - fft.LastTime).TotalSeconds > step) { if (data.isFullIntervalExists) { var interpolatedData = SignalProcessing.InterpolateData(data.timestamps, data.prices, TimeSpan.FromSeconds(5)); - var fftFull = FFT.Analyze(interpolatedData.timestamps, interpolatedData.values, message.Figi+"_full", TimeSpan.FromSeconds(15), TimeSpan.FromHours(24)); + fftFull = FFT.Analyze(interpolatedData.timestamps, interpolatedData.values, message.Figi+"_full", TimeSpan.FromSeconds(120), TimeSpan.FromHours(24)); fft = FFT.ReAnalyze(fftFull, message.Figi, TimeSpan.FromMinutes(3), TimeSpan.FromMinutes(40)); + //highFreq = FFT.ReAnalyze(fftFull, message.Figi + "_low_freq", TimeSpan.FromMinutes(20), TimeSpan.FromMinutes(60)); + //lowFreq = FFT.ReAnalyze(fftFull, message.Figi + "_high_freq", TimeSpan.FromMinutes(3), TimeSpan.FromMinutes(20)); + await _tradeDataProvider.SetFFtResult(fft); - await _tradeDataProvider.SetFFtResult(fftFull); + //await _tradeDataProvider.SetFFtResult(fftFull); + //await _tradeDataProvider.SetFFtResult(lowFreq); + //await _tradeDataProvider.SetFFtResult(highFreq); + } + + //var highFreqData = await GetData(message, TimeSpan.FromMinutes(120)); + + //if (highFreqData.isFullIntervalExists) + //{ + // var interpolatehighFreqData = SignalProcessing.InterpolateData(data.timestamps, data.prices, TimeSpan.FromSeconds(5)); + // highFreq = FFT.Analyze(interpolatehighFreqData.timestamps, interpolatehighFreqData.values, message.Figi + "_high_freq", TimeSpan.FromSeconds(20), TimeSpan.FromMinutes(120)); + // await _tradeDataProvider.SetFFtResult(highFreq); + //} } - else + + + + position = FFT.Check(fft, message.Time); + if (position == Math.Declisions.Dtos.FFT.Enums.ValueAmplitudePosition.UpperThen30Decil) { - position = FFT.Check(fft, message.Time); - if (position == Math.Declisions.Dtos.FFT.Enums.ValueAmplitudePosition.UpperThen30Decil) - { - await LogPrice(message, "upper30percent", message.Value); - } - if (position == Math.Declisions.Dtos.FFT.Enums.ValueAmplitudePosition.LowerThenMediana) - { - await LogPrice(message, "lower30percent", message.Value); - } + await LogPrice(message, "upper30percent", message.Value); } + if (position == Math.Declisions.Dtos.FFT.Enums.ValueAmplitudePosition.LowerThenMedianaGrowing) + { + await LogPrice(message, "lower30percent", message.Value); + } + + //var hposition = FFT.CheckExtremums(highFreq, message.Time); + //if (hposition == Math.Declisions.Dtos.FFT.Enums.ValueAmplitudePosition.UpperThen30Decil) + //{ + // await LogPrice(message, "high_freq_high", message.Value); + //} + //if (hposition == Math.Declisions.Dtos.FFT.Enums.ValueAmplitudePosition.LowerThen30Decil) + //{ + // await LogPrice(message, "high_freq_low", message.Value); + //} + + + //var gposition = FFT.CheckSign(highFreq, message.Time); + //if (gposition == Math.Declisions.Dtos.FFT.Enums.ValueAmplitudePosition.Growing) + //{ + // await LogPrice(message, "growing", message.Value); + //} + //if (gposition == Math.Declisions.Dtos.FFT.Enums.ValueAmplitudePosition.Falling) + //{ + // await LogPrice(message, "falling", message.Value); + //} + + + //var lposition = FFT.CheckExtremums(lowFreq, message.Time); + //if (lposition == Math.Declisions.Dtos.FFT.Enums.ValueAmplitudePosition.UpperThen30Decil) + //{ + // await LogPrice(message, "low_freq_high", message.Value); + //} + //if (lposition == Math.Declisions.Dtos.FFT.Enums.ValueAmplitudePosition.LowerThen30Decil) + //{ + // await LogPrice(message, "low_freq_low", message.Value); + //} return position; } @@ -821,7 +871,7 @@ INewPrice message, int windowMaxSize, decimal uptrendStartingDetectionMeanfullSt var res = GetInitDict(1); var position = await CheckHarmonicPosition(message); - if (position == ValueAmplitudePosition.LowerThenMediana) + if (position == ValueAmplitudePosition.LowerThenMedianaGrowing) { //res[TradingEvent.UptrendStart] = Constants.UppingCoefficient; res[TradingEvent.DowntrendEnd] = Constants.UppingCoefficient; diff --git a/KLHZ.Trader.Service/Controllers/PlayController.cs b/KLHZ.Trader.Service/Controllers/PlayController.cs index 209de79..acf2fc9 100644 --- a/KLHZ.Trader.Service/Controllers/PlayController.cs +++ b/KLHZ.Trader.Service/Controllers/PlayController.cs @@ -54,6 +54,7 @@ namespace KLHZ.Trader.Service.Controllers }) .ToArrayAsync(); + foreach (var mess in data) { await _dataBus.Broadcast(mess);