чистка мусора

dev
vlad zverzhkhovskiy 2025-10-15 12:42:34 +03:00
parent a20609ae72
commit f7cecbe44a
15 changed files with 88 additions and 802 deletions

View File

@ -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; }
}
}

View File

@ -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,
}
}

View File

@ -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<CachedValue[]> GetDataFromTimeWindowCache(string key, TimeWindowCacheType timeWindowCacheType);
/// <summary>
/// Число заявок на продаже в стакане.
/// </summary>
public decimal AsksCount { get; }
/// <summary>
/// Число заявок на покупку в стакане.
/// </summary>
public decimal BidsCount { get; }
public ValueTask<(DateTime time, decimal price)> GetLastValues();
}
}

View File

@ -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; }
}
}

View File

@ -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)
{
}
}
}

View File

@ -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<CachedValue> _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<CachedValue[]> 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);
}
}
}
}
}

View File

@ -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;
}
}
}

View File

@ -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<string, TimeWindowCacheItem> _20_secTimeWindows = new();
private readonly ConcurrentDictionary<string, TimeWindowCacheItem> _1_minTimeWindows = new();
private readonly ConcurrentDictionary<string, TimeWindowCacheItem> _5_minTimeWindows = new();
private readonly ConcurrentDictionary<string, TimeWindowCacheItem> _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<CachedValue[]> GetDataFromTimeWindowCache(string key, TimeWindowCacheType timeWindowCacheType)
{
var dict = GetDict(timeWindowCacheType);
if (dict.TryGetValue(key, out var cahcheItem))
{
return cahcheItem.GetValues();
}
return ValueTask.FromResult(Array.Empty<CachedValue>());
}
private ConcurrentDictionary<string, TimeWindowCacheItem> 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<DateTime>(), Array.Empty<decimal>()));
}
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<DateTime>(), Array.Empty<decimal>(), 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();
}
}
}
}

View File

@ -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<string, LinkedList<ITradeDataItem>> _items = new Dictionary<string, LinkedList<ITradeDataItem>>();
public ValueTask<CachedValue[]> GetDataFromTimeWindowCache(string key, TimeWindowCacheType timeWindowCacheType)
{
return ValueTask.FromResult(Array.Empty<CachedValue>());
}
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;

View File

@ -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;
}
}
}

View File

@ -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());
}
}
}

View File

@ -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 = "",

View File

@ -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;
}
}

View File

@ -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; }
}
}

View File

@ -34,6 +34,7 @@ namespace KLHZ.Trader.Core.Exchange.Services
private readonly ConcurrentDictionary<string, SupportLevel[]> SupportLevels = new();
private readonly ConcurrentDictionary<string, decimal> _pirsonValues = new();
private readonly ConcurrentDictionary<string, LinkedList<SupportLevel[]>> SupportLevelsHistory = new();
private readonly ConcurrentDictionary<string, DateTime> _supportLevelsCalculationTimes = new();
private readonly ConcurrentDictionary<string, DateTime> _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<ImmutableDictionary<TradingEvent, decimal>> 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<ImmutableDictionary<TradingEvent, decimal>> ProcessPirson(PirsonCalculatingResult pirson, ITradeDataItem message)
private ImmutableDictionary<TradingEvent, decimal> 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<SupportLevel[]>();
SupportLevelsHistory[message.Figi].AddLast(finalLevels);
}
await _tradeDataProvider.LogPrice(message, "support_level_calc", message.Price);
}