klhztrader/KLHZ.Trader.Core.Math/Declisions/Services/Cache/PriceHistoryCacheUnit2.cs

225 lines
7.9 KiB
C#

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