diff --git a/KLHZ.Trader.Core.Contracts/Declisions/Dtos/CachedValue.cs b/KLHZ.Trader.Core.Contracts/Declisions/Dtos/CachedValue.cs deleted file mode 100644 index 7fcb4f5..0000000 --- a/KLHZ.Trader.Core.Contracts/Declisions/Dtos/CachedValue.cs +++ /dev/null @@ -1,26 +0,0 @@ -using KLHZ.Trader.Core.Contracts.Messaging.Dtos.Interfaces; - -namespace KLHZ.Trader.Core.Contracts.Declisions.Dtos -{ - public class CachedValue : ITradeDataItem - { - public DateTime Time { get; init; } - public long Count { get; init; } - public decimal Price { get; init; } - public decimal Value { get; init; } - public decimal Value2 { get; init; } - - public CachedValue() - { - Figi = string.Empty; - Ticker = string.Empty; - Direction = 0; - IsHistoricalData = false; - } - - public bool IsHistoricalData { get; init; } - public required string Figi { get; init; } - public required string Ticker { get; init; } - public int Direction { get; init; } - } -} diff --git a/KLHZ.Trader.Core.Contracts/Declisions/Dtos/Enums/TimeWindowCacheType.cs b/KLHZ.Trader.Core.Contracts/Declisions/Dtos/Enums/TimeWindowCacheType.cs deleted file mode 100644 index b24c595..0000000 --- a/KLHZ.Trader.Core.Contracts/Declisions/Dtos/Enums/TimeWindowCacheType.cs +++ /dev/null @@ -1,11 +0,0 @@ -namespace KLHZ.Trader.Core.Contracts.Declisions.Dtos.Enums -{ - public enum TimeWindowCacheType - { - None = 0, - _20_Seconds = 2001, - _1_Minute = 1, - _5_Minutes = 2, - _15_Minutes = 15, - } -} diff --git a/KLHZ.Trader.Core.Contracts/Declisions/Interfaces/IPriceHistoryCacheUnit.cs b/KLHZ.Trader.Core.Contracts/Declisions/Interfaces/IPriceHistoryCacheUnit.cs deleted file mode 100644 index d10a80b..0000000 --- a/KLHZ.Trader.Core.Contracts/Declisions/Interfaces/IPriceHistoryCacheUnit.cs +++ /dev/null @@ -1,32 +0,0 @@ -using KLHZ.Trader.Core.Contracts.Declisions.Dtos; -using KLHZ.Trader.Core.Contracts.Declisions.Dtos.Enums; -using KLHZ.Trader.Core.Contracts.Messaging.Dtos.Interfaces; - -namespace KLHZ.Trader.Core.Contracts.Declisions.Interfaces -{ - public interface IPriceHistoryCacheUnit - { - public string Figi { get; } - public int Length { get; } - public ValueTask AddData(ITradeDataItem priceChange); - public ValueTask<(DateTime[] timestamps, decimal[] prices)> GetData(int? length = null); - public ValueTask<(DateTime[] timestamps, decimal[] prices, bool isFullIntervalExists)> GetData(TimeSpan period); - public ValueTask AddOrderbook(IOrderbook orderbook); - - public ValueTask AddDataToTimeWindowCache(string key, CachedValue data, TimeWindowCacheType timeWindowCacheType); - - public ValueTask GetDataFromTimeWindowCache(string key, TimeWindowCacheType timeWindowCacheType); - - /// - /// Число заявок на продаже в стакане. - /// - public decimal AsksCount { get; } - - /// - /// Число заявок на покупку в стакане. - /// - public decimal BidsCount { get; } - - public ValueTask<(DateTime time, decimal price)> GetLastValues(); - } -} diff --git a/KLHZ.Trader.Core.Contracts/Messaging/Dtos/AttachedInfo.cs b/KLHZ.Trader.Core.Contracts/Messaging/Dtos/AttachedInfo.cs new file mode 100644 index 0000000..48debc3 --- /dev/null +++ b/KLHZ.Trader.Core.Contracts/Messaging/Dtos/AttachedInfo.cs @@ -0,0 +1,9 @@ +namespace KLHZ.Trader.Core.Contracts.Messaging.Dtos +{ + public class AttachedInfo + { + public required string Key { get; init; } + public decimal Value1 { get; init; } + public decimal Value2 { get; init; } + } +} diff --git a/KLHZ.Trader.Core.Contracts/Messaging/Dtos/Interfaces/ITradeDataItem.cs b/KLHZ.Trader.Core.Contracts/Messaging/Dtos/Interfaces/ITradeDataItem.cs index 87b8559..7bc5cd0 100644 --- a/KLHZ.Trader.Core.Contracts/Messaging/Dtos/Interfaces/ITradeDataItem.cs +++ b/KLHZ.Trader.Core.Contracts/Messaging/Dtos/Interfaces/ITradeDataItem.cs @@ -11,5 +11,10 @@ public int Direction { get; } public decimal Value { get; } public decimal Value2 { get; } + public AttachedInfo? AttachedInfo => null; + public void SetAttachedInfo(AttachedInfo attachedInfo) + { + + } } } diff --git a/KLHZ.Trader.Core.Math/Declisions/Dtos/TimeWindowCacheItem.cs b/KLHZ.Trader.Core.Math/Declisions/Dtos/TimeWindowCacheItem.cs deleted file mode 100644 index 003ead5..0000000 --- a/KLHZ.Trader.Core.Math/Declisions/Dtos/TimeWindowCacheItem.cs +++ /dev/null @@ -1,65 +0,0 @@ -using KLHZ.Trader.Core.Contracts.Declisions.Dtos; -using KLHZ.Trader.Core.Contracts.Declisions.Dtos.Enums; - -namespace KLHZ.Trader.Core.Math.Declisions.Dtos -{ - internal class TimeWindowCacheItem - { - private readonly object _locker = new(); - private readonly LinkedList _cachedValues = new(); - - public readonly TimeSpan WindowSize; - public readonly string Key; - - public TimeWindowCacheItem(string key, TimeWindowCacheType window) - { - Key = key; - WindowSize = GetTimeSpan(window); - } - - public ValueTask AddData(CachedValue cachedValue) - { - lock (_locker) - { - _cachedValues.AddLast(cachedValue); - while (_cachedValues.Last != null && _cachedValues.First != null - && _cachedValues.Last.Value.Time - _cachedValues.First.Value.Time > WindowSize) - { - _cachedValues.RemoveFirst(); - } - } - return ValueTask.CompletedTask; - } - - public ValueTask GetValues() - { - lock (_locker) - { - return ValueTask.FromResult(_cachedValues.ToArray()); - } - } - - private static TimeSpan GetTimeSpan(TimeWindowCacheType type) - { - switch (type) - { - case TimeWindowCacheType._5_Minutes: - { - return TimeSpan.FromMinutes(5); - } - case TimeWindowCacheType._15_Minutes: - { - return TimeSpan.FromMinutes(15); - } - case TimeWindowCacheType._20_Seconds: - { - return TimeSpan.FromSeconds(20); - } - default: - { - return TimeSpan.FromMinutes(1); - } - } - } - } -} diff --git a/KLHZ.Trader.Core.Math/Declisions/Dtos/TwoLocalTrendsResultDto.cs b/KLHZ.Trader.Core.Math/Declisions/Dtos/TwoLocalTrendsResultDto.cs deleted file mode 100644 index 0d3b251..0000000 --- a/KLHZ.Trader.Core.Math/Declisions/Dtos/TwoLocalTrendsResultDto.cs +++ /dev/null @@ -1,27 +0,0 @@ -namespace KLHZ.Trader.Core.Math.Declisions.Dtos -{ - internal readonly struct TwoLocalTrendsResultDto - { - public readonly int Start; - public readonly int Bound; - public readonly int End; - public readonly decimal DiffStart; - public readonly decimal DiffEnd; - public readonly bool Success; - public readonly TimeSpan PeriodStart; - public readonly TimeSpan PeriodEnd; - - public TwoLocalTrendsResultDto(bool success, decimal diffStart, decimal diffEnd, int start, int bound, int end, - TimeSpan periodStart, TimeSpan periodEnd) - { - Success = success; - DiffStart = diffStart; - DiffEnd = diffEnd; - Start = start; - Bound = bound; - End = end; - PeriodStart = periodStart; - PeriodEnd = periodEnd; - } - } -} diff --git a/KLHZ.Trader.Core.Math/Declisions/Services/Cache/PriceHistoryCacheUnit2.cs b/KLHZ.Trader.Core.Math/Declisions/Services/Cache/PriceHistoryCacheUnit2.cs deleted file mode 100644 index bba5509..0000000 --- a/KLHZ.Trader.Core.Math/Declisions/Services/Cache/PriceHistoryCacheUnit2.cs +++ /dev/null @@ -1,224 +0,0 @@ -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.Contracts.Messaging.Dtos.Interfaces; -using KLHZ.Trader.Core.Math.Declisions.Dtos; -using System.Collections.Concurrent; - -namespace KLHZ.Trader.Core.Math.Declisions.Services.Cache -{ - public class PriceHistoryCacheUnit2 : IPriceHistoryCacheUnit - { - public const int CacheMaxLength = 30000; - private const int _arrayMaxLength = 60000; - - public string Figi { get; init; } - - public int Length - { - get - { - lock (_locker) - { - return _length; - } - } - } - - public decimal AsksCount - { - get - { - lock (_locker) - { - return _asksCount; - } - } - } - - public decimal BidsCount - { - get - { - lock (_locker) - { - return _bidsCount; - } - } - } - - private readonly object _locker = new(); - private readonly decimal[] Prices = new decimal[_arrayMaxLength]; - private readonly DateTime[] Timestamps = new DateTime[_arrayMaxLength]; - private readonly ConcurrentDictionary _20_secTimeWindows = new(); - private readonly ConcurrentDictionary _1_minTimeWindows = new(); - private readonly ConcurrentDictionary _5_minTimeWindows = new(); - private readonly ConcurrentDictionary _15_minTimeWindows = new(); - - private int _length = 0; - private int _pointer = -1; - - private long _asksCount = 1; - private long _bidsCount = 1; - - public ValueTask AddDataToTimeWindowCache(string key, CachedValue data, TimeWindowCacheType timeWindowCacheType) - { - var dict = GetDict(timeWindowCacheType); - if (!dict.TryGetValue(key, out var cahcheItem)) - { - dict.TryAdd(key, new TimeWindowCacheItem(key, timeWindowCacheType)); - } - dict[key].AddData(data); - return ValueTask.CompletedTask; - } - - public ValueTask GetDataFromTimeWindowCache(string key, TimeWindowCacheType timeWindowCacheType) - { - var dict = GetDict(timeWindowCacheType); - if (dict.TryGetValue(key, out var cahcheItem)) - { - return cahcheItem.GetValues(); - } - return ValueTask.FromResult(Array.Empty()); - } - - private ConcurrentDictionary GetDict(TimeWindowCacheType timeWindowCacheType) - { - switch (timeWindowCacheType) - { - case TimeWindowCacheType._5_Minutes: - { - return _5_minTimeWindows; - } - case TimeWindowCacheType._15_Minutes: - { - return _15_minTimeWindows; - } - case TimeWindowCacheType._20_Seconds: - { - return _20_secTimeWindows; - } - default: - { - return _1_minTimeWindows; ; - } - } - } - - public ValueTask AddData(ITradeDataItem priceChange) - { - if (priceChange.Figi != Figi) return ValueTask.CompletedTask; - lock (_locker) - { - _pointer++; - Prices[_pointer] = priceChange.Price; - Timestamps[_pointer] = priceChange.Time; - if (_length < CacheMaxLength) - { - _length++; - } - - if (_pointer == _arrayMaxLength - 1) - { - Array.Copy(Prices, Prices.Length - CacheMaxLength, Prices, 0, CacheMaxLength); - Array.Copy(Timestamps, Timestamps.Length - CacheMaxLength, Timestamps, 0, CacheMaxLength); - _pointer = CacheMaxLength - 1; - } - } - return ValueTask.CompletedTask; - } - - public ValueTask<(DateTime[] timestamps, decimal[] prices)> GetData(int? length = null) - { - lock (_locker) - { - if (_pointer < 0) - { - return ValueTask.FromResult((Array.Empty(), Array.Empty())); - } - else - { - var dataLength = length.HasValue ? System.Math.Min(length.Value, _length) : _length; - var prices = new decimal[dataLength]; - var timestamps = new DateTime[dataLength]; - var index = 1 + _pointer - dataLength; - Array.Copy(Prices, index, prices, 0, prices.Length); - Array.Copy(Timestamps, index, timestamps, 0, timestamps.Length); - return ValueTask.FromResult((timestamps, prices)); - } - } - } - - public ValueTask AddOrderbook(IOrderbook orderbook) - { - if (orderbook.Figi != Figi) return ValueTask.CompletedTask; - lock (_locker) - { - _asksCount = orderbook.AsksCount; - _bidsCount = orderbook.BidsCount; - } - return ValueTask.CompletedTask; - } - - public ValueTask<(DateTime time, decimal price)> GetLastValues() - { - lock (_locker) - { - return _pointer >= 0 ? ValueTask.FromResult((Timestamps[_pointer], Prices[_pointer])) : ValueTask.FromResult((DateTime.UtcNow, 0m)); - } - } - - public ValueTask<(DateTime[] timestamps, decimal[] prices, bool isFullIntervalExists)> GetData(TimeSpan period) - { - lock (_locker) - { - if (_pointer < 0) - { - return ValueTask.FromResult((Array.Empty(), Array.Empty(), false)); - } - else - { - var i = _pointer; - var lastTime = Timestamps[i]; - for (i = _pointer - 1; i >= 0; i--) - { - var currentTime = Timestamps[i]; - if (lastTime - currentTime >= period) - { - break; - } - } - - var dataLength = _pointer - i; - var prices = new decimal[dataLength]; - var timestamps = new DateTime[dataLength]; - var index = 1 + _pointer - dataLength; - Array.Copy(Prices, index, prices, 0, prices.Length); - Array.Copy(Timestamps, index, timestamps, 0, timestamps.Length); - return ValueTask.FromResult((timestamps, prices, i + 1 != 0)); - } - } - } - - public PriceHistoryCacheUnit2(string figi, params ITradeDataItem[] priceChanges) - { - Figi = figi; - - - if (priceChanges.Length == 0) - { - return; - } - - var selectedPriceChanges = priceChanges - .OrderBy(pc => pc.Time) - .Skip(priceChanges.Length - CacheMaxLength) - .ToArray(); - - foreach (var pc in selectedPriceChanges) - { - AddData(pc).AsTask().Wait(); - } - } - } -} diff --git a/KLHZ.Trader.Core.Math/Declisions/Services/Cache/PriceHistoryCacheUnit3.cs b/KLHZ.Trader.Core.Math/Declisions/Services/Cache/PriceHistoryCacheUnit3.cs index 3aed46f..bce1bdb 100644 --- a/KLHZ.Trader.Core.Math/Declisions/Services/Cache/PriceHistoryCacheUnit3.cs +++ b/KLHZ.Trader.Core.Math/Declisions/Services/Cache/PriceHistoryCacheUnit3.cs @@ -1,6 +1,4 @@ -using KLHZ.Trader.Core.Contracts.Declisions.Dtos; -using KLHZ.Trader.Core.Contracts.Declisions.Dtos.Enums; -using KLHZ.Trader.Core.Contracts.Messaging.Dtos.Interfaces; +using KLHZ.Trader.Core.Contracts.Messaging.Dtos.Interfaces; namespace KLHZ.Trader.Core.Math.Declisions.Services.Cache { @@ -29,11 +27,6 @@ namespace KLHZ.Trader.Core.Math.Declisions.Services.Cache private readonly object _locker = new(); private readonly Dictionary> _items = new Dictionary>(); - public ValueTask GetDataFromTimeWindowCache(string key, TimeWindowCacheType timeWindowCacheType) - { - return ValueTask.FromResult(Array.Empty()); - } - public ValueTask AddData(ITradeDataItem item, string? key = null) { lock (_locker) @@ -103,7 +96,6 @@ namespace KLHZ.Trader.Core.Math.Declisions.Services.Cache return ValueTask.FromResult(res.ToArray()); } - public ValueTask<(DateTime time, decimal price)> GetLastValues(string? key = null) { key = key ?? string.Empty; diff --git a/KLHZ.Trader.Core.Math/Declisions/Utils/ShapeAreaCalculator.cs b/KLHZ.Trader.Core.Math/Declisions/Utils/ShapeAreaCalculator.cs deleted file mode 100644 index 135a114..0000000 --- a/KLHZ.Trader.Core.Math/Declisions/Utils/ShapeAreaCalculator.cs +++ /dev/null @@ -1,98 +0,0 @@ -namespace KLHZ.Trader.Core.Math.Declisions.Utils -{ - public static class ShapeAreaCalculator - { - // Метод для расчёта площади треугольника - public static double CalculateTriangleArea( - DateTime pointA_X, decimal pointA_Y, - DateTime pointB_X, decimal pointB_Y, - DateTime pointC_X, decimal pointC_Y) - { - // Определяем самую позднюю точку - DateTime latestPoint = new[] { pointA_X, pointB_X, pointC_X }.Max(); - - // Смещённые координаты - double offsetA = GetOffsetInSeconds(latestPoint, pointA_X); - double offsetB = GetOffsetInSeconds(latestPoint, pointB_X); - double offsetC = GetOffsetInSeconds(latestPoint, pointC_X); - - // Расчёт расстояний - double sideAB = DistanceBetweenPoints(offsetA, (double)pointA_Y, offsetB, (double)pointB_Y); - double sideBC = DistanceBetweenPoints(offsetB, (double)pointB_Y, offsetC, (double)pointC_Y); - double sideCA = DistanceBetweenPoints(offsetC, (double)pointC_Y, offsetA, (double)pointA_Y); - - // Формула Герона - double semiPerimeter = (sideAB + sideBC + sideCA) / 2; - return System.Math.Sqrt(semiPerimeter * (semiPerimeter - sideAB) * - (semiPerimeter - sideBC) * (semiPerimeter - sideCA)); - } - - // Метод для расчёта площади четырёхугольника - public static double CalculateQuadrilateralArea( - DateTime pointA_X, decimal pointA_Y, - DateTime pointB_X, decimal pointB_Y, - DateTime pointC_X, decimal pointC_Y, - DateTime pointD_X, decimal pointD_Y) - { - // Определяем самую позднюю точку - DateTime latestPoint = new[] { pointA_X, pointB_X, pointC_X, pointD_X }.Max(); - - // Смещённые координаты - double offsetA = GetOffsetInSeconds(latestPoint, pointA_X); - double offsetB = GetOffsetInSeconds(latestPoint, pointB_X); - double offsetC = GetOffsetInSeconds(latestPoint, pointC_X); - double offsetD = GetOffsetInSeconds(latestPoint, pointD_X); - - // Суммируем площади двух треугольников - double firstTriangleArea = CalculateTriangleArea(pointA_X, pointA_Y, pointB_X, pointB_Y, pointD_X, pointD_Y); - double secondTriangleArea = CalculateTriangleArea(pointB_X, pointB_Y, pointC_X, pointC_Y, pointD_X, pointD_Y); - - return firstTriangleArea + secondTriangleArea; - } - - // Вспомогательные методы - - // Конвертация разницы времён в секунды - private static double GetOffsetInSeconds(DateTime referencePoint, DateTime targetPoint) - { - return (referencePoint - targetPoint).TotalSeconds; - } - - // Евклидово расстояние между двумя точками - private static double DistanceBetweenPoints(double x1, double y1, double x2, double y2) - { - return System.Math.Sqrt(System.Math.Pow(x2 - x1, 2) + System.Math.Pow(y2 - y1, 2)); - } - - public static bool TryGetAreasRelation(DateTime[] times, decimal[] values, decimal currentValue, TimeSpan boundTimeSpan, out double relation) - { - var upArea = 0d; - var downArea = 0d; - var startTime = times[times.Length - 1]; - for (int i = 1; i < times.Length - 2; i++) - { - var k = values.Length - i; - if (startTime - times[k] > boundTimeSpan) - { - break; - } - var point = (double)(values[k] - currentValue); - var time = times[k]; - var timePrev = times[k - 1]; - var dt = (time - timePrev).TotalSeconds; - var ar = dt * point; - if (ar > 0) - { - upArea += ar; - } - else - { - downArea += System.Math.Abs(ar); - } - } - var area = downArea + upArea; - relation = area != 0 ? (double)upArea / area * 100 : 0; - return area != 0; - } - } -} diff --git a/KLHZ.Trader.Core.Tests/HistoryCacheUnit2Tests.cs b/KLHZ.Trader.Core.Tests/HistoryCacheUnit2Tests.cs deleted file mode 100644 index fd06664..0000000 --- a/KLHZ.Trader.Core.Tests/HistoryCacheUnit2Tests.cs +++ /dev/null @@ -1,293 +0,0 @@ -using KLHZ.Trader.Core.DataLayer.Entities.Prices; -using KLHZ.Trader.Core.Math.Declisions.Services.Cache; - -namespace KLHZ.Trader.Core.Tests -{ - public class HistoryCacheUnit2Tests - { - private static PriceChange[] GetHistory(int count, string figi) - { - var res = new PriceChange[count]; - if (count != 0) - { - var startDt = DateTime.UtcNow.AddSeconds(-count); - for (int i = 0; i < count; i++) - { - startDt = startDt.AddSeconds(1); - res[i] = new PriceChange() - { - Figi = figi, - Ticker = figi + "_ticker", - Id = i, - Time = startDt, - Price = (decimal)(i + 0.5) - }; - } - } - return res; - } - - [Test] - public void Test1() - { - var count = 0; - var figi = "figi"; - var hist = GetHistory(count, figi); - var cacheUnit = new PriceHistoryCacheUnit2("", hist); - var data = cacheUnit.GetData().Result; - - Assert.That(data.prices.Length == count); - Assert.That(data.timestamps.Length == count); - } - - [Test] - public void Test2() - { - var count = 1; - var figi = "figi"; - var hist = GetHistory(count, figi); - var cacheUnit = new PriceHistoryCacheUnit2(figi, hist); - var data = cacheUnit.GetData().Result; - - Assert.That(data.prices.Length == count); - Assert.That(data.timestamps.Length == count); - for (var i = 0; i < count; i++) - { - Assert.That((float)hist[i].Price, Is.EqualTo(data.prices[i])); - Assert.That(hist[i].Time, Is.EqualTo(data.timestamps[i])); - } - } - - [Test] - public void Test3() - { - var count = 20; - var figi = "figi"; - var hist = GetHistory(count, figi); - var cacheUnit = new PriceHistoryCacheUnit2(figi, hist); - var data = cacheUnit.GetData().Result; - - Assert.That(data.prices.Length == count); - Assert.That(data.timestamps.Length == count); - - for (var i = 0; i < count; i++) - { - Assert.That((float)hist[i].Price, Is.EqualTo(data.prices[i])); - Assert.That(hist[i].Time, Is.EqualTo(data.timestamps[i])); - } - } - - [Test] - public void Test4() - { - var count = PriceHistoryCacheUnit2.CacheMaxLength; - var figi = "figi"; - var hist = GetHistory(count, figi); - var cacheUnit = new PriceHistoryCacheUnit2(figi, hist); - var data = cacheUnit.GetData().Result; - - Assert.That(data.prices.Length == count); - Assert.That(data.timestamps.Length == count); - - for (var i = 0; i < count; i++) - { - Assert.That((float)hist[i].Price, Is.EqualTo(data.prices[i])); - Assert.That(hist[i].Time, Is.EqualTo(data.timestamps[i])); - } - } - - [Test] - public void Test5() - { - var shift = 7; - var count = PriceHistoryCacheUnit2.CacheMaxLength + shift; - var figi = "figi"; - var hist = GetHistory(count, figi); - var cacheUnit = new PriceHistoryCacheUnit2(figi, hist); - var data = cacheUnit.GetData().Result; - - Assert.That(data.prices.Length == count - shift); - Assert.That(data.timestamps.Length == count - shift); - - for (var i = 0; i < count; i++) - { - var k = i + shift; - if (k < hist.Length) - { - Assert.That((float)hist[k].Price, Is.EqualTo(data.prices[i])); - Assert.That(hist[k].Time, Is.EqualTo(data.timestamps[i])); - } - } - } - - [Test] - public void Test6() - { - var shift = 10; - var count = PriceHistoryCacheUnit2.CacheMaxLength + shift; - var figi = "figi"; - var hist = GetHistory(count, figi); - var cacheUnit = new PriceHistoryCacheUnit2(figi, hist); - var data = cacheUnit.GetData().Result; - - Assert.That(data.prices.Length == count - shift); - Assert.That(data.timestamps.Length == count - shift); - - for (var i = 0; i < count; i++) - { - var k = i + shift; - if (k < hist.Length) - { - Assert.That((float)hist[k].Price, Is.EqualTo(data.prices[i])); - Assert.That(hist[k].Time, Is.EqualTo(data.timestamps[i])); - } - } - } - - [Test] - public void Test7() - { - var shift = 334; - var count = PriceHistoryCacheUnit2.CacheMaxLength + shift; - var figi = "figi"; - var hist = GetHistory(count, figi); - var cacheUnit = new PriceHistoryCacheUnit2(figi, hist); - var data = cacheUnit.GetData().Result; - - Assert.That(data.prices.Length == count - shift); - Assert.That(data.timestamps.Length == count - shift); - - for (var i = 0; i < count; i++) - { - var k = i + shift; - if (k < hist.Length) - { - Assert.That((float)hist[k].Price, Is.EqualTo(data.prices[i])); - Assert.That(hist[k].Time, Is.EqualTo(data.timestamps[i])); - } - } - } - - - [Test] - public void Test8() - { - var count = PriceHistoryCacheUnit2.CacheMaxLength; - var figi = "figi"; - var hist = GetHistory(count, figi); - var cacheUnit = new PriceHistoryCacheUnit2(figi, hist); - var data = cacheUnit.GetData().Result; - - Assert.That(data.prices.Length == count); - Assert.That(data.timestamps.Length == count); - - for (var i = 0; i < count; i++) - { - Assert.That((float)hist[i].Price, Is.EqualTo(data.prices[i])); - Assert.That(hist[i].Time, Is.EqualTo(data.timestamps[i])); - } - - var newData1 = new PriceChange() { Figi = figi, Ticker = figi, Price = 100500, Time = DateTime.UtcNow }; - - cacheUnit.AddData(newData1); - - var data2 = cacheUnit.GetData().Result; - Assert.IsTrue(data2.prices[data2.prices.Length - 1] == newData1.Price); - Assert.IsTrue(data2.timestamps[data2.timestamps.Length - 1] == newData1.Time); - - var newData2 = new PriceChange() { Figi = figi, Ticker = figi, Price = 100501, Time = DateTime.UtcNow }; - - cacheUnit.AddData(newData2); - - var data3 = cacheUnit.GetData().Result; - Assert.IsTrue(data3.prices[data3.prices.Length - 1] == newData2.Price); - Assert.IsTrue(data3.timestamps[data3.timestamps.Length - 1] == newData2.Time); - } - - [Test] - public void Test9() - { - var cacheUnit = new PriceHistoryCacheUnit2(""); - for (int i = 0; i < 5 * PriceHistoryCacheUnit2.CacheMaxLength; i++) - { - cacheUnit.AddData(new PriceChange() { Figi = "", Ticker = "", Price = i, Time = DateTime.UtcNow }); - if (i >= PriceHistoryCacheUnit2.CacheMaxLength) - { - var data = cacheUnit.GetData().Result; - Assert.IsTrue(data.prices.Length == PriceHistoryCacheUnit2.CacheMaxLength); - Assert.IsTrue(data.prices.Last() == i); - var lastValues = cacheUnit.GetLastValues().Result; - Assert.IsTrue(data.prices.Last() == lastValues.price); - Assert.IsTrue(data.timestamps.Last() == lastValues.time); - } - } - } - - [Test] - public void Test10() - { - var length = 77; - var shift = 334; - var count = PriceHistoryCacheUnit2.CacheMaxLength + shift; - var figi = "figi"; - var hist = GetHistory(count, figi); - var cacheUnit = new PriceHistoryCacheUnit2(figi, hist); - var data = cacheUnit.GetData(length).Result; - - Assert.That(data.prices.Length == length); - Assert.That(data.timestamps.Length == length); - - Assert.That(data.prices.Last() == hist.Last().Price); - Assert.That(data.timestamps.Last() == hist.Last().Time); - for (var i = 1; i <= length; i++) - { - Assert.That(hist[hist.Length - i].Price, Is.EqualTo(data.prices[data.prices.Length - i])); - Assert.That(hist[hist.Length - i].Time, Is.EqualTo(data.timestamps[data.prices.Length - i])); - } - } - - [Test] - public void Test11() - { - var length = 77; - var shift = 334; - var count = PriceHistoryCacheUnit2.CacheMaxLength + shift; - var figi = "figi"; - var hist = GetHistory(count, figi); - var cacheUnit = new PriceHistoryCacheUnit2(figi, hist); - var data = cacheUnit.GetData(length).Result; - var span = TimeSpan.FromSeconds(100); - var dataForPeriod = cacheUnit.GetData(span).Result; - - Assert.That(dataForPeriod.isFullIntervalExists); - Assert.That(data.prices.Last() == dataForPeriod.prices.Last()); - Assert.That(data.timestamps.Last() == dataForPeriod.timestamps.Last()); - var dt = dataForPeriod.timestamps.Last() - dataForPeriod.timestamps.First(); - Assert.That(dt <= span); - Assert.That(data.prices.Length == length); - Assert.That(data.timestamps.Length == length); - - Assert.That(data.prices.Last() == hist.Last().Price); - Assert.That(data.timestamps.Last() == hist.Last().Time); - } - - [Test] - public void Test12() - { - var count = 30; - var figi = "figi"; - var hist = GetHistory(count, figi); - var cacheUnit = new PriceHistoryCacheUnit2(figi, hist); - var data = cacheUnit.GetData().Result; - var span = TimeSpan.FromSeconds(100); - var dataForPeriod = cacheUnit.GetData(span).Result; - - Assert.IsFalse(dataForPeriod.isFullIntervalExists); - Assert.That(data.prices.Last() == dataForPeriod.prices.Last()); - Assert.That(data.timestamps.Last() == dataForPeriod.timestamps.Last()); - - Assert.That(data.prices.First() == dataForPeriod.prices.First()); - Assert.That(data.timestamps.First() == dataForPeriod.timestamps.First()); - } - } -} \ No newline at end of file diff --git a/KLHZ.Trader.Core.Tests/StatisticTests.cs b/KLHZ.Trader.Core.Tests/StatisticTests.cs index b5013ba..e21400b 100644 --- a/KLHZ.Trader.Core.Tests/StatisticTests.cs +++ b/KLHZ.Trader.Core.Tests/StatisticTests.cs @@ -1,4 +1,4 @@ -using KLHZ.Trader.Core.Contracts.Declisions.Dtos; +using KLHZ.Trader.Core.Contracts.Messaging.Dtos; using KLHZ.Trader.Core.Math.Declisions.Utils; using System.Security.Cryptography; @@ -33,7 +33,7 @@ namespace KLHZ.Trader.Core.Tests } - var res = Statistics.CalcHistogram(data.Select(d => new CachedValue() + var res = Statistics.CalcHistogram(data.Select(d => new TradeDataItem() { Figi = "", Ticker = "", diff --git a/KLHZ.Trader.Core/DataLayer/Entities/Prices/PriceChange.cs b/KLHZ.Trader.Core/DataLayer/Entities/Prices/PriceChange.cs index 64eae6b..5f7497d 100644 --- a/KLHZ.Trader.Core/DataLayer/Entities/Prices/PriceChange.cs +++ b/KLHZ.Trader.Core/DataLayer/Entities/Prices/PriceChange.cs @@ -1,4 +1,5 @@ -using KLHZ.Trader.Core.Contracts.Messaging.Dtos.Interfaces; +using KLHZ.Trader.Core.Contracts.Messaging.Dtos; +using KLHZ.Trader.Core.Contracts.Messaging.Dtos.Interfaces; using System.ComponentModel.DataAnnotations.Schema; namespace KLHZ.Trader.Core.DataLayer.Entities.Prices @@ -6,6 +7,8 @@ namespace KLHZ.Trader.Core.DataLayer.Entities.Prices [Table("price_changes")] public class PriceChange : ITradeDataItem { + private readonly object _locker = new(); + [Column("id")] public long Id { get; set; } @@ -34,5 +37,27 @@ namespace KLHZ.Trader.Core.DataLayer.Entities.Prices [NotMapped] public decimal Value2 { get; set; } + + [NotMapped] + public AttachedInfo? AttachedInfo + { + get + { + lock (_locker) + { + return _attachedInfo; + } + } + } + + public void SetAttachedInfo(AttachedInfo? attachedInfo) + { + lock (_locker) + { + _attachedInfo = attachedInfo; + } + } + + private AttachedInfo? _attachedInfo; } } diff --git a/KLHZ.Trader.Core/DataLayer/Entities/Prices/ProcessedPrice.cs b/KLHZ.Trader.Core/DataLayer/Entities/Prices/ProcessedPrice.cs index 1e7c970..a2083c5 100644 --- a/KLHZ.Trader.Core/DataLayer/Entities/Prices/ProcessedPrice.cs +++ b/KLHZ.Trader.Core/DataLayer/Entities/Prices/ProcessedPrice.cs @@ -1,4 +1,5 @@ -using KLHZ.Trader.Core.Contracts.Messaging.Dtos.Interfaces; +using KLHZ.Trader.Core.Contracts.Messaging.Dtos; +using KLHZ.Trader.Core.Contracts.Messaging.Dtos.Interfaces; using System.ComponentModel.DataAnnotations.Schema; namespace KLHZ.Trader.Core.DataLayer.Entities.Prices @@ -37,5 +38,8 @@ namespace KLHZ.Trader.Core.DataLayer.Entities.Prices [NotMapped] public decimal Value2 { get; set; } + + [NotMapped] + public AttachedInfo? AttachedInfo { get; private set; } } } diff --git a/KLHZ.Trader.Core/Exchange/Services/Trader.cs b/KLHZ.Trader.Core/Exchange/Services/Trader.cs index 3368b72..1939c0b 100644 --- a/KLHZ.Trader.Core/Exchange/Services/Trader.cs +++ b/KLHZ.Trader.Core/Exchange/Services/Trader.cs @@ -34,6 +34,7 @@ namespace KLHZ.Trader.Core.Exchange.Services private readonly ConcurrentDictionary SupportLevels = new(); private readonly ConcurrentDictionary _pirsonValues = new(); + private readonly ConcurrentDictionary> SupportLevelsHistory = new(); private readonly ConcurrentDictionary _supportLevelsCalculationTimes = new(); private readonly ConcurrentDictionary _usedSupportLevels = new(); @@ -168,22 +169,32 @@ namespace KLHZ.Trader.Core.Exchange.Services { if (message.Figi == "FUTIMOEXF000") { - await CalcSupportLevels(message, 3, 5); - var stops = GetStops(message); - var pirson = await CalcPirson(message); - var mavRes = await CalcTimeWindowAverageValue(message); - var declisionPirson = await ProcessPirson(pirson, message); - var declisionsSupportLevels = await ProcessSupportLevels(message); - var declisionsStops = ProcessStops(stops, 2m); - var res = TraderUtils.MergeResultsMult(declisionPirson, declisionsSupportLevels); - res = TraderUtils.MergeResultsMult(res, declisionsStops); - res = TraderUtils.MergeResultsMax(res, mavRes); + await CalcSupportLevels(message, 2, 3); - await ExecuteDeclisions(res.ToImmutableDictionary(), message, stops, 1); + var stops = GetStops(message); + + var declisionsSupportLevels = await ProcessSupportLevels(message); + //var pirson = await CalcPirson(message); + //var mavRes = await CalcTimeWindowAverageValue(message); + + //var declisionPirson = ProcessPirson(pirson, message); + //var declisionsStops = ProcessStops(stops, 2m); + + + //var res = TraderUtils.MergeResultsMult(declisionPirson, declisionsSupportLevels); + //res = TraderUtils.MergeResultsMult(res, declisionsStops); + //res = TraderUtils.MergeResultsMax(res, mavRes); + + //await ExecuteDeclisions(res.ToImmutableDictionary(), message, stops, 1); _oldItems[message.Figi] = message; } } + private async Task CheckTradingMode() + { + + } + private async Task> CalcTimeWindowAverageValue(ITradeDataItem message) { var res = TraderUtils.GetInitDict(Constants.BlockingCoefficient); @@ -216,7 +227,7 @@ namespace KLHZ.Trader.Core.Exchange.Services return res.ToImmutableDictionary(); } - private async Task> ProcessPirson(PirsonCalculatingResult pirson, ITradeDataItem message) + private ImmutableDictionary ProcessPirson(PirsonCalculatingResult pirson, ITradeDataItem message) { var res = TraderUtils.GetInitDict(Constants.BlockingCoefficient); if (pirson.Success && _pirsonValues.TryGetValue(message.Figi, out var olddpirs)) @@ -265,7 +276,7 @@ namespace KLHZ.Trader.Core.Exchange.Services { await _tradeDataProvider.LogPrice(message, "privcesDiff", pricesDiff); await _tradeDataProvider.LogPrice(message, "tradevolume_diff", tradesDiff); - await _tradeDataProvider.AddData(message.Figi, "5min_diff", new Contracts.Declisions.Dtos.CachedValue() + await _tradeDataProvider.AddData(message.Figi, "5min_diff", new TradeDataItem() { Time = message.Time, Value2 = tradesDiff, @@ -404,6 +415,22 @@ namespace KLHZ.Trader.Core.Exchange.Services } SupportLevels[message.Figi] = finalLevels; + if (SupportLevelsHistory.TryGetValue(message.Figi, out var list)) + { + list.AddLast(finalLevels); + while (list.Last != null && list.First != null + && list.Last.Value.Length > 0 && list.First.Value.Length > 0 + && (list.Last.Value[0].CalculatedAt - list.First.Value[0].CalculatedAt).TotalHours > 3) + { + list.RemoveFirst(); + } + } + else + { + SupportLevelsHistory[message.Figi] = new LinkedList(); + SupportLevelsHistory[message.Figi].AddLast(finalLevels); + } + await _tradeDataProvider.LogPrice(message, "support_level_calc", message.Price); }