225 lines
7.8 KiB
C#
225 lines
7.8 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(INewPrice priceChange)
|
|
{
|
|
if (priceChange.Figi != Figi) return ValueTask.CompletedTask;
|
|
lock (_locker)
|
|
{
|
|
_pointer++;
|
|
Prices[_pointer] = priceChange.Value;
|
|
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 INewPrice[] 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();
|
|
}
|
|
}
|
|
}
|
|
}
|