Compare commits
16 Commits
bfa3ef6605
...
022cee9a19
Author | SHA1 | Date |
---|---|---|
|
022cee9a19 | |
|
c2612ba85a | |
|
f4fb12407a | |
|
167c2ba119 | |
|
e57d67d5db | |
|
06cf8ddc19 | |
|
ffc21531b5 | |
|
8aac315338 | |
|
584e378990 | |
|
41d33356dd | |
|
e2726586e5 | |
|
9554b988e3 | |
|
64c702ebf1 | |
|
00de5bee2e | |
|
50938f093e | |
|
03b0f5582e |
|
@ -1,4 +1,4 @@
|
||||||
namespace KLHZ.Trader.Core.Exchange.Models.AssetsAccounting
|
namespace KLHZ.Trader.Core.Contracts.Common.Enums
|
||||||
{
|
{
|
||||||
public enum PositionType
|
public enum PositionType
|
||||||
{
|
{
|
|
@ -1,4 +1,4 @@
|
||||||
namespace KLHZ.Trader.Core.DataLayer.Entities.Trades.Enums
|
namespace KLHZ.Trader.Core.Contracts.Common.Enums
|
||||||
{
|
{
|
||||||
public enum TradeDirection
|
public enum TradeDirection
|
||||||
{
|
{
|
|
@ -1,8 +1,26 @@
|
||||||
namespace KLHZ.Trader.Core.Contracts.Declisions.Dtos
|
using KLHZ.Trader.Core.Contracts.Messaging.Dtos.Interfaces;
|
||||||
|
|
||||||
|
namespace KLHZ.Trader.Core.Contracts.Declisions.Dtos
|
||||||
{
|
{
|
||||||
public class CachedValue
|
public class CachedValue : ITradeDataItem
|
||||||
{
|
{
|
||||||
public DateTime Time { get; init; }
|
public DateTime Time { get; init; }
|
||||||
|
public long Count { get; init; }
|
||||||
|
public decimal Price { get; init; }
|
||||||
public decimal Value { 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; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,14 +4,9 @@
|
||||||
public enum TradingEvent
|
public enum TradingEvent
|
||||||
{
|
{
|
||||||
None = 0,
|
None = 0,
|
||||||
StopBuy = 1,
|
CloseLong = 1,
|
||||||
LongOpen = 2,
|
OpenLong = 2,
|
||||||
ShortClose = 4,
|
CloseShort = 4,
|
||||||
LongClose = 8,
|
OpenShort = 8,
|
||||||
ShortOpen = 16,
|
|
||||||
UptrendEnd = 32,
|
|
||||||
UptrendStart = 64,
|
|
||||||
DowntrendEnd = 128,
|
|
||||||
DowntrendStart = 256,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,7 @@ namespace KLHZ.Trader.Core.Contracts.Declisions.Interfaces
|
||||||
{
|
{
|
||||||
public string Figi { get; }
|
public string Figi { get; }
|
||||||
public int Length { get; }
|
public int Length { get; }
|
||||||
public ValueTask AddData(INewPrice priceChange);
|
public ValueTask AddData(ITradeDataItem priceChange);
|
||||||
public ValueTask<(DateTime[] timestamps, decimal[] prices)> GetData(int? length = null);
|
public ValueTask<(DateTime[] timestamps, decimal[] prices)> GetData(int? length = null);
|
||||||
public ValueTask<(DateTime[] timestamps, decimal[] prices, bool isFullIntervalExists)> GetData(TimeSpan period);
|
public ValueTask<(DateTime[] timestamps, decimal[] prices, bool isFullIntervalExists)> GetData(TimeSpan period);
|
||||||
public ValueTask AddOrderbook(IOrderbook orderbook);
|
public ValueTask AddOrderbook(IOrderbook orderbook);
|
||||||
|
|
|
@ -1,9 +0,0 @@
|
||||||
namespace KLHZ.Trader.Core.Contracts.Messaging.Dtos.Interfaces
|
|
||||||
{
|
|
||||||
public interface ILockableObject
|
|
||||||
{
|
|
||||||
public Task<bool> Lock(TimeSpan duration);
|
|
||||||
|
|
||||||
public void Unlock();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,15 +0,0 @@
|
||||||
namespace KLHZ.Trader.Core.Contracts.Messaging.Dtos.Interfaces
|
|
||||||
{
|
|
||||||
public interface INewCandle
|
|
||||||
{
|
|
||||||
public bool IsHistoricalData { get; set; }
|
|
||||||
public decimal Open { get; set; }
|
|
||||||
public decimal Close { get; set; }
|
|
||||||
public decimal High { get; set; }
|
|
||||||
public decimal Low { get; set; }
|
|
||||||
public decimal Volume { get; set; }
|
|
||||||
public string Figi { get; set; }
|
|
||||||
public string Ticker { get; set; }
|
|
||||||
public DateTime Time { get; set; }
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,13 +0,0 @@
|
||||||
namespace KLHZ.Trader.Core.Contracts.Messaging.Dtos.Interfaces
|
|
||||||
{
|
|
||||||
public interface INewPrice
|
|
||||||
{
|
|
||||||
public bool IsHistoricalData { get; set; }
|
|
||||||
public decimal Value { get; set; }
|
|
||||||
public string Figi { get; set; }
|
|
||||||
public string Ticker { get; set; }
|
|
||||||
public DateTime Time { get; set; }
|
|
||||||
public long Count { get; set; }
|
|
||||||
public int Direction { get; set; }
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,6 +1,6 @@
|
||||||
namespace KLHZ.Trader.Core.Contracts.Messaging.Dtos.Interfaces
|
namespace KLHZ.Trader.Core.Contracts.Messaging.Dtos.Interfaces
|
||||||
{
|
{
|
||||||
public interface IProcessedPrice : INewPrice
|
public interface IProcessedPrice : ITradeDataItem
|
||||||
{
|
{
|
||||||
public string Processor { get; set; }
|
public string Processor { get; set; }
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,6 +12,5 @@ namespace KLHZ.Trader.Core.Contracts.Messaging.Dtos.Interfaces
|
||||||
public string AccountId { get; }
|
public string AccountId { get; }
|
||||||
public string? OrderId { get; }
|
public string? OrderId { get; }
|
||||||
public bool EnableMargin { get; }
|
public bool EnableMargin { get; }
|
||||||
public ILockableObject? ExchangeObject { get; }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
namespace KLHZ.Trader.Core.Contracts.Messaging.Dtos.Interfaces
|
||||||
|
{
|
||||||
|
public interface ITradeDataItem
|
||||||
|
{
|
||||||
|
public bool IsHistoricalData { get; }
|
||||||
|
public decimal Price { get; }
|
||||||
|
public string Figi { get; }
|
||||||
|
public string Ticker { get; }
|
||||||
|
public DateTime Time { get; }
|
||||||
|
public long Count { get; }
|
||||||
|
public int Direction { get; }
|
||||||
|
public decimal Value { get; }
|
||||||
|
public decimal Value2 { get; }
|
||||||
|
}
|
||||||
|
}
|
|
@ -12,7 +12,6 @@ namespace KLHZ.Trader.Core.Contracts.Messaging.Dtos
|
||||||
public long Count { get; init; }
|
public long Count { get; init; }
|
||||||
public required string AccountId { get; init; }
|
public required string AccountId { get; init; }
|
||||||
public bool EnableMargin { get; init; } = true;
|
public bool EnableMargin { get; init; } = true;
|
||||||
public ILockableObject? ExchangeObject { get; init; }
|
|
||||||
public string? OrderId { get; init; }
|
public string? OrderId { get; init; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,14 +2,16 @@
|
||||||
|
|
||||||
namespace KLHZ.Trader.Core.Contracts.Messaging.Dtos
|
namespace KLHZ.Trader.Core.Contracts.Messaging.Dtos
|
||||||
{
|
{
|
||||||
public class NewPriceMessage : INewPrice
|
public class TradeDataItem : ITradeDataItem
|
||||||
{
|
{
|
||||||
public decimal Value { get; set; }
|
public decimal Price { get; set; }
|
||||||
public required string Figi { get; set; }
|
public required string Figi { get; set; }
|
||||||
public required string Ticker { get; set; }
|
public required string Ticker { get; set; }
|
||||||
public DateTime Time { get; set; }
|
public DateTime Time { get; set; }
|
||||||
public bool IsHistoricalData { get; set; }
|
public bool IsHistoricalData { get; set; }
|
||||||
public long Count { get; set; }
|
public long Count { get; set; }
|
||||||
public int Direction { get; set; }
|
public int Direction { get; set; }
|
||||||
|
public decimal Value { get; init; }
|
||||||
|
public decimal Value2 { get; init; }
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -6,10 +6,10 @@ namespace KLHZ.Trader.Core.Contracts.Messaging.Interfaces
|
||||||
public interface IDataBus
|
public interface IDataBus
|
||||||
{
|
{
|
||||||
public bool AddChannel(string key, Channel<IOrderbook> channel);
|
public bool AddChannel(string key, Channel<IOrderbook> channel);
|
||||||
public bool AddChannel(string key, Channel<INewPrice> channel);
|
public bool AddChannel(string key, Channel<ITradeDataItem> channel);
|
||||||
public bool AddChannel(string key, Channel<IMessage> channel);
|
public bool AddChannel(string key, Channel<IMessage> channel);
|
||||||
public bool AddChannel(string key, Channel<ITradeCommand> channel);
|
public bool AddChannel(string key, Channel<ITradeCommand> channel);
|
||||||
public Task Broadcast(INewPrice newPriceMessage);
|
public Task Broadcast(ITradeDataItem newPriceMessage);
|
||||||
public Task Broadcast(IOrderbook orderbook);
|
public Task Broadcast(IOrderbook orderbook);
|
||||||
public Task Broadcast(IMessage message);
|
public Task Broadcast(IMessage message);
|
||||||
public Task Broadcast(ITradeCommand message);
|
public Task Broadcast(ITradeCommand message);
|
||||||
|
|
|
@ -0,0 +1,10 @@
|
||||||
|
namespace KLHZ.Trader.Core.Math.Declisions.Dtos
|
||||||
|
{
|
||||||
|
public class ConvolutionResult
|
||||||
|
{
|
||||||
|
public decimal Sum { get; set; }
|
||||||
|
public decimal Value { get; set; }
|
||||||
|
public int Shift { get; set; }
|
||||||
|
public decimal Leverage { get; set; }
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
namespace KLHZ.Trader.Core.Math.Declisions.Dtos
|
||||||
|
{
|
||||||
|
public class HistItem
|
||||||
|
{
|
||||||
|
public decimal Value { get; set; }
|
||||||
|
public decimal Count { get; set; }
|
||||||
|
}
|
||||||
|
}
|
|
@ -22,7 +22,7 @@ namespace KLHZ.Trader.Core.Math.Declisions.Dtos
|
||||||
lock (_locker)
|
lock (_locker)
|
||||||
{
|
{
|
||||||
_cachedValues.AddLast(cachedValue);
|
_cachedValues.AddLast(cachedValue);
|
||||||
if (_cachedValues.Last != null && _cachedValues.First != null
|
while (_cachedValues.Last != null && _cachedValues.First != null
|
||||||
&& _cachedValues.Last.Value.Time - _cachedValues.First.Value.Time > WindowSize)
|
&& _cachedValues.Last.Value.Time - _cachedValues.First.Value.Time > WindowSize)
|
||||||
{
|
{
|
||||||
_cachedValues.RemoveFirst();
|
_cachedValues.RemoveFirst();
|
||||||
|
|
|
@ -105,13 +105,13 @@ namespace KLHZ.Trader.Core.Math.Declisions.Services.Cache
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public ValueTask AddData(INewPrice priceChange)
|
public ValueTask AddData(ITradeDataItem priceChange)
|
||||||
{
|
{
|
||||||
if (priceChange.Figi != Figi) return ValueTask.CompletedTask;
|
if (priceChange.Figi != Figi) return ValueTask.CompletedTask;
|
||||||
lock (_locker)
|
lock (_locker)
|
||||||
{
|
{
|
||||||
_pointer++;
|
_pointer++;
|
||||||
Prices[_pointer] = priceChange.Value;
|
Prices[_pointer] = priceChange.Price;
|
||||||
Timestamps[_pointer] = priceChange.Time;
|
Timestamps[_pointer] = priceChange.Time;
|
||||||
if (_length < CacheMaxLength)
|
if (_length < CacheMaxLength)
|
||||||
{
|
{
|
||||||
|
@ -200,7 +200,7 @@ namespace KLHZ.Trader.Core.Math.Declisions.Services.Cache
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public PriceHistoryCacheUnit2(string figi, params INewPrice[] priceChanges)
|
public PriceHistoryCacheUnit2(string figi, params ITradeDataItem[] priceChanges)
|
||||||
{
|
{
|
||||||
Figi = figi;
|
Figi = figi;
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,168 @@
|
||||||
|
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.Math.Declisions.Services.Cache
|
||||||
|
{
|
||||||
|
public class PriceHistoryCacheUnit3
|
||||||
|
{
|
||||||
|
private readonly Dictionary<string, TimeSpan> _windowSizes = new()
|
||||||
|
{
|
||||||
|
{string.Empty, TimeSpan.FromDays(3)}
|
||||||
|
};
|
||||||
|
|
||||||
|
public string Figi { get; init; }
|
||||||
|
|
||||||
|
public IOrderbook? Orderbook
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
lock (_locker)
|
||||||
|
{
|
||||||
|
return _orderbook;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private IOrderbook? _orderbook;
|
||||||
|
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
key = key ?? string.Empty;
|
||||||
|
if (key == string.Empty)
|
||||||
|
{
|
||||||
|
if (item.Figi != Figi)
|
||||||
|
{
|
||||||
|
throw new ArgumentException("Для дефолтного кеша Figi должен совпадать с figi элемента.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var windowSize = _windowSizes.TryGetValue(key, out var size) ? size : TimeSpan.FromMinutes(60);
|
||||||
|
if (!_items.TryGetValue(key, out var list))
|
||||||
|
{
|
||||||
|
list = new LinkedList<ITradeDataItem>();
|
||||||
|
_items[key] = list;
|
||||||
|
}
|
||||||
|
list.AddFirst(item);
|
||||||
|
while (list.Last != null && list.First != null
|
||||||
|
&& list.First.Value.Time - list.Last.Value.Time > windowSize)
|
||||||
|
{
|
||||||
|
list.RemoveLast();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ValueTask.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ValueTask AddOrderbook(IOrderbook orderbook)
|
||||||
|
{
|
||||||
|
if (orderbook.Figi != Figi) return ValueTask.CompletedTask;
|
||||||
|
lock (_locker)
|
||||||
|
{
|
||||||
|
_orderbook = orderbook;
|
||||||
|
}
|
||||||
|
return ValueTask.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ValueTask<ITradeDataItem[]> GetData(TimeSpan? period = null, string? key = null, Func<ITradeDataItem, bool>? selector = null)
|
||||||
|
{
|
||||||
|
key = key ?? string.Empty;
|
||||||
|
var res = new List<ITradeDataItem>();
|
||||||
|
selector = selector ?? defaultSelector;
|
||||||
|
lock (_locker)
|
||||||
|
{
|
||||||
|
if (_items.TryGetValue(key, out var list) && list.First != null && list.Last != null)
|
||||||
|
{
|
||||||
|
var startTime = list.First.Value.Time;
|
||||||
|
if (period.HasValue)
|
||||||
|
{
|
||||||
|
foreach (var item in list)
|
||||||
|
{
|
||||||
|
if (startTime - item.Time < period.Value && selector(item))
|
||||||
|
{
|
||||||
|
res.Add(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return ValueTask.FromResult(list.Where(selector).Reverse().ToArray());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
res.Reverse();
|
||||||
|
return ValueTask.FromResult(res.ToArray());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public ValueTask<(DateTime time, decimal price)> GetLastValues(string? key = null)
|
||||||
|
{
|
||||||
|
key = key ?? string.Empty;
|
||||||
|
lock (_locker)
|
||||||
|
{
|
||||||
|
if (_items.TryGetValue(key, out var list) && list.First != null && list.Last != null)
|
||||||
|
{
|
||||||
|
return ValueTask.FromResult((list.Last.Value.Time, list.Last.Value.Price));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ValueTask.FromResult((DateTime.MinValue, 0m));
|
||||||
|
}
|
||||||
|
|
||||||
|
public ValueTask<ITradeDataItem[]> GetData(TimeSpan shift, TimeSpan period, string? key = null, Func<ITradeDataItem, bool>? selector = null)
|
||||||
|
{
|
||||||
|
key = key ?? string.Empty;
|
||||||
|
var res = new List<ITradeDataItem>();
|
||||||
|
var maxPeriod = shift + period;
|
||||||
|
selector = selector ?? defaultSelector;
|
||||||
|
lock (_locker)
|
||||||
|
{
|
||||||
|
if (_items.TryGetValue(key, out var list) && list.First != null && list.Last != null)
|
||||||
|
{
|
||||||
|
var startTime = list.First.Value.Time;
|
||||||
|
|
||||||
|
foreach (var item in list)
|
||||||
|
{
|
||||||
|
var dt = startTime - item.Time;
|
||||||
|
if (dt > shift && dt < maxPeriod && selector(item))
|
||||||
|
{
|
||||||
|
res.Add(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
res.Reverse();
|
||||||
|
return ValueTask.FromResult(res.ToArray());
|
||||||
|
}
|
||||||
|
|
||||||
|
public PriceHistoryCacheUnit3(string figi, params ITradeDataItem[] priceChanges)
|
||||||
|
{
|
||||||
|
Figi = figi;
|
||||||
|
|
||||||
|
|
||||||
|
if (priceChanges.Length == 0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var selectedPriceChanges = priceChanges
|
||||||
|
.OrderBy(pc => pc.Time)
|
||||||
|
.ToArray();
|
||||||
|
|
||||||
|
foreach (var pc in selectedPriceChanges)
|
||||||
|
{
|
||||||
|
AddData(pc).AsTask().Wait();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool defaultSelector(ITradeDataItem item) => true;
|
||||||
|
}
|
||||||
|
}
|
|
@ -71,19 +71,19 @@ namespace KLHZ.Trader.Core.Math.Declisions.Utils
|
||||||
var diff2 = y2_approximated[0] - y2_approximated[y2_approximated.Count - 1];
|
var diff2 = y2_approximated[0] - y2_approximated[y2_approximated.Count - 1];
|
||||||
if (diff1 <= -meanfullDiff && diff2 >= meanfullDiff)
|
if (diff1 <= -meanfullDiff && diff2 >= meanfullDiff)
|
||||||
{
|
{
|
||||||
res |= TradingEvent.DowntrendEnd;
|
res |= TradingEvent.CloseShort;
|
||||||
}
|
}
|
||||||
else if (diff1 >= meanfullDiff && diff2 <= -meanfullDiff)
|
else if (diff1 >= meanfullDiff && diff2 <= -meanfullDiff)
|
||||||
{
|
{
|
||||||
res |= TradingEvent.UptrendEnd;
|
res |= TradingEvent.CloseLong;
|
||||||
}
|
}
|
||||||
else if (diff1 <= -meanfullDiff && diff2 >= meanfullDiff)
|
else if (diff1 <= -meanfullDiff && diff2 >= meanfullDiff)
|
||||||
{
|
{
|
||||||
res |= TradingEvent.DowntrendEnd;
|
res |= TradingEvent.CloseShort;
|
||||||
}
|
}
|
||||||
else if (diff1 >= 0 && diff2 <= -meanfullDiff)
|
else if (diff1 >= 0 && diff2 <= -meanfullDiff)
|
||||||
{
|
{
|
||||||
res |= TradingEvent.DowntrendStart;
|
res |= TradingEvent.OpenShort;
|
||||||
}
|
}
|
||||||
success = true;
|
success = true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -97,7 +97,7 @@ namespace KLHZ.Trader.Core.Math.Declisions.Utils
|
||||||
if (diffTotal >= uptrendEndingDetectionMeanfullStep
|
if (diffTotal >= uptrendEndingDetectionMeanfullStep
|
||||||
&& times[crossings[0]] - times[crossings[1]] >= timeForUptreandStart)
|
&& times[crossings[0]] - times[crossings[1]] >= timeForUptreandStart)
|
||||||
{
|
{
|
||||||
res |= TradingEvent.UptrendEnd;
|
res |= TradingEvent.CloseLong;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -115,7 +115,7 @@ namespace KLHZ.Trader.Core.Math.Declisions.Utils
|
||||||
if (pricesForFinalComparison[crossings[0]] - pricesForFinalComparison[crossings[1]] <= uptrendStartingDetectionMeanfullStep
|
if (pricesForFinalComparison[crossings[0]] - pricesForFinalComparison[crossings[1]] <= uptrendStartingDetectionMeanfullStep
|
||||||
&& times[crossings[0]] - times[crossings[1]] >= timeForUptreandStart)
|
&& times[crossings[0]] - times[crossings[1]] >= timeForUptreandStart)
|
||||||
{
|
{
|
||||||
res |= TradingEvent.UptrendStart;
|
res |= TradingEvent.OpenLong;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -188,23 +188,23 @@ namespace KLHZ.Trader.Core.Math.Declisions.Utils
|
||||||
// если фильтрация окном 15 наползает на окно 120 сверху, потенциальное время закрытия лонга и возможно открытия шорта
|
// если фильтрация окном 15 наползает на окно 120 сверху, потенциальное время закрытия лонга и возможно открытия шорта
|
||||||
if (twavss[size - 1] <= twavbs[size - 1] && twavss[size - 2] > twavbs[size - 2])
|
if (twavss[size - 1] <= twavbs[size - 1] && twavss[size - 2] > twavbs[size - 2])
|
||||||
{
|
{
|
||||||
if (!uptrendEndingDetectionMeanfullStep.HasValue || ((d1 >= uptrendEndingDetectionMeanfullStep
|
if (!uptrendEndingDetectionMeanfullStep.HasValue || ((d1 >= uptrendEndingDetectionMeanfullStep
|
||||||
//|| d2 >= uptrendEndingDetectionMeanfullStep
|
//|| d2 >= uptrendEndingDetectionMeanfullStep
|
||||||
)
|
)
|
||||||
&& dt>TimeSpan.FromSeconds(10)))
|
&& dt > TimeSpan.FromSeconds(10)))
|
||||||
{
|
{
|
||||||
res |= TradingEvent.UptrendEnd;
|
res |= TradingEvent.CloseLong;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
// если фильтрация окном 120 наползает на окно 15 сверху, потенциальное время открытия лонга и закрытия шорта
|
// если фильтрация окном 120 наползает на окно 15 сверху, потенциальное время открытия лонга и закрытия шорта
|
||||||
if (twavss[size - 1] >= twavbs[size - 1] && twavss[size - 2] < twavbs[size - 2])
|
if (twavss[size - 1] >= twavbs[size - 1] && twavss[size - 2] < twavbs[size - 2])
|
||||||
{
|
{
|
||||||
if (!uptrendStartingDetectionMeanfullStep.HasValue ||( (d1 <= uptrendStartingDetectionMeanfullStep
|
if (!uptrendStartingDetectionMeanfullStep.HasValue || ((d1 <= uptrendStartingDetectionMeanfullStep
|
||||||
// || d2 <= uptrendStartingDetectionMeanfullStep
|
// || d2 <= uptrendStartingDetectionMeanfullStep
|
||||||
) && dt > TimeSpan.FromSeconds(10)))
|
) && dt > TimeSpan.FromSeconds(10)))
|
||||||
{
|
{
|
||||||
res |= TradingEvent.UptrendStart;
|
res |= TradingEvent.OpenLong;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,7 @@
|
||||||
namespace KLHZ.Trader.Core.Math.Declisions.Utils
|
using KLHZ.Trader.Core.Contracts.Messaging.Dtos;
|
||||||
|
using KLHZ.Trader.Core.Contracts.Messaging.Dtos.Interfaces;
|
||||||
|
|
||||||
|
namespace KLHZ.Trader.Core.Math.Declisions.Utils
|
||||||
{
|
{
|
||||||
public static class SignalProcessing
|
public static class SignalProcessing
|
||||||
{
|
{
|
||||||
|
@ -54,15 +57,112 @@
|
||||||
return (res2.ToArray(), res.ToArray());
|
return (res2.ToArray(), res.ToArray());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static ITradeDataItem[] InterpolateData(ITradeDataItem[] items, TimeSpan timeStep)
|
||||||
|
{
|
||||||
|
var result = new List<ITradeDataItem>();
|
||||||
|
|
||||||
|
var firstItem = items[0];
|
||||||
|
var startTime = new DateTime(firstItem.Time.Year, firstItem.Time.Month, firstItem.Time.Day, 0, 0, 0, DateTimeKind.Utc);
|
||||||
|
var dt = items[0].Time - startTime;
|
||||||
|
|
||||||
|
var totalSteps = System.Math.Ceiling((items[items.Length - 1].Time - firstItem.Time).TotalSeconds / timeStep.TotalSeconds);
|
||||||
|
var deltaSeconds = System.Math.Floor(dt.TotalSeconds / timeStep.TotalSeconds);
|
||||||
|
startTime = startTime.AddSeconds(deltaSeconds * timeStep.TotalSeconds);
|
||||||
|
|
||||||
|
var firstBound = startTime;
|
||||||
|
var secondBound = startTime + timeStep;
|
||||||
|
var boundD1 = 0;
|
||||||
|
var boundD2 = 0;
|
||||||
|
for (int i = 0; i < totalSteps; i++)
|
||||||
|
{
|
||||||
|
var countD1 = 0;
|
||||||
|
var sumD1 = 0m;
|
||||||
|
var cD1 = 0L;
|
||||||
|
|
||||||
|
var countD2 = 0;
|
||||||
|
var sumD2 = 0m;
|
||||||
|
var cD2 = 0L;
|
||||||
|
|
||||||
|
for (int i1 = boundD1; i1 < items.Length; i1++)
|
||||||
|
{
|
||||||
|
if (items[i1].Direction == 1)
|
||||||
|
{
|
||||||
|
if (items[i1].Time > firstBound && items[i1].Time <= secondBound)
|
||||||
|
{
|
||||||
|
countD1++;
|
||||||
|
sumD1 += items[i1].Price;
|
||||||
|
cD1 += items[i1].Count;
|
||||||
|
}
|
||||||
|
else if (countD1 != 0)
|
||||||
|
{
|
||||||
|
boundD1 = i1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i1 = boundD2; i1 < items.Length; i1++)
|
||||||
|
{
|
||||||
|
if (items[i1].Direction == 2)
|
||||||
|
{
|
||||||
|
if (items[i1].Time > firstBound && items[i1].Time <= secondBound)
|
||||||
|
{
|
||||||
|
countD2++;
|
||||||
|
sumD2 += items[i1].Price;
|
||||||
|
cD2 += items[i1].Count;
|
||||||
|
}
|
||||||
|
else if (countD2 != 0)
|
||||||
|
{
|
||||||
|
boundD2 = i1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (countD1 != 0)
|
||||||
|
{
|
||||||
|
result.Add(new TradeDataItem()
|
||||||
|
{
|
||||||
|
Figi = firstItem.Figi,
|
||||||
|
Ticker = firstItem.Ticker,
|
||||||
|
Price = sumD1 / countD1,
|
||||||
|
Time = secondBound,
|
||||||
|
IsHistoricalData = firstItem.IsHistoricalData,
|
||||||
|
Direction = 1,
|
||||||
|
Count = cD1
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (countD2 != 0)
|
||||||
|
{
|
||||||
|
result.Add(new TradeDataItem()
|
||||||
|
{
|
||||||
|
Figi = firstItem.Figi,
|
||||||
|
Ticker = firstItem.Ticker,
|
||||||
|
Price = sumD2 / countD2,
|
||||||
|
Time = secondBound,
|
||||||
|
IsHistoricalData = firstItem.IsHistoricalData,
|
||||||
|
Direction = 2,
|
||||||
|
Count = cD2
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
firstBound += timeStep;
|
||||||
|
secondBound += timeStep;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return result.OrderBy(r => r.Time).ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
public static decimal[] CalcDiffs(decimal[] values)
|
public static decimal[] CalcDiffs(decimal[] values)
|
||||||
{
|
{
|
||||||
if (values.Length < 1) throw new ArgumentException();
|
if (values.Length < 1) throw new ArgumentException();
|
||||||
|
|
||||||
var resArray = new decimal[values.Length-1];
|
var resArray = new decimal[values.Length - 1];
|
||||||
for (int i=1; i<values.Length; i++)
|
for (int i = 1; i < values.Length; i++)
|
||||||
{
|
{
|
||||||
resArray[i - 1] = values[i] - values[i-1];
|
resArray[i - 1] = values[i] - values[i - 1];
|
||||||
}
|
}
|
||||||
return resArray;
|
return resArray;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,18 +1,19 @@
|
||||||
using KLHZ.Trader.Core.Contracts.Declisions.Dtos;
|
using KLHZ.Trader.Core.Contracts.Messaging.Dtos.Interfaces;
|
||||||
using System;
|
using KLHZ.Trader.Core.Math.Declisions.Dtos;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace KLHZ.Trader.Core.Math.Declisions.Utils
|
namespace KLHZ.Trader.Core.Math.Declisions.Utils
|
||||||
{
|
{
|
||||||
public static class Statistics
|
public static class Statistics
|
||||||
{
|
{
|
||||||
|
|
||||||
public static decimal Mean(this CachedValue[] values)
|
public static decimal MeanCount(this ITradeDataItem[] values)
|
||||||
{
|
{
|
||||||
return values.Sum(x => x.Value)/ values.Length;
|
return values.Sum(x => x.Count) / values.Length;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static decimal MeanPrice(this ITradeDataItem[] values)
|
||||||
|
{
|
||||||
|
return values.Sum(x => x.Price) / values.Length;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static (decimal mean, decimal std) CaclSigma(decimal[] values)
|
private static (decimal mean, decimal std) CaclSigma(decimal[] values)
|
||||||
|
@ -26,12 +27,12 @@ namespace KLHZ.Trader.Core.Math.Declisions.Utils
|
||||||
data[i] = v * v;
|
data[i] = v * v;
|
||||||
}
|
}
|
||||||
var std = System.Math.Pow((double)(data.Sum() / (data.Length - 1)), 0.5);
|
var std = System.Math.Pow((double)(data.Sum() / (data.Length - 1)), 0.5);
|
||||||
return (mean,(decimal)std);
|
return (mean, (decimal)std);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static decimal[] ClearNSigmaReqursive(decimal[] values, int depth = 0, int sigmasCount = 3)
|
public static decimal[] ClearNSigmaReqursive(decimal[] values, int depth = 0, int sigmasCount = 3)
|
||||||
{
|
{
|
||||||
if (values.Length <= 1 || depth>10) return values;
|
if (values.Length <= 1 || depth > 10) return values;
|
||||||
var sigmaRes = CaclSigma(values);
|
var sigmaRes = CaclSigma(values);
|
||||||
var std = sigmaRes.std;
|
var std = sigmaRes.std;
|
||||||
var mean = sigmaRes.mean;
|
var mean = sigmaRes.mean;
|
||||||
|
@ -39,19 +40,182 @@ namespace KLHZ.Trader.Core.Math.Declisions.Utils
|
||||||
var _3std = sigmasCount * std;
|
var _3std = sigmasCount * std;
|
||||||
foreach (var v in values)
|
foreach (var v in values)
|
||||||
{
|
{
|
||||||
if (System.Math.Abs(mean - v)< _3std)
|
if (System.Math.Abs(mean - v) < _3std)
|
||||||
{
|
{
|
||||||
forRes.Add(v);
|
forRes.Add(v);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (forRes.Count != values.Length)
|
if (forRes.Count != values.Length)
|
||||||
{
|
{
|
||||||
return ClearNSigmaReqursive(forRes.ToArray(), depth+1);
|
return ClearNSigmaReqursive(forRes.ToArray(), depth + 1);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
return forRes.ToArray();
|
return forRes.ToArray();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static bool TryCalcTimeWindowsDiff(this ITradeDataItem[] values, TimeSpan boundLeft, TimeSpan boundRight,
|
||||||
|
Func<ITradeDataItem, decimal> fieldSelector, bool calcMean, out decimal result, out decimal resultRelative)
|
||||||
|
{
|
||||||
|
result = default;
|
||||||
|
resultRelative = default;
|
||||||
|
if (values.Length > 1)
|
||||||
|
{
|
||||||
|
var shiftTimeR = values.Last().Time - boundRight;
|
||||||
|
var shiftTimeL = values.Last().Time - boundLeft;
|
||||||
|
|
||||||
|
var valuesOld = values.Where(b => b.Time < shiftTimeR && b.Time >= shiftTimeL).ToArray();
|
||||||
|
var valuesNew = values.Where(b => b.Time >= shiftTimeR).ToArray();
|
||||||
|
|
||||||
|
if (valuesOld.Length > 0 && valuesNew.Length > 0)
|
||||||
|
{
|
||||||
|
var valNew = valuesNew.Sum(fieldSelector);
|
||||||
|
var valOld = valuesOld.Sum(fieldSelector);
|
||||||
|
|
||||||
|
if (calcMean)
|
||||||
|
{
|
||||||
|
valNew = valNew / valuesNew.Length;
|
||||||
|
valOld = valOld / valuesOld.Length;
|
||||||
|
}
|
||||||
|
result = valNew - valOld;
|
||||||
|
resultRelative = (valNew - valOld) / valOld;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
public static bool TryCalcTimeDiff(this ITradeDataItem[] values, TimeSpan boundLeft, TimeSpan boundRight,
|
||||||
|
Func<ITradeDataItem, decimal> fieldSelector, bool calcMean, out decimal result)
|
||||||
|
{
|
||||||
|
result = default;
|
||||||
|
if (values.Length > 1)
|
||||||
|
{
|
||||||
|
var shiftTimeR = values.Last().Time - boundRight;
|
||||||
|
var shiftTimeL = values.Last().Time - boundLeft;
|
||||||
|
|
||||||
|
var valuesOld = values.Where(b => b.Time < shiftTimeR && b.Time >= shiftTimeL).ToArray();
|
||||||
|
var valuesNew = values.Where(b => b.Time >= shiftTimeR).ToArray();
|
||||||
|
|
||||||
|
if (valuesOld.Length > 0 && valuesNew.Length > 0)
|
||||||
|
{
|
||||||
|
var valNew = fieldSelector(valuesNew.Last());
|
||||||
|
var valOld = fieldSelector(valuesOld.Last());
|
||||||
|
|
||||||
|
result = valNew - valOld;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
public static bool TryCalcPirsonCorrelation(this ITradeDataItem[] values, TimeSpan period, out decimal result)
|
||||||
|
{
|
||||||
|
result = default;
|
||||||
|
if (values.Any())
|
||||||
|
{
|
||||||
|
var shiftTimeDiffs1 = values.Last().Time - period;
|
||||||
|
values = values.Where(b => b.Time >= shiftTimeDiffs1).ToArray();
|
||||||
|
if (values.Any())
|
||||||
|
{
|
||||||
|
var tradevolume_diffMean = values.MeanCount();
|
||||||
|
var dprice_diffMean = values.MeanPrice();
|
||||||
|
var sum1 = (double)values.Sum(d => (d.Value2 - tradevolume_diffMean) * (d.Value - dprice_diffMean));
|
||||||
|
var sum2 = values.Sum(d => (d.Value2 - tradevolume_diffMean) * (d.Value2 - tradevolume_diffMean));
|
||||||
|
var sum3 = values.Sum(d => (d.Value - dprice_diffMean) * (d.Value - dprice_diffMean));
|
||||||
|
if (sum2 != 0 && sum3 != 0)
|
||||||
|
{
|
||||||
|
result = (decimal)(sum1 / System.Math.Sqrt((double)(sum2 * sum3)));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static HistItem[] CalcHistogram(ITradeDataItem[] values)
|
||||||
|
{
|
||||||
|
var result = new Dictionary<decimal, decimal>();
|
||||||
|
foreach (var item in values)
|
||||||
|
{
|
||||||
|
if (!result.TryGetValue(item.Price, out var val))
|
||||||
|
{
|
||||||
|
result[item.Price] = 0;
|
||||||
|
}
|
||||||
|
result[item.Price] = val + 1;
|
||||||
|
}
|
||||||
|
return result.Select(r => new HistItem() { Value = r.Key, Count = r.Value }).OrderBy(i => i.Value).ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static decimal[] GetParabolaCore(int leverageSize, decimal maxValue = 1)
|
||||||
|
{
|
||||||
|
var d = new double[2 * leverageSize + 1];
|
||||||
|
for (int i = 0; i < d.Length; i++)
|
||||||
|
{
|
||||||
|
d[i] = -System.Math.Pow((i - leverageSize), 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
var min = d.Min();
|
||||||
|
var amin = System.Math.Abs(min);
|
||||||
|
for (int i = 0; i < d.Length; i++)
|
||||||
|
{
|
||||||
|
d[i] = (d[i] - min) / amin * (double)maxValue;
|
||||||
|
}
|
||||||
|
return d.Select(i => (decimal)i).ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ConvolutionResult[] CalcConvolution(HistItem[] hist, int leverageSize)
|
||||||
|
{
|
||||||
|
if (hist.Length > 2 * leverageSize + 1)
|
||||||
|
{
|
||||||
|
var results = new List<ConvolutionResult>();
|
||||||
|
var coreSize = 2 * leverageSize + 1;
|
||||||
|
for (int shift = 0; shift < hist.Length - coreSize; shift++)
|
||||||
|
{
|
||||||
|
var s = 0m;
|
||||||
|
var k = 0;
|
||||||
|
for (int i = 0; i < 2 * leverageSize + 1; i++)
|
||||||
|
{
|
||||||
|
var core = GetParabolaCore(leverageSize, hist[i + shift].Count);
|
||||||
|
s += (hist[i + shift].Count - core[i]) * (hist[i + shift].Count - core[i]);
|
||||||
|
if (i == leverageSize)
|
||||||
|
{
|
||||||
|
k = i + shift;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
s = s / coreSize;
|
||||||
|
s = (decimal)System.Math.Pow((double)s, 0.5d);
|
||||||
|
//s /= coreSum;
|
||||||
|
results.Add(new ConvolutionResult()
|
||||||
|
{
|
||||||
|
Leverage = leverageSize,
|
||||||
|
Sum = s,
|
||||||
|
Value = hist[k].Value,
|
||||||
|
Shift = k,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return results.OrderByDescending(r => r.Value).ToArray();
|
||||||
|
}
|
||||||
|
return Array.Empty<ConvolutionResult>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void MergeConvolutionResults(List<ConvolutionResult> results, List<ConvolutionResult> mergedResults)
|
||||||
|
{
|
||||||
|
if (results.Count == 0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var resultsOrdered = results.OrderBy(r => r.Sum).ToList();
|
||||||
|
var res = resultsOrdered[0];
|
||||||
|
var b1 = res.Shift - res.Leverage;
|
||||||
|
var b2 = res.Shift + res.Leverage;
|
||||||
|
var forMerge = results.Where(r => r.Shift >= b1 && r.Shift <= b2).ToList();
|
||||||
|
res.Sum = forMerge.Sum(r => r.Sum);
|
||||||
|
foreach (var m in forMerge)
|
||||||
|
{
|
||||||
|
results.Remove(m);
|
||||||
|
}
|
||||||
|
mergedResults.Add(res);
|
||||||
|
MergeConvolutionResults(results, mergedResults);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,7 +20,7 @@ namespace KLHZ.Trader.Core.Tests
|
||||||
Ticker = figi + "_ticker",
|
Ticker = figi + "_ticker",
|
||||||
Id = i,
|
Id = i,
|
||||||
Time = startDt,
|
Time = startDt,
|
||||||
Value = (decimal)(i + 0.5)
|
Price = (decimal)(i + 0.5)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -53,7 +53,7 @@ namespace KLHZ.Trader.Core.Tests
|
||||||
Assert.That(data.timestamps.Length == count);
|
Assert.That(data.timestamps.Length == count);
|
||||||
for (var i = 0; i < count; i++)
|
for (var i = 0; i < count; i++)
|
||||||
{
|
{
|
||||||
Assert.That((float)hist[i].Value, Is.EqualTo(data.prices[i]));
|
Assert.That((float)hist[i].Price, Is.EqualTo(data.prices[i]));
|
||||||
Assert.That(hist[i].Time, Is.EqualTo(data.timestamps[i]));
|
Assert.That(hist[i].Time, Is.EqualTo(data.timestamps[i]));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -72,7 +72,7 @@ namespace KLHZ.Trader.Core.Tests
|
||||||
|
|
||||||
for (var i = 0; i < count; i++)
|
for (var i = 0; i < count; i++)
|
||||||
{
|
{
|
||||||
Assert.That((float)hist[i].Value, Is.EqualTo(data.prices[i]));
|
Assert.That((float)hist[i].Price, Is.EqualTo(data.prices[i]));
|
||||||
Assert.That(hist[i].Time, Is.EqualTo(data.timestamps[i]));
|
Assert.That(hist[i].Time, Is.EqualTo(data.timestamps[i]));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -91,7 +91,7 @@ namespace KLHZ.Trader.Core.Tests
|
||||||
|
|
||||||
for (var i = 0; i < count; i++)
|
for (var i = 0; i < count; i++)
|
||||||
{
|
{
|
||||||
Assert.That((float)hist[i].Value, Is.EqualTo(data.prices[i]));
|
Assert.That((float)hist[i].Price, Is.EqualTo(data.prices[i]));
|
||||||
Assert.That(hist[i].Time, Is.EqualTo(data.timestamps[i]));
|
Assert.That(hist[i].Time, Is.EqualTo(data.timestamps[i]));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -114,7 +114,7 @@ namespace KLHZ.Trader.Core.Tests
|
||||||
var k = i + shift;
|
var k = i + shift;
|
||||||
if (k < hist.Length)
|
if (k < hist.Length)
|
||||||
{
|
{
|
||||||
Assert.That((float)hist[k].Value, Is.EqualTo(data.prices[i]));
|
Assert.That((float)hist[k].Price, Is.EqualTo(data.prices[i]));
|
||||||
Assert.That(hist[k].Time, Is.EqualTo(data.timestamps[i]));
|
Assert.That(hist[k].Time, Is.EqualTo(data.timestamps[i]));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -138,7 +138,7 @@ namespace KLHZ.Trader.Core.Tests
|
||||||
var k = i + shift;
|
var k = i + shift;
|
||||||
if (k < hist.Length)
|
if (k < hist.Length)
|
||||||
{
|
{
|
||||||
Assert.That((float)hist[k].Value, Is.EqualTo(data.prices[i]));
|
Assert.That((float)hist[k].Price, Is.EqualTo(data.prices[i]));
|
||||||
Assert.That(hist[k].Time, Is.EqualTo(data.timestamps[i]));
|
Assert.That(hist[k].Time, Is.EqualTo(data.timestamps[i]));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -162,7 +162,7 @@ namespace KLHZ.Trader.Core.Tests
|
||||||
var k = i + shift;
|
var k = i + shift;
|
||||||
if (k < hist.Length)
|
if (k < hist.Length)
|
||||||
{
|
{
|
||||||
Assert.That((float)hist[k].Value, Is.EqualTo(data.prices[i]));
|
Assert.That((float)hist[k].Price, Is.EqualTo(data.prices[i]));
|
||||||
Assert.That(hist[k].Time, Is.EqualTo(data.timestamps[i]));
|
Assert.That(hist[k].Time, Is.EqualTo(data.timestamps[i]));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -183,24 +183,24 @@ namespace KLHZ.Trader.Core.Tests
|
||||||
|
|
||||||
for (var i = 0; i < count; i++)
|
for (var i = 0; i < count; i++)
|
||||||
{
|
{
|
||||||
Assert.That((float)hist[i].Value, Is.EqualTo(data.prices[i]));
|
Assert.That((float)hist[i].Price, Is.EqualTo(data.prices[i]));
|
||||||
Assert.That(hist[i].Time, Is.EqualTo(data.timestamps[i]));
|
Assert.That(hist[i].Time, Is.EqualTo(data.timestamps[i]));
|
||||||
}
|
}
|
||||||
|
|
||||||
var newData1 = new PriceChange() { Figi = figi, Ticker = figi, Value = 100500, Time = DateTime.UtcNow };
|
var newData1 = new PriceChange() { Figi = figi, Ticker = figi, Price = 100500, Time = DateTime.UtcNow };
|
||||||
|
|
||||||
cacheUnit.AddData(newData1);
|
cacheUnit.AddData(newData1);
|
||||||
|
|
||||||
var data2 = cacheUnit.GetData().Result;
|
var data2 = cacheUnit.GetData().Result;
|
||||||
Assert.IsTrue(data2.prices[data2.prices.Length - 1] == newData1.Value);
|
Assert.IsTrue(data2.prices[data2.prices.Length - 1] == newData1.Price);
|
||||||
Assert.IsTrue(data2.timestamps[data2.timestamps.Length - 1] == newData1.Time);
|
Assert.IsTrue(data2.timestamps[data2.timestamps.Length - 1] == newData1.Time);
|
||||||
|
|
||||||
var newData2 = new PriceChange() { Figi = figi, Ticker = figi, Value = 100501, Time = DateTime.UtcNow };
|
var newData2 = new PriceChange() { Figi = figi, Ticker = figi, Price = 100501, Time = DateTime.UtcNow };
|
||||||
|
|
||||||
cacheUnit.AddData(newData2);
|
cacheUnit.AddData(newData2);
|
||||||
|
|
||||||
var data3 = cacheUnit.GetData().Result;
|
var data3 = cacheUnit.GetData().Result;
|
||||||
Assert.IsTrue(data3.prices[data3.prices.Length - 1] == newData2.Value);
|
Assert.IsTrue(data3.prices[data3.prices.Length - 1] == newData2.Price);
|
||||||
Assert.IsTrue(data3.timestamps[data3.timestamps.Length - 1] == newData2.Time);
|
Assert.IsTrue(data3.timestamps[data3.timestamps.Length - 1] == newData2.Time);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -210,7 +210,7 @@ namespace KLHZ.Trader.Core.Tests
|
||||||
var cacheUnit = new PriceHistoryCacheUnit2("");
|
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 });
|
cacheUnit.AddData(new PriceChange() { Figi = "", Ticker = "", Price = i, Time = DateTime.UtcNow });
|
||||||
if (i >= PriceHistoryCacheUnit2.CacheMaxLength)
|
if (i >= PriceHistoryCacheUnit2.CacheMaxLength)
|
||||||
{
|
{
|
||||||
var data = cacheUnit.GetData().Result;
|
var data = cacheUnit.GetData().Result;
|
||||||
|
@ -237,11 +237,11 @@ namespace KLHZ.Trader.Core.Tests
|
||||||
Assert.That(data.prices.Length == length);
|
Assert.That(data.prices.Length == length);
|
||||||
Assert.That(data.timestamps.Length == length);
|
Assert.That(data.timestamps.Length == length);
|
||||||
|
|
||||||
Assert.That(data.prices.Last() == hist.Last().Value);
|
Assert.That(data.prices.Last() == hist.Last().Price);
|
||||||
Assert.That(data.timestamps.Last() == hist.Last().Time);
|
Assert.That(data.timestamps.Last() == hist.Last().Time);
|
||||||
for (var i = 1; i <= length; i++)
|
for (var i = 1; i <= length; i++)
|
||||||
{
|
{
|
||||||
Assert.That(hist[hist.Length - i].Value, Is.EqualTo(data.prices[data.prices.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]));
|
Assert.That(hist[hist.Length - i].Time, Is.EqualTo(data.timestamps[data.prices.Length - i]));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -267,7 +267,7 @@ namespace KLHZ.Trader.Core.Tests
|
||||||
Assert.That(data.prices.Length == length);
|
Assert.That(data.prices.Length == length);
|
||||||
Assert.That(data.timestamps.Length == length);
|
Assert.That(data.timestamps.Length == length);
|
||||||
|
|
||||||
Assert.That(data.prices.Last() == hist.Last().Value);
|
Assert.That(data.prices.Last() == hist.Last().Price);
|
||||||
Assert.That(data.timestamps.Last() == hist.Last().Time);
|
Assert.That(data.timestamps.Last() == hist.Last().Time);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,191 @@
|
||||||
|
using KLHZ.Trader.Core.DataLayer.Entities.Prices;
|
||||||
|
using KLHZ.Trader.Core.Math.Declisions.Services.Cache;
|
||||||
|
|
||||||
|
namespace KLHZ.Trader.Core.Tests
|
||||||
|
{
|
||||||
|
public class HistoryCacheUnit3Tests
|
||||||
|
{
|
||||||
|
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),
|
||||||
|
Value = i % 2 == 0 ? i : 0
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void Test1()
|
||||||
|
{
|
||||||
|
var count = 0;
|
||||||
|
var figi = "figi";
|
||||||
|
var hist = GetHistory(count, figi);
|
||||||
|
var cacheUnit = new PriceHistoryCacheUnit3(figi, hist);
|
||||||
|
var data = cacheUnit.GetData().Result;
|
||||||
|
|
||||||
|
Assert.That(data.Length == count);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void Test2()
|
||||||
|
{
|
||||||
|
var count = 1111;
|
||||||
|
var figi = "figi";
|
||||||
|
var hist = GetHistory(count, figi);
|
||||||
|
var cacheUnit = new PriceHistoryCacheUnit3(figi, hist);
|
||||||
|
var data = cacheUnit.GetData().Result;
|
||||||
|
|
||||||
|
Assert.That(data.Length == count);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void Test3()
|
||||||
|
{
|
||||||
|
var count = 60 * 60 * 3 * 24;
|
||||||
|
var figi = "figi";
|
||||||
|
var hist = GetHistory(count, figi);
|
||||||
|
var cacheUnit = new PriceHistoryCacheUnit3(figi, hist);
|
||||||
|
var data = cacheUnit.GetData().Result;
|
||||||
|
|
||||||
|
Assert.That(data.Length == count);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void Test4()
|
||||||
|
{
|
||||||
|
var count = 60 * 60 * 3 * 24;
|
||||||
|
var figi = "figi";
|
||||||
|
var hist = GetHistory(count, figi);
|
||||||
|
var cacheUnit = new PriceHistoryCacheUnit3(figi);
|
||||||
|
|
||||||
|
foreach (var item in hist)
|
||||||
|
{
|
||||||
|
cacheUnit.AddData(item);
|
||||||
|
}
|
||||||
|
var data = cacheUnit.GetData().Result;
|
||||||
|
|
||||||
|
Assert.That(data.Length == count);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void Test5()
|
||||||
|
{
|
||||||
|
var count = 60 * 60 * 3 * 24 + 10;
|
||||||
|
var figi = "figi";
|
||||||
|
var hist = GetHistory(count, figi);
|
||||||
|
var cacheUnit = new PriceHistoryCacheUnit3(figi);
|
||||||
|
|
||||||
|
for (int i = 0; i < hist.Length - 1; i++)
|
||||||
|
{
|
||||||
|
cacheUnit.AddData(hist[i]);
|
||||||
|
}
|
||||||
|
cacheUnit.AddData(hist.Last());
|
||||||
|
var data = cacheUnit.GetData().Result;
|
||||||
|
var dt = data.Last().Time - data.First().Time;
|
||||||
|
Assert.That(dt <= TimeSpan.FromDays(3));
|
||||||
|
Assert.That(dt > TimeSpan.Zero);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void Test6()
|
||||||
|
{
|
||||||
|
var count = 60 * 60 * 3 * 24 + 10;
|
||||||
|
var figi = "figi";
|
||||||
|
var hist = GetHistory(count, figi);
|
||||||
|
var cacheUnit = new PriceHistoryCacheUnit3(figi);
|
||||||
|
|
||||||
|
for (int i = 0; i < hist.Length - 1; i++)
|
||||||
|
{
|
||||||
|
cacheUnit.AddData(hist[i], "kkk");
|
||||||
|
}
|
||||||
|
var data = cacheUnit.GetData(key: "kkk").Result;
|
||||||
|
var dt = data.Last().Time - data.First().Time;
|
||||||
|
Assert.That(dt <= TimeSpan.FromHours(1));
|
||||||
|
Assert.That(dt > TimeSpan.Zero);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void Test7()
|
||||||
|
{
|
||||||
|
var count = 60 * 60 * 3 * 24 + 10;
|
||||||
|
var figi = "figi";
|
||||||
|
var hist = GetHistory(count, figi);
|
||||||
|
var cacheUnit = new PriceHistoryCacheUnit3(figi);
|
||||||
|
|
||||||
|
for (int i = 0; i < hist.Length - 1; i++)
|
||||||
|
{
|
||||||
|
cacheUnit.AddData(hist[i], "kkk");
|
||||||
|
}
|
||||||
|
var data = cacheUnit.GetData(TimeSpan.FromMinutes(3), key: "kkk").Result;
|
||||||
|
var dt = data.Last().Time - data.First().Time;
|
||||||
|
Assert.That(dt <= TimeSpan.FromMinutes(3));
|
||||||
|
Assert.That(dt > TimeSpan.Zero);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void Test8()
|
||||||
|
{
|
||||||
|
var count = 60 * 60 * 3 * 24 + 10;
|
||||||
|
var figi = "figi";
|
||||||
|
var hist = GetHistory(count, figi);
|
||||||
|
var cacheUnit = new PriceHistoryCacheUnit3(figi);
|
||||||
|
|
||||||
|
for (int i = 0; i < hist.Length - 1; i++)
|
||||||
|
{
|
||||||
|
cacheUnit.AddData(hist[i], "kkk");
|
||||||
|
}
|
||||||
|
var data = cacheUnit.GetData(TimeSpan.FromMinutes(4), TimeSpan.FromMinutes(3), key: "kkk").Result;
|
||||||
|
var dt = data.Last().Time - data.First().Time;
|
||||||
|
Assert.That(dt <= TimeSpan.FromMinutes(3));
|
||||||
|
Assert.That(dt > TimeSpan.Zero);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void Test9()
|
||||||
|
{
|
||||||
|
var count = 60 * 60 * 3 * 24 + 10;
|
||||||
|
var figi = "figi";
|
||||||
|
var hist = GetHistory(count, figi);
|
||||||
|
var cacheUnit = new PriceHistoryCacheUnit3(figi);
|
||||||
|
|
||||||
|
for (int i = 0; i < hist.Length - 1; i++)
|
||||||
|
{
|
||||||
|
cacheUnit.AddData(hist[i], "kkk");
|
||||||
|
}
|
||||||
|
var data = cacheUnit.GetData(TimeSpan.FromMinutes(4), TimeSpan.FromMinutes(3), key: "kkk", (i) => i.Value != 0).Result;
|
||||||
|
|
||||||
|
Assert.That(data.All(d => d.Value != 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void Test10()
|
||||||
|
{
|
||||||
|
var count = 60 * 60 * 3 * 24 + 10;
|
||||||
|
var figi = "figi";
|
||||||
|
var hist = GetHistory(count, figi);
|
||||||
|
var cacheUnit = new PriceHistoryCacheUnit3(figi);
|
||||||
|
|
||||||
|
for (int i = 0; i < hist.Length - 1; i++)
|
||||||
|
{
|
||||||
|
cacheUnit.AddData(hist[i], "kkk");
|
||||||
|
}
|
||||||
|
var data = cacheUnit.GetData(selector: (i) => i.Value != 0).Result;
|
||||||
|
|
||||||
|
Assert.That(data.All(d => d.Value != 0));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -33,7 +33,7 @@ namespace KLHZ.Trader.Core.Tests
|
||||||
|
|
||||||
if (LocalTrends.TryGetLocalTrends(data.timestamps, data.prices, TimeSpan.FromSeconds(49), TimeSpan.FromSeconds(49), 22, out var res))
|
if (LocalTrends.TryGetLocalTrends(data.timestamps, data.prices, TimeSpan.FromSeconds(49), TimeSpan.FromSeconds(49), 22, out var res))
|
||||||
{
|
{
|
||||||
Assert.That(res == Contracts.Declisions.Dtos.Enums.TradingEvent.UptrendEnd);
|
Assert.That(res == Contracts.Declisions.Dtos.Enums.TradingEvent.CloseLong);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
using KLHZ.Trader.Core.Math.Declisions.Utils;
|
using KLHZ.Trader.Core.Contracts.Messaging.Dtos;
|
||||||
|
using KLHZ.Trader.Core.Contracts.Messaging.Dtos.Interfaces;
|
||||||
|
using KLHZ.Trader.Core.Math.Declisions.Utils;
|
||||||
using System.Security.Cryptography;
|
using System.Security.Cryptography;
|
||||||
|
|
||||||
namespace KLHZ.Trader.Core.Tests
|
namespace KLHZ.Trader.Core.Tests
|
||||||
|
@ -33,10 +35,35 @@ namespace KLHZ.Trader.Core.Tests
|
||||||
var res = SignalProcessing.CalcDiffs(da.ToArray());
|
var res = SignalProcessing.CalcDiffs(da.ToArray());
|
||||||
|
|
||||||
Assert.IsTrue(res.Length - da.Count == -1);
|
Assert.IsTrue(res.Length - da.Count == -1);
|
||||||
foreach(var r in res)
|
foreach (var r in res)
|
||||||
{
|
{
|
||||||
Assert.IsTrue(r == 1);
|
Assert.IsTrue(r == 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public static void Test3()
|
||||||
|
{
|
||||||
|
var results = new List<ITradeDataItem>();
|
||||||
|
var times = new List<DateTime>();
|
||||||
|
var startDt = DateTime.UtcNow;
|
||||||
|
for (int i = 0; i < 100; i++)
|
||||||
|
{
|
||||||
|
startDt = startDt.AddSeconds(((double)(RandomNumberGenerator.GetInt32(1, 100))) / 100);
|
||||||
|
var t = new TradeDataItem()
|
||||||
|
{
|
||||||
|
Figi = "",
|
||||||
|
Ticker = "",
|
||||||
|
Time = startDt,
|
||||||
|
Count = 1,
|
||||||
|
Direction = RandomNumberGenerator.GetInt32(1, 3),
|
||||||
|
IsHistoricalData = true,
|
||||||
|
Price = (decimal)System.Math.Sin(0.01 * i) + (decimal)System.Math.Cos(0.01 * i),
|
||||||
|
};
|
||||||
|
results.Add(t);
|
||||||
|
}
|
||||||
|
|
||||||
|
var res = SignalProcessing.InterpolateData(results.ToArray(), TimeSpan.FromSeconds(5));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,20 +1,16 @@
|
||||||
using KLHZ.Trader.Core.Math.Declisions.Utils;
|
using KLHZ.Trader.Core.Contracts.Declisions.Dtos;
|
||||||
using System;
|
using KLHZ.Trader.Core.Math.Declisions.Utils;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Security.Cryptography;
|
using System.Security.Cryptography;
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace KLHZ.Trader.Core.Tests
|
namespace KLHZ.Trader.Core.Tests
|
||||||
{
|
{
|
||||||
internal class StatisticTests
|
internal class StatisticTests
|
||||||
{
|
{
|
||||||
[Test]
|
[Test]
|
||||||
public static void Test()
|
public static void Test1()
|
||||||
{
|
{
|
||||||
var data = new decimal[1000];
|
var data = new decimal[1000];
|
||||||
for(int i = 0; i < data.Length; i++)
|
for (int i = 0; i < data.Length; i++)
|
||||||
{
|
{
|
||||||
data[i] = RandomNumberGenerator.GetInt32(-10, 10);
|
data[i] = RandomNumberGenerator.GetInt32(-10, 10);
|
||||||
}
|
}
|
||||||
|
@ -26,5 +22,27 @@ namespace KLHZ.Trader.Core.Tests
|
||||||
Assert.IsTrue(data.Length != res.Length);
|
Assert.IsTrue(data.Length != res.Length);
|
||||||
Assert.IsTrue(res[0] != 1000);
|
Assert.IsTrue(res[0] != 1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public static void Test2()
|
||||||
|
{
|
||||||
|
var data = new decimal[1000];
|
||||||
|
for (int i = 0; i < data.Length; i++)
|
||||||
|
{
|
||||||
|
data[i] = RandomNumberGenerator.GetInt32(-10, 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
var res = Statistics.CalcHistogram(data.Select(d => new CachedValue()
|
||||||
|
{
|
||||||
|
Figi = "",
|
||||||
|
Ticker = "",
|
||||||
|
Direction = 1,
|
||||||
|
Price = d / 2,
|
||||||
|
Time = DateTime.UtcNow,
|
||||||
|
}).ToArray());
|
||||||
|
|
||||||
|
Statistics.CalcConvolution(res, 5);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,7 +10,7 @@ namespace KLHZ.Trader.Core.Common.Messaging.Services
|
||||||
private readonly ConcurrentDictionary<string, Channel<IOrderbook>> _orderbooksChannels = new();
|
private readonly ConcurrentDictionary<string, Channel<IOrderbook>> _orderbooksChannels = new();
|
||||||
private readonly ConcurrentDictionary<string, Channel<IMessage>> _messagesChannels = new();
|
private readonly ConcurrentDictionary<string, Channel<IMessage>> _messagesChannels = new();
|
||||||
private readonly ConcurrentDictionary<string, Channel<ITradeCommand>> _commandsChannel = new();
|
private readonly ConcurrentDictionary<string, Channel<ITradeCommand>> _commandsChannel = new();
|
||||||
private readonly ConcurrentDictionary<string, Channel<INewPrice>> _priceChannels = new();
|
private readonly ConcurrentDictionary<string, Channel<ITradeDataItem>> _priceChannels = new();
|
||||||
|
|
||||||
public bool AddChannel(string key, Channel<IMessage> channel)
|
public bool AddChannel(string key, Channel<IMessage> channel)
|
||||||
{
|
{
|
||||||
|
@ -22,7 +22,7 @@ namespace KLHZ.Trader.Core.Common.Messaging.Services
|
||||||
return _commandsChannel.TryAdd(key, channel);
|
return _commandsChannel.TryAdd(key, channel);
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool AddChannel(string key, Channel<INewPrice> channel)
|
public bool AddChannel(string key, Channel<ITradeDataItem> channel)
|
||||||
{
|
{
|
||||||
return _priceChannels.TryAdd(key, channel);
|
return _priceChannels.TryAdd(key, channel);
|
||||||
}
|
}
|
||||||
|
@ -32,7 +32,7 @@ namespace KLHZ.Trader.Core.Common.Messaging.Services
|
||||||
return _orderbooksChannels.TryAdd(key, channel);
|
return _orderbooksChannels.TryAdd(key, channel);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task Broadcast(INewPrice newPriceMessage)
|
public async Task Broadcast(ITradeDataItem newPriceMessage)
|
||||||
{
|
{
|
||||||
foreach (var channel in _priceChannels.Values)
|
foreach (var channel in _priceChannels.Values)
|
||||||
{
|
{
|
||||||
|
|
|
@ -3,8 +3,6 @@
|
||||||
public enum DeclisionTradeAction
|
public enum DeclisionTradeAction
|
||||||
{
|
{
|
||||||
Unknown = 0,
|
Unknown = 0,
|
||||||
StopBuy = 1,
|
|
||||||
StopBuyShortTime = 2,
|
|
||||||
OpenLong = 100,
|
OpenLong = 100,
|
||||||
OpenLongReal = 101,
|
OpenLongReal = 101,
|
||||||
CloseLong = 200,
|
CloseLong = 200,
|
||||||
|
|
|
@ -5,10 +5,5 @@
|
||||||
Unknown = 0,
|
Unknown = 0,
|
||||||
Ask = 1,
|
Ask = 1,
|
||||||
Bid = 2,
|
Bid = 2,
|
||||||
AsksSummary10 = 3,
|
|
||||||
BidsSummary10 = 4,
|
|
||||||
AsksSummary4 = 5,
|
|
||||||
BidsSummary4 = 6,
|
|
||||||
BidsAsksSummary4_2min = 7,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,7 @@ using System.ComponentModel.DataAnnotations.Schema;
|
||||||
namespace KLHZ.Trader.Core.DataLayer.Entities.Prices
|
namespace KLHZ.Trader.Core.DataLayer.Entities.Prices
|
||||||
{
|
{
|
||||||
[Table("price_changes")]
|
[Table("price_changes")]
|
||||||
public class PriceChange : INewPrice
|
public class PriceChange : ITradeDataItem
|
||||||
{
|
{
|
||||||
[Column("id")]
|
[Column("id")]
|
||||||
public long Id { get; set; }
|
public long Id { get; set; }
|
||||||
|
@ -13,7 +13,7 @@ namespace KLHZ.Trader.Core.DataLayer.Entities.Prices
|
||||||
public DateTime Time { get; set; }
|
public DateTime Time { get; set; }
|
||||||
|
|
||||||
[Column("value")]
|
[Column("value")]
|
||||||
public decimal Value { get; set; }
|
public decimal Price { get; set; }
|
||||||
|
|
||||||
[Column("figi")]
|
[Column("figi")]
|
||||||
public required string Figi { get; set; }
|
public required string Figi { get; set; }
|
||||||
|
@ -28,5 +28,11 @@ namespace KLHZ.Trader.Core.DataLayer.Entities.Prices
|
||||||
|
|
||||||
[Column("direction")]
|
[Column("direction")]
|
||||||
public int Direction { get; set; }
|
public int Direction { get; set; }
|
||||||
|
|
||||||
|
[NotMapped]
|
||||||
|
public decimal Value { get; set; }
|
||||||
|
|
||||||
|
[NotMapped]
|
||||||
|
public decimal Value2 { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,7 +13,7 @@ namespace KLHZ.Trader.Core.DataLayer.Entities.Prices
|
||||||
public DateTime Time { get; set; }
|
public DateTime Time { get; set; }
|
||||||
|
|
||||||
[Column("value")]
|
[Column("value")]
|
||||||
public decimal Value { get; set; }
|
public decimal Price { get; set; }
|
||||||
|
|
||||||
[Column("figi")]
|
[Column("figi")]
|
||||||
public required string Figi { get; set; }
|
public required string Figi { get; set; }
|
||||||
|
@ -31,5 +31,11 @@ namespace KLHZ.Trader.Core.DataLayer.Entities.Prices
|
||||||
|
|
||||||
[NotMapped]
|
[NotMapped]
|
||||||
public int Direction { get; set; }
|
public int Direction { get; set; }
|
||||||
|
|
||||||
|
[NotMapped]
|
||||||
|
public decimal Value { get; set; }
|
||||||
|
|
||||||
|
[NotMapped]
|
||||||
|
public decimal Value2 { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +0,0 @@
|
||||||
namespace KLHZ.Trader.Core.DataLayer.Entities.Trades.Enums
|
|
||||||
{
|
|
||||||
public enum AssetType
|
|
||||||
{
|
|
||||||
Unknown = 0,
|
|
||||||
Common = 1,
|
|
||||||
Future = 2,
|
|
||||||
Currency = 3,
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,9 +0,0 @@
|
||||||
namespace KLHZ.Trader.Core.DataLayer.Entities.Trades.Enums
|
|
||||||
{
|
|
||||||
public enum PositionType
|
|
||||||
{
|
|
||||||
Unknown = 0,
|
|
||||||
Long = 1,
|
|
||||||
Short = 2
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,52 +0,0 @@
|
||||||
using KLHZ.Trader.Core.DataLayer.Entities.Trades.Enums;
|
|
||||||
using System.ComponentModel.DataAnnotations.Schema;
|
|
||||||
|
|
||||||
namespace KLHZ.Trader.Core.DataLayer.Entities.Trades
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Сделка, совершенная ботом.
|
|
||||||
/// </summary>
|
|
||||||
[Table("trades")]
|
|
||||||
public class Trade
|
|
||||||
{
|
|
||||||
[Column("trade_id")]
|
|
||||||
public long Id { get; set; }
|
|
||||||
|
|
||||||
[Column("bought_at")]
|
|
||||||
public DateTime BoughtAt { get; set; }
|
|
||||||
|
|
||||||
[Column("account_id")]
|
|
||||||
public required string AccountId { get; set; }
|
|
||||||
|
|
||||||
[Column("figi")]
|
|
||||||
public required string Figi { get; set; }
|
|
||||||
|
|
||||||
[Column("ticker")]
|
|
||||||
public required string Ticker { get; set; }
|
|
||||||
|
|
||||||
[Column("price")]
|
|
||||||
|
|
||||||
public decimal Price { get; set; }
|
|
||||||
|
|
||||||
[Column("count")]
|
|
||||||
public decimal Count { get; set; }
|
|
||||||
|
|
||||||
[Column("count_lots")]
|
|
||||||
public decimal CountLots { get; set; }
|
|
||||||
|
|
||||||
[Column("archive_status")]
|
|
||||||
public int ArchiveStatus { get; set; }
|
|
||||||
|
|
||||||
[Column("direction")]
|
|
||||||
public TradeDirection Direction { get; set; }
|
|
||||||
|
|
||||||
[Column("position_type")]
|
|
||||||
public PositionType Position { get; set; }
|
|
||||||
|
|
||||||
[Column("asset_type")]
|
|
||||||
public AssetType Asset { get; set; }
|
|
||||||
|
|
||||||
[Column("asset_id")]
|
|
||||||
public Guid AssetId { get; set; }
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,7 +1,6 @@
|
||||||
using KLHZ.Trader.Core.DataLayer.Entities.Declisions;
|
using KLHZ.Trader.Core.DataLayer.Entities.Declisions;
|
||||||
using KLHZ.Trader.Core.DataLayer.Entities.Orders;
|
using KLHZ.Trader.Core.DataLayer.Entities.Orders;
|
||||||
using KLHZ.Trader.Core.DataLayer.Entities.Prices;
|
using KLHZ.Trader.Core.DataLayer.Entities.Prices;
|
||||||
using KLHZ.Trader.Core.DataLayer.Entities.Trades;
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
|
|
||||||
|
@ -9,7 +8,6 @@ namespace KLHZ.Trader.Core.DataLayer
|
||||||
{
|
{
|
||||||
public class TraderDbContext : DbContext
|
public class TraderDbContext : DbContext
|
||||||
{
|
{
|
||||||
public DbSet<Trade> Trades { get; set; }
|
|
||||||
public DbSet<Declision> Declisions { get; set; }
|
public DbSet<Declision> Declisions { get; set; }
|
||||||
public DbSet<PriceChange> PriceChanges { get; set; }
|
public DbSet<PriceChange> PriceChanges { get; set; }
|
||||||
public DbSet<ProcessedPrice> ProcessedPrices { get; set; }
|
public DbSet<ProcessedPrice> ProcessedPrices { get; set; }
|
||||||
|
@ -23,15 +21,6 @@ namespace KLHZ.Trader.Core.DataLayer
|
||||||
{
|
{
|
||||||
modelBuilder.UseSerialColumns();
|
modelBuilder.UseSerialColumns();
|
||||||
|
|
||||||
modelBuilder.Entity<Trade>(entity =>
|
|
||||||
{
|
|
||||||
entity.HasKey(e1 => e1.Id);
|
|
||||||
entity.Property(e => e.BoughtAt)
|
|
||||||
.HasConversion(
|
|
||||||
v => v.ToUniversalTime(),
|
|
||||||
v => DateTime.SpecifyKind(v, DateTimeKind.Utc));
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity<Declision>(entity =>
|
modelBuilder.Entity<Declision>(entity =>
|
||||||
{
|
{
|
||||||
entity.HasKey(e1 => e1.Id);
|
entity.HasKey(e1 => e1.Id);
|
||||||
|
|
|
@ -15,10 +15,11 @@
|
||||||
internal const string AreasRelationProcessor = "balancescalc30min";
|
internal const string AreasRelationProcessor = "balancescalc30min";
|
||||||
internal readonly static TimeSpan AreasRelationWindow = TimeSpan.FromMinutes(15);
|
internal readonly static TimeSpan AreasRelationWindow = TimeSpan.FromMinutes(15);
|
||||||
|
|
||||||
|
internal const decimal ForceExecuteCoefficient = 500000m;
|
||||||
internal const decimal PowerUppingCoefficient = 1.69m;
|
internal const decimal PowerUppingCoefficient = 1.69m;
|
||||||
internal const decimal UppingCoefficient = 1.3m;
|
internal const decimal UppingCoefficient = 1.3m;
|
||||||
internal const decimal LowingCoefficient = .76m;
|
internal const decimal LowingCoefficient = .76m;
|
||||||
internal const decimal PowerLowingCoefficient = .59m;
|
internal const decimal PowerLowingCoefficient = .59m;
|
||||||
internal const decimal BlockingCoefficient = 0m;
|
internal const decimal BlockingCoefficient = 0.01m;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
using KLHZ.Trader.Core.Exchange.Models.AssetsAccounting;
|
using KLHZ.Trader.Core.Contracts.Common.Enums;
|
||||||
|
using KLHZ.Trader.Core.Exchange.Models.AssetsAccounting;
|
||||||
using System.Collections.Immutable;
|
using System.Collections.Immutable;
|
||||||
|
|
||||||
namespace KLHZ.Trader.Core.Exchange.Interfaces
|
namespace KLHZ.Trader.Core.Exchange.Interfaces
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
namespace KLHZ.Trader.Core.Exchange.Models.AssetsAccounting
|
using KLHZ.Trader.Core.Contracts.Common.Enums;
|
||||||
|
|
||||||
|
namespace KLHZ.Trader.Core.Exchange.Models.AssetsAccounting
|
||||||
{
|
{
|
||||||
public class Asset
|
public class Asset
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,9 +0,0 @@
|
||||||
namespace KLHZ.Trader.Core.Exchange.Models.AssetsAccounting
|
|
||||||
{
|
|
||||||
public enum DealDirection
|
|
||||||
{
|
|
||||||
Unknown = 0,
|
|
||||||
Buy = 1,
|
|
||||||
Sell = 2
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,12 +0,0 @@
|
||||||
namespace KLHZ.Trader.Core.Exchange.Models.AssetsAccounting
|
|
||||||
{
|
|
||||||
public class DealResult
|
|
||||||
{
|
|
||||||
public required string AccountId { get; set; }
|
|
||||||
public required string Figi { get; set; }
|
|
||||||
public decimal Price { get; set; }
|
|
||||||
public decimal Count { get; set; }
|
|
||||||
public bool Success { get; set; }
|
|
||||||
public DealDirection Direction { get; set; }
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,15 +0,0 @@
|
||||||
namespace KLHZ.Trader.Core.Exchange.Models.AssetsAccounting
|
|
||||||
{
|
|
||||||
public class Order
|
|
||||||
{
|
|
||||||
public required string AccountId { get; init; }
|
|
||||||
public required string Figi { get; init; }
|
|
||||||
public required string Ticker { get; init; }
|
|
||||||
public required string OrderId { get; init; }
|
|
||||||
public decimal Price { get; init; }
|
|
||||||
public long Count { get; init; }
|
|
||||||
public DateTime ExpirationTime { get; init; }
|
|
||||||
public DateTime OpenDate { get; init; }
|
|
||||||
public DealDirection Direction { get; init; }
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -2,16 +2,14 @@
|
||||||
{
|
{
|
||||||
public class ExchangeConfig
|
public class ExchangeConfig
|
||||||
{
|
{
|
||||||
public bool ExchangeDataRecievingEnabled { get; set; }
|
public bool ExchangeDataRecievingEnabled { get; init; }
|
||||||
public decimal StopBuyLengthMinuts { get; set; }
|
public decimal FutureComission { get; init; }
|
||||||
public decimal FutureComission { get; set; }
|
public decimal ShareComission { get; init; }
|
||||||
public decimal ShareComission { get; set; }
|
public decimal AccountCashPart { get; init; }
|
||||||
public decimal AccountCashPart { get; set; }
|
public decimal AccountCashPartFutures { get; init; }
|
||||||
public decimal AccountCashPartFutures { get; set; }
|
public string[] DataRecievingInstrumentsFigis { get; init; } = [];
|
||||||
public decimal DefaultBuyPartOfAccount { get; set; }
|
public string[] TradingInstrumentsFigis { get; init; } = [];
|
||||||
public string[] DataRecievingInstrumentsFigis { get; set; } = [];
|
public string[] ManagingAccountNamePatterns { get; init; } = [];
|
||||||
public string[] TradingInstrumentsFigis { get; set; } = [];
|
public InstrumentSettings[] InstrumentsSettings { get; init; } = [];
|
||||||
public string[] ManagingAccountNamePatterns { get; set; } = [];
|
|
||||||
public InstrumentSettings[] InstrumentsSettings { get; set; } = [];
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
using KLHZ.Trader.Core.Contracts.Declisions.Dtos.Enums;
|
||||||
|
using KLHZ.Trader.Core.Contracts.Messaging.Dtos.Interfaces;
|
||||||
|
using System.Collections.Immutable;
|
||||||
|
|
||||||
|
namespace KLHZ.Trader.Core.Exchange.Models.Trading
|
||||||
|
{
|
||||||
|
internal class DeferredDeclision
|
||||||
|
{
|
||||||
|
public ImmutableDictionary<TradingEvent, decimal> Events { get; init; } = ImmutableDictionary<TradingEvent, decimal>.Empty;
|
||||||
|
public Stops Stops { get; init; }
|
||||||
|
public DateTime ExpirationTime { get; init; }
|
||||||
|
public required ITradeDataItem Message { get; init; }
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
namespace KLHZ.Trader.Core.Exchange.Models.Trading
|
||||||
|
{
|
||||||
|
internal class PirsonCalculatingResult
|
||||||
|
{
|
||||||
|
public bool Success { get; init; }
|
||||||
|
public decimal Pirson { get; init; }
|
||||||
|
public decimal PriceDiff { get; init; }
|
||||||
|
public decimal TradesDiff { get; init; }
|
||||||
|
public decimal TradesDiffRelative { get; init; }
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,32 @@
|
||||||
|
using KLHZ.Trader.Core.Contracts.Common.Enums;
|
||||||
|
|
||||||
|
namespace KLHZ.Trader.Core.Exchange.Models.Trading
|
||||||
|
{
|
||||||
|
public readonly struct Stops
|
||||||
|
{
|
||||||
|
public readonly decimal LongStopLossShift;
|
||||||
|
public readonly decimal LongTakeProfitShift;
|
||||||
|
public readonly decimal ShortStopLossShift;
|
||||||
|
public readonly decimal ShortTakeProfitShift;
|
||||||
|
|
||||||
|
public Stops(decimal longStopLossShift, decimal longTakeProfitShift, decimal shortStopLossShift, decimal shortTakeProfitShift)
|
||||||
|
{
|
||||||
|
LongStopLossShift = longStopLossShift;
|
||||||
|
LongTakeProfitShift = longTakeProfitShift;
|
||||||
|
ShortStopLossShift = shortStopLossShift;
|
||||||
|
ShortTakeProfitShift = shortTakeProfitShift;
|
||||||
|
}
|
||||||
|
|
||||||
|
public (decimal takeProfit, decimal stopLoss) GetStops(PositionType positionType)
|
||||||
|
{
|
||||||
|
if (positionType == PositionType.Short)
|
||||||
|
{
|
||||||
|
return (ShortTakeProfitShift, ShortStopLossShift);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return (LongTakeProfitShift, LongStopLossShift);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
namespace KLHZ.Trader.Core.Exchange.Models.Trading
|
||||||
|
{
|
||||||
|
public class SupportLevel
|
||||||
|
{
|
||||||
|
public decimal Value { get; init; }
|
||||||
|
public decimal LowValue { get; init; }
|
||||||
|
public decimal HighValue { get; init; }
|
||||||
|
public DateTime? LastLevelTime { get; init; }
|
||||||
|
public DateTime CalculatedAt { get; init; }
|
||||||
|
}
|
||||||
|
}
|
|
@ -6,7 +6,6 @@ using KLHZ.Trader.Core.DataLayer.Entities.Orders;
|
||||||
using KLHZ.Trader.Core.DataLayer.Entities.Prices;
|
using KLHZ.Trader.Core.DataLayer.Entities.Prices;
|
||||||
using KLHZ.Trader.Core.Exchange.Extentions;
|
using KLHZ.Trader.Core.Exchange.Extentions;
|
||||||
using KLHZ.Trader.Core.Exchange.Models.Configs;
|
using KLHZ.Trader.Core.Exchange.Models.Configs;
|
||||||
using KLHZ.Trader.Core.Exchange.Utils;
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Microsoft.Extensions.Hosting;
|
using Microsoft.Extensions.Hosting;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
@ -150,45 +149,13 @@ namespace KLHZ.Trader.Core.Exchange.Services
|
||||||
Figi = response.Trade.Figi,
|
Figi = response.Trade.Figi,
|
||||||
Ticker = _tradeDataProvider.GetTickerByFigi(response.Trade.Figi),
|
Ticker = _tradeDataProvider.GetTickerByFigi(response.Trade.Figi),
|
||||||
Time = response.Trade.Time.ToDateTime().ToUniversalTime(),
|
Time = response.Trade.Time.ToDateTime().ToUniversalTime(),
|
||||||
Value = response.Trade.Price,
|
Price = response.Trade.Price,
|
||||||
IsHistoricalData = false,
|
IsHistoricalData = false,
|
||||||
Direction = (int)response.Trade.Direction,
|
Direction = (int)response.Trade.Direction,
|
||||||
Count = response.Trade.Quantity,
|
Count = response.Trade.Quantity,
|
||||||
};
|
};
|
||||||
|
|
||||||
//await _tradeDataProvider.AddData(message, TimeSpan.FromHours(7));
|
|
||||||
await _eventBus.Broadcast(message);
|
await _eventBus.Broadcast(message);
|
||||||
|
|
||||||
var exchangeState = ExchangeScheduler.GetCurrentState();
|
|
||||||
if (exchangeState == Models.Trading.ExchangeState.ClearingTime
|
|
||||||
&& lastUpdateDict.TryGetValue(message.Figi, out var pri)
|
|
||||||
&& (DateTime.UtcNow - pri.Time).Minutes > 3)
|
|
||||||
{
|
|
||||||
var assets = _portfolioWrapper.Accounts.Values.SelectMany(a => a.Assets.Values).Where(a => a.Figi == message.Figi).ToArray();
|
|
||||||
|
|
||||||
foreach (var a in assets)
|
|
||||||
{
|
|
||||||
using var context = await _dbContextFactory.CreateDbContextAsync();
|
|
||||||
context.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking;
|
|
||||||
await context.Trades.AddAsync(new DataLayer.Entities.Trades.Trade()
|
|
||||||
{
|
|
||||||
AssetId = a.AssetId,
|
|
||||||
AccountId = string.Empty,
|
|
||||||
Figi = message.Figi,
|
|
||||||
Ticker = string.Empty,
|
|
||||||
ArchiveStatus = 0,
|
|
||||||
Asset = (KLHZ.Trader.Core.DataLayer.Entities.Trades.Enums.AssetType)(int)a.Type,
|
|
||||||
BoughtAt = DateTime.UtcNow,
|
|
||||||
Count = 0,
|
|
||||||
Direction = a.Count > 0 ? DataLayer.Entities.Trades.Enums.TradeDirection.Buy : DataLayer.Entities.Trades.Enums.TradeDirection.Sell,
|
|
||||||
Position = a.Count > 0 ? DataLayer.Entities.Trades.Enums.PositionType.Long : DataLayer.Entities.Trades.Enums.PositionType.Short,
|
|
||||||
Price = message.Value,
|
|
||||||
});
|
|
||||||
await context.SaveChangesAsync();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
lastUpdateDict[message.Figi] = message;
|
|
||||||
|
|
||||||
pricesBuffer.Add(message);
|
pricesBuffer.Add(message);
|
||||||
}
|
}
|
||||||
if (response.Orderbook != null)
|
if (response.Orderbook != null)
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
using KLHZ.Trader.Core.Common.Extentions;
|
using KLHZ.Trader.Core.Common.Extentions;
|
||||||
|
using KLHZ.Trader.Core.Contracts.Common.Enums;
|
||||||
using KLHZ.Trader.Core.DataLayer;
|
using KLHZ.Trader.Core.DataLayer;
|
||||||
using KLHZ.Trader.Core.Exchange.Extentions;
|
using KLHZ.Trader.Core.Exchange.Extentions;
|
||||||
using KLHZ.Trader.Core.Exchange.Interfaces;
|
using KLHZ.Trader.Core.Exchange.Interfaces;
|
||||||
using KLHZ.Trader.Core.Exchange.Models.AssetsAccounting;
|
|
||||||
using KLHZ.Trader.Core.Exchange.Models.Configs;
|
using KLHZ.Trader.Core.Exchange.Models.Configs;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
@ -50,13 +50,11 @@ namespace KLHZ.Trader.Core.Exchange.Services
|
||||||
|
|
||||||
public ImmutableDictionary<string, Asset> Assets => GetAssets();
|
public ImmutableDictionary<string, Asset> Assets => GetAssets();
|
||||||
|
|
||||||
|
|
||||||
private readonly InvestApiClient _investApiClient;
|
private readonly InvestApiClient _investApiClient;
|
||||||
private readonly IDbContextFactory<TraderDbContext> _dbContextFactory;
|
private readonly IDbContextFactory<TraderDbContext> _dbContextFactory;
|
||||||
private readonly ILogger<TraderDataProvider> _logger;
|
private readonly ILogger<TraderDataProvider> _logger;
|
||||||
private readonly IOptions<ExchangeConfig> _options;
|
private readonly IOptions<ExchangeConfig> _options;
|
||||||
|
|
||||||
|
|
||||||
private readonly Dictionary<string, Asset> _assets = new();
|
private readonly Dictionary<string, Asset> _assets = new();
|
||||||
private readonly ConcurrentDictionary<string, DateTime> _usedOrderIds = new();
|
private readonly ConcurrentDictionary<string, DateTime> _usedOrderIds = new();
|
||||||
|
|
||||||
|
@ -140,26 +138,20 @@ namespace KLHZ.Trader.Core.Exchange.Services
|
||||||
using var context = await _dbContextFactory.CreateDbContextAsync();
|
using var context = await _dbContextFactory.CreateDbContextAsync();
|
||||||
context.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking;
|
context.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking;
|
||||||
|
|
||||||
var trades = await context.Trades
|
|
||||||
.Where(t => t.ArchiveStatus == 0)
|
|
||||||
.OrderByDescending(t => t.BoughtAt)
|
|
||||||
.ToListAsync();
|
|
||||||
|
|
||||||
var oldAssets = _assets.ToDictionary();
|
var oldAssets = _assets.ToDictionary();
|
||||||
_assets.Clear();
|
_assets.Clear();
|
||||||
foreach (var position in portfolio.Positions)
|
foreach (var position in portfolio.Positions)
|
||||||
{
|
{
|
||||||
oldAssets.TryGetValue(position.Figi, out var oldAsset);
|
oldAssets.TryGetValue(position.Figi, out var oldAsset);
|
||||||
var newAssetId = oldAsset?.AssetId ?? Guid.NewGuid();
|
var newAssetId = oldAsset?.AssetId ?? Guid.NewGuid();
|
||||||
var trade = trades.FirstOrDefault(t => t.Figi == position.Figi && t.AssetId == newAssetId);
|
|
||||||
var asset = new Asset()
|
var asset = new Asset()
|
||||||
{
|
{
|
||||||
AssetId = newAssetId,
|
AssetId = newAssetId,
|
||||||
AccountId = AccountId,
|
AccountId = AccountId,
|
||||||
Figi = position.Figi,
|
Figi = position.Figi,
|
||||||
Ticker = position.Ticker,
|
Ticker = position.Ticker,
|
||||||
BoughtAt = trade?.BoughtAt ?? DateTime.UtcNow,
|
BoughtAt = oldAsset?.BoughtAt ?? DateTime.UtcNow,
|
||||||
BoughtPrice = trade?.Price ?? position.AveragePositionPrice,
|
BoughtPrice = oldAsset?.BoughtPrice ?? position.AveragePositionPrice,
|
||||||
Type = position.InstrumentType.ParseInstrumentType(),
|
Type = position.InstrumentType.ParseInstrumentType(),
|
||||||
Position = position.Quantity > 0 ? PositionType.Long : PositionType.Short,
|
Position = position.Quantity > 0 ? PositionType.Long : PositionType.Short,
|
||||||
BlockedItems = position.BlockedLots,
|
BlockedItems = position.BlockedLots,
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,15 +1,11 @@
|
||||||
using KLHZ.Trader.Core.Contracts.Declisions.Dtos;
|
using KLHZ.Trader.Core.Contracts.Messaging.Dtos;
|
||||||
using KLHZ.Trader.Core.Contracts.Declisions.Dtos.Enums;
|
|
||||||
using KLHZ.Trader.Core.Contracts.Declisions.Interfaces;
|
|
||||||
using KLHZ.Trader.Core.Contracts.Messaging.Dtos;
|
|
||||||
using KLHZ.Trader.Core.Contracts.Messaging.Dtos.Interfaces;
|
using KLHZ.Trader.Core.Contracts.Messaging.Dtos.Interfaces;
|
||||||
using KLHZ.Trader.Core.DataLayer;
|
using KLHZ.Trader.Core.DataLayer;
|
||||||
using KLHZ.Trader.Core.DataLayer.Entities.Declisions;
|
using KLHZ.Trader.Core.DataLayer.Entities.Declisions;
|
||||||
|
using KLHZ.Trader.Core.DataLayer.Entities.Declisions.Enums;
|
||||||
using KLHZ.Trader.Core.DataLayer.Entities.Prices;
|
using KLHZ.Trader.Core.DataLayer.Entities.Prices;
|
||||||
using KLHZ.Trader.Core.Exchange.Models.Configs;
|
using KLHZ.Trader.Core.Exchange.Models.Configs;
|
||||||
using KLHZ.Trader.Core.Math.Declisions.Dtos.FFT;
|
|
||||||
using KLHZ.Trader.Core.Math.Declisions.Services.Cache;
|
using KLHZ.Trader.Core.Math.Declisions.Services.Cache;
|
||||||
using KLHZ.Trader.Core.Math.Declisions.Utils;
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using Microsoft.Extensions.Options;
|
using Microsoft.Extensions.Options;
|
||||||
|
@ -22,15 +18,15 @@ namespace KLHZ.Trader.Core.Exchange.Services
|
||||||
{
|
{
|
||||||
public class TraderDataProvider
|
public class TraderDataProvider
|
||||||
{
|
{
|
||||||
private readonly ConcurrentDictionary<string, IPriceHistoryCacheUnit> _historyCash = new();
|
private readonly ConcurrentDictionary<string, PriceHistoryCacheUnit3> _historyCash3 = new();
|
||||||
|
|
||||||
private readonly InvestApiClient _investApiClient;
|
private readonly InvestApiClient _investApiClient;
|
||||||
private readonly IDbContextFactory<TraderDbContext> _dbContextFactory;
|
private readonly IDbContextFactory<TraderDbContext> _dbContextFactory;
|
||||||
private readonly ILogger<TraderDataProvider> _logger;
|
private readonly ILogger<TraderDataProvider> _logger;
|
||||||
private readonly string[] _instrumentsFigis = [];
|
private readonly string[] _instrumentsFigis = [];
|
||||||
|
private readonly string[] _tradingInstrumentsFigis = [];
|
||||||
|
|
||||||
public readonly ConcurrentDictionary<string, IOrderbook> Orderbooks = new();
|
public readonly ConcurrentDictionary<string, IOrderbook> Orderbooks = new();
|
||||||
private readonly ConcurrentDictionary<string, FFTAnalyzeResult> _fftResults = new();
|
|
||||||
private readonly ConcurrentDictionary<string, string> _tickersCache = new();
|
private readonly ConcurrentDictionary<string, string> _tickersCache = new();
|
||||||
private readonly ConcurrentDictionary<string, AssetType> _assetTypesCache = new();
|
private readonly ConcurrentDictionary<string, AssetType> _assetTypesCache = new();
|
||||||
|
|
||||||
|
@ -45,159 +41,77 @@ namespace KLHZ.Trader.Core.Exchange.Services
|
||||||
_dbContextFactory = dbContextFactory;
|
_dbContextFactory = dbContextFactory;
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
_instrumentsFigis = options.Value.DataRecievingInstrumentsFigis.ToArray();
|
_instrumentsFigis = options.Value.DataRecievingInstrumentsFigis.ToArray();
|
||||||
|
_tradingInstrumentsFigis = options.Value.TradingInstrumentsFigis.ToArray();
|
||||||
_isDataRecievingAllowed = options.Value.ExchangeDataRecievingEnabled;
|
_isDataRecievingAllowed = options.Value.ExchangeDataRecievingEnabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ValueTask<FFTAnalyzeResult> GetFFtResult(string figi)
|
|
||||||
{
|
|
||||||
if (_fftResults.TryGetValue(figi, out var res))
|
|
||||||
{
|
|
||||||
return ValueTask.FromResult(res);
|
|
||||||
}
|
|
||||||
return ValueTask.FromResult<FFTAnalyzeResult>(FFTAnalyzeResult.Empty);
|
|
||||||
}
|
|
||||||
|
|
||||||
public ValueTask SetFFtResult(FFTAnalyzeResult result)
|
|
||||||
{
|
|
||||||
_fftResults[result.Key] = result;
|
|
||||||
return ValueTask.CompletedTask;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async ValueTask<decimal> GetLastPrice(string figi)
|
public async ValueTask<decimal> GetLastPrice(string figi)
|
||||||
{
|
{
|
||||||
var res = 0m;
|
var res = 0m;
|
||||||
if (_historyCash.TryGetValue(figi, out var unit))
|
if (_historyCash3.TryGetValue(figi, out var unit))
|
||||||
{
|
{
|
||||||
res = (await unit.GetLastValues()).price;
|
res = (await unit.GetLastValues()).price;
|
||||||
}
|
}
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async ValueTask<(DateTime[] timestamps, decimal[] prices, bool isFullIntervalExists)> GetData(string figi, TimeSpan timeSpan)
|
public async ValueTask AddData(ITradeDataItem message)
|
||||||
{
|
|
||||||
if (_historyCash.TryGetValue(figi, out var unit))
|
|
||||||
{
|
|
||||||
var res = await unit.GetData(timeSpan);
|
|
||||||
return (res.timestamps, res.prices, res.isFullIntervalExists);
|
|
||||||
}
|
|
||||||
return (Array.Empty<DateTime>(), Array.Empty<decimal>(), false);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async ValueTask<(DateTime[] timestamps, decimal[] prices)> GetData(string figi, int? length = null)
|
|
||||||
{
|
|
||||||
if (_historyCash.TryGetValue(figi, out var unit))
|
|
||||||
{
|
|
||||||
var res = await unit.GetData(length);
|
|
||||||
return (res.timestamps, res.prices);
|
|
||||||
}
|
|
||||||
return (Array.Empty<DateTime>(), Array.Empty<decimal>());
|
|
||||||
}
|
|
||||||
|
|
||||||
public async ValueTask AddData(INewPrice message, TimeSpan? clearingInterval = null)
|
|
||||||
{
|
{
|
||||||
if (message.Direction != 1) return;
|
if (message.Direction != 1) return;
|
||||||
if (_historyCash.TryGetValue(message.Figi, out var unit))
|
if (_historyCash3.TryGetValue(message.Figi, out var unit))
|
||||||
{
|
{
|
||||||
if (clearingInterval.HasValue)
|
|
||||||
{
|
|
||||||
var lasts = await unit.GetLastValues();
|
|
||||||
if (message.Time - lasts.time > clearingInterval.Value)
|
|
||||||
{
|
|
||||||
unit = new PriceHistoryCacheUnit2(message.Figi);
|
|
||||||
_historyCash[message.Figi] = unit;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
await unit.AddData(message);
|
await unit.AddData(message);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
unit = new PriceHistoryCacheUnit2(message.Figi, message);
|
unit = new PriceHistoryCacheUnit3(message.Figi, message);
|
||||||
_historyCash.TryAdd(message.Figi, unit);
|
_historyCash3.TryAdd(message.Figi, unit);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async ValueTask AddDataTo1MinuteWindowCache(string figi, string key, CachedValue data)
|
public async ValueTask AddData(string figi, string key, ITradeDataItem data)
|
||||||
{
|
{
|
||||||
if (!_historyCash.TryGetValue(figi, out var unit))
|
if (!_historyCash3.TryGetValue(figi, out var item))
|
||||||
{
|
{
|
||||||
unit = new PriceHistoryCacheUnit2(figi);
|
item = new PriceHistoryCacheUnit3(figi);
|
||||||
_historyCash.TryAdd(figi, unit);
|
_historyCash3.TryAdd(figi, item);
|
||||||
}
|
}
|
||||||
await _historyCash[figi].AddDataToTimeWindowCache(key, data, TimeWindowCacheType._1_Minute);
|
|
||||||
|
await _historyCash3[figi].AddData(data, key);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async ValueTask AddDataTo20SecondsWindowCache(string figi, string key, CachedValue data)
|
public ValueTask<ITradeDataItem[]> GetDataForTimeWindow(string figi, TimeSpan time, string? key = null, Func<ITradeDataItem, bool>? selector = null)
|
||||||
{
|
{
|
||||||
if (!_historyCash.TryGetValue(figi, out var unit))
|
if (_historyCash3.TryGetValue(figi, out var cahcheItem))
|
||||||
{
|
{
|
||||||
unit = new PriceHistoryCacheUnit2(figi);
|
return cahcheItem.GetData(time, key: key, selector);
|
||||||
_historyCash.TryAdd(figi, unit);
|
|
||||||
}
|
}
|
||||||
await _historyCash[figi].AddDataToTimeWindowCache(key, data, TimeWindowCacheType._20_Seconds);
|
return ValueTask.FromResult(Array.Empty<ITradeDataItem>());
|
||||||
}
|
}
|
||||||
|
|
||||||
public async ValueTask AddDataTo5MinuteWindowCache(string figi, string key, CachedValue data)
|
public ValueTask<ITradeDataItem[]> GetDataFrom20SecondsWindowCache2(string figi, string key)
|
||||||
{
|
{
|
||||||
if (!_historyCash.TryGetValue(figi, out var unit))
|
return GetDataForTimeWindow(figi, TimeSpan.FromSeconds(20), key);
|
||||||
{
|
|
||||||
unit = new PriceHistoryCacheUnit2(figi);
|
|
||||||
_historyCash.TryAdd(figi, unit);
|
|
||||||
}
|
|
||||||
await _historyCash[figi].AddDataToTimeWindowCache(key, data, TimeWindowCacheType._5_Minutes);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async ValueTask AddDataTo15MinuteWindowCache(string figi, string key, CachedValue data)
|
public ValueTask<ITradeDataItem[]> GetDataFrom1MinuteWindowCache2(string figi, string key)
|
||||||
{
|
{
|
||||||
if (!_historyCash.TryGetValue(figi, out var unit))
|
return GetDataForTimeWindow(figi, TimeSpan.FromSeconds(60), key);
|
||||||
{
|
|
||||||
unit = new PriceHistoryCacheUnit2(figi);
|
|
||||||
_historyCash.TryAdd(figi, unit);
|
|
||||||
}
|
|
||||||
await _historyCash[figi].AddDataToTimeWindowCache(key, data, TimeWindowCacheType._15_Minutes);
|
|
||||||
}
|
}
|
||||||
|
public ValueTask<ITradeDataItem[]> GetDataFrom5MinuteWindowCache2(string figi, string key)
|
||||||
public ValueTask<CachedValue[]> GetDataFrom20SecondsWindowCache(string figi, string key)
|
|
||||||
{
|
{
|
||||||
if (_historyCash.TryGetValue(figi, out var cahcheItem))
|
return GetDataForTimeWindow(figi, TimeSpan.FromMinutes(5), key);
|
||||||
{
|
|
||||||
return cahcheItem.GetDataFromTimeWindowCache(key, TimeWindowCacheType._20_Seconds);
|
|
||||||
}
|
|
||||||
return ValueTask.FromResult(Array.Empty<CachedValue>());
|
|
||||||
}
|
}
|
||||||
|
public ValueTask<ITradeDataItem[]> GetDataFrom15MinuteWindowCache2(string figi, string key)
|
||||||
public ValueTask<CachedValue[]> GetDataFrom1MinuteWindowCache(string figi, string key)
|
|
||||||
{
|
{
|
||||||
if (_historyCash.TryGetValue(figi, out var cahcheItem))
|
return GetDataForTimeWindow(figi, TimeSpan.FromMinutes(15), key);
|
||||||
{
|
|
||||||
return cahcheItem.GetDataFromTimeWindowCache(key, TimeWindowCacheType._1_Minute);
|
|
||||||
}
|
|
||||||
return ValueTask.FromResult(Array.Empty<CachedValue>());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public ValueTask<CachedValue[]> GetDataFrom5MinuteWindowCache(string figi, string key)
|
|
||||||
{
|
|
||||||
if (_historyCash.TryGetValue(figi, out var cahcheItem))
|
|
||||||
{
|
|
||||||
return cahcheItem.GetDataFromTimeWindowCache(key, TimeWindowCacheType._5_Minutes);
|
|
||||||
}
|
|
||||||
return ValueTask.FromResult(Array.Empty<CachedValue>());
|
|
||||||
}
|
|
||||||
|
|
||||||
public ValueTask<CachedValue[]> GetDataFrom15MinuteWindowCache(string figi, string key)
|
|
||||||
{
|
|
||||||
if (_historyCash.TryGetValue(figi, out var cahcheItem))
|
|
||||||
{
|
|
||||||
return cahcheItem.GetDataFromTimeWindowCache(key, TimeWindowCacheType._15_Minutes);
|
|
||||||
}
|
|
||||||
return ValueTask.FromResult(Array.Empty<CachedValue>());
|
|
||||||
}
|
|
||||||
|
|
||||||
public async ValueTask AddOrderbook(IOrderbook orderbook)
|
public async ValueTask AddOrderbook(IOrderbook orderbook)
|
||||||
{
|
{
|
||||||
if (!_historyCash.TryGetValue(orderbook.Figi, out var unit))
|
if (!_historyCash3.TryGetValue(orderbook.Figi, out var unit))
|
||||||
{
|
{
|
||||||
unit = new PriceHistoryCacheUnit2(orderbook.Figi);
|
unit = new PriceHistoryCacheUnit3(orderbook.Figi);
|
||||||
_historyCash.TryAdd(orderbook.Figi, unit);
|
_historyCash3.TryAdd(orderbook.Figi, unit);
|
||||||
}
|
}
|
||||||
Orderbooks[orderbook.Figi] = orderbook;
|
Orderbooks[orderbook.Figi] = orderbook;
|
||||||
await unit.AddOrderbook(orderbook);
|
await unit.AddOrderbook(orderbook);
|
||||||
|
@ -230,18 +144,18 @@ namespace KLHZ.Trader.Core.Exchange.Services
|
||||||
|
|
||||||
if (_isDataRecievingAllowed)
|
if (_isDataRecievingAllowed)
|
||||||
{
|
{
|
||||||
var time = DateTime.UtcNow.AddHours(-1.5);
|
var time = DateTime.UtcNow.AddHours(-20);
|
||||||
using var context1 = await _dbContextFactory.CreateDbContextAsync();
|
using var context1 = await _dbContextFactory.CreateDbContextAsync();
|
||||||
context1.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking;
|
context1.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking;
|
||||||
var data = await context1.PriceChanges
|
var data = await context1.PriceChanges
|
||||||
.Where(c => _instrumentsFigis.Contains(c.Figi) && c.Time >= time)
|
.Where(c => _tradingInstrumentsFigis.Contains(c.Figi) && c.Time >= time)
|
||||||
.OrderBy(c => c.Time)
|
.OrderBy(c => c.Time)
|
||||||
.Select(c => new NewPriceMessage()
|
.Select(c => new TradeDataItem()
|
||||||
{
|
{
|
||||||
Figi = c.Figi,
|
Figi = c.Figi,
|
||||||
Ticker = c.Ticker,
|
Ticker = c.Ticker,
|
||||||
Time = c.Time,
|
Time = c.Time,
|
||||||
Value = c.Value,
|
Price = c.Price,
|
||||||
IsHistoricalData = true,
|
IsHistoricalData = true,
|
||||||
Direction = c.Direction,
|
Direction = c.Direction,
|
||||||
Count = c.Count,
|
Count = c.Count,
|
||||||
|
@ -251,15 +165,6 @@ namespace KLHZ.Trader.Core.Exchange.Services
|
||||||
foreach (var price in data)
|
foreach (var price in data)
|
||||||
{
|
{
|
||||||
await AddData(price);
|
await AddData(price);
|
||||||
|
|
||||||
var cachedData = await GetData(price.Figi);
|
|
||||||
if ((DateTime.UtcNow - price.Time).TotalMinutes < 5)
|
|
||||||
{
|
|
||||||
if (ShapeAreaCalculator.TryGetAreasRelation(cachedData.timestamps, cachedData.prices, price.Value, Constants.AreasRelationWindow, out var rel))
|
|
||||||
{
|
|
||||||
await AddDataTo1MinuteWindowCache(price.Figi, Constants._1minCacheKey, new CachedValue() { Time = price.Time, Value = (decimal)rel });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -307,6 +212,59 @@ namespace KLHZ.Trader.Core.Exchange.Services
|
||||||
await _forSave.Writer.WriteAsync(declision);
|
await _forSave.Writer.WriteAsync(declision);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal async Task LogPrice(ITradeDataItem message, string processor, decimal value)
|
||||||
|
{
|
||||||
|
await LogPrice(new ProcessedPrice()
|
||||||
|
{
|
||||||
|
Figi = message.Figi,
|
||||||
|
Ticker = message.Ticker,
|
||||||
|
Processor = processor,
|
||||||
|
Time = message.Time,
|
||||||
|
Price = value,
|
||||||
|
}, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal async Task LogPrice(string figi, string ticker, DateTime time, decimal value, string processor)
|
||||||
|
{
|
||||||
|
await LogPrice(new ProcessedPrice()
|
||||||
|
{
|
||||||
|
Figi = figi,
|
||||||
|
Ticker = ticker,
|
||||||
|
Processor = processor,
|
||||||
|
Time = time,
|
||||||
|
Price = value,
|
||||||
|
}, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal async Task LogDeclision(DeclisionTradeAction action, ITradeDataItem message, decimal? profit = null)
|
||||||
|
{
|
||||||
|
await LogDeclision(new Declision()
|
||||||
|
{
|
||||||
|
AccountId = string.Empty,
|
||||||
|
Figi = message.Figi,
|
||||||
|
Ticker = message.Ticker,
|
||||||
|
Value = profit,
|
||||||
|
Price = message.Price,
|
||||||
|
Time = message.IsHistoricalData ? message.Time : DateTime.UtcNow,
|
||||||
|
Action = action,
|
||||||
|
}, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal async Task LogDeclision(DeclisionTradeAction action, decimal price, DateTime time, ITradeDataItem message)
|
||||||
|
{
|
||||||
|
await LogDeclision(new Declision()
|
||||||
|
{
|
||||||
|
AccountId = string.Empty,
|
||||||
|
Figi = message.Figi,
|
||||||
|
Ticker = message.Ticker,
|
||||||
|
Value = price,
|
||||||
|
Price = price,
|
||||||
|
Time = time,
|
||||||
|
Action = action,
|
||||||
|
}, false);
|
||||||
|
}
|
||||||
|
|
||||||
private async Task WritePricesTask()
|
private async Task WritePricesTask()
|
||||||
{
|
{
|
||||||
var buffer1 = new List<ProcessedPrice>();
|
var buffer1 = new List<ProcessedPrice>();
|
||||||
|
|
|
@ -23,10 +23,7 @@ namespace KLHZ.Trader.Core.Exchange.Utils
|
||||||
var time = TimeOnly.FromDateTime(dt);
|
var time = TimeOnly.FromDateTime(dt);
|
||||||
if (day == DayOfWeek.Sunday || day == DayOfWeek.Saturday)
|
if (day == DayOfWeek.Sunday || day == DayOfWeek.Saturday)
|
||||||
{
|
{
|
||||||
if (time > _openTimeHoliday && time < _closeTimeHoliday)
|
return ExchangeState.Close;
|
||||||
{
|
|
||||||
return ExchangeState.Open;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
|
@ -0,0 +1,95 @@
|
||||||
|
using KLHZ.Trader.Core.Common;
|
||||||
|
using KLHZ.Trader.Core.Contracts.Declisions.Dtos.Enums;
|
||||||
|
using KLHZ.Trader.Core.Contracts.Messaging.Dtos.Interfaces;
|
||||||
|
using KLHZ.Trader.Core.DataLayer.Entities.Prices;
|
||||||
|
using KLHZ.Trader.Core.Exchange.Interfaces;
|
||||||
|
using KLHZ.Trader.Core.Exchange.Models.AssetsAccounting;
|
||||||
|
|
||||||
|
namespace KLHZ.Trader.Core.Exchange.Utils
|
||||||
|
{
|
||||||
|
internal static class TraderUtils
|
||||||
|
{
|
||||||
|
internal static Dictionary<TradingEvent, decimal> MergeResultsMult(IDictionary<TradingEvent, decimal> result, IDictionary<TradingEvent, decimal> data)
|
||||||
|
{
|
||||||
|
var res = new Dictionary<TradingEvent, decimal>();
|
||||||
|
foreach (var k in result.Keys)
|
||||||
|
{
|
||||||
|
var valRes = result[k];
|
||||||
|
var valData = data[k];
|
||||||
|
res[k] = valRes * valData;
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static Dictionary<TradingEvent, decimal> MergeResultsMax(IDictionary<TradingEvent, decimal> result, IDictionary<TradingEvent, decimal> data)
|
||||||
|
{
|
||||||
|
var res = new Dictionary<TradingEvent, decimal>();
|
||||||
|
foreach (var k in result.Keys)
|
||||||
|
{
|
||||||
|
var valRes = result[k];
|
||||||
|
var valData = result[k];
|
||||||
|
res[k] = System.Math.Max(valRes, valData);
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static Dictionary<TradingEvent, decimal> GetInitDict(decimal initValue)
|
||||||
|
{
|
||||||
|
var values = Enum.GetValues<TradingEvent>();
|
||||||
|
return values.ToDictionary(v => v, v => initValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static bool IsOperationAllowed(IManagedAccount account, decimal boutPrice, decimal count,
|
||||||
|
decimal accountCashPartFutures, decimal accountCashPart)
|
||||||
|
{
|
||||||
|
if (!BotModeSwitcher.CanPurchase()) return false;
|
||||||
|
|
||||||
|
var balance = account.Balance;
|
||||||
|
var total = account.Total;
|
||||||
|
|
||||||
|
var futures = account.Assets.Values.FirstOrDefault(v => v.Type == AssetType.Futures);
|
||||||
|
if (futures != null)
|
||||||
|
{
|
||||||
|
if ((balance - boutPrice * count) / total < accountCashPartFutures) return false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if ((balance - boutPrice * count) / total < accountCashPart) return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static ITradeDataItem FilterHighFreqValues(ITradeDataItem message, Dictionary<string, List<ITradeDataItem>> pricesCache1)
|
||||||
|
{
|
||||||
|
if (!pricesCache1.TryGetValue(message.Figi, out var list))
|
||||||
|
{
|
||||||
|
list = new List<ITradeDataItem>();
|
||||||
|
pricesCache1[message.Figi] = list;
|
||||||
|
}
|
||||||
|
list.Add(message);
|
||||||
|
|
||||||
|
if ((list.Last().Time - list.First().Time).TotalSeconds < 0.5)
|
||||||
|
{
|
||||||
|
list.Add(message);
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
message = new PriceChange()
|
||||||
|
{
|
||||||
|
Figi = message.Figi,
|
||||||
|
Ticker = message.Ticker,
|
||||||
|
Count = list.Sum(l => l.Count),
|
||||||
|
Direction = message.Direction,
|
||||||
|
IsHistoricalData = message.IsHistoricalData,
|
||||||
|
Time = message.Time,
|
||||||
|
Price = list.Sum(l => l.Price) / list.Count
|
||||||
|
};
|
||||||
|
list.Clear();
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,8 +2,6 @@ using KLHZ.Trader.Core.Contracts.Messaging.Dtos;
|
||||||
using KLHZ.Trader.Core.Contracts.Messaging.Interfaces;
|
using KLHZ.Trader.Core.Contracts.Messaging.Interfaces;
|
||||||
using KLHZ.Trader.Core.DataLayer;
|
using KLHZ.Trader.Core.DataLayer;
|
||||||
using KLHZ.Trader.Core.DataLayer.Entities.Orders;
|
using KLHZ.Trader.Core.DataLayer.Entities.Orders;
|
||||||
using KLHZ.Trader.Core.DataLayer.Entities.Prices;
|
|
||||||
using KLHZ.Trader.Core.Exchange.Services;
|
|
||||||
using KLHZ.Trader.Core.Math.Declisions.Utils;
|
using KLHZ.Trader.Core.Math.Declisions.Utils;
|
||||||
using KLHZ.Trader.Service.Models;
|
using KLHZ.Trader.Service.Models;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
@ -15,15 +13,13 @@ namespace KLHZ.Trader.Service.Controllers
|
||||||
[Route("[controller]/[action]")]
|
[Route("[controller]/[action]")]
|
||||||
public class PlayController : ControllerBase
|
public class PlayController : ControllerBase
|
||||||
{
|
{
|
||||||
private readonly TraderDataProvider _traderDataProvider;
|
|
||||||
private readonly IDataBus _dataBus;
|
private readonly IDataBus _dataBus;
|
||||||
private readonly IDbContextFactory<TraderDbContext> _dbContextFactory;
|
private readonly IDbContextFactory<TraderDbContext> _dbContextFactory;
|
||||||
|
|
||||||
public PlayController(IDataBus dataBus, IDbContextFactory<TraderDbContext> dbContextFactory, TraderDataProvider traderDataProvider)
|
public PlayController(IDataBus dataBus, IDbContextFactory<TraderDbContext> dbContextFactory)
|
||||||
{
|
{
|
||||||
_dbContextFactory = dbContextFactory;
|
_dbContextFactory = dbContextFactory;
|
||||||
_dataBus = dataBus;
|
_dataBus = dataBus;
|
||||||
_traderDataProvider = traderDataProvider;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpGet]
|
[HttpGet]
|
||||||
|
@ -54,12 +50,12 @@ namespace KLHZ.Trader.Service.Controllers
|
||||||
var prices = await context1.PriceChanges
|
var prices = await context1.PriceChanges
|
||||||
.Where(c => (c.Figi == figi1 || c.Figi == figi2) && c.Time >= time1 && c.Time < time2)
|
.Where(c => (c.Figi == figi1 || c.Figi == figi2) && c.Time >= time1 && c.Time < time2)
|
||||||
.OrderBy(c => c.Time)
|
.OrderBy(c => c.Time)
|
||||||
.Select(c => new NewPriceMessage()
|
.Select(c => new TradeDataItem()
|
||||||
{
|
{
|
||||||
Figi = c.Figi,
|
Figi = c.Figi,
|
||||||
Ticker = c.Ticker,
|
Ticker = c.Ticker,
|
||||||
Time = c.Time,
|
Time = c.Time,
|
||||||
Value = c.Value,
|
Price = c.Price,
|
||||||
IsHistoricalData = true,
|
IsHistoricalData = true,
|
||||||
Direction = c.Direction,
|
Direction = c.Direction,
|
||||||
Count = c.Count,
|
Count = c.Count,
|
||||||
|
@ -240,333 +236,120 @@ namespace KLHZ.Trader.Service.Controllers
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpGet]
|
[HttpGet]
|
||||||
public async Task CalcOrderbookMeanav(string figi)
|
public async Task RunConvolution(double? shift = null)
|
||||||
{
|
{
|
||||||
try
|
var timeStep = 30;
|
||||||
|
var time = DateTime.UtcNow.AddDays(-shift ?? -7).Date;
|
||||||
|
while (time < DateTime.UtcNow)
|
||||||
{
|
{
|
||||||
var t = DateTime.UtcNow.AddHours(-4);
|
var forSave = new List<Core.DataLayer.Entities.Prices.ProcessedPrice>();
|
||||||
|
time = time.AddMinutes(timeStep);
|
||||||
|
var figi1 = "FUTIMOEXF000";
|
||||||
|
//var figi1 = "BBG004730N88";
|
||||||
|
var figi2 = "FUTIMOEXF000";
|
||||||
|
//var figi2 = "FUTIMOEXF000";
|
||||||
|
var time2 = time;
|
||||||
|
//var time1 = new DateTime(2025, 9, 24, 11, 00, 0, DateTimeKind.Utc);
|
||||||
|
//var time2 = DateTime.UtcNow.AddMinutes(18);
|
||||||
using var context1 = await _dbContextFactory.CreateDbContextAsync();
|
using var context1 = await _dbContextFactory.CreateDbContextAsync();
|
||||||
context1.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking;
|
context1.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking;
|
||||||
var data = await context1.OrderbookItems
|
|
||||||
.Where(i => i.Time > t && i.Figi == figi && (i.ItemType == Core.DataLayer.Entities.Orders.Enums.OrderbookItemType.BidsSummary4 || i.ItemType == Core.DataLayer.Entities.Orders.Enums.OrderbookItemType.AsksSummary4))
|
var time1 = time2.AddHours(-3);
|
||||||
.OrderBy(i => i.Time)
|
var prices = await context1.PriceChanges
|
||||||
|
.Where(c => (c.Figi == figi1 || c.Figi == figi2) && c.Time >= time1 && c.Time < time2 && c.Direction == 1)
|
||||||
|
.OrderBy(c => c.Time)
|
||||||
|
.Select(c => new TradeDataItem()
|
||||||
|
{
|
||||||
|
Figi = c.Figi,
|
||||||
|
Ticker = c.Ticker,
|
||||||
|
Time = c.Time,
|
||||||
|
Price = c.Price,
|
||||||
|
IsHistoricalData = true,
|
||||||
|
Direction = c.Direction,
|
||||||
|
Count = c.Count,
|
||||||
|
})
|
||||||
.ToArrayAsync();
|
.ToArrayAsync();
|
||||||
var bids = new LinkedList<OrderbookItem>();
|
var pricesToSave = prices.Where(p => p.Time < time && p.Time >= time.AddMinutes(-timeStep)).ToArray();
|
||||||
var asks = new LinkedList<OrderbookItem>();
|
if (pricesToSave.Any())
|
||||||
var buffer = new List<OrderbookItem>();
|
|
||||||
var dt = TimeSpan.FromMinutes(1);
|
|
||||||
var q = data.ToLookup(d => d.Time);
|
|
||||||
foreach (var d in q)
|
|
||||||
{
|
{
|
||||||
var pair = d.DistinctBy(www => www.ItemType).OrderBy(www => www.ItemType).ToArray();
|
var p1 = pricesToSave.Last();
|
||||||
if (pair.Length == 2)
|
forSave.Add(new Core.DataLayer.Entities.Prices.ProcessedPrice()
|
||||||
{
|
{
|
||||||
bids.AddLast(pair[1]);
|
Figi = p1.Figi,
|
||||||
if (bids.Last().Time - bids.First().Time > dt)
|
Processor = "support_level_calc",
|
||||||
{
|
Ticker = p1.Ticker,
|
||||||
bids.RemoveFirst();
|
Count = p1.Count,
|
||||||
}
|
Direction = p1.Direction,
|
||||||
if (pair[0].Count != 0)
|
Time = p1.Time,
|
||||||
{
|
Price = p1.Price,
|
||||||
pair[1].Price = ((decimal)pair[1].Count) / ((decimal)pair[0].Count);
|
|
||||||
buffer.Add(new OrderbookItem()
|
|
||||||
{
|
|
||||||
Figi = pair[1].Figi,
|
|
||||||
Ticker = pair[1].Ticker,
|
|
||||||
Count = 1,
|
|
||||||
ItemType = Core.DataLayer.Entities.Orders.Enums.OrderbookItemType.BidsAsksSummary4_2min,
|
|
||||||
Price = bids.Sum(b => b.Price) / bids.Count,
|
|
||||||
Time = pair[1].Time,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (buffer.Count > 10000)
|
|
||||||
{
|
|
||||||
await context1.OrderbookItems.AddRangeAsync(buffer);
|
|
||||||
await context1.SaveChangesAsync();
|
|
||||||
buffer.Clear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (buffer.Count > 0)
|
|
||||||
{
|
|
||||||
await context1.OrderbookItems.AddRangeAsync(buffer);
|
|
||||||
await context1.SaveChangesAsync();
|
|
||||||
buffer.Clear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpGet]
|
|
||||||
public async Task CalcTradesMeanav(string figi)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var t = DateTime.UtcNow.AddHours(-4);
|
|
||||||
using var context1 = await _dbContextFactory.CreateDbContextAsync();
|
|
||||||
context1.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking;
|
|
||||||
var data = await context1.PriceChanges
|
|
||||||
.Where(i => i.Time > t && i.Figi == figi)
|
|
||||||
.OrderBy(i => i.Time)
|
|
||||||
.ToArrayAsync();
|
|
||||||
var sells = new LinkedList<PriceChange>();
|
|
||||||
var buys = new LinkedList<PriceChange>();
|
|
||||||
var buffer = new List<ProcessedPrice>();
|
|
||||||
var dt = TimeSpan.FromMinutes(1);
|
|
||||||
foreach (var d in data)
|
|
||||||
{
|
|
||||||
if (d.Direction == 1)
|
|
||||||
{
|
|
||||||
if (buys.Last().Time - buys.First().Time > dt)
|
|
||||||
{
|
|
||||||
buys.RemoveFirst();
|
|
||||||
}
|
|
||||||
buys.AddLast(d);
|
|
||||||
}
|
|
||||||
if (d.Direction == 2)
|
|
||||||
{
|
|
||||||
sells.AddLast(d);
|
|
||||||
if (sells.Last().Time - sells.First().Time > dt)
|
|
||||||
{
|
|
||||||
sells.RemoveFirst();
|
|
||||||
}
|
|
||||||
sells.AddLast(d);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (sells.Count > 0 && buys.Count > 0)
|
|
||||||
{
|
|
||||||
var meanS = ((decimal)sells.Sum(s => s.Count)) / sells.Count;
|
|
||||||
var meanB = ((decimal)buys.Sum(s => s.Count)) / buys.Count;
|
|
||||||
if (meanS != 0)
|
|
||||||
{
|
|
||||||
buffer.Add(new ProcessedPrice()
|
|
||||||
{
|
|
||||||
Figi = d.Figi,
|
|
||||||
Processor = "tradesbalance",
|
|
||||||
Ticker = d.Ticker,
|
|
||||||
Count = 1,
|
|
||||||
Direction = 0,
|
|
||||||
Time = d.Time,
|
|
||||||
Value = meanB / meanS
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (buffer.Count > 10000)
|
|
||||||
{
|
|
||||||
await context1.ProcessedPrices.AddRangeAsync(buffer);
|
|
||||||
await context1.SaveChangesAsync();
|
|
||||||
buffer.Clear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (buffer.Count > 0)
|
|
||||||
{
|
|
||||||
await context1.ProcessedPrices.AddRangeAsync(buffer);
|
|
||||||
await context1.SaveChangesAsync();
|
|
||||||
buffer.Clear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
[HttpGet]
|
|
||||||
public async Task TestInmterpolate(string figi)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var t1 = DateTime.UtcNow.AddHours(-4);
|
|
||||||
var t2 = DateTime.UtcNow.AddHours(1);
|
|
||||||
|
|
||||||
//t1 = new DateTime(2025, 9, 15, 10, 1, 0, DateTimeKind.Utc);
|
|
||||||
//t2 = new DateTime(2025, 9, 15, 10, 1, 50, DateTimeKind.Utc);
|
|
||||||
using var context1 = await _dbContextFactory.CreateDbContextAsync();
|
|
||||||
context1.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking;
|
|
||||||
var data = await context1.PriceChanges
|
|
||||||
.Where(i => i.Time >= t1 && i.Time <= t2 && i.Figi == figi)
|
|
||||||
.OrderBy(i => i.Time)
|
|
||||||
.ToArrayAsync();
|
|
||||||
|
|
||||||
var buffer = new List<ProcessedPrice>();
|
|
||||||
var res = SignalProcessing.InterpolateData(data.Select(d => d.Time).ToArray(), data.Select(d => d.Value).ToArray(),
|
|
||||||
TimeSpan.FromSeconds(1));
|
|
||||||
|
|
||||||
|
|
||||||
for (int i = 0; i < res.Item1.Length; i++)
|
|
||||||
{
|
|
||||||
buffer.Add(new ProcessedPrice()
|
|
||||||
{
|
|
||||||
Figi = figi,
|
|
||||||
Processor = "1secinterpol",
|
|
||||||
Ticker = data[0].Ticker,
|
|
||||||
Count = 1,
|
|
||||||
Direction = 0,
|
|
||||||
Time = res.Item1[i],
|
|
||||||
Value = (decimal)res.Item2[i]
|
|
||||||
});
|
});
|
||||||
|
var leverage = 3;
|
||||||
|
var hist = Statistics.CalcHistogram(prices);
|
||||||
|
var convs = Statistics.CalcConvolution(hist, leverage).ToList();
|
||||||
|
var orderedConvs = convs.OrderByDescending(c => c.Sum).Take(5).ToList();
|
||||||
|
orderedConvs = orderedConvs.OrderBy(c => c.Value).ToList();
|
||||||
|
var values = new List<decimal>();
|
||||||
|
|
||||||
|
foreach (var c in orderedConvs)
|
||||||
|
{
|
||||||
|
var left = c.Value - 0.5m * (leverage - 1);
|
||||||
|
var right = c.Value + 0.5m * (leverage + 1);
|
||||||
|
|
||||||
|
if (values.Count > 0)
|
||||||
|
{
|
||||||
|
var last = values.Last();
|
||||||
|
if (last < left)
|
||||||
|
{
|
||||||
|
values.Add(left);
|
||||||
|
values.Add(right);
|
||||||
|
}
|
||||||
|
else if (last >= left && last < right)
|
||||||
|
{
|
||||||
|
values.Remove(last);
|
||||||
|
values.Add(right);
|
||||||
|
}
|
||||||
|
else if (last >= right)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
values.Add(left);
|
||||||
|
values.Add(right);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var pairs = new List<(decimal left, decimal right)>();
|
||||||
|
for (int i = 0; i < values.Count - 1; i += 2)
|
||||||
|
{
|
||||||
|
pairs.Add((values[i], values[i + 1]));
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var price in pricesToSave)
|
||||||
|
{
|
||||||
|
foreach (var p in pairs)
|
||||||
|
{
|
||||||
|
if (price.Price >= p.left && price.Price <= p.right)
|
||||||
|
{
|
||||||
|
forSave.Add(new Core.DataLayer.Entities.Prices.ProcessedPrice()
|
||||||
|
{
|
||||||
|
Figi = price.Figi,
|
||||||
|
Processor = "support_level",
|
||||||
|
Ticker = price.Ticker,
|
||||||
|
Count = price.Count,
|
||||||
|
Direction = price.Direction,
|
||||||
|
Time = price.Time,
|
||||||
|
Price = price.Price,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
await context1.ProcessedPrices.AddRangeAsync(forSave);
|
||||||
|
await context1.SaveChangesAsync();
|
||||||
}
|
}
|
||||||
await context1.ProcessedPrices.AddRangeAsync(buffer);
|
|
||||||
await context1.SaveChangesAsync();
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpGet]
|
|
||||||
public async Task TestFFT(string figi)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var t1 = new DateTime(2025, 9, 15, 6, 30, 0, DateTimeKind.Utc);
|
|
||||||
var t2 = new DateTime(2025, 9, 15, 7, 50, 50, DateTimeKind.Utc);
|
|
||||||
|
|
||||||
using var context1 = await _dbContextFactory.CreateDbContextAsync();
|
|
||||||
context1.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking;
|
|
||||||
var data = await context1.PriceChanges
|
|
||||||
.Where(i => i.Time >= t1 && i.Time <= t2 && i.Figi == figi)
|
|
||||||
.OrderBy(i => i.Time)
|
|
||||||
.ToArrayAsync();
|
|
||||||
|
|
||||||
var buffer = new List<ProcessedPrice>();
|
|
||||||
var values = data.Select(d => d.Value).ToArray();
|
|
||||||
var times = data.Select(d => d.Time).ToArray();
|
|
||||||
var res = SignalProcessing.InterpolateData(times, values,
|
|
||||||
TimeSpan.FromSeconds(10));
|
|
||||||
|
|
||||||
//FFT.GetMainHarmonictPeriod(res.Item2, res.Item1.Last() - res.Item1.First());
|
|
||||||
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//[HttpGet]
|
|
||||||
//public async Task GetBalance(string figi)
|
|
||||||
//{
|
|
||||||
// try
|
|
||||||
// {
|
|
||||||
// var time1 = DateTime.UtcNow.AddDays(-7);
|
|
||||||
// using var context1 = await _dbContextFactory.CreateDbContextAsync();
|
|
||||||
// context1.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking;
|
|
||||||
// var data = await context1.PriceChanges
|
|
||||||
// .Where(c => c.Figi == figi && c.Time >= time1)
|
|
||||||
// .OrderBy(c => c.Time)
|
|
||||||
// .Select(c => new NewPriceMessage()
|
|
||||||
// {
|
|
||||||
// Figi = figi,
|
|
||||||
// Ticker = c.Ticker,
|
|
||||||
// Time = c.Time,
|
|
||||||
// Value = c.Value,
|
|
||||||
// IsHistoricalData = true
|
|
||||||
// })
|
|
||||||
// .ToArrayAsync();
|
|
||||||
// var buffer = new List<ProcessedPrice>();
|
|
||||||
// var list = new LinkedList<(DateTime time, double value)>();
|
|
||||||
// var previous = -1000d;
|
|
||||||
// foreach (var mess in data)
|
|
||||||
// {
|
|
||||||
// await _traderDataProvider.AddData(mess, TimeSpan.FromHours(6));
|
|
||||||
// var dataFromCache = await _traderDataProvider.GetData(figi, TimeSpan.FromMinutes(15));
|
|
||||||
// if (dataFromCache.isFullIntervalExists)
|
|
||||||
// {
|
|
||||||
// if (ShapeAreaCalculator.TryGetAreasRelation(dataFromCache.timestamps, dataFromCache.prices,mess.Value,out var res))
|
|
||||||
// {
|
|
||||||
// if (list.Count > 0 && mess.Time - list.Last().time > TimeSpan.FromMinutes(5))
|
|
||||||
// {
|
|
||||||
// list.Clear();
|
|
||||||
// }
|
|
||||||
// list.AddLast((mess.Time, res));
|
|
||||||
// if (list.Last().time - list.First().time > TimeSpan.FromMinutes(1))
|
|
||||||
// {
|
|
||||||
// list.RemoveFirst();
|
|
||||||
// }
|
|
||||||
// var newRes = (decimal)(list.Sum(i => i.value) / list.Count);
|
|
||||||
// if (list.Count > 0)
|
|
||||||
// {
|
|
||||||
// try
|
|
||||||
// {
|
|
||||||
// buffer.Add(new ProcessedPrice()
|
|
||||||
// {
|
|
||||||
// Figi = figi,
|
|
||||||
// Processor = "balancescalc30min",
|
|
||||||
// Ticker = mess.Ticker,
|
|
||||||
// Time = mess.Time,
|
|
||||||
// Value = newRes,
|
|
||||||
// //Value = (decimal)res,
|
|
||||||
// });
|
|
||||||
// }
|
|
||||||
// catch(Exception ex)
|
|
||||||
// {
|
|
||||||
|
|
||||||
// }
|
|
||||||
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// else
|
|
||||||
// {
|
|
||||||
// previous = -1d;
|
|
||||||
// }
|
|
||||||
// if (buffer.Count > 10000)
|
|
||||||
// {
|
|
||||||
// await context1.ProcessedPrices.AddRangeAsync(buffer);
|
|
||||||
// await context1.SaveChangesAsync();
|
|
||||||
// buffer.Clear();
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// if (buffer.Count > 0)
|
|
||||||
// {
|
|
||||||
// await context1.ProcessedPrices.AddRangeAsync(buffer);
|
|
||||||
// await context1.SaveChangesAsync();
|
|
||||||
// buffer.Clear();
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// catch (Exception ex)
|
|
||||||
// {
|
|
||||||
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
|
|
||||||
////[HttpGet]
|
|
||||||
//public async Task LoadTradesToHistory(string figi)
|
|
||||||
//{
|
|
||||||
// try
|
|
||||||
// {
|
|
||||||
// using var context1 = await _dbContextFactory.CreateDbContextAsync();
|
|
||||||
// context1.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking;
|
|
||||||
// var data = await context1.InstrumentTrades
|
|
||||||
// .Where(c => c.Figi == figi)
|
|
||||||
// .OrderBy(c => c.BoughtAt)
|
|
||||||
// .Select(c => new PriceChange()
|
|
||||||
// {
|
|
||||||
// Figi = figi,
|
|
||||||
// Ticker = c.Ticker,
|
|
||||||
// Time = c.BoughtAt,
|
|
||||||
// Value = c.Price,
|
|
||||||
// IsHistoricalData = true
|
|
||||||
// })
|
|
||||||
// .ToArrayAsync();
|
|
||||||
// await context1.PriceChanges.Where(p => p.Figi == figi).ExecuteDeleteAsync();
|
|
||||||
// await context1.PriceChanges.AddRangeAsync(data);
|
|
||||||
// await context1.SaveChangesAsync();
|
|
||||||
// }
|
|
||||||
// catch (Exception ex)
|
|
||||||
// {
|
|
||||||
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,11 +2,11 @@
|
||||||
|
|
||||||
namespace KLHZ.Trader.Service.Models
|
namespace KLHZ.Trader.Service.Models
|
||||||
{
|
{
|
||||||
public class TimeSeriesData
|
internal class TimeSeriesData
|
||||||
{
|
{
|
||||||
public required string Figi { get; set; }
|
public required string Figi { get; set; }
|
||||||
public DateTime Time { get; set; }
|
public DateTime Time { get; set; }
|
||||||
public INewPrice? NewPrice { get; set; }
|
public ITradeDataItem? NewPrice { get; set; }
|
||||||
public IOrderbook? Orderbook { get; set; }
|
public IOrderbook? Orderbook { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,17 +8,15 @@
|
||||||
},
|
},
|
||||||
"LokiUrl": "",
|
"LokiUrl": "",
|
||||||
"ExchangeConfig": {
|
"ExchangeConfig": {
|
||||||
"StopBuyLengthMinuts": 15,
|
|
||||||
"ExchangeDataRecievingEnabled": true,
|
"ExchangeDataRecievingEnabled": true,
|
||||||
"Token": "",
|
"Token": "",
|
||||||
"ManagingAccountNamePatterns": [ "автотрейд" ],
|
"ManagingAccountNamePatterns": [ "автотрейд" ],
|
||||||
"DataRecievingInstrumentsFigis": [ "BBG004730N88", "FUTIMOEXF000", "FUTGMKN09250", "FUTBR1025000", "FUTNG0925000", "FUTNASD09250" ],
|
"DataRecievingInstrumentsFigis": [ "BBG004730N88", "FUTIMOEXF000", "FUTGMKN09250", "FUTBR1025000", "FUTNG0925000", "FUTNASD09250" ],
|
||||||
"TradingInstrumentsFigis": [ "FUTIMOEXF000" ],
|
"TradingInstrumentsFigis": [ "FUTIMOEXF000"],
|
||||||
"FutureComission": 0.0025,
|
"FutureComission": 0.0025,
|
||||||
"ShareComission": 0.0004,
|
"ShareComission": 0.0004,
|
||||||
"AccountCashPart": 0.05,
|
"AccountCashPart": 0.05,
|
||||||
"AccountCashPartFutures": 0.5,
|
"AccountCashPartFutures": 0.5,
|
||||||
"DefaultBuyPartOfAccount": 0.3333,
|
|
||||||
"InstrumentsSettings": [
|
"InstrumentsSettings": [
|
||||||
{
|
{
|
||||||
"Figi": "FUTIMOEXF000",
|
"Figi": "FUTIMOEXF000",
|
||||||
|
|
Loading…
Reference in New Issue