From d634cfac7382e86f46a75eecc102b54dc98cacec Mon Sep 17 00:00:00 2001 From: vlad zverzhkhovskiy Date: Tue, 2 Sep 2025 12:17:19 +0300 Subject: [PATCH] =?UTF-8?q?=D1=83=D0=B4=D0=B0=D0=BB=D0=B8=D0=BB=20=D0=BA?= =?UTF-8?q?=D0=B0=D0=BB=D0=BC=D0=B0=D0=BD=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Declisions/Dtos/Enums/TradingEvent.cs | 13 + .../Declisions/Dtos/TwoPeriodsResultDto.cs | 6 +- .../Interfaces/IPriceHistoryCacheUnit.cs | 2 +- .../Interfaces/ITradingEventsDetector.cs | 4 +- .../{Intarfaces => Interfaces}/IMessage.cs | 2 +- .../{Intarfaces => Interfaces}/INewCandle.cs | 2 +- .../{Intarfaces => Interfaces}/INewPrice.cs | 2 +- .../IProcessedPrice.cs | 2 +- .../Messaging/Dtos/MessageForAdmin.cs | 2 +- .../Messaging/Dtos/NewPriceMessage.cs | 2 +- .../Messaging/Interfaces/IDataBus.cs | 2 +- KLHZ.Trader.Core.Math/AssemblySettings.cs | 4 + .../{Utils => Common}/Lines.cs | 4 +- .../Declisions/Dtos/PeriodPricesInfoDto.cs | 2 +- .../Declisions/Dtos/ProcessedPrice.cs | 2 +- .../{ => Cache}/PriceHistoryCacheUnit.cs | 4 +- .../{ => Cache}/PriceHistoryCacheUnit2.cs | 13 +- .../IntervalsTradingEventsDetector.cs | 14 + .../Declisions/Services/KalmanPredictor.cs | 319 ------------------ .../Declisions/Utils/MovingAverage.cs | 76 +++++ .../Declisions/Utils/TwoPeriods.cs | 76 ++++- .../KLHZ.Trader.Core.Math.csproj | 2 - .../HistoryCacheUnit2Tests.cs | 4 +- .../HistoryCacheUnitTests.cs | 2 +- .../HistoryProcessingInstrumentsTests.cs | 18 +- KLHZ.Trader.Core.Tests/LinesProcessingTest.cs | 6 +- .../Common/Messaging/Services/DataBus.cs | 2 +- .../Services/ProcessedPricesLogger.cs | 6 +- .../DataLayer/Entities/Prices/Candle.cs | 2 +- .../DataLayer/Entities/Prices/PriceChange.cs | 2 +- .../Entities/Prices/ProcessedPrice.cs | 2 +- .../Services/TradingEventsDetector.cs | 41 --- KLHZ.Trader.Core/Exchange/ExchangeConfig.cs | 3 +- .../Exchange/Services/ExchangeDataReader.cs | 2 +- .../Services/Trader.cs | 15 +- KLHZ.Trader.Core/TG/Services/BotStarter.cs | 2 +- .../Controllers/ExchangeDataController.cs | 66 ++++ KLHZ.Trader.Service/Program.cs | 4 +- KLHZ.Trader.Service/appsettings.json | 3 +- 39 files changed, 297 insertions(+), 438 deletions(-) create mode 100644 KLHZ.Trader.Core.Contracts/Declisions/Dtos/Enums/TradingEvent.cs rename KLHZ.Trader.Core/Declisions/Dtos/TwoPeriodsProcessingDto.cs => KLHZ.Trader.Core.Contracts/Declisions/Dtos/TwoPeriodsResultDto.cs (72%) rename KLHZ.Trader.Core.Contracts/Messaging/Dtos/{Intarfaces => Interfaces}/IMessage.cs (55%) rename KLHZ.Trader.Core.Contracts/Messaging/Dtos/{Intarfaces => Interfaces}/INewCandle.cs (84%) rename KLHZ.Trader.Core.Contracts/Messaging/Dtos/{Intarfaces => Interfaces}/INewPrice.cs (78%) rename KLHZ.Trader.Core.Contracts/Messaging/Dtos/{Intarfaces => Interfaces}/IProcessedPrice.cs (61%) create mode 100644 KLHZ.Trader.Core.Math/AssemblySettings.cs rename KLHZ.Trader.Core.Math/{Utils => Common}/Lines.cs (88%) rename {KLHZ.Trader.Core => KLHZ.Trader.Core.Math}/Declisions/Dtos/PeriodPricesInfoDto.cs (91%) rename KLHZ.Trader.Core.Math/Declisions/Services/{ => Cache}/PriceHistoryCacheUnit.cs (92%) rename KLHZ.Trader.Core.Math/Declisions/Services/{ => Cache}/PriceHistoryCacheUnit2.cs (83%) create mode 100644 KLHZ.Trader.Core.Math/Declisions/Services/EventsDetection/IntervalsTradingEventsDetector.cs delete mode 100644 KLHZ.Trader.Core.Math/Declisions/Services/KalmanPredictor.cs create mode 100644 KLHZ.Trader.Core.Math/Declisions/Utils/MovingAverage.cs rename KLHZ.Trader.Core/Declisions/Utils/HistoryProcessingInstruments.cs => KLHZ.Trader.Core.Math/Declisions/Utils/TwoPeriods.cs (74%) rename KLHZ.Trader.Core/{Declisions => Common/Messaging}/Services/ProcessedPricesLogger.cs (88%) delete mode 100644 KLHZ.Trader.Core/Declisions/Services/TradingEventsDetector.cs rename KLHZ.Trader.Core/{Declisions => Exchange}/Services/Trader.cs (92%) create mode 100644 KLHZ.Trader.Service/Controllers/ExchangeDataController.cs diff --git a/KLHZ.Trader.Core.Contracts/Declisions/Dtos/Enums/TradingEvent.cs b/KLHZ.Trader.Core.Contracts/Declisions/Dtos/Enums/TradingEvent.cs new file mode 100644 index 0000000..3ebc7a7 --- /dev/null +++ b/KLHZ.Trader.Core.Contracts/Declisions/Dtos/Enums/TradingEvent.cs @@ -0,0 +1,13 @@ +namespace KLHZ.Trader.Core.Contracts.Declisions.Dtos.Enums +{ + [Flags] + public enum TradingEvent + { + None = 0, + StopBuy = 1, + LongOpen = 2, + ShortClose = 4, + LongClose = 8, + ShortOpen = 16, + } +} diff --git a/KLHZ.Trader.Core/Declisions/Dtos/TwoPeriodsProcessingDto.cs b/KLHZ.Trader.Core.Contracts/Declisions/Dtos/TwoPeriodsResultDto.cs similarity index 72% rename from KLHZ.Trader.Core/Declisions/Dtos/TwoPeriodsProcessingDto.cs rename to KLHZ.Trader.Core.Contracts/Declisions/Dtos/TwoPeriodsResultDto.cs index 0adbcac..83c07a1 100644 --- a/KLHZ.Trader.Core/Declisions/Dtos/TwoPeriodsProcessingDto.cs +++ b/KLHZ.Trader.Core.Contracts/Declisions/Dtos/TwoPeriodsResultDto.cs @@ -1,6 +1,6 @@ -namespace KLHZ.Trader.Core.Declisions.Dtos +namespace KLHZ.Trader.Core.Contracts.Declisions.Dtos { - internal readonly struct TwoPeriodsProcessingDto + public readonly struct TwoPeriodsResultDto { public readonly int Start; public readonly int Bound; @@ -11,7 +11,7 @@ public readonly TimeSpan PeriodStart; public readonly TimeSpan PeriodEnd; - public TwoPeriodsProcessingDto(bool success, float diffStart, float diffEnd, int start, int bound, int end, + public TwoPeriodsResultDto(bool success, float diffStart, float diffEnd, int start, int bound, int end, TimeSpan periodStart, TimeSpan periodEnd) { Success = success; diff --git a/KLHZ.Trader.Core.Contracts/Declisions/Interfaces/IPriceHistoryCacheUnit.cs b/KLHZ.Trader.Core.Contracts/Declisions/Interfaces/IPriceHistoryCacheUnit.cs index a457116..b3a40a7 100644 --- a/KLHZ.Trader.Core.Contracts/Declisions/Interfaces/IPriceHistoryCacheUnit.cs +++ b/KLHZ.Trader.Core.Contracts/Declisions/Interfaces/IPriceHistoryCacheUnit.cs @@ -1,4 +1,4 @@ -using KLHZ.Trader.Core.Contracts.Messaging.Dtos.Intarfaces; +using KLHZ.Trader.Core.Contracts.Messaging.Dtos.Interfaces; namespace KLHZ.Trader.Core.Contracts.Declisions.Interfaces { diff --git a/KLHZ.Trader.Core.Contracts/Declisions/Interfaces/ITradingEventsDetector.cs b/KLHZ.Trader.Core.Contracts/Declisions/Interfaces/ITradingEventsDetector.cs index cd624de..47b1fd1 100644 --- a/KLHZ.Trader.Core.Contracts/Declisions/Interfaces/ITradingEventsDetector.cs +++ b/KLHZ.Trader.Core.Contracts/Declisions/Interfaces/ITradingEventsDetector.cs @@ -1,9 +1,9 @@ -using KLHZ.Trader.Core.Contracts.Declisions.Dtos; +using KLHZ.Trader.Core.Contracts.Declisions.Dtos.Enums; namespace KLHZ.Trader.Core.Contracts.Declisions.Interfaces { public interface ITradingEventsDetector { - public ValueTask Detect(IPriceHistoryCacheUnit unit); + public ValueTask Detect(IPriceHistoryCacheUnit unit); } } diff --git a/KLHZ.Trader.Core.Contracts/Messaging/Dtos/Intarfaces/IMessage.cs b/KLHZ.Trader.Core.Contracts/Messaging/Dtos/Interfaces/IMessage.cs similarity index 55% rename from KLHZ.Trader.Core.Contracts/Messaging/Dtos/Intarfaces/IMessage.cs rename to KLHZ.Trader.Core.Contracts/Messaging/Dtos/Interfaces/IMessage.cs index 1951ad7..06c81eb 100644 --- a/KLHZ.Trader.Core.Contracts/Messaging/Dtos/Intarfaces/IMessage.cs +++ b/KLHZ.Trader.Core.Contracts/Messaging/Dtos/Interfaces/IMessage.cs @@ -1,4 +1,4 @@ -namespace KLHZ.Trader.Core.Contracts.Messaging.Dtos.Intarfaces +namespace KLHZ.Trader.Core.Contracts.Messaging.Dtos.Interfaces { public interface IMessage { diff --git a/KLHZ.Trader.Core.Contracts/Messaging/Dtos/Intarfaces/INewCandle.cs b/KLHZ.Trader.Core.Contracts/Messaging/Dtos/Interfaces/INewCandle.cs similarity index 84% rename from KLHZ.Trader.Core.Contracts/Messaging/Dtos/Intarfaces/INewCandle.cs rename to KLHZ.Trader.Core.Contracts/Messaging/Dtos/Interfaces/INewCandle.cs index a54330e..4b92142 100644 --- a/KLHZ.Trader.Core.Contracts/Messaging/Dtos/Intarfaces/INewCandle.cs +++ b/KLHZ.Trader.Core.Contracts/Messaging/Dtos/Interfaces/INewCandle.cs @@ -1,4 +1,4 @@ -namespace KLHZ.Trader.Core.Contracts.Messaging.Dtos.Intarfaces +namespace KLHZ.Trader.Core.Contracts.Messaging.Dtos.Interfaces { public interface INewCandle { diff --git a/KLHZ.Trader.Core.Contracts/Messaging/Dtos/Intarfaces/INewPrice.cs b/KLHZ.Trader.Core.Contracts/Messaging/Dtos/Interfaces/INewPrice.cs similarity index 78% rename from KLHZ.Trader.Core.Contracts/Messaging/Dtos/Intarfaces/INewPrice.cs rename to KLHZ.Trader.Core.Contracts/Messaging/Dtos/Interfaces/INewPrice.cs index 80dc8f9..6528736 100644 --- a/KLHZ.Trader.Core.Contracts/Messaging/Dtos/Intarfaces/INewPrice.cs +++ b/KLHZ.Trader.Core.Contracts/Messaging/Dtos/Interfaces/INewPrice.cs @@ -1,4 +1,4 @@ -namespace KLHZ.Trader.Core.Contracts.Messaging.Dtos.Intarfaces +namespace KLHZ.Trader.Core.Contracts.Messaging.Dtos.Interfaces { public interface INewPrice { diff --git a/KLHZ.Trader.Core.Contracts/Messaging/Dtos/Intarfaces/IProcessedPrice.cs b/KLHZ.Trader.Core.Contracts/Messaging/Dtos/Interfaces/IProcessedPrice.cs similarity index 61% rename from KLHZ.Trader.Core.Contracts/Messaging/Dtos/Intarfaces/IProcessedPrice.cs rename to KLHZ.Trader.Core.Contracts/Messaging/Dtos/Interfaces/IProcessedPrice.cs index 02c1c6b..fb943d8 100644 --- a/KLHZ.Trader.Core.Contracts/Messaging/Dtos/Intarfaces/IProcessedPrice.cs +++ b/KLHZ.Trader.Core.Contracts/Messaging/Dtos/Interfaces/IProcessedPrice.cs @@ -1,4 +1,4 @@ -namespace KLHZ.Trader.Core.Contracts.Messaging.Dtos.Intarfaces +namespace KLHZ.Trader.Core.Contracts.Messaging.Dtos.Interfaces { public interface IProcessedPrice : INewPrice { diff --git a/KLHZ.Trader.Core.Contracts/Messaging/Dtos/MessageForAdmin.cs b/KLHZ.Trader.Core.Contracts/Messaging/Dtos/MessageForAdmin.cs index 548bc07..51264ec 100644 --- a/KLHZ.Trader.Core.Contracts/Messaging/Dtos/MessageForAdmin.cs +++ b/KLHZ.Trader.Core.Contracts/Messaging/Dtos/MessageForAdmin.cs @@ -1,4 +1,4 @@ -using KLHZ.Trader.Core.Contracts.Messaging.Dtos.Intarfaces; +using KLHZ.Trader.Core.Contracts.Messaging.Dtos.Interfaces; namespace KLHZ.Trader.Core.Contracts.Messaging.Dtos { diff --git a/KLHZ.Trader.Core.Contracts/Messaging/Dtos/NewPriceMessage.cs b/KLHZ.Trader.Core.Contracts/Messaging/Dtos/NewPriceMessage.cs index fddd16d..fe87cdd 100644 --- a/KLHZ.Trader.Core.Contracts/Messaging/Dtos/NewPriceMessage.cs +++ b/KLHZ.Trader.Core.Contracts/Messaging/Dtos/NewPriceMessage.cs @@ -1,4 +1,4 @@ -using KLHZ.Trader.Core.Contracts.Messaging.Dtos.Intarfaces; +using KLHZ.Trader.Core.Contracts.Messaging.Dtos.Interfaces; namespace KLHZ.Trader.Core.Contracts.Messaging.Dtos { diff --git a/KLHZ.Trader.Core.Contracts/Messaging/Interfaces/IDataBus.cs b/KLHZ.Trader.Core.Contracts/Messaging/Interfaces/IDataBus.cs index f538319..825192d 100644 --- a/KLHZ.Trader.Core.Contracts/Messaging/Interfaces/IDataBus.cs +++ b/KLHZ.Trader.Core.Contracts/Messaging/Interfaces/IDataBus.cs @@ -1,5 +1,5 @@ using KLHZ.Trader.Core.Contracts.Messaging.Dtos; -using KLHZ.Trader.Core.Contracts.Messaging.Dtos.Intarfaces; +using KLHZ.Trader.Core.Contracts.Messaging.Dtos.Interfaces; using System.Threading.Channels; namespace KLHZ.Trader.Core.Contracts.Messaging.Interfaces diff --git a/KLHZ.Trader.Core.Math/AssemblySettings.cs b/KLHZ.Trader.Core.Math/AssemblySettings.cs new file mode 100644 index 0000000..8e82cac --- /dev/null +++ b/KLHZ.Trader.Core.Math/AssemblySettings.cs @@ -0,0 +1,4 @@ +using System.Runtime.CompilerServices; + +//Тесты +[assembly: InternalsVisibleTo("KLHZ.Trader.Core.Tests")] \ No newline at end of file diff --git a/KLHZ.Trader.Core.Math/Utils/Lines.cs b/KLHZ.Trader.Core.Math/Common/Lines.cs similarity index 88% rename from KLHZ.Trader.Core.Math/Utils/Lines.cs rename to KLHZ.Trader.Core.Math/Common/Lines.cs index 0395a64..5736969 100644 --- a/KLHZ.Trader.Core.Math/Utils/Lines.cs +++ b/KLHZ.Trader.Core.Math/Common/Lines.cs @@ -1,4 +1,4 @@ -namespace KLHZ.Trader.Core.Math.Utils +namespace KLHZ.Trader.Core.Math.Common { public static class Lines { @@ -14,7 +14,7 @@ { var dtime = (float)(time2 - time1).TotalSeconds; - var dval1 = (val1_2 - val1_1); + var dval1 = val1_2 - val1_1; var k1 = dval1 / dtime; var b1 = val1_1; diff --git a/KLHZ.Trader.Core/Declisions/Dtos/PeriodPricesInfoDto.cs b/KLHZ.Trader.Core.Math/Declisions/Dtos/PeriodPricesInfoDto.cs similarity index 91% rename from KLHZ.Trader.Core/Declisions/Dtos/PeriodPricesInfoDto.cs rename to KLHZ.Trader.Core.Math/Declisions/Dtos/PeriodPricesInfoDto.cs index 01a0e88..399d83b 100644 --- a/KLHZ.Trader.Core/Declisions/Dtos/PeriodPricesInfoDto.cs +++ b/KLHZ.Trader.Core.Math/Declisions/Dtos/PeriodPricesInfoDto.cs @@ -1,4 +1,4 @@ -namespace KLHZ.Trader.Core.Declisions.Dtos +namespace KLHZ.Trader.Core.Math.Declisions.Dtos { internal readonly struct PeriodPricesInfoDto { diff --git a/KLHZ.Trader.Core.Math/Declisions/Dtos/ProcessedPrice.cs b/KLHZ.Trader.Core.Math/Declisions/Dtos/ProcessedPrice.cs index f291d1d..a8d0d78 100644 --- a/KLHZ.Trader.Core.Math/Declisions/Dtos/ProcessedPrice.cs +++ b/KLHZ.Trader.Core.Math/Declisions/Dtos/ProcessedPrice.cs @@ -1,4 +1,4 @@ -using KLHZ.Trader.Core.Contracts.Messaging.Dtos.Intarfaces; +using KLHZ.Trader.Core.Contracts.Messaging.Dtos.Interfaces; namespace KLHZ.Trader.Core.Math.Declisions.Dtos { diff --git a/KLHZ.Trader.Core.Math/Declisions/Services/PriceHistoryCacheUnit.cs b/KLHZ.Trader.Core.Math/Declisions/Services/Cache/PriceHistoryCacheUnit.cs similarity index 92% rename from KLHZ.Trader.Core.Math/Declisions/Services/PriceHistoryCacheUnit.cs rename to KLHZ.Trader.Core.Math/Declisions/Services/Cache/PriceHistoryCacheUnit.cs index cd7dd69..05ee3a6 100644 --- a/KLHZ.Trader.Core.Math/Declisions/Services/PriceHistoryCacheUnit.cs +++ b/KLHZ.Trader.Core.Math/Declisions/Services/Cache/PriceHistoryCacheUnit.cs @@ -1,7 +1,7 @@ using KLHZ.Trader.Core.Contracts.Declisions.Interfaces; -using KLHZ.Trader.Core.Contracts.Messaging.Dtos.Intarfaces; +using KLHZ.Trader.Core.Contracts.Messaging.Dtos.Interfaces; -namespace KLHZ.Trader.Core.Math.Declisions.Services +namespace KLHZ.Trader.Core.Math.Declisions.Services.Cache { public class PriceHistoryCacheUnit : IPriceHistoryCacheUnit { diff --git a/KLHZ.Trader.Core.Math/Declisions/Services/PriceHistoryCacheUnit2.cs b/KLHZ.Trader.Core.Math/Declisions/Services/Cache/PriceHistoryCacheUnit2.cs similarity index 83% rename from KLHZ.Trader.Core.Math/Declisions/Services/PriceHistoryCacheUnit2.cs rename to KLHZ.Trader.Core.Math/Declisions/Services/Cache/PriceHistoryCacheUnit2.cs index be6251b..b2479cd 100644 --- a/KLHZ.Trader.Core.Math/Declisions/Services/PriceHistoryCacheUnit2.cs +++ b/KLHZ.Trader.Core.Math/Declisions/Services/Cache/PriceHistoryCacheUnit2.cs @@ -1,8 +1,7 @@ using KLHZ.Trader.Core.Contracts.Declisions.Interfaces; -using KLHZ.Trader.Core.Contracts.Messaging.Dtos.Intarfaces; -using System.Reflection; +using KLHZ.Trader.Core.Contracts.Messaging.Dtos.Interfaces; -namespace KLHZ.Trader.Core.Math.Declisions.Services +namespace KLHZ.Trader.Core.Math.Declisions.Services.Cache { public class PriceHistoryCacheUnit2 : IPriceHistoryCacheUnit { @@ -57,14 +56,14 @@ namespace KLHZ.Trader.Core.Math.Declisions.Services { if (_pointer < 0) { - return ValueTask.FromResult((Array.Empty(), Array.Empty ())); + return ValueTask.FromResult((Array.Empty(), Array.Empty())); } else { var prices = new float[_length]; var timestamps = new DateTime[_length]; - Array.Copy(Prices,1+ _pointer - _length, prices, 0, prices.Length); - Array.Copy(Timestamps,1+ _pointer - _length, timestamps, 0, timestamps.Length); + Array.Copy(Prices, 1 + _pointer - _length, prices, 0, prices.Length); + Array.Copy(Timestamps, 1 + _pointer - _length, timestamps, 0, timestamps.Length); return ValueTask.FromResult((timestamps, prices)); } } @@ -85,7 +84,7 @@ namespace KLHZ.Trader.Core.Math.Declisions.Services .Skip(priceChanges.Length - CacheMaxLength) .ToArray(); - foreach ( var pc in selectedPriceChanges) + foreach (var pc in selectedPriceChanges) { AddData(pc); } diff --git a/KLHZ.Trader.Core.Math/Declisions/Services/EventsDetection/IntervalsTradingEventsDetector.cs b/KLHZ.Trader.Core.Math/Declisions/Services/EventsDetection/IntervalsTradingEventsDetector.cs new file mode 100644 index 0000000..2d3d4ca --- /dev/null +++ b/KLHZ.Trader.Core.Math/Declisions/Services/EventsDetection/IntervalsTradingEventsDetector.cs @@ -0,0 +1,14 @@ +using KLHZ.Trader.Core.Contracts.Declisions.Dtos.Enums; +using KLHZ.Trader.Core.Contracts.Declisions.Interfaces; +using KLHZ.Trader.Core.Math.Declisions.Utils; + +namespace KLHZ.Trader.Core.Math.Declisions.Services.EventsDetection +{ + public class IntervalsTradingEventsDetector : ITradingEventsDetector + { + public ValueTask Detect(IPriceHistoryCacheUnit unit) + { + return ValueTask.FromResult(TwoPeriods.Detect(unit)); + } + } +} diff --git a/KLHZ.Trader.Core.Math/Declisions/Services/KalmanPredictor.cs b/KLHZ.Trader.Core.Math/Declisions/Services/KalmanPredictor.cs deleted file mode 100644 index 9a6a521..0000000 --- a/KLHZ.Trader.Core.Math/Declisions/Services/KalmanPredictor.cs +++ /dev/null @@ -1,319 +0,0 @@ -using KLHZ.Trader.Core.Contracts.Declisions.Dtos; -using KLHZ.Trader.Core.Contracts.Messaging.Dtos.Intarfaces; -using KLHZ.Trader.Core.Contracts.Messaging.Interfaces; -using KLHZ.Trader.Core.Math.Declisions.Dtos; -using MathNet.Filtering.Kalman; -using MathNet.Numerics.LinearAlgebra; -using Microsoft.Extensions.Hosting; -using System.Threading.Channels; -using static System.Runtime.InteropServices.JavaScript.JSType; - -namespace KLHZ.Trader.Core.Math.Declisions.Services -{ - public class KalmanPredictor : IHostedService - { - private readonly double r = 1000.0; // Measurement covariance - private readonly PriceHistoryCacheUnit _cache = new PriceHistoryCacheUnit(""); - private readonly Channel _messages = Channel.CreateUnbounded(); - private readonly IDataBus _dataBus; - - private DiscreteKalmanFilter? _dkf; - - public KalmanPredictor(IDataBus bus) - { - _dataBus = bus; - bus.AddChannel(nameof(KalmanPredictor), _messages); - _ = ProcessMessages(); - } - - public Task StartAsync(CancellationToken cancellationToken) - { - return Task.CompletedTask; - } - - public Task StopAsync(CancellationToken cancellationToken) - { - return Task.CompletedTask; - } - - private async Task Init() - { - - } - - private async Task ProcessCalman(INewPrice message) - { - var H = Matrix.Build.Dense(2, 2, new[] { 1d, 0d, 0d, 1d }); // Measurement matrix - var length = _cache.Length; - - if (length > 2 && _dkf != null) - { - var data = await _cache.GetData(); - - var dt = (data.timestamps[data.prices.Length - 1] - data.timestamps[data.prices.Length - 2]).TotalSeconds; - if (dt < 0.0000001 || dt > 1800) return; - var F = Matrix.Build.Dense(2, 2, new[] { 1d, 0d, dt, 1 }); // State transition matrix - var R = Matrix.Build.Dense(2, 2, new[] { r, r / dt, r / dt, 2 * r / (dt * dt) }); - _dkf.Predict(F); - - var state = _dkf.State; - var value = state[0, 0] + dt * state[1, 0]; - if (!double.IsNaN(value)) - { - await _dataBus.BroadcastProcessedPrice(new ProcessedPrice() - { - Figi = message.Figi, - Processor = nameof(KalmanPredictor) + "_predict", - Ticker = message.Ticker, - IsHistoricalData = message.IsHistoricalData, - Time = message.Time, - Value = (decimal)value, - }); - } - - - var dprice = data.prices[data.prices.Length - 1] - data.prices[data.prices.Length - 2]; - var k = dprice / dt; - var b = data.prices[data.prices.Length - 2]; - var z = Matrix.Build.Dense(2, 1, new[] { b, k }); - - _dkf.Update(z, H, R); - - var state2 = _dkf.State; - var value2 = state2[0, 0] + dt * state2[1, 0]; - if (!double.IsNaN(value)) - { - await _dataBus.BroadcastProcessedPrice(new ProcessedPrice() - { - Figi = message.Figi, - Processor = nameof(KalmanPredictor) + "_state", - Ticker = message.Ticker, - IsHistoricalData = message.IsHistoricalData, - Time = message.Time, - Value = (decimal)value, - }); - } - } - else if (length >= 2) - { - var data = await _cache.GetData(); - var dprice = data.prices[data.prices.Length - 1] - data.prices[data.prices.Length - 2]; - var dt = (data.timestamps[data.prices.Length - 1] - data.timestamps[data.prices.Length - 2]).TotalSeconds; - if (dt < 0.0000001) return; - var k = dprice / dt; - var b = data.prices[data.prices.Length - 2]; - - - Matrix x0 = Matrix.Build.Dense(2, 1, new[] { b, k }); - Matrix P0 = Matrix.Build.Dense(2, 2, new[] { r, r / dt, r / dt, 2 * r / (dt * dt) }); - - _dkf = new DiscreteKalmanFilter(x0, P0); - } - } - private async Task ProcessMovAv(INewPrice message, int window) - { - var data = await _cache.GetData(); - if (data.prices.Length < window) return; - var sum = 0f; - var count = 0; - for (int i = 1; i <= window; i++) - { - sum += data.prices[data.prices.Length - i]; - count++; - } - - await _dataBus.BroadcastProcessedPrice(new ProcessedPrice() - { - Figi = message.Figi, - Processor = nameof(KalmanPredictor) + "_mov_av_window" + window.ToString(), - Ticker = message.Ticker, - IsHistoricalData = message.IsHistoricalData, - Time = message.Time, - Value = (decimal)(sum / count), - }); - } - - private static (DateTime time, float value) CalcTimeWindowAverageValue(DateTime[] timestamps, float[] values, int window, int shift = 0) - { - var sum = values[values.Length - 1 - shift]; - var count = 1; - var startTime = timestamps[timestamps.Length - 1 - shift]; - for (int i = 2; i < values.Length && startTime - timestamps[values.Length - i - shift] < TimeSpan.FromSeconds(window); i++) - { - sum += values[values.Length - i - shift]; - count++; - } - return (startTime, sum / count); - } - - private static TradingEventsDto CheckByWindowAverageMean(DateTime[] timestamps, float[] prices, int size, float meanfullStep = 3f) - { - var twav15s = new float[size]; - var twav120s = new float[size]; - var times = new DateTime[size]; - - for (int shift = 0; shift < size; shift++) - { - var twav15 = CalcTimeWindowAverageValue(timestamps, prices, 15, shift); - var twav120 = CalcTimeWindowAverageValue(timestamps, prices, 120, shift); - twav15s[size - 1 - shift] = twav15.value; - twav120s[size - 1 - shift] = twav120.value; - times[size - 1 - shift] = twav120.time; - - if (shift > 0) - { - var isCrossing = Utils.Lines.IsLinesCrossing( - times[size - 1 - shift], - times[size - 2 - shift], - twav15s[size - 1 - shift], - twav15s[size - 2 - shift], - twav120s[size - 1 - shift], - twav120s[size - 2 - shift]); - if (shift == 1 && !isCrossing) //если нет пересечения скользящих средний с окном 120 и 15 секунд между - //текущей и предыдущей точкой - можно не продолжать выполнение. - { - break; - } - if (shift > 1 && isCrossing) - { - // если фильтрация окном 120 наползает на окно 15 сверху, потенциальное время открытия лонга и закрытия шорта - if (twav120s[size - 1] <= twav15s[size - 1] && twav120s[size - 2] > twav15s[size - 2] ) - { - if (twav15s[size - 1 - shift] - twav15s[size - 1] >= meanfullStep) - { - return new TradingEventsDto(false, true); - } - } - - // если фильтрация окном 15 наползает на окно 120 сверху, потенциальное время закрытия лонга и возможно открытия шорта - if (twav15s[size - 1] <= twav120s[size - 1] && twav15s[size - 2] > twav120s[size - 2]) - { - if (twav15s[size - 1 - shift] - twav15s[size - 1] <= - meanfullStep) - { - return new TradingEventsDto(true, false); - } - } - } - } - } - - return new TradingEventsDto(false, false); - } - - private async Task ProcessTimeWindow(INewPrice message, int window) - { - var data = await _cache.GetData(); - var sum = data.prices[data.prices.Length - 1]; - var count = 1; - var startTime = data.timestamps[data.prices.Length - 1]; - for (int i = 2; i < data.prices.Length && startTime - data.timestamps[data.prices.Length - i] < TimeSpan.FromSeconds(window); i++) - { - sum += data.prices[data.prices.Length - i]; - count++; - } - - await _dataBus.BroadcastProcessedPrice(new ProcessedPrice() - { - Figi = message.Figi, - Processor = nameof(KalmanPredictor) + "_timeWindow" + window.ToString(), - Ticker = message.Ticker, - IsHistoricalData = message.IsHistoricalData, - Time = message.Time, - Value = (decimal)(sum / count), - }); - - var diffValue = System.Math.Abs((decimal)(sum / count) - message.Value); - await _dataBus.BroadcastProcessedPrice(new ProcessedPrice() - { - Figi = message.Figi, - Processor = nameof(KalmanPredictor) + "_diff" + window.ToString(), - Ticker = message.Ticker, - IsHistoricalData = message.IsHistoricalData, - Time = message.Time, - Value = diffValue > 3 ? diffValue : 0, - }); - } - - private async Task ProcessMessages() - { - while (await _messages.Reader.WaitToReadAsync()) - { - var message = await _messages.Reader.ReadAsync(); - await _cache.AddData(message); - try - { - var data = await _cache.GetData(); - - - var size = 50; - var twav15s = new float[size]; - var twav120s = new float[size]; - var times = new DateTime[size]; - - for (int shift = 0; shift < size; shift++) - { - var twav15 = CalcTimeWindowAverageValue(data.timestamps, data.prices, 15, shift); - var twav120 = CalcTimeWindowAverageValue(data.timestamps, data.prices, 120, shift); - twav15s[size - 1-shift] = twav15.value; - twav120s[size - 1 - shift] = twav120.value; - times[size - 1 - shift] = twav120.time; - - if (shift>0) - { - var isCrossing = Utils.Lines.IsLinesCrossing( - times[size - 1 - shift], - times[size - 2 - shift], - twav15s[size - 1 - shift], - twav15s[size - 2 - shift], - twav120s[size - 1 - shift], - twav120s[size - 2 - shift]); - if (shift == 1 && !isCrossing) //если нет пересечения скользящих средний с окном 120 и 15 секунд между - //текущей и предыдущей точкой - можно не продолжать выполнение. - { - break; - } - if (shift>1 && isCrossing) - { - // если фильтрация окном 120 наползает на окно 15 сверху, потенциальное время открытия лонга - if (twav120s[size - 1] <= twav15s[size - 1] && twav120s[size - 2] > twav15s[size - 2]) - { - - } - - // если фильтрация окном 15 наползает на окно 120 сверху, потенциальное время закрытия лонга и открытия шорта - if (twav15s[size - 1] <= twav120s[size - 1] && twav15s[size - 2] > twav120s[size - 2]) - { - - } - } - } - } - - - //if (isCrossing && twav120_2 <= twav15_2)// если филтрация окном 120 наползает на окно 15 сверху, потенциальное время открытия лонга - //{ - // for (int i=shift;i<111; i++) - // { - - // } - //} - - //if (isCrossing && twav15_2 <= twav120_2)// если филтрация окном 15 наползает на окно 120 сверху, потенциальное время закрытия лонга - //{ - - //} - - //await ProcessCalman(message); - await ProcessMovAv(message, 3); - await ProcessTimeWindow(message, 5); - await ProcessTimeWindow(message, 15); - await ProcessTimeWindow(message, 120); - } - catch (Exception ex) - { - - } - } - } - } -} diff --git a/KLHZ.Trader.Core.Math/Declisions/Utils/MovingAverage.cs b/KLHZ.Trader.Core.Math/Declisions/Utils/MovingAverage.cs new file mode 100644 index 0000000..8784a47 --- /dev/null +++ b/KLHZ.Trader.Core.Math/Declisions/Utils/MovingAverage.cs @@ -0,0 +1,76 @@ +using KLHZ.Trader.Core.Contracts.Declisions.Dtos; +using KLHZ.Trader.Core.Math.Common; + +namespace KLHZ.Trader.Core.Math.Declisions.Utils +{ + public static class MovingAverage + { + private static (DateTime time, float value) CalcTimeWindowAverageValue(DateTime[] timestamps, float[] values, int window, int shift = 0) + { + var sum = values[values.Length - 1 - shift]; + var count = 1; + var startTime = timestamps[timestamps.Length - 1 - shift]; + for (int i = 2; i < values.Length && startTime - timestamps[values.Length - i - shift] < TimeSpan.FromSeconds(window); i++) + { + sum += values[values.Length - i - shift]; + count++; + } + return (startTime, sum / count); + } + + public static TradingEventsDto CheckByWindowAverageMean(DateTime[] timestamps, float[] prices, int size, float meanfullStep = 3f) + { + var twav15s = new float[size]; + var twav120s = new float[size]; + var times = new DateTime[size]; + + for (int shift = 0; shift < size; shift++) + { + var twav15 = CalcTimeWindowAverageValue(timestamps, prices, 15, shift); + var twav120 = CalcTimeWindowAverageValue(timestamps, prices, 120, shift); + twav15s[size - 1 - shift] = twav15.value; + twav120s[size - 1 - shift] = twav120.value; + times[size - 1 - shift] = twav120.time; + + if (shift > 0) + { + var isCrossing = Lines.IsLinesCrossing( + times[size - 1 - shift], + times[size - 2 - shift], + twav15s[size - 1 - shift], + twav15s[size - 2 - shift], + twav120s[size - 1 - shift], + twav120s[size - 2 - shift]); + if (shift == 1 && !isCrossing) //если нет пересечения скользящих средний с окном 120 и 15 секунд между + //текущей и предыдущей точкой - можно не продолжать выполнение. + { + break; + } + if (shift > 1 && isCrossing) + { + // если фильтрация окном 120 наползает на окно 15 сверху, потенциальное время открытия лонга и закрытия шорта + if (twav120s[size - 1] <= twav15s[size - 1] && twav120s[size - 2] > twav15s[size - 2]) + { + if (twav15s[size - 1 - shift] - twav15s[size - 1] >= meanfullStep) + { + return new TradingEventsDto(false, true); + } + } + + // если фильтрация окном 15 наползает на окно 120 сверху, потенциальное время закрытия лонга и возможно открытия шорта + if (twav15s[size - 1] <= twav120s[size - 1] && twav15s[size - 2] > twav120s[size - 2]) + { + if (twav15s[size - 1 - shift] - twav15s[size - 1] <= -meanfullStep) + { + return new TradingEventsDto(true, false); + } + } + } + } + } + + return new TradingEventsDto(false, false); + } + + } +} diff --git a/KLHZ.Trader.Core/Declisions/Utils/HistoryProcessingInstruments.cs b/KLHZ.Trader.Core.Math/Declisions/Utils/TwoPeriods.cs similarity index 74% rename from KLHZ.Trader.Core/Declisions/Utils/HistoryProcessingInstruments.cs rename to KLHZ.Trader.Core.Math/Declisions/Utils/TwoPeriods.cs index 01a9d98..947070b 100644 --- a/KLHZ.Trader.Core/Declisions/Utils/HistoryProcessingInstruments.cs +++ b/KLHZ.Trader.Core.Math/Declisions/Utils/TwoPeriods.cs @@ -1,10 +1,15 @@ -using KLHZ.Trader.Core.Contracts.Declisions.Interfaces; -using KLHZ.Trader.Core.Declisions.Dtos; -using KLHZ.Trader.Core.Math.Declisions.Services; +using KLHZ.Trader.Core.Contracts.Declisions.Dtos; +using KLHZ.Trader.Core.Contracts.Declisions.Dtos.Enums; +using KLHZ.Trader.Core.Contracts.Declisions.Interfaces; +using KLHZ.Trader.Core.Math.Declisions.Dtos; +using KLHZ.Trader.Core.Math.Declisions.Services.Cache; -namespace KLHZ.Trader.Core.Declisions.Utils +namespace KLHZ.Trader.Core.Math.Declisions.Utils { - internal static class HistoryProcessingInstruments + /// + /// Обработка последних интервалов истории изменения цен. + /// + public static class TwoPeriods { internal static PeriodPricesInfoDto GetPriceDiffForTimeSpan(this IPriceHistoryCacheUnit unit, TimeSpan timeShift, TimeSpan timeSpan, int? pointsShift = null) { @@ -25,7 +30,7 @@ namespace KLHZ.Trader.Core.Declisions.Utils int count = 0; for (int i = times.Length - 1; i > -1; i--) { - if ((times[i] <= intervalEnd || (pointsShift.HasValue && count < pointsShift.Value)) && intervaEndIndex < 0) + if ((times[i] <= intervalEnd || pointsShift.HasValue && count < pointsShift.Value) && intervaEndIndex < 0) { intervaEndIndex = i; } @@ -88,7 +93,7 @@ namespace KLHZ.Trader.Core.Declisions.Utils if (k2 == 0 && k1 != 0) return 1000; return (float)(k1 / k2); } - internal static float CalcTrendRelationAbs(TwoPeriodsProcessingDto data) + internal static float CalcTrendRelationAbs(TwoPeriodsResultDto data) { var k1 = System.Math.Abs(data.DiffStart) / System.Math.Abs(data.PeriodStart.TotalSeconds); var k2 = System.Math.Abs(data.DiffEnd) / System.Math.Abs(data.PeriodEnd.TotalSeconds); @@ -127,7 +132,7 @@ namespace KLHZ.Trader.Core.Declisions.Utils var trendRelation = CalcTrendRelationAbs(startDiff, endDiff); var isEndLocal = endDiff.PeriodDiff == 0; - var res = totalDiff.Success && isStartGrows && (isEndStable || isEndFalls) && (trendRelation >= 2 && !isEndLocal); + var res = totalDiff.Success && isStartGrows && (isEndStable || isEndFalls) && trendRelation >= 2 && !isEndLocal; if (res) { @@ -180,9 +185,9 @@ namespace KLHZ.Trader.Core.Declisions.Utils return res; } - internal static TwoPeriodsProcessingDto GetTwoPeriodsProcessingData(this (DateTime[] timestamps, float[] prices) data, TimeSpan shift, int shiftPointsStart, int shiftPointsEnd, TimeSpan firstPeriod, float meanfullDiff) + internal static TwoPeriodsResultDto GetTwoPeriodsProcessingData(this (DateTime[] timestamps, float[] prices) data, TimeSpan shift, int shiftPointsStart, int shiftPointsEnd, TimeSpan firstPeriod, float meanfullDiff) { - var res = new TwoPeriodsProcessingDto(success: false, 0, 0, 0, 0, 0, TimeSpan.Zero, TimeSpan.Zero); + var res = new TwoPeriodsResultDto(success: false, 0, 0, 0, 0, 0, TimeSpan.Zero, TimeSpan.Zero); var time = data.timestamps; var prices = data.prices; int count = -1; @@ -198,7 +203,7 @@ namespace KLHZ.Trader.Core.Declisions.Utils bound = i; shift = lastTime - time[i]; } - if (((lastTime - time[i]) >= shift + firstPeriod)) + if (lastTime - time[i] >= shift + firstPeriod) { start = i; @@ -212,12 +217,12 @@ namespace KLHZ.Trader.Core.Declisions.Utils { var diff1 = prices[bound] - prices[start]; var diff2 = prices[end] - prices[bound]; - res = new TwoPeriodsProcessingDto(true, diff1, diff2, start, bound, end, time[bound] - time[start], time[end] - time[bound]); + res = new TwoPeriodsResultDto(true, diff1, diff2, start, bound, end, time[bound] - time[start], time[end] - time[bound]); } return res; } - internal static bool CheckLongClose(this IPriceHistoryCacheUnit unit, TimeSpan firstPeriod, TimeSpan secondPeriod, float meanfullDiff, int pointsStart, int pointsEnd) + public static bool CheckLongClose(this IPriceHistoryCacheUnit unit, TimeSpan firstPeriod, TimeSpan secondPeriod, float meanfullDiff, int pointsStart, int pointsEnd) { var data = unit.GetData().Result; var periodStat = data.GetTwoPeriodsProcessingData(secondPeriod, pointsStart, pointsEnd, firstPeriod, meanfullDiff); @@ -239,7 +244,7 @@ namespace KLHZ.Trader.Core.Declisions.Utils { } - return isStartOk && isEndOk && (data.prices[periodStat.End] - data.prices[periodStat.Start] >= meanfullDiff); + return isStartOk && isEndOk && data.prices[periodStat.End] - data.prices[periodStat.Start] >= meanfullDiff; } internal static bool CheckUptrendStarting2(this IPriceHistoryCacheUnit unit, TimeSpan firstPeriod, TimeSpan secondPeriod, float meanfullDiff) @@ -293,7 +298,7 @@ namespace KLHZ.Trader.Core.Declisions.Utils } - internal static bool CheckLongOpen(this IPriceHistoryCacheUnit unit, TimeSpan firstPeriod, TimeSpan secondPeriod, float meanfullDiff, int pointsStart, int pointsEnd) + public static bool CheckLongOpen(this IPriceHistoryCacheUnit unit, TimeSpan firstPeriod, TimeSpan secondPeriod, float meanfullDiff, int pointsStart, int pointsEnd) { var data = unit.GetData().Result; var periodStat = data.GetTwoPeriodsProcessingData(secondPeriod, pointsStart, pointsEnd, firstPeriod, meanfullDiff); @@ -315,7 +320,46 @@ namespace KLHZ.Trader.Core.Declisions.Utils { } - return isStartOk && isEndOk && (data.prices[periodStat.Start] - data.prices[periodStat.End] >= meanfullDiff); + return isStartOk && isEndOk && data.prices[periodStat.Start] - data.prices[periodStat.End] >= meanfullDiff; + } + + public static TradingEvent Detect(IPriceHistoryCacheUnit data) + { + float meanfullDiff; + if (data.Figi == "BBG004730N88") + { + meanfullDiff = 0.05f; + } + else if (data.Figi == "FUTIMOEXF000") + { + meanfullDiff = 1f; + } + else + { + return TradingEvent.None; + } + + var res = TradingEvent.None; + //var downtrendStarts = data.CheckDowntrendStarting(TimeSpan.FromSeconds(30), TimeSpan.FromSeconds(7), meanfullDiff); + var uptrendStarts = data.CheckLongOpen(TimeSpan.FromSeconds(60), TimeSpan.FromSeconds(7), meanfullDiff, 8, 3); + var uptrendStarts2 = data.CheckLongOpen(TimeSpan.FromSeconds(60), TimeSpan.FromSeconds(3), meanfullDiff, 15, 2); + var downtrendEnds = data.CheckLongOpen(TimeSpan.FromSeconds(120), TimeSpan.FromSeconds(10), meanfullDiff, 15, 5); + uptrendStarts |= downtrendEnds; + uptrendStarts |= uptrendStarts2; + if (uptrendStarts) + { + res |= TradingEvent.LongClose; + } + //var downtrendEnds = data.CheckDowntrendEnding(TimeSpan.FromSeconds(60), TimeSpan.FromSeconds(15), meanfullDiff); + + var uptrendEnds = data.CheckLongClose(TimeSpan.FromSeconds(15), TimeSpan.FromSeconds(20), meanfullDiff * 1.5f, 8, 8); + var uptrendEnds2 = data.CheckLongClose(TimeSpan.FromSeconds(120), TimeSpan.FromSeconds(30), meanfullDiff, 15, 8); + uptrendEnds |= uptrendEnds2; + if (uptrendEnds) + { + res |= TradingEvent.LongOpen; + } + return res; } } } diff --git a/KLHZ.Trader.Core.Math/KLHZ.Trader.Core.Math.csproj b/KLHZ.Trader.Core.Math/KLHZ.Trader.Core.Math.csproj index 615143f..a52c9ca 100644 --- a/KLHZ.Trader.Core.Math/KLHZ.Trader.Core.Math.csproj +++ b/KLHZ.Trader.Core.Math/KLHZ.Trader.Core.Math.csproj @@ -7,8 +7,6 @@ - - diff --git a/KLHZ.Trader.Core.Tests/HistoryCacheUnit2Tests.cs b/KLHZ.Trader.Core.Tests/HistoryCacheUnit2Tests.cs index a55ea65..edb1c33 100644 --- a/KLHZ.Trader.Core.Tests/HistoryCacheUnit2Tests.cs +++ b/KLHZ.Trader.Core.Tests/HistoryCacheUnit2Tests.cs @@ -1,5 +1,5 @@ using KLHZ.Trader.Core.DataLayer.Entities.Prices; -using KLHZ.Trader.Core.Math.Declisions.Services; +using KLHZ.Trader.Core.Math.Declisions.Services.Cache; namespace KLHZ.Trader.Core.Tests { @@ -208,7 +208,7 @@ namespace KLHZ.Trader.Core.Tests public void Test9() { var cacheUnit = new PriceHistoryCacheUnit2(""); - for(int i= 0; i < 5*PriceHistoryCacheUnit2.CacheMaxLength; i++) + for (int i = 0; i < 5 * PriceHistoryCacheUnit2.CacheMaxLength; i++) { cacheUnit.AddData(new PriceChange() { Figi = "", Ticker = "", Value = i, Time = DateTime.UtcNow }); if (i >= 500) diff --git a/KLHZ.Trader.Core.Tests/HistoryCacheUnitTests.cs b/KLHZ.Trader.Core.Tests/HistoryCacheUnitTests.cs index 9783514..d721f91 100644 --- a/KLHZ.Trader.Core.Tests/HistoryCacheUnitTests.cs +++ b/KLHZ.Trader.Core.Tests/HistoryCacheUnitTests.cs @@ -1,5 +1,5 @@ using KLHZ.Trader.Core.DataLayer.Entities.Prices; -using KLHZ.Trader.Core.Math.Declisions.Services; +using KLHZ.Trader.Core.Math.Declisions.Services.Cache; namespace KLHZ.Trader.Core.Tests { diff --git a/KLHZ.Trader.Core.Tests/HistoryProcessingInstrumentsTests.cs b/KLHZ.Trader.Core.Tests/HistoryProcessingInstrumentsTests.cs index 58e40a7..fb32831 100644 --- a/KLHZ.Trader.Core.Tests/HistoryProcessingInstrumentsTests.cs +++ b/KLHZ.Trader.Core.Tests/HistoryProcessingInstrumentsTests.cs @@ -1,6 +1,6 @@ using KLHZ.Trader.Core.DataLayer.Entities.Prices; -using KLHZ.Trader.Core.Declisions.Utils; -using KLHZ.Trader.Core.Math.Declisions.Services; +using KLHZ.Trader.Core.Math.Declisions.Services.Cache; +using KLHZ.Trader.Core.Math.Declisions.Utils; namespace KLHZ.Trader.Core.Tests { @@ -45,7 +45,7 @@ namespace KLHZ.Trader.Core.Tests var periodLength = 4; var shift = 0; - var result = HistoryProcessingInstruments.GetPriceDiffForTimeSpan(unit, TimeSpan.FromSeconds(shift), TimeSpan.FromSeconds(periodLength)); + var result = TwoPeriods.GetPriceDiffForTimeSpan(unit, TimeSpan.FromSeconds(shift), TimeSpan.FromSeconds(periodLength)); var maxValue = startValue + (step > 0 ? (step * count) - step * shift : (step * count) - (step * (shift + periodLength))); var minValue = startValue + (step > 0 ? (step * count) - (step * (shift + periodLength)) : (step * count) - step * shift); @@ -79,7 +79,7 @@ namespace KLHZ.Trader.Core.Tests var periodLength = 4; var shift = 0; - var result = HistoryProcessingInstruments.GetPriceDiffForTimeSpan(unit, TimeSpan.FromSeconds(shift), TimeSpan.FromSeconds(periodLength)); + var result = TwoPeriods.GetPriceDiffForTimeSpan(unit, TimeSpan.FromSeconds(shift), TimeSpan.FromSeconds(periodLength)); var maxValue = startValue + (step > 0 ? (step * count) - step * shift : (step * count) - (step * (shift + periodLength))); var minValue = startValue + (step > 0 ? (step * count) - (step * (shift + periodLength)) : (step * count) - step * shift); @@ -114,7 +114,7 @@ namespace KLHZ.Trader.Core.Tests var periodLength = 4; var shift = 1; - var result = HistoryProcessingInstruments.GetPriceDiffForTimeSpan(unit, TimeSpan.FromSeconds(shift), TimeSpan.FromSeconds(periodLength)); + var result = TwoPeriods.GetPriceDiffForTimeSpan(unit, TimeSpan.FromSeconds(shift), TimeSpan.FromSeconds(periodLength)); var maxValue = startValue + (step > 0 ? (step * count) - step * shift : (step * count) - (step * (shift + periodLength))); var minValue = startValue + (step > 0 ? (step * count) - (step * (shift + periodLength)) : (step * count) - step * shift); @@ -149,7 +149,7 @@ namespace KLHZ.Trader.Core.Tests var periodLength = 4; var shift = 0; - var result = HistoryProcessingInstruments.GetPriceDiffForTimeSpan(unit, TimeSpan.FromSeconds(shift), TimeSpan.FromSeconds(periodLength)); + var result = TwoPeriods.GetPriceDiffForTimeSpan(unit, TimeSpan.FromSeconds(shift), TimeSpan.FromSeconds(periodLength)); var maxValue = startValue + (step > 0 ? (step * count) - step * shift : (step * count) - (step * (shift + periodLength))); var minValue = startValue + (step > 0 ? (step * count) - (step * (shift + periodLength)) : (step * count) - step * shift); @@ -184,7 +184,7 @@ namespace KLHZ.Trader.Core.Tests var periodLength = 4; var shift = 3; - var result = HistoryProcessingInstruments.GetPriceDiffForTimeSpan(unit, TimeSpan.FromSeconds(shift), TimeSpan.FromSeconds(periodLength)); + var result = TwoPeriods.GetPriceDiffForTimeSpan(unit, TimeSpan.FromSeconds(shift), TimeSpan.FromSeconds(periodLength)); var maxValue = startValue + (step > 0 ? (step * count) - step * shift : (step * count) - (step * (shift + periodLength))); var minValue = startValue + (step > 0 ? (step * count) - (step * (shift + periodLength)) : (step * count) - step * shift); @@ -219,7 +219,7 @@ namespace KLHZ.Trader.Core.Tests var periodLength = 4; var shift = 3; - var result = HistoryProcessingInstruments.GetPriceDiffForTimeSpan(unit, TimeSpan.FromSeconds(shift), TimeSpan.FromSeconds(periodLength)); + var result = TwoPeriods.GetPriceDiffForTimeSpan(unit, TimeSpan.FromSeconds(shift), TimeSpan.FromSeconds(periodLength)); var maxValue1 = startValue + (step > 0 ? (step * count) - step * shift : (step * count) - (step * (shift + periodLength))); var minValue1 = startValue + (step > 0 ? (step * count) - (step * (shift + periodLength)) : (step * count) - step * shift); @@ -260,7 +260,7 @@ namespace KLHZ.Trader.Core.Tests } - var result2 = HistoryProcessingInstruments.GetPriceDiffForTimeSpan(unit2, TimeSpan.FromSeconds(shift), TimeSpan.FromSeconds(periodLength)); + var result2 = TwoPeriods.GetPriceDiffForTimeSpan(unit2, TimeSpan.FromSeconds(shift), TimeSpan.FromSeconds(periodLength)); var maxValue2 = 100; var minValue2 = -100; diff --git a/KLHZ.Trader.Core.Tests/LinesProcessingTest.cs b/KLHZ.Trader.Core.Tests/LinesProcessingTest.cs index 22776da..fc475b6 100644 --- a/KLHZ.Trader.Core.Tests/LinesProcessingTest.cs +++ b/KLHZ.Trader.Core.Tests/LinesProcessingTest.cs @@ -1,3 +1,5 @@ +using KLHZ.Trader.Core.Math.Common; + namespace KLHZ.Trader.Core.Tests { public class LinesProcessingTest @@ -14,7 +16,7 @@ namespace KLHZ.Trader.Core.Tests var val2_1 = -0.5f; var val2_2 = 0.5f; - Assert.IsTrue(KLHZ.Trader.Core.Math.Utils.Lines.IsLinesCrossing(time1, time2, val1_1, val1_2, val2_1, val2_2)); + Assert.IsTrue(Lines.IsLinesCrossing(time1, time2, val1_1, val1_2, val2_1, val2_2)); } [Test] @@ -29,7 +31,7 @@ namespace KLHZ.Trader.Core.Tests var val2_1 = 0.5f; var val2_2 = -0.5f; - Assert.IsFalse(KLHZ.Trader.Core.Math.Utils.Lines.IsLinesCrossing(time1, time2, val1_1, val1_2, val2_1, val2_2)); + Assert.IsFalse(Lines.IsLinesCrossing(time1, time2, val1_1, val1_2, val2_1, val2_2)); } } } \ No newline at end of file diff --git a/KLHZ.Trader.Core/Common/Messaging/Services/DataBus.cs b/KLHZ.Trader.Core/Common/Messaging/Services/DataBus.cs index 2c34454..198fa0d 100644 --- a/KLHZ.Trader.Core/Common/Messaging/Services/DataBus.cs +++ b/KLHZ.Trader.Core/Common/Messaging/Services/DataBus.cs @@ -1,5 +1,5 @@ using KLHZ.Trader.Core.Contracts.Messaging.Dtos; -using KLHZ.Trader.Core.Contracts.Messaging.Dtos.Intarfaces; +using KLHZ.Trader.Core.Contracts.Messaging.Dtos.Interfaces; using KLHZ.Trader.Core.Contracts.Messaging.Interfaces; using System.Collections.Concurrent; using System.Threading.Channels; diff --git a/KLHZ.Trader.Core/Declisions/Services/ProcessedPricesLogger.cs b/KLHZ.Trader.Core/Common/Messaging/Services/ProcessedPricesLogger.cs similarity index 88% rename from KLHZ.Trader.Core/Declisions/Services/ProcessedPricesLogger.cs rename to KLHZ.Trader.Core/Common/Messaging/Services/ProcessedPricesLogger.cs index 2d3f667..0a93705 100644 --- a/KLHZ.Trader.Core/Declisions/Services/ProcessedPricesLogger.cs +++ b/KLHZ.Trader.Core/Common/Messaging/Services/ProcessedPricesLogger.cs @@ -1,4 +1,4 @@ -using KLHZ.Trader.Core.Contracts.Messaging.Dtos.Intarfaces; +using KLHZ.Trader.Core.Contracts.Messaging.Dtos.Interfaces; using KLHZ.Trader.Core.Contracts.Messaging.Interfaces; using KLHZ.Trader.Core.DataLayer; using Microsoft.EntityFrameworkCore; @@ -6,7 +6,7 @@ using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; using System.Threading.Channels; -namespace KLHZ.Trader.Core.Declisions.Services +namespace KLHZ.Trader.Core.Common.Messaging.Services { public class ProcessedPricesLogger : IHostedService { @@ -44,7 +44,7 @@ namespace KLHZ.Trader.Core.Declisions.Services Value = message.Value, }); - if (buffer.Count > 10000 || (DateTime.UtcNow - lastWrite) > TimeSpan.FromSeconds(5) || _channel.Reader.Count == 0) + if (buffer.Count > 10000 || DateTime.UtcNow - lastWrite > TimeSpan.FromSeconds(5) || _channel.Reader.Count == 0) { lastWrite = DateTime.UtcNow; using var context = await _dbContextFactory.CreateDbContextAsync(); diff --git a/KLHZ.Trader.Core/DataLayer/Entities/Prices/Candle.cs b/KLHZ.Trader.Core/DataLayer/Entities/Prices/Candle.cs index e01bc8d..670990f 100644 --- a/KLHZ.Trader.Core/DataLayer/Entities/Prices/Candle.cs +++ b/KLHZ.Trader.Core/DataLayer/Entities/Prices/Candle.cs @@ -1,4 +1,4 @@ -using KLHZ.Trader.Core.Contracts.Messaging.Dtos.Intarfaces; +using KLHZ.Trader.Core.Contracts.Messaging.Dtos.Interfaces; using System.ComponentModel.DataAnnotations.Schema; namespace KLHZ.Trader.Core.DataLayer.Entities.Prices diff --git a/KLHZ.Trader.Core/DataLayer/Entities/Prices/PriceChange.cs b/KLHZ.Trader.Core/DataLayer/Entities/Prices/PriceChange.cs index 6aed638..37f81c8 100644 --- a/KLHZ.Trader.Core/DataLayer/Entities/Prices/PriceChange.cs +++ b/KLHZ.Trader.Core/DataLayer/Entities/Prices/PriceChange.cs @@ -1,4 +1,4 @@ -using KLHZ.Trader.Core.Contracts.Messaging.Dtos.Intarfaces; +using KLHZ.Trader.Core.Contracts.Messaging.Dtos.Interfaces; using System.ComponentModel.DataAnnotations.Schema; namespace KLHZ.Trader.Core.DataLayer.Entities.Prices diff --git a/KLHZ.Trader.Core/DataLayer/Entities/Prices/ProcessedPrice.cs b/KLHZ.Trader.Core/DataLayer/Entities/Prices/ProcessedPrice.cs index 57aa685..ffcfbb6 100644 --- a/KLHZ.Trader.Core/DataLayer/Entities/Prices/ProcessedPrice.cs +++ b/KLHZ.Trader.Core/DataLayer/Entities/Prices/ProcessedPrice.cs @@ -1,4 +1,4 @@ -using KLHZ.Trader.Core.Contracts.Messaging.Dtos.Intarfaces; +using KLHZ.Trader.Core.Contracts.Messaging.Dtos.Interfaces; using System.ComponentModel.DataAnnotations.Schema; namespace KLHZ.Trader.Core.DataLayer.Entities.Prices diff --git a/KLHZ.Trader.Core/Declisions/Services/TradingEventsDetector.cs b/KLHZ.Trader.Core/Declisions/Services/TradingEventsDetector.cs deleted file mode 100644 index d65efde..0000000 --- a/KLHZ.Trader.Core/Declisions/Services/TradingEventsDetector.cs +++ /dev/null @@ -1,41 +0,0 @@ -using KLHZ.Trader.Core.Contracts.Declisions.Dtos; -using KLHZ.Trader.Core.Contracts.Declisions.Interfaces; -using KLHZ.Trader.Core.Declisions.Utils; - -namespace KLHZ.Trader.Core.Declisions.Services -{ - public class TradingEventsDetector : ITradingEventsDetector - { - public async ValueTask Detect(IPriceHistoryCacheUnit data) - { - await Task.Delay(0); - float meanfullDiff; - if (data.Figi == "BBG004730N88") - { - meanfullDiff = 0.05f; - } - else if (data.Figi == "FUTIMOEXF000") - { - meanfullDiff = 1f; - } - else - { - return TradingEventsDto.Empty; - } - - //var downtrendStarts = data.CheckDowntrendStarting(TimeSpan.FromSeconds(30), TimeSpan.FromSeconds(7), meanfullDiff); - var uptrendStarts = data.CheckLongOpen(TimeSpan.FromSeconds(60), TimeSpan.FromSeconds(7), meanfullDiff, 8, 3); - var uptrendStarts2 = data.CheckLongOpen(TimeSpan.FromSeconds(60), TimeSpan.FromSeconds(3), meanfullDiff, 15, 2); - var downtrendEnds = data.CheckLongOpen(TimeSpan.FromSeconds(120), TimeSpan.FromSeconds(10), meanfullDiff, 15, 5); - uptrendStarts |= downtrendEnds; - uptrendStarts |= uptrendStarts2; - //var downtrendEnds = data.CheckDowntrendEnding(TimeSpan.FromSeconds(60), TimeSpan.FromSeconds(15), meanfullDiff); - - var uptrendEnds = data.CheckLongClose(TimeSpan.FromSeconds(15), TimeSpan.FromSeconds(20), meanfullDiff * 1.5f, 8, 8); - var uptrendEnds2 = data.CheckLongClose(TimeSpan.FromSeconds(120), TimeSpan.FromSeconds(30), meanfullDiff, 15, 8); - uptrendEnds |= uptrendEnds2; - - return new TradingEventsDto(uptrendEnds, uptrendStarts); - } - } -} diff --git a/KLHZ.Trader.Core/Exchange/ExchangeConfig.cs b/KLHZ.Trader.Core/Exchange/ExchangeConfig.cs index b3b4484..ef535f7 100644 --- a/KLHZ.Trader.Core/Exchange/ExchangeConfig.cs +++ b/KLHZ.Trader.Core/Exchange/ExchangeConfig.cs @@ -8,7 +8,8 @@ public decimal AccountCashPart { get; set; } public decimal AccountCashPartFutures { get; set; } public decimal DefaultBuyPartOfAccount { get; set; } - public string[] AllowedInstrumentsFigis { get; set; } = []; + public string[] DataRecievingInstrumentsFigis { get; set; } = []; + public string[] TradingInstrumentsFigis { get; set; } = []; public string[] ManagingAccountNamePatterns { get; set; } = []; } } diff --git a/KLHZ.Trader.Core/Exchange/Services/ExchangeDataReader.cs b/KLHZ.Trader.Core/Exchange/Services/ExchangeDataReader.cs index c2d69aa..266eb93 100644 --- a/KLHZ.Trader.Core/Exchange/Services/ExchangeDataReader.cs +++ b/KLHZ.Trader.Core/Exchange/Services/ExchangeDataReader.cs @@ -33,7 +33,7 @@ namespace KLHZ.Trader.Core.Exchange.Services _eventBus = eventBus; _dbContextFactory = dbContextFactory; _investApiClient = investApiClient; - _instrumentsFigis = options.Value.AllowedInstrumentsFigis.ToArray(); + _instrumentsFigis = options.Value.DataRecievingInstrumentsFigis.ToArray(); _logger = logger; _managedAccountNamePatterns = options.Value.ManagingAccountNamePatterns.ToArray(); } diff --git a/KLHZ.Trader.Core/Declisions/Services/Trader.cs b/KLHZ.Trader.Core/Exchange/Services/Trader.cs similarity index 92% rename from KLHZ.Trader.Core/Declisions/Services/Trader.cs rename to KLHZ.Trader.Core/Exchange/Services/Trader.cs index bb86654..b05104e 100644 --- a/KLHZ.Trader.Core/Declisions/Services/Trader.cs +++ b/KLHZ.Trader.Core/Exchange/Services/Trader.cs @@ -1,25 +1,25 @@ using KLHZ.Trader.Core.Common; +using KLHZ.Trader.Core.Contracts.Declisions.Dtos.Enums; using KLHZ.Trader.Core.Contracts.Declisions.Interfaces; using KLHZ.Trader.Core.Contracts.Messaging.Dtos.Enums; -using KLHZ.Trader.Core.Contracts.Messaging.Dtos.Intarfaces; +using KLHZ.Trader.Core.Contracts.Messaging.Dtos.Interfaces; using KLHZ.Trader.Core.Contracts.Messaging.Interfaces; using KLHZ.Trader.Core.DataLayer; using KLHZ.Trader.Core.DataLayer.Entities.Declisions; -using KLHZ.Trader.Core.Exchange; using KLHZ.Trader.Core.Exchange.Extentions; using KLHZ.Trader.Core.Exchange.Models; -using KLHZ.Trader.Core.Exchange.Services; -using KLHZ.Trader.Core.Math.Declisions.Services; +using KLHZ.Trader.Core.Math.Declisions.Services.Cache; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using System.Collections.Concurrent; using System.Threading.Channels; using Tinkoff.InvestApi; using AssetType = KLHZ.Trader.Core.Exchange.Models.AssetType; -namespace KLHZ.Trader.Core.Declisions.Services +namespace KLHZ.Trader.Core.Exchange.Services { public class Trader : IHostedService { @@ -31,6 +31,7 @@ namespace KLHZ.Trader.Core.Declisions.Services private readonly ConcurrentDictionary Accounts = new(); private readonly ConcurrentDictionary _historyCash = new(); private readonly ITradingEventsDetector _tradingEventsDetector; + private readonly ILogger _logger; private readonly decimal _futureComission; @@ -107,7 +108,7 @@ namespace KLHZ.Trader.Core.Declisions.Services try { - if (result.LongOpen) + if ((result & TradingEvent.LongOpen) == TradingEvent.LongOpen) { using var context = await _dbContextFactory.CreateDbContextAsync(); context.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking; @@ -122,7 +123,7 @@ namespace KLHZ.Trader.Core.Declisions.Services }); await context.SaveChangesAsync(); } - if (result.LongClose) + if ((result & TradingEvent.LongClose) == TradingEvent.LongClose) { using var context = await _dbContextFactory.CreateDbContextAsync(); context.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking; diff --git a/KLHZ.Trader.Core/TG/Services/BotStarter.cs b/KLHZ.Trader.Core/TG/Services/BotStarter.cs index 4f35dd1..7adad5b 100644 --- a/KLHZ.Trader.Core/TG/Services/BotStarter.cs +++ b/KLHZ.Trader.Core/TG/Services/BotStarter.cs @@ -1,4 +1,4 @@ -using KLHZ.Trader.Core.Contracts.Messaging.Dtos.Intarfaces; +using KLHZ.Trader.Core.Contracts.Messaging.Dtos.Interfaces; using KLHZ.Trader.Core.Contracts.Messaging.Interfaces; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Options; diff --git a/KLHZ.Trader.Service/Controllers/ExchangeDataController.cs b/KLHZ.Trader.Service/Controllers/ExchangeDataController.cs new file mode 100644 index 0000000..8bbac0f --- /dev/null +++ b/KLHZ.Trader.Service/Controllers/ExchangeDataController.cs @@ -0,0 +1,66 @@ +using Grpc.Core; +using Microsoft.AspNetCore.Mvc; +using Tinkoff.InvestApi; +using Tinkoff.InvestApi.V1; + +namespace KLHZ.Trader.Service.Controllers +{ + [ApiController] + [Route("[controller]/[action]")] + public class ExchangeDataController : ControllerBase + { + private readonly InvestApiClient _investApiClient; + + public ExchangeDataController(InvestApiClient investApiClient) + { + _investApiClient = investApiClient; + } + + [HttpGet] + public async Task GetFigi([FromQuery] string ticker, [FromQuery] string? classCode = "TQBR") + { + var req = new InstrumentRequest() + { + ClassCode = classCode, + IdType = InstrumentIdType.Ticker, + Id = ticker, + }; + + try + { + var res = await _investApiClient.Instruments.GetInstrumentByAsync(req); + return res.Instrument.Figi; + } + catch (RpcException ex) + { + if (ex.StatusCode == Grpc.Core.StatusCode.NotFound && classCode == "TQBR") + { + return await GetFigi(ticker, "SPBFUT"); + } + } + return null; + } + + [HttpGet] + public async Task GetTicker([FromQuery] string figi) + { + var req = new InstrumentRequest() + { + ClassCode = "figi", + IdType = InstrumentIdType.Figi, + Id = figi, + }; + + try + { + var res = await _investApiClient.Instruments.GetInstrumentByAsync(req); + return res.Instrument.Ticker; + } + catch (Exception ex) + { + + } + return null; + } + } +} diff --git a/KLHZ.Trader.Service/Program.cs b/KLHZ.Trader.Service/Program.cs index 12bd241..de04e35 100644 --- a/KLHZ.Trader.Service/Program.cs +++ b/KLHZ.Trader.Service/Program.cs @@ -3,9 +3,9 @@ using KLHZ.Trader.Core.Common.Messaging.Services; using KLHZ.Trader.Core.Contracts.Declisions.Interfaces; using KLHZ.Trader.Core.Contracts.Messaging.Interfaces; using KLHZ.Trader.Core.DataLayer; -using KLHZ.Trader.Core.Declisions.Services; using KLHZ.Trader.Core.Exchange; using KLHZ.Trader.Core.Exchange.Services; +using KLHZ.Trader.Core.Math.Declisions.Services.EventsDetection; using KLHZ.Trader.Core.TG; using KLHZ.Trader.Core.TG.Services; using KLHZ.Trader.Service.Infrastructure; @@ -53,7 +53,7 @@ builder.Services.AddHostedService(); builder.Services.AddSingleton(); builder.Services.AddSingleton(); builder.Services.AddSingleton(); -builder.Services.AddSingleton(); +builder.Services.AddSingleton(); for (int i = 0; i < 10; i++) { diff --git a/KLHZ.Trader.Service/appsettings.json b/KLHZ.Trader.Service/appsettings.json index e48f767..33976e3 100644 --- a/KLHZ.Trader.Service/appsettings.json +++ b/KLHZ.Trader.Service/appsettings.json @@ -11,7 +11,8 @@ "ExchangeDataRecievingEnabled": true, "Token": "", "ManagingAccountNamePatterns": [ "автотрейд 1" ], - "AllowedInstrumentsFigis": [ "BBG004730N88", "FUTIMOEXF000" ], + "DataRecievingInstrumentsFigis": [ "BBG004730N88", "FUTIMOEXF000", "FUTGMKN09250", "FUTBR1025000" ], + "TradingInstrumentsFigis": [ "FUTIMOEXF000" ], "FutureComission": 0.0025, "ShareComission": 0.0004, "AccountCashPart": 0.05,