Compare commits
3 Commits
6770d12640
...
ecd9a70e2e
Author | SHA1 | Date |
---|---|---|
|
ecd9a70e2e | |
|
f7cecbe44a | |
|
a20609ae72 |
|
@ -1,26 +0,0 @@
|
||||||
using KLHZ.Trader.Core.Contracts.Messaging.Dtos.Interfaces;
|
|
||||||
|
|
||||||
namespace KLHZ.Trader.Core.Contracts.Declisions.Dtos
|
|
||||||
{
|
|
||||||
public class CachedValue : ITradeDataItem
|
|
||||||
{
|
|
||||||
public DateTime Time { get; init; }
|
|
||||||
public long Count { get; init; }
|
|
||||||
public decimal Price { get; init; }
|
|
||||||
public decimal Value { get; init; }
|
|
||||||
public decimal Value2 { get; init; }
|
|
||||||
|
|
||||||
public CachedValue()
|
|
||||||
{
|
|
||||||
Figi = string.Empty;
|
|
||||||
Ticker = string.Empty;
|
|
||||||
Direction = 0;
|
|
||||||
IsHistoricalData = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool IsHistoricalData { get; init; }
|
|
||||||
public required string Figi { get; init; }
|
|
||||||
public required string Ticker { get; init; }
|
|
||||||
public int Direction { get; init; }
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,11 +0,0 @@
|
||||||
namespace KLHZ.Trader.Core.Contracts.Declisions.Dtos.Enums
|
|
||||||
{
|
|
||||||
public enum TimeWindowCacheType
|
|
||||||
{
|
|
||||||
None = 0,
|
|
||||||
_20_Seconds = 2001,
|
|
||||||
_1_Minute = 1,
|
|
||||||
_5_Minutes = 2,
|
|
||||||
_15_Minutes = 15,
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,32 +0,0 @@
|
||||||
using KLHZ.Trader.Core.Contracts.Declisions.Dtos;
|
|
||||||
using KLHZ.Trader.Core.Contracts.Declisions.Dtos.Enums;
|
|
||||||
using KLHZ.Trader.Core.Contracts.Messaging.Dtos.Interfaces;
|
|
||||||
|
|
||||||
namespace KLHZ.Trader.Core.Contracts.Declisions.Interfaces
|
|
||||||
{
|
|
||||||
public interface IPriceHistoryCacheUnit
|
|
||||||
{
|
|
||||||
public string Figi { get; }
|
|
||||||
public int Length { get; }
|
|
||||||
public ValueTask AddData(ITradeDataItem priceChange);
|
|
||||||
public ValueTask<(DateTime[] timestamps, decimal[] prices)> GetData(int? length = null);
|
|
||||||
public ValueTask<(DateTime[] timestamps, decimal[] prices, bool isFullIntervalExists)> GetData(TimeSpan period);
|
|
||||||
public ValueTask AddOrderbook(IOrderbook orderbook);
|
|
||||||
|
|
||||||
public ValueTask AddDataToTimeWindowCache(string key, CachedValue data, TimeWindowCacheType timeWindowCacheType);
|
|
||||||
|
|
||||||
public ValueTask<CachedValue[]> GetDataFromTimeWindowCache(string key, TimeWindowCacheType timeWindowCacheType);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Число заявок на продаже в стакане.
|
|
||||||
/// </summary>
|
|
||||||
public decimal AsksCount { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Число заявок на покупку в стакане.
|
|
||||||
/// </summary>
|
|
||||||
public decimal BidsCount { get; }
|
|
||||||
|
|
||||||
public ValueTask<(DateTime time, decimal price)> GetLastValues();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
namespace KLHZ.Trader.Core.Contracts.Messaging.Dtos
|
||||||
|
{
|
||||||
|
public class AttachedInfo
|
||||||
|
{
|
||||||
|
public required string Key { get; init; }
|
||||||
|
public decimal Value1 { get; init; }
|
||||||
|
public decimal Value2 { get; init; }
|
||||||
|
}
|
||||||
|
}
|
|
@ -11,5 +11,10 @@
|
||||||
public int Direction { get; }
|
public int Direction { get; }
|
||||||
public decimal Value { get; }
|
public decimal Value { get; }
|
||||||
public decimal Value2 { get; }
|
public decimal Value2 { get; }
|
||||||
|
public AttachedInfo? AttachedInfo => null;
|
||||||
|
public void SetAttachedInfo(AttachedInfo attachedInfo)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
using KLHZ.Trader.Core.Contracts.Messaging.Dtos.Interfaces;
|
using KLHZ.Trader.Core.Contracts.Messaging.Dtos.Interfaces;
|
||||||
|
using System.ComponentModel.DataAnnotations.Schema;
|
||||||
|
|
||||||
namespace KLHZ.Trader.Core.Contracts.Messaging.Dtos
|
namespace KLHZ.Trader.Core.Contracts.Messaging.Dtos
|
||||||
{
|
{
|
||||||
|
@ -13,5 +14,27 @@ namespace KLHZ.Trader.Core.Contracts.Messaging.Dtos
|
||||||
public int Direction { get; set; }
|
public int Direction { get; set; }
|
||||||
public decimal Value { get; init; }
|
public decimal Value { get; init; }
|
||||||
public decimal Value2 { get; init; }
|
public decimal Value2 { get; init; }
|
||||||
|
[NotMapped]
|
||||||
|
public AttachedInfo? AttachedInfo
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
lock (_locker)
|
||||||
|
{
|
||||||
|
return _attachedInfo;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetAttachedInfo(AttachedInfo? attachedInfo)
|
||||||
|
{
|
||||||
|
lock (_locker)
|
||||||
|
{
|
||||||
|
_attachedInfo = attachedInfo;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private AttachedInfo? _attachedInfo;
|
||||||
|
private readonly object _locker = new();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,65 +0,0 @@
|
||||||
using KLHZ.Trader.Core.Contracts.Declisions.Dtos;
|
|
||||||
using KLHZ.Trader.Core.Contracts.Declisions.Dtos.Enums;
|
|
||||||
|
|
||||||
namespace KLHZ.Trader.Core.Math.Declisions.Dtos
|
|
||||||
{
|
|
||||||
internal class TimeWindowCacheItem
|
|
||||||
{
|
|
||||||
private readonly object _locker = new();
|
|
||||||
private readonly LinkedList<CachedValue> _cachedValues = new();
|
|
||||||
|
|
||||||
public readonly TimeSpan WindowSize;
|
|
||||||
public readonly string Key;
|
|
||||||
|
|
||||||
public TimeWindowCacheItem(string key, TimeWindowCacheType window)
|
|
||||||
{
|
|
||||||
Key = key;
|
|
||||||
WindowSize = GetTimeSpan(window);
|
|
||||||
}
|
|
||||||
|
|
||||||
public ValueTask AddData(CachedValue cachedValue)
|
|
||||||
{
|
|
||||||
lock (_locker)
|
|
||||||
{
|
|
||||||
_cachedValues.AddLast(cachedValue);
|
|
||||||
while (_cachedValues.Last != null && _cachedValues.First != null
|
|
||||||
&& _cachedValues.Last.Value.Time - _cachedValues.First.Value.Time > WindowSize)
|
|
||||||
{
|
|
||||||
_cachedValues.RemoveFirst();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ValueTask.CompletedTask;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ValueTask<CachedValue[]> GetValues()
|
|
||||||
{
|
|
||||||
lock (_locker)
|
|
||||||
{
|
|
||||||
return ValueTask.FromResult(_cachedValues.ToArray());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static TimeSpan GetTimeSpan(TimeWindowCacheType type)
|
|
||||||
{
|
|
||||||
switch (type)
|
|
||||||
{
|
|
||||||
case TimeWindowCacheType._5_Minutes:
|
|
||||||
{
|
|
||||||
return TimeSpan.FromMinutes(5);
|
|
||||||
}
|
|
||||||
case TimeWindowCacheType._15_Minutes:
|
|
||||||
{
|
|
||||||
return TimeSpan.FromMinutes(15);
|
|
||||||
}
|
|
||||||
case TimeWindowCacheType._20_Seconds:
|
|
||||||
{
|
|
||||||
return TimeSpan.FromSeconds(20);
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
{
|
|
||||||
return TimeSpan.FromMinutes(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,27 +0,0 @@
|
||||||
namespace KLHZ.Trader.Core.Math.Declisions.Dtos
|
|
||||||
{
|
|
||||||
internal readonly struct TwoLocalTrendsResultDto
|
|
||||||
{
|
|
||||||
public readonly int Start;
|
|
||||||
public readonly int Bound;
|
|
||||||
public readonly int End;
|
|
||||||
public readonly decimal DiffStart;
|
|
||||||
public readonly decimal DiffEnd;
|
|
||||||
public readonly bool Success;
|
|
||||||
public readonly TimeSpan PeriodStart;
|
|
||||||
public readonly TimeSpan PeriodEnd;
|
|
||||||
|
|
||||||
public TwoLocalTrendsResultDto(bool success, decimal diffStart, decimal diffEnd, int start, int bound, int end,
|
|
||||||
TimeSpan periodStart, TimeSpan periodEnd)
|
|
||||||
{
|
|
||||||
Success = success;
|
|
||||||
DiffStart = diffStart;
|
|
||||||
DiffEnd = diffEnd;
|
|
||||||
Start = start;
|
|
||||||
Bound = bound;
|
|
||||||
End = end;
|
|
||||||
PeriodStart = periodStart;
|
|
||||||
PeriodEnd = periodEnd;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,224 +0,0 @@
|
||||||
using KLHZ.Trader.Core.Contracts.Declisions.Dtos;
|
|
||||||
using KLHZ.Trader.Core.Contracts.Declisions.Dtos.Enums;
|
|
||||||
using KLHZ.Trader.Core.Contracts.Declisions.Interfaces;
|
|
||||||
using KLHZ.Trader.Core.Contracts.Messaging.Dtos.Interfaces;
|
|
||||||
using KLHZ.Trader.Core.Math.Declisions.Dtos;
|
|
||||||
using System.Collections.Concurrent;
|
|
||||||
|
|
||||||
namespace KLHZ.Trader.Core.Math.Declisions.Services.Cache
|
|
||||||
{
|
|
||||||
public class PriceHistoryCacheUnit2 : IPriceHistoryCacheUnit
|
|
||||||
{
|
|
||||||
public const int CacheMaxLength = 30000;
|
|
||||||
private const int _arrayMaxLength = 60000;
|
|
||||||
|
|
||||||
public string Figi { get; init; }
|
|
||||||
|
|
||||||
public int Length
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
lock (_locker)
|
|
||||||
{
|
|
||||||
return _length;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public decimal AsksCount
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
lock (_locker)
|
|
||||||
{
|
|
||||||
return _asksCount;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public decimal BidsCount
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
lock (_locker)
|
|
||||||
{
|
|
||||||
return _bidsCount;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private readonly object _locker = new();
|
|
||||||
private readonly decimal[] Prices = new decimal[_arrayMaxLength];
|
|
||||||
private readonly DateTime[] Timestamps = new DateTime[_arrayMaxLength];
|
|
||||||
private readonly ConcurrentDictionary<string, TimeWindowCacheItem> _20_secTimeWindows = new();
|
|
||||||
private readonly ConcurrentDictionary<string, TimeWindowCacheItem> _1_minTimeWindows = new();
|
|
||||||
private readonly ConcurrentDictionary<string, TimeWindowCacheItem> _5_minTimeWindows = new();
|
|
||||||
private readonly ConcurrentDictionary<string, TimeWindowCacheItem> _15_minTimeWindows = new();
|
|
||||||
|
|
||||||
private int _length = 0;
|
|
||||||
private int _pointer = -1;
|
|
||||||
|
|
||||||
private long _asksCount = 1;
|
|
||||||
private long _bidsCount = 1;
|
|
||||||
|
|
||||||
public ValueTask AddDataToTimeWindowCache(string key, CachedValue data, TimeWindowCacheType timeWindowCacheType)
|
|
||||||
{
|
|
||||||
var dict = GetDict(timeWindowCacheType);
|
|
||||||
if (!dict.TryGetValue(key, out var cahcheItem))
|
|
||||||
{
|
|
||||||
dict.TryAdd(key, new TimeWindowCacheItem(key, timeWindowCacheType));
|
|
||||||
}
|
|
||||||
dict[key].AddData(data);
|
|
||||||
return ValueTask.CompletedTask;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ValueTask<CachedValue[]> GetDataFromTimeWindowCache(string key, TimeWindowCacheType timeWindowCacheType)
|
|
||||||
{
|
|
||||||
var dict = GetDict(timeWindowCacheType);
|
|
||||||
if (dict.TryGetValue(key, out var cahcheItem))
|
|
||||||
{
|
|
||||||
return cahcheItem.GetValues();
|
|
||||||
}
|
|
||||||
return ValueTask.FromResult(Array.Empty<CachedValue>());
|
|
||||||
}
|
|
||||||
|
|
||||||
private ConcurrentDictionary<string, TimeWindowCacheItem> GetDict(TimeWindowCacheType timeWindowCacheType)
|
|
||||||
{
|
|
||||||
switch (timeWindowCacheType)
|
|
||||||
{
|
|
||||||
case TimeWindowCacheType._5_Minutes:
|
|
||||||
{
|
|
||||||
return _5_minTimeWindows;
|
|
||||||
}
|
|
||||||
case TimeWindowCacheType._15_Minutes:
|
|
||||||
{
|
|
||||||
return _15_minTimeWindows;
|
|
||||||
}
|
|
||||||
case TimeWindowCacheType._20_Seconds:
|
|
||||||
{
|
|
||||||
return _20_secTimeWindows;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
{
|
|
||||||
return _1_minTimeWindows; ;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public ValueTask AddData(ITradeDataItem priceChange)
|
|
||||||
{
|
|
||||||
if (priceChange.Figi != Figi) return ValueTask.CompletedTask;
|
|
||||||
lock (_locker)
|
|
||||||
{
|
|
||||||
_pointer++;
|
|
||||||
Prices[_pointer] = priceChange.Price;
|
|
||||||
Timestamps[_pointer] = priceChange.Time;
|
|
||||||
if (_length < CacheMaxLength)
|
|
||||||
{
|
|
||||||
_length++;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_pointer == _arrayMaxLength - 1)
|
|
||||||
{
|
|
||||||
Array.Copy(Prices, Prices.Length - CacheMaxLength, Prices, 0, CacheMaxLength);
|
|
||||||
Array.Copy(Timestamps, Timestamps.Length - CacheMaxLength, Timestamps, 0, CacheMaxLength);
|
|
||||||
_pointer = CacheMaxLength - 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ValueTask.CompletedTask;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ValueTask<(DateTime[] timestamps, decimal[] prices)> GetData(int? length = null)
|
|
||||||
{
|
|
||||||
lock (_locker)
|
|
||||||
{
|
|
||||||
if (_pointer < 0)
|
|
||||||
{
|
|
||||||
return ValueTask.FromResult((Array.Empty<DateTime>(), Array.Empty<decimal>()));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var dataLength = length.HasValue ? System.Math.Min(length.Value, _length) : _length;
|
|
||||||
var prices = new decimal[dataLength];
|
|
||||||
var timestamps = new DateTime[dataLength];
|
|
||||||
var index = 1 + _pointer - dataLength;
|
|
||||||
Array.Copy(Prices, index, prices, 0, prices.Length);
|
|
||||||
Array.Copy(Timestamps, index, timestamps, 0, timestamps.Length);
|
|
||||||
return ValueTask.FromResult((timestamps, prices));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public ValueTask AddOrderbook(IOrderbook orderbook)
|
|
||||||
{
|
|
||||||
if (orderbook.Figi != Figi) return ValueTask.CompletedTask;
|
|
||||||
lock (_locker)
|
|
||||||
{
|
|
||||||
_asksCount = orderbook.AsksCount;
|
|
||||||
_bidsCount = orderbook.BidsCount;
|
|
||||||
}
|
|
||||||
return ValueTask.CompletedTask;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ValueTask<(DateTime time, decimal price)> GetLastValues()
|
|
||||||
{
|
|
||||||
lock (_locker)
|
|
||||||
{
|
|
||||||
return _pointer >= 0 ? ValueTask.FromResult((Timestamps[_pointer], Prices[_pointer])) : ValueTask.FromResult((DateTime.UtcNow, 0m));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public ValueTask<(DateTime[] timestamps, decimal[] prices, bool isFullIntervalExists)> GetData(TimeSpan period)
|
|
||||||
{
|
|
||||||
lock (_locker)
|
|
||||||
{
|
|
||||||
if (_pointer < 0)
|
|
||||||
{
|
|
||||||
return ValueTask.FromResult((Array.Empty<DateTime>(), Array.Empty<decimal>(), false));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var i = _pointer;
|
|
||||||
var lastTime = Timestamps[i];
|
|
||||||
for (i = _pointer - 1; i >= 0; i--)
|
|
||||||
{
|
|
||||||
var currentTime = Timestamps[i];
|
|
||||||
if (lastTime - currentTime >= period)
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var dataLength = _pointer - i;
|
|
||||||
var prices = new decimal[dataLength];
|
|
||||||
var timestamps = new DateTime[dataLength];
|
|
||||||
var index = 1 + _pointer - dataLength;
|
|
||||||
Array.Copy(Prices, index, prices, 0, prices.Length);
|
|
||||||
Array.Copy(Timestamps, index, timestamps, 0, timestamps.Length);
|
|
||||||
return ValueTask.FromResult((timestamps, prices, i + 1 != 0));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public PriceHistoryCacheUnit2(string figi, params ITradeDataItem[] priceChanges)
|
|
||||||
{
|
|
||||||
Figi = figi;
|
|
||||||
|
|
||||||
|
|
||||||
if (priceChanges.Length == 0)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var selectedPriceChanges = priceChanges
|
|
||||||
.OrderBy(pc => pc.Time)
|
|
||||||
.Skip(priceChanges.Length - CacheMaxLength)
|
|
||||||
.ToArray();
|
|
||||||
|
|
||||||
foreach (var pc in selectedPriceChanges)
|
|
||||||
{
|
|
||||||
AddData(pc).AsTask().Wait();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,6 +1,4 @@
|
||||||
using KLHZ.Trader.Core.Contracts.Declisions.Dtos;
|
using KLHZ.Trader.Core.Contracts.Messaging.Dtos.Interfaces;
|
||||||
using KLHZ.Trader.Core.Contracts.Declisions.Dtos.Enums;
|
|
||||||
using KLHZ.Trader.Core.Contracts.Messaging.Dtos.Interfaces;
|
|
||||||
|
|
||||||
namespace KLHZ.Trader.Core.Math.Declisions.Services.Cache
|
namespace KLHZ.Trader.Core.Math.Declisions.Services.Cache
|
||||||
{
|
{
|
||||||
|
@ -29,11 +27,6 @@ namespace KLHZ.Trader.Core.Math.Declisions.Services.Cache
|
||||||
private readonly object _locker = new();
|
private readonly object _locker = new();
|
||||||
private readonly Dictionary<string, LinkedList<ITradeDataItem>> _items = new Dictionary<string, LinkedList<ITradeDataItem>>();
|
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)
|
public ValueTask AddData(ITradeDataItem item, string? key = null)
|
||||||
{
|
{
|
||||||
lock (_locker)
|
lock (_locker)
|
||||||
|
@ -103,7 +96,6 @@ namespace KLHZ.Trader.Core.Math.Declisions.Services.Cache
|
||||||
return ValueTask.FromResult(res.ToArray());
|
return ValueTask.FromResult(res.ToArray());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public ValueTask<(DateTime time, decimal price)> GetLastValues(string? key = null)
|
public ValueTask<(DateTime time, decimal price)> GetLastValues(string? key = null)
|
||||||
{
|
{
|
||||||
key = key ?? string.Empty;
|
key = key ?? string.Empty;
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
using KLHZ.Trader.Core.Contracts.Declisions.Dtos.Enums;
|
using KLHZ.Trader.Core.Contracts.Declisions.Dtos.Enums;
|
||||||
|
using KLHZ.Trader.Core.Contracts.Messaging.Dtos.Interfaces;
|
||||||
using MathNet.Numerics;
|
using MathNet.Numerics;
|
||||||
|
|
||||||
namespace KLHZ.Trader.Core.Math.Declisions.Utils
|
namespace KLHZ.Trader.Core.Math.Declisions.Utils
|
||||||
|
@ -116,5 +117,34 @@ namespace KLHZ.Trader.Core.Math.Declisions.Utils
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static bool TryCalcTrendDiff(ITradeDataItem[] data, out decimal diff)
|
||||||
|
{
|
||||||
|
diff = 0;
|
||||||
|
if (data.Length <= 1)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var startTime = data[0].Time;
|
||||||
|
var x = new double[data.Length];
|
||||||
|
for (int i = 0; i < data.Length; i++)
|
||||||
|
{
|
||||||
|
x[i] = (data[i].Time - startTime).TotalSeconds;
|
||||||
|
}
|
||||||
|
if (x.Min() == x.Max())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
var line = Fit.Line(x.ToArray(), data.Select(d => (double)d.Price).ToArray());
|
||||||
|
|
||||||
|
var p1 = line.A + line.B * 0;
|
||||||
|
var p2 = line.A + line.B * (data[data.Length - 1].Time - data[0].Time).TotalSeconds;
|
||||||
|
|
||||||
|
diff = (decimal)(p2 - p1);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,98 +0,0 @@
|
||||||
namespace KLHZ.Trader.Core.Math.Declisions.Utils
|
|
||||||
{
|
|
||||||
public static class ShapeAreaCalculator
|
|
||||||
{
|
|
||||||
// Метод для расчёта площади треугольника
|
|
||||||
public static double CalculateTriangleArea(
|
|
||||||
DateTime pointA_X, decimal pointA_Y,
|
|
||||||
DateTime pointB_X, decimal pointB_Y,
|
|
||||||
DateTime pointC_X, decimal pointC_Y)
|
|
||||||
{
|
|
||||||
// Определяем самую позднюю точку
|
|
||||||
DateTime latestPoint = new[] { pointA_X, pointB_X, pointC_X }.Max();
|
|
||||||
|
|
||||||
// Смещённые координаты
|
|
||||||
double offsetA = GetOffsetInSeconds(latestPoint, pointA_X);
|
|
||||||
double offsetB = GetOffsetInSeconds(latestPoint, pointB_X);
|
|
||||||
double offsetC = GetOffsetInSeconds(latestPoint, pointC_X);
|
|
||||||
|
|
||||||
// Расчёт расстояний
|
|
||||||
double sideAB = DistanceBetweenPoints(offsetA, (double)pointA_Y, offsetB, (double)pointB_Y);
|
|
||||||
double sideBC = DistanceBetweenPoints(offsetB, (double)pointB_Y, offsetC, (double)pointC_Y);
|
|
||||||
double sideCA = DistanceBetweenPoints(offsetC, (double)pointC_Y, offsetA, (double)pointA_Y);
|
|
||||||
|
|
||||||
// Формула Герона
|
|
||||||
double semiPerimeter = (sideAB + sideBC + sideCA) / 2;
|
|
||||||
return System.Math.Sqrt(semiPerimeter * (semiPerimeter - sideAB) *
|
|
||||||
(semiPerimeter - sideBC) * (semiPerimeter - sideCA));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Метод для расчёта площади четырёхугольника
|
|
||||||
public static double CalculateQuadrilateralArea(
|
|
||||||
DateTime pointA_X, decimal pointA_Y,
|
|
||||||
DateTime pointB_X, decimal pointB_Y,
|
|
||||||
DateTime pointC_X, decimal pointC_Y,
|
|
||||||
DateTime pointD_X, decimal pointD_Y)
|
|
||||||
{
|
|
||||||
// Определяем самую позднюю точку
|
|
||||||
DateTime latestPoint = new[] { pointA_X, pointB_X, pointC_X, pointD_X }.Max();
|
|
||||||
|
|
||||||
// Смещённые координаты
|
|
||||||
double offsetA = GetOffsetInSeconds(latestPoint, pointA_X);
|
|
||||||
double offsetB = GetOffsetInSeconds(latestPoint, pointB_X);
|
|
||||||
double offsetC = GetOffsetInSeconds(latestPoint, pointC_X);
|
|
||||||
double offsetD = GetOffsetInSeconds(latestPoint, pointD_X);
|
|
||||||
|
|
||||||
// Суммируем площади двух треугольников
|
|
||||||
double firstTriangleArea = CalculateTriangleArea(pointA_X, pointA_Y, pointB_X, pointB_Y, pointD_X, pointD_Y);
|
|
||||||
double secondTriangleArea = CalculateTriangleArea(pointB_X, pointB_Y, pointC_X, pointC_Y, pointD_X, pointD_Y);
|
|
||||||
|
|
||||||
return firstTriangleArea + secondTriangleArea;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Вспомогательные методы
|
|
||||||
|
|
||||||
// Конвертация разницы времён в секунды
|
|
||||||
private static double GetOffsetInSeconds(DateTime referencePoint, DateTime targetPoint)
|
|
||||||
{
|
|
||||||
return (referencePoint - targetPoint).TotalSeconds;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Евклидово расстояние между двумя точками
|
|
||||||
private static double DistanceBetweenPoints(double x1, double y1, double x2, double y2)
|
|
||||||
{
|
|
||||||
return System.Math.Sqrt(System.Math.Pow(x2 - x1, 2) + System.Math.Pow(y2 - y1, 2));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool TryGetAreasRelation(DateTime[] times, decimal[] values, decimal currentValue, TimeSpan boundTimeSpan, out double relation)
|
|
||||||
{
|
|
||||||
var upArea = 0d;
|
|
||||||
var downArea = 0d;
|
|
||||||
var startTime = times[times.Length - 1];
|
|
||||||
for (int i = 1; i < times.Length - 2; i++)
|
|
||||||
{
|
|
||||||
var k = values.Length - i;
|
|
||||||
if (startTime - times[k] > boundTimeSpan)
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
var point = (double)(values[k] - currentValue);
|
|
||||||
var time = times[k];
|
|
||||||
var timePrev = times[k - 1];
|
|
||||||
var dt = (time - timePrev).TotalSeconds;
|
|
||||||
var ar = dt * point;
|
|
||||||
if (ar > 0)
|
|
||||||
{
|
|
||||||
upArea += ar;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
downArea += System.Math.Abs(ar);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
var area = downArea + upArea;
|
|
||||||
relation = area != 0 ? (double)upArea / area * 100 : 0;
|
|
||||||
return area != 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,4 +1,4 @@
|
||||||
using KLHZ.Trader.Core.Exchange.Models.Trading;
|
using KLHZ.Trader.Core.Exchange.Models.Trading.Enums;
|
||||||
using KLHZ.Trader.Core.Exchange.Utils;
|
using KLHZ.Trader.Core.Exchange.Utils;
|
||||||
|
|
||||||
namespace KLHZ.Trader.Core.Tests
|
namespace KLHZ.Trader.Core.Tests
|
||||||
|
|
|
@ -1,293 +0,0 @@
|
||||||
using KLHZ.Trader.Core.DataLayer.Entities.Prices;
|
|
||||||
using KLHZ.Trader.Core.Math.Declisions.Services.Cache;
|
|
||||||
|
|
||||||
namespace KLHZ.Trader.Core.Tests
|
|
||||||
{
|
|
||||||
public class HistoryCacheUnit2Tests
|
|
||||||
{
|
|
||||||
private static PriceChange[] GetHistory(int count, string figi)
|
|
||||||
{
|
|
||||||
var res = new PriceChange[count];
|
|
||||||
if (count != 0)
|
|
||||||
{
|
|
||||||
var startDt = DateTime.UtcNow.AddSeconds(-count);
|
|
||||||
for (int i = 0; i < count; i++)
|
|
||||||
{
|
|
||||||
startDt = startDt.AddSeconds(1);
|
|
||||||
res[i] = new PriceChange()
|
|
||||||
{
|
|
||||||
Figi = figi,
|
|
||||||
Ticker = figi + "_ticker",
|
|
||||||
Id = i,
|
|
||||||
Time = startDt,
|
|
||||||
Price = (decimal)(i + 0.5)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void Test1()
|
|
||||||
{
|
|
||||||
var count = 0;
|
|
||||||
var figi = "figi";
|
|
||||||
var hist = GetHistory(count, figi);
|
|
||||||
var cacheUnit = new PriceHistoryCacheUnit2("", hist);
|
|
||||||
var data = cacheUnit.GetData().Result;
|
|
||||||
|
|
||||||
Assert.That(data.prices.Length == count);
|
|
||||||
Assert.That(data.timestamps.Length == count);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void Test2()
|
|
||||||
{
|
|
||||||
var count = 1;
|
|
||||||
var figi = "figi";
|
|
||||||
var hist = GetHistory(count, figi);
|
|
||||||
var cacheUnit = new PriceHistoryCacheUnit2(figi, hist);
|
|
||||||
var data = cacheUnit.GetData().Result;
|
|
||||||
|
|
||||||
Assert.That(data.prices.Length == count);
|
|
||||||
Assert.That(data.timestamps.Length == count);
|
|
||||||
for (var i = 0; i < count; i++)
|
|
||||||
{
|
|
||||||
Assert.That((float)hist[i].Price, Is.EqualTo(data.prices[i]));
|
|
||||||
Assert.That(hist[i].Time, Is.EqualTo(data.timestamps[i]));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void Test3()
|
|
||||||
{
|
|
||||||
var count = 20;
|
|
||||||
var figi = "figi";
|
|
||||||
var hist = GetHistory(count, figi);
|
|
||||||
var cacheUnit = new PriceHistoryCacheUnit2(figi, hist);
|
|
||||||
var data = cacheUnit.GetData().Result;
|
|
||||||
|
|
||||||
Assert.That(data.prices.Length == count);
|
|
||||||
Assert.That(data.timestamps.Length == count);
|
|
||||||
|
|
||||||
for (var i = 0; i < count; i++)
|
|
||||||
{
|
|
||||||
Assert.That((float)hist[i].Price, Is.EqualTo(data.prices[i]));
|
|
||||||
Assert.That(hist[i].Time, Is.EqualTo(data.timestamps[i]));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void Test4()
|
|
||||||
{
|
|
||||||
var count = PriceHistoryCacheUnit2.CacheMaxLength;
|
|
||||||
var figi = "figi";
|
|
||||||
var hist = GetHistory(count, figi);
|
|
||||||
var cacheUnit = new PriceHistoryCacheUnit2(figi, hist);
|
|
||||||
var data = cacheUnit.GetData().Result;
|
|
||||||
|
|
||||||
Assert.That(data.prices.Length == count);
|
|
||||||
Assert.That(data.timestamps.Length == count);
|
|
||||||
|
|
||||||
for (var i = 0; i < count; i++)
|
|
||||||
{
|
|
||||||
Assert.That((float)hist[i].Price, Is.EqualTo(data.prices[i]));
|
|
||||||
Assert.That(hist[i].Time, Is.EqualTo(data.timestamps[i]));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void Test5()
|
|
||||||
{
|
|
||||||
var shift = 7;
|
|
||||||
var count = PriceHistoryCacheUnit2.CacheMaxLength + shift;
|
|
||||||
var figi = "figi";
|
|
||||||
var hist = GetHistory(count, figi);
|
|
||||||
var cacheUnit = new PriceHistoryCacheUnit2(figi, hist);
|
|
||||||
var data = cacheUnit.GetData().Result;
|
|
||||||
|
|
||||||
Assert.That(data.prices.Length == count - shift);
|
|
||||||
Assert.That(data.timestamps.Length == count - shift);
|
|
||||||
|
|
||||||
for (var i = 0; i < count; i++)
|
|
||||||
{
|
|
||||||
var k = i + shift;
|
|
||||||
if (k < hist.Length)
|
|
||||||
{
|
|
||||||
Assert.That((float)hist[k].Price, Is.EqualTo(data.prices[i]));
|
|
||||||
Assert.That(hist[k].Time, Is.EqualTo(data.timestamps[i]));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void Test6()
|
|
||||||
{
|
|
||||||
var shift = 10;
|
|
||||||
var count = PriceHistoryCacheUnit2.CacheMaxLength + shift;
|
|
||||||
var figi = "figi";
|
|
||||||
var hist = GetHistory(count, figi);
|
|
||||||
var cacheUnit = new PriceHistoryCacheUnit2(figi, hist);
|
|
||||||
var data = cacheUnit.GetData().Result;
|
|
||||||
|
|
||||||
Assert.That(data.prices.Length == count - shift);
|
|
||||||
Assert.That(data.timestamps.Length == count - shift);
|
|
||||||
|
|
||||||
for (var i = 0; i < count; i++)
|
|
||||||
{
|
|
||||||
var k = i + shift;
|
|
||||||
if (k < hist.Length)
|
|
||||||
{
|
|
||||||
Assert.That((float)hist[k].Price, Is.EqualTo(data.prices[i]));
|
|
||||||
Assert.That(hist[k].Time, Is.EqualTo(data.timestamps[i]));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void Test7()
|
|
||||||
{
|
|
||||||
var shift = 334;
|
|
||||||
var count = PriceHistoryCacheUnit2.CacheMaxLength + shift;
|
|
||||||
var figi = "figi";
|
|
||||||
var hist = GetHistory(count, figi);
|
|
||||||
var cacheUnit = new PriceHistoryCacheUnit2(figi, hist);
|
|
||||||
var data = cacheUnit.GetData().Result;
|
|
||||||
|
|
||||||
Assert.That(data.prices.Length == count - shift);
|
|
||||||
Assert.That(data.timestamps.Length == count - shift);
|
|
||||||
|
|
||||||
for (var i = 0; i < count; i++)
|
|
||||||
{
|
|
||||||
var k = i + shift;
|
|
||||||
if (k < hist.Length)
|
|
||||||
{
|
|
||||||
Assert.That((float)hist[k].Price, Is.EqualTo(data.prices[i]));
|
|
||||||
Assert.That(hist[k].Time, Is.EqualTo(data.timestamps[i]));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void Test8()
|
|
||||||
{
|
|
||||||
var count = PriceHistoryCacheUnit2.CacheMaxLength;
|
|
||||||
var figi = "figi";
|
|
||||||
var hist = GetHistory(count, figi);
|
|
||||||
var cacheUnit = new PriceHistoryCacheUnit2(figi, hist);
|
|
||||||
var data = cacheUnit.GetData().Result;
|
|
||||||
|
|
||||||
Assert.That(data.prices.Length == count);
|
|
||||||
Assert.That(data.timestamps.Length == count);
|
|
||||||
|
|
||||||
for (var i = 0; i < count; i++)
|
|
||||||
{
|
|
||||||
Assert.That((float)hist[i].Price, Is.EqualTo(data.prices[i]));
|
|
||||||
Assert.That(hist[i].Time, Is.EqualTo(data.timestamps[i]));
|
|
||||||
}
|
|
||||||
|
|
||||||
var newData1 = new PriceChange() { Figi = figi, Ticker = figi, Price = 100500, Time = DateTime.UtcNow };
|
|
||||||
|
|
||||||
cacheUnit.AddData(newData1);
|
|
||||||
|
|
||||||
var data2 = cacheUnit.GetData().Result;
|
|
||||||
Assert.IsTrue(data2.prices[data2.prices.Length - 1] == newData1.Price);
|
|
||||||
Assert.IsTrue(data2.timestamps[data2.timestamps.Length - 1] == newData1.Time);
|
|
||||||
|
|
||||||
var newData2 = new PriceChange() { Figi = figi, Ticker = figi, Price = 100501, Time = DateTime.UtcNow };
|
|
||||||
|
|
||||||
cacheUnit.AddData(newData2);
|
|
||||||
|
|
||||||
var data3 = cacheUnit.GetData().Result;
|
|
||||||
Assert.IsTrue(data3.prices[data3.prices.Length - 1] == newData2.Price);
|
|
||||||
Assert.IsTrue(data3.timestamps[data3.timestamps.Length - 1] == newData2.Time);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void Test9()
|
|
||||||
{
|
|
||||||
var cacheUnit = new PriceHistoryCacheUnit2("");
|
|
||||||
for (int i = 0; i < 5 * PriceHistoryCacheUnit2.CacheMaxLength; i++)
|
|
||||||
{
|
|
||||||
cacheUnit.AddData(new PriceChange() { Figi = "", Ticker = "", Price = i, Time = DateTime.UtcNow });
|
|
||||||
if (i >= PriceHistoryCacheUnit2.CacheMaxLength)
|
|
||||||
{
|
|
||||||
var data = cacheUnit.GetData().Result;
|
|
||||||
Assert.IsTrue(data.prices.Length == PriceHistoryCacheUnit2.CacheMaxLength);
|
|
||||||
Assert.IsTrue(data.prices.Last() == i);
|
|
||||||
var lastValues = cacheUnit.GetLastValues().Result;
|
|
||||||
Assert.IsTrue(data.prices.Last() == lastValues.price);
|
|
||||||
Assert.IsTrue(data.timestamps.Last() == lastValues.time);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void Test10()
|
|
||||||
{
|
|
||||||
var length = 77;
|
|
||||||
var shift = 334;
|
|
||||||
var count = PriceHistoryCacheUnit2.CacheMaxLength + shift;
|
|
||||||
var figi = "figi";
|
|
||||||
var hist = GetHistory(count, figi);
|
|
||||||
var cacheUnit = new PriceHistoryCacheUnit2(figi, hist);
|
|
||||||
var data = cacheUnit.GetData(length).Result;
|
|
||||||
|
|
||||||
Assert.That(data.prices.Length == length);
|
|
||||||
Assert.That(data.timestamps.Length == length);
|
|
||||||
|
|
||||||
Assert.That(data.prices.Last() == hist.Last().Price);
|
|
||||||
Assert.That(data.timestamps.Last() == hist.Last().Time);
|
|
||||||
for (var i = 1; i <= length; i++)
|
|
||||||
{
|
|
||||||
Assert.That(hist[hist.Length - i].Price, Is.EqualTo(data.prices[data.prices.Length - i]));
|
|
||||||
Assert.That(hist[hist.Length - i].Time, Is.EqualTo(data.timestamps[data.prices.Length - i]));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void Test11()
|
|
||||||
{
|
|
||||||
var length = 77;
|
|
||||||
var shift = 334;
|
|
||||||
var count = PriceHistoryCacheUnit2.CacheMaxLength + shift;
|
|
||||||
var figi = "figi";
|
|
||||||
var hist = GetHistory(count, figi);
|
|
||||||
var cacheUnit = new PriceHistoryCacheUnit2(figi, hist);
|
|
||||||
var data = cacheUnit.GetData(length).Result;
|
|
||||||
var span = TimeSpan.FromSeconds(100);
|
|
||||||
var dataForPeriod = cacheUnit.GetData(span).Result;
|
|
||||||
|
|
||||||
Assert.That(dataForPeriod.isFullIntervalExists);
|
|
||||||
Assert.That(data.prices.Last() == dataForPeriod.prices.Last());
|
|
||||||
Assert.That(data.timestamps.Last() == dataForPeriod.timestamps.Last());
|
|
||||||
var dt = dataForPeriod.timestamps.Last() - dataForPeriod.timestamps.First();
|
|
||||||
Assert.That(dt <= span);
|
|
||||||
Assert.That(data.prices.Length == length);
|
|
||||||
Assert.That(data.timestamps.Length == length);
|
|
||||||
|
|
||||||
Assert.That(data.prices.Last() == hist.Last().Price);
|
|
||||||
Assert.That(data.timestamps.Last() == hist.Last().Time);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void Test12()
|
|
||||||
{
|
|
||||||
var count = 30;
|
|
||||||
var figi = "figi";
|
|
||||||
var hist = GetHistory(count, figi);
|
|
||||||
var cacheUnit = new PriceHistoryCacheUnit2(figi, hist);
|
|
||||||
var data = cacheUnit.GetData().Result;
|
|
||||||
var span = TimeSpan.FromSeconds(100);
|
|
||||||
var dataForPeriod = cacheUnit.GetData(span).Result;
|
|
||||||
|
|
||||||
Assert.IsFalse(dataForPeriod.isFullIntervalExists);
|
|
||||||
Assert.That(data.prices.Last() == dataForPeriod.prices.Last());
|
|
||||||
Assert.That(data.timestamps.Last() == dataForPeriod.timestamps.Last());
|
|
||||||
|
|
||||||
Assert.That(data.prices.First() == dataForPeriod.prices.First());
|
|
||||||
Assert.That(data.timestamps.First() == dataForPeriod.timestamps.First());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,4 +1,4 @@
|
||||||
using KLHZ.Trader.Core.Contracts.Declisions.Dtos;
|
using KLHZ.Trader.Core.Contracts.Messaging.Dtos;
|
||||||
using KLHZ.Trader.Core.Math.Declisions.Utils;
|
using KLHZ.Trader.Core.Math.Declisions.Utils;
|
||||||
using System.Security.Cryptography;
|
using System.Security.Cryptography;
|
||||||
|
|
||||||
|
@ -33,7 +33,7 @@ namespace KLHZ.Trader.Core.Tests
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
var res = Statistics.CalcHistogram(data.Select(d => new CachedValue()
|
var res = Statistics.CalcHistogram(data.Select(d => new TradeDataItem()
|
||||||
{
|
{
|
||||||
Figi = "",
|
Figi = "",
|
||||||
Ticker = "",
|
Ticker = "",
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
using KLHZ.Trader.Core.Contracts.Messaging.Dtos.Interfaces;
|
using KLHZ.Trader.Core.Contracts.Messaging.Dtos;
|
||||||
|
using KLHZ.Trader.Core.Contracts.Messaging.Dtos.Interfaces;
|
||||||
using System.ComponentModel.DataAnnotations.Schema;
|
using System.ComponentModel.DataAnnotations.Schema;
|
||||||
|
|
||||||
namespace KLHZ.Trader.Core.DataLayer.Entities.Prices
|
namespace KLHZ.Trader.Core.DataLayer.Entities.Prices
|
||||||
|
@ -34,5 +35,28 @@ namespace KLHZ.Trader.Core.DataLayer.Entities.Prices
|
||||||
|
|
||||||
[NotMapped]
|
[NotMapped]
|
||||||
public decimal Value2 { get; set; }
|
public decimal Value2 { get; set; }
|
||||||
|
|
||||||
|
[NotMapped]
|
||||||
|
public AttachedInfo? AttachedInfo
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
lock (_locker)
|
||||||
|
{
|
||||||
|
return _attachedInfo;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetAttachedInfo(AttachedInfo? attachedInfo)
|
||||||
|
{
|
||||||
|
lock (_locker)
|
||||||
|
{
|
||||||
|
_attachedInfo = attachedInfo;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private AttachedInfo? _attachedInfo;
|
||||||
|
private readonly object _locker = new();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
using KLHZ.Trader.Core.Contracts.Messaging.Dtos.Interfaces;
|
using KLHZ.Trader.Core.Contracts.Messaging.Dtos;
|
||||||
|
using KLHZ.Trader.Core.Contracts.Messaging.Dtos.Interfaces;
|
||||||
using System.ComponentModel.DataAnnotations.Schema;
|
using System.ComponentModel.DataAnnotations.Schema;
|
||||||
|
|
||||||
namespace KLHZ.Trader.Core.DataLayer.Entities.Prices
|
namespace KLHZ.Trader.Core.DataLayer.Entities.Prices
|
||||||
|
@ -37,5 +38,8 @@ namespace KLHZ.Trader.Core.DataLayer.Entities.Prices
|
||||||
|
|
||||||
[NotMapped]
|
[NotMapped]
|
||||||
public decimal Value2 { get; set; }
|
public decimal Value2 { get; set; }
|
||||||
|
|
||||||
|
[NotMapped]
|
||||||
|
public AttachedInfo? AttachedInfo { get; private set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,24 +2,14 @@
|
||||||
{
|
{
|
||||||
internal static class Constants
|
internal static class Constants
|
||||||
{
|
{
|
||||||
internal const string _1minCacheKey = "1min";
|
|
||||||
internal const string _15minSellCacheKey = "5min_sell";
|
|
||||||
internal const string _5minSellCacheKey = "5min_sell";
|
|
||||||
internal const string _5minBuyCacheKey = "5min_buy";
|
|
||||||
internal const string _15minBuyCacheKey = "5min_buy";
|
|
||||||
internal const string _1minSellCacheKey = "1min_sell";
|
|
||||||
internal const string _1minBuyCacheKey = "1min_buy";
|
|
||||||
|
|
||||||
internal const string BigWindowCrossingAverageProcessor = "Trader_big";
|
|
||||||
internal const string SmallWindowCrossingAverageProcessor = "Trader_small";
|
|
||||||
internal const string AreasRelationProcessor = "balancescalc30min";
|
|
||||||
internal readonly static TimeSpan AreasRelationWindow = TimeSpan.FromMinutes(15);
|
|
||||||
|
|
||||||
internal const decimal ForceExecuteCoefficient = 500000m;
|
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 = 0.01m;
|
internal const decimal BlockingCoefficient = 0.01m;
|
||||||
|
|
||||||
|
internal const string PriceIsInSupportLevel = "PriceIsInSupportLevel";
|
||||||
|
internal const string PriceIsNotInSupportLevel = "PriceIsNotInSupportLevel";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,14 +0,0 @@
|
||||||
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; }
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,4 +1,4 @@
|
||||||
namespace KLHZ.Trader.Core.Exchange.Models.Trading
|
namespace KLHZ.Trader.Core.Exchange.Models.Trading.Enums
|
||||||
{
|
{
|
||||||
internal enum ExchangeState
|
internal enum ExchangeState
|
||||||
{
|
{
|
|
@ -0,0 +1,14 @@
|
||||||
|
namespace KLHZ.Trader.Core.Exchange.Models.Trading.Enums
|
||||||
|
{
|
||||||
|
[Flags]
|
||||||
|
internal enum TradingMode
|
||||||
|
{
|
||||||
|
None = 0,
|
||||||
|
Growing = 1,
|
||||||
|
Falling = 2,
|
||||||
|
InSupportLevel = 4,
|
||||||
|
TryingGrowFromSupportLevel = 8,
|
||||||
|
TryingFallFromSupportLevel = 16,
|
||||||
|
Bezpont = 32,
|
||||||
|
}
|
||||||
|
}
|
|
@ -10,6 +10,7 @@ using KLHZ.Trader.Core.Exchange.Interfaces;
|
||||||
using KLHZ.Trader.Core.Exchange.Models.AssetsAccounting;
|
using KLHZ.Trader.Core.Exchange.Models.AssetsAccounting;
|
||||||
using KLHZ.Trader.Core.Exchange.Models.Configs;
|
using KLHZ.Trader.Core.Exchange.Models.Configs;
|
||||||
using KLHZ.Trader.Core.Exchange.Models.Trading;
|
using KLHZ.Trader.Core.Exchange.Models.Trading;
|
||||||
|
using KLHZ.Trader.Core.Exchange.Models.Trading.Enums;
|
||||||
using KLHZ.Trader.Core.Exchange.Utils;
|
using KLHZ.Trader.Core.Exchange.Utils;
|
||||||
using KLHZ.Trader.Core.Math.Declisions.Utils;
|
using KLHZ.Trader.Core.Math.Declisions.Utils;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
@ -32,11 +33,12 @@ namespace KLHZ.Trader.Core.Exchange.Services
|
||||||
private readonly ExchangeConfig _exchangeConfig;
|
private readonly ExchangeConfig _exchangeConfig;
|
||||||
private readonly ILogger<Trader> _logger;
|
private readonly ILogger<Trader> _logger;
|
||||||
|
|
||||||
private readonly ConcurrentDictionary<string, DeferredDeclision> DeferredDeclisions = new();
|
|
||||||
private readonly ConcurrentDictionary<string, SupportLevel[]> SupportLevels = new();
|
private readonly ConcurrentDictionary<string, SupportLevel[]> SupportLevels = new();
|
||||||
private readonly ConcurrentDictionary<string, decimal> _pirsonValues = new();
|
private readonly ConcurrentDictionary<string, decimal> _pirsonValues = new();
|
||||||
|
private readonly ConcurrentDictionary<string, LinkedList<SupportLevel[]>> SupportLevelsHistory = new();
|
||||||
|
|
||||||
private readonly ConcurrentDictionary<string, DateTime> _supportLevelsCalculationTimes = new();
|
private readonly ConcurrentDictionary<string, DateTime> _supportLevelsCalculationTimes = new();
|
||||||
|
private readonly ConcurrentDictionary<string, DateTime> _marginCloses = new();
|
||||||
private readonly ConcurrentDictionary<string, DateTime> _usedSupportLevels = new();
|
private readonly ConcurrentDictionary<string, DateTime> _usedSupportLevels = new();
|
||||||
private readonly ConcurrentDictionary<string, DateTime> _usedSupportLevelsForClosing = new();
|
private readonly ConcurrentDictionary<string, DateTime> _usedSupportLevelsForClosing = new();
|
||||||
private readonly ConcurrentDictionary<string, ITradeDataItem> _oldItems = new();
|
private readonly ConcurrentDictionary<string, ITradeDataItem> _oldItems = new();
|
||||||
|
@ -169,90 +171,173 @@ namespace KLHZ.Trader.Core.Exchange.Services
|
||||||
{
|
{
|
||||||
if (message.Figi == "FUTIMOEXF000")
|
if (message.Figi == "FUTIMOEXF000")
|
||||||
{
|
{
|
||||||
if (DeferredDeclisions.TryGetValue(message.Figi, out var dec))
|
await CalcSupportLevels(message, 3, 3);
|
||||||
{
|
var processSupportLevelsRes = await ProcessSupportLevels(message);
|
||||||
if (dec.ExpirationTime < message.Time)
|
|
||||||
{
|
|
||||||
if (dec.Events[TradingEvent.OpenShort] > Constants.BlockingCoefficient)
|
|
||||||
{
|
|
||||||
if (dec.Message.Price < message.Price)
|
|
||||||
{
|
|
||||||
var stops2 = GetStops(message);
|
|
||||||
var declisionsStops2 = ProcessStops(stops2, 2m);
|
|
||||||
var e = TraderUtils.MergeResultsMult(dec.Events, declisionsStops2);
|
|
||||||
await ExecuteDeclisions(e.ToImmutableDictionary(), message, stops2, 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (dec.Events[TradingEvent.OpenLong] > Constants.BlockingCoefficient)
|
|
||||||
{
|
|
||||||
if (dec.Message.Price > message.Price)
|
|
||||||
{
|
|
||||||
var stops2 = GetStops(message);
|
|
||||||
var declisionsStops2 = ProcessStops(stops2, 2m);
|
|
||||||
var e = TraderUtils.MergeResultsMult(dec.Events, declisionsStops2);
|
|
||||||
await ExecuteDeclisions(e.ToImmutableDictionary(), message, stops2, 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (dec.Events[TradingEvent.CloseLong] > Constants.BlockingCoefficient
|
|
||||||
|| dec.Events[TradingEvent.CloseShort] > Constants.BlockingCoefficient)
|
|
||||||
{
|
|
||||||
await ExecuteDeclisions(dec.Events, dec.Message, dec.Stops, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
DeferredDeclisions.TryRemove(message.Figi, out _);
|
var mode = await CalcTradingMode(message);
|
||||||
|
var stops = GetStops(message);
|
||||||
|
if ((mode & TradingMode.InSupportLevel) == TradingMode.InSupportLevel)
|
||||||
|
{
|
||||||
|
if ((mode & TradingMode.Bezpont) == TradingMode.Bezpont)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var mavRes = await CalcTimeWindowAverageValue(message, true);
|
||||||
|
await ExecuteDeclisions(mavRes, message, stops, 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else
|
||||||
await CalcSupportLevels(message, 3, 5);
|
|
||||||
var stops = GetStops(message);
|
|
||||||
var pirson = await CalcPirson(message);
|
|
||||||
var mavRes = await CalcTimeWindowAverageValue(message);
|
|
||||||
var declisionPirson = await ProcessPirson(pirson, message);
|
|
||||||
var declisionsSupportLevels = await ProcessSupportLevels(message);
|
|
||||||
var declisionsStops = ProcessStops(stops, 2m);
|
|
||||||
var res = TraderUtils.MergeResultsMult(declisionPirson, declisionsSupportLevels);
|
|
||||||
res = TraderUtils.MergeResultsMult(res, declisionsStops);
|
|
||||||
res = TraderUtils.MergeResultsMax(res, mavRes);
|
|
||||||
|
|
||||||
await ExecuteDeclisions(res.ToImmutableDictionary(), message, stops, 1);
|
|
||||||
|
|
||||||
var declision = new DeferredDeclision()
|
|
||||||
{
|
{
|
||||||
Message = message,
|
var mavRes = await CalcTimeWindowAverageValue(message);
|
||||||
Stops = stops,
|
var pirson = await CalcPirson(message);
|
||||||
Events = res.ToImmutableDictionary(),
|
var declisionPirson = ProcessPirson(pirson, message);
|
||||||
ExpirationTime = message.Time.AddSeconds(5)
|
var declisionsStops = ProcessStops(stops, 2m);
|
||||||
};
|
var tradingModeResult = ProcessTradingMode(mode);
|
||||||
|
|
||||||
if (declision.Events.Values.Any(v => v > Constants.BlockingCoefficient))
|
var res = TraderUtils.MergeResultsMult(declisionPirson, processSupportLevelsRes);
|
||||||
{
|
res = TraderUtils.MergeResultsMult(res, declisionsStops);
|
||||||
//DeferredDeclisions.TryAdd(message.Figi, declision);
|
res = TraderUtils.MergeResultsMult(res, tradingModeResult);
|
||||||
|
res = TraderUtils.MergeResultsMax(res, mavRes);
|
||||||
|
|
||||||
|
await ExecuteDeclisions(res.ToImmutableDictionary(), message, stops, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
_oldItems[message.Figi] = message;
|
_oldItems[message.Figi] = message;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<ImmutableDictionary<TradingEvent, decimal>> CalcTimeWindowAverageValue(ITradeDataItem message)
|
private async Task<TradingMode> CalcTradingMode(ITradeDataItem message)
|
||||||
|
{
|
||||||
|
var res = TradingMode.None;
|
||||||
|
|
||||||
|
//var res1hour = await CalcTradingMode(message, TimeSpan.FromMinutes(60),8);
|
||||||
|
//var res30min = await CalcTradingMode(message, TimeSpan.FromMinutes(30),6);
|
||||||
|
|
||||||
|
var res20min = await CalcTradingMode(message, TimeSpan.FromMinutes(20), 3);
|
||||||
|
var res10min = await CalcTradingMode(message, TimeSpan.FromMinutes(10), 3);
|
||||||
|
//res |= res1hour;
|
||||||
|
//res|=res30min;
|
||||||
|
res |= res20min;
|
||||||
|
res &= res10min;
|
||||||
|
|
||||||
|
if ((res & TradingMode.TryingGrowFromSupportLevel) == TradingMode.TryingGrowFromSupportLevel)
|
||||||
|
{
|
||||||
|
await _tradeDataProvider.LogPrice(message, "TryingGrowFromSupportLevel", message.Price);
|
||||||
|
}
|
||||||
|
if ((res & TradingMode.TryingFallFromSupportLevel) == TradingMode.TryingFallFromSupportLevel)
|
||||||
|
{
|
||||||
|
await _tradeDataProvider.LogPrice(message, "TryingFallFromSupportLevel", message.Price);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((res & TradingMode.InSupportLevel) == TradingMode.InSupportLevel)
|
||||||
|
{
|
||||||
|
await _tradeDataProvider.LogPrice(message, "InSupportLevel", message.Price);
|
||||||
|
}
|
||||||
|
if ((res & TradingMode.Growing) == TradingMode.Growing)
|
||||||
|
{
|
||||||
|
await _tradeDataProvider.LogPrice(message, "InSupportLevelGrowing", message.Price);
|
||||||
|
}
|
||||||
|
if ((res & TradingMode.Falling) == TradingMode.Falling)
|
||||||
|
{
|
||||||
|
await _tradeDataProvider.LogPrice(message, "InSupportLevelGrowingFalling", message.Price);
|
||||||
|
}
|
||||||
|
if ((res & TradingMode.Bezpont) == TradingMode.Bezpont)
|
||||||
|
{
|
||||||
|
var data = await _tradeDataProvider.GetDataForTimeWindow(message.Figi, TimeSpan.FromMinutes(30), selector: (i) => i.Direction == 1);
|
||||||
|
|
||||||
|
if (data.Any() && (data.Max(d => d.Price) - data.Min(d => d.Price)) < 4)
|
||||||
|
{
|
||||||
|
await _tradeDataProvider.LogPrice(message, "Bezpont", message.Price);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
res |= ~TradingMode.Bezpont;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<TradingMode> CalcTradingMode(ITradeDataItem message, TimeSpan period, decimal meanfullValue)
|
||||||
|
{
|
||||||
|
var res = TradingMode.None;
|
||||||
|
|
||||||
|
var data = await _tradeDataProvider.GetDataForTimeWindow(message.Figi, period);
|
||||||
|
if (LocalTrends.TryCalcTrendDiff(data, out var diff))
|
||||||
|
{
|
||||||
|
var items = data.Where(d => d.AttachedInfo != null).ToArray();
|
||||||
|
var itemsInSupportLevels = (decimal)items.Count(i => i.AttachedInfo?.Key == Constants.PriceIsInSupportLevel);
|
||||||
|
var itemsHigherThenSupportLevel = (decimal)items.Count(i => i.AttachedInfo?.Key == Constants.PriceIsNotInSupportLevel
|
||||||
|
&& (i.AttachedInfo?.Value2 < i.Price));
|
||||||
|
var itemsLowerThenSupportLevel = (decimal)items.Count(i => i.AttachedInfo?.Key == Constants.PriceIsNotInSupportLevel
|
||||||
|
&& (i.AttachedInfo?.Value1 > i.Price));
|
||||||
|
|
||||||
|
if (items.Length > 0)
|
||||||
|
{
|
||||||
|
var itemsInSupportLevelsRel = itemsInSupportLevels / items.Length;
|
||||||
|
var itemsHigherThenSupportLevelRel = itemsHigherThenSupportLevel / items.Length;
|
||||||
|
var itemsLowerThenSupportLevelRel = itemsLowerThenSupportLevel / items.Length;
|
||||||
|
|
||||||
|
if (itemsInSupportLevelsRel > 0.7m && message?.AttachedInfo?.Key == Constants.PriceIsInSupportLevel)
|
||||||
|
{
|
||||||
|
res |= TradingMode.InSupportLevel;
|
||||||
|
if (itemsHigherThenSupportLevelRel > 0.05m && (itemsLowerThenSupportLevelRel == 0 || itemsHigherThenSupportLevelRel / itemsLowerThenSupportLevelRel > 2))
|
||||||
|
{
|
||||||
|
res |= TradingMode.TryingGrowFromSupportLevel;
|
||||||
|
}
|
||||||
|
if (itemsLowerThenSupportLevelRel > 0.05m && (itemsHigherThenSupportLevelRel == 0 || itemsLowerThenSupportLevelRel / itemsHigherThenSupportLevelRel > 2))
|
||||||
|
{
|
||||||
|
res |= TradingMode.TryingGrowFromSupportLevel;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (diff > meanfullValue)
|
||||||
|
{
|
||||||
|
res |= TradingMode.Growing;
|
||||||
|
}
|
||||||
|
if (diff < -meanfullValue)
|
||||||
|
{
|
||||||
|
res |= TradingMode.Falling;
|
||||||
|
}
|
||||||
|
if (itemsInSupportLevelsRel > 0.8m && message?.AttachedInfo?.Key == Constants.PriceIsInSupportLevel &&
|
||||||
|
System.Math.Abs(diff) < 1.5m * meanfullValue)
|
||||||
|
{
|
||||||
|
res |= TradingMode.Bezpont;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<ImmutableDictionary<TradingEvent, decimal>> CalcTimeWindowAverageValue(ITradeDataItem message, bool calcOpens = false)
|
||||||
{
|
{
|
||||||
var res = TraderUtils.GetInitDict(Constants.BlockingCoefficient);
|
var res = TraderUtils.GetInitDict(Constants.BlockingCoefficient);
|
||||||
var cacheSize = TimeSpan.FromSeconds(60 * 60);
|
var cacheSize = TimeSpan.FromSeconds(60 * 60);
|
||||||
var data = await _tradeDataProvider.GetDataForTimeWindow(message.Figi, cacheSize, selector: (i) => i.Direction == 1);
|
var data = await _tradeDataProvider.GetDataForTimeWindow(message.Figi, cacheSize, selector: (i) => i.Direction == 1);
|
||||||
|
|
||||||
|
if (calcOpens)
|
||||||
|
{
|
||||||
|
var re = MovingAverage.CheckByWindowAverageMean2(data, 100, 15, 300, -2m, 2m);
|
||||||
|
|
||||||
|
if ((re.events & TradingEvent.OpenShort) == TradingEvent.OpenShort)
|
||||||
|
{
|
||||||
|
res[TradingEvent.OpenShort] = Constants.PowerUppingCoefficient;
|
||||||
|
}
|
||||||
|
if ((re.events & TradingEvent.OpenLong) == TradingEvent.OpenLong)
|
||||||
|
{
|
||||||
|
res[TradingEvent.OpenLong] = Constants.PowerUppingCoefficient;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var closings = MovingAverage.CheckByWindowAverageMean2(data, data.Length, 15, 300, -5m, 5m);
|
var closings = MovingAverage.CheckByWindowAverageMean2(data, data.Length, 15, 300, -5m, 5m);
|
||||||
//var re = MovingAverage.CheckByWindowAverageMean2(data, 100, 15, 300, -4m, 4m);
|
|
||||||
if (closings.smallWindowAv != 0)
|
if (closings.smallWindowAv != 0)
|
||||||
{
|
{
|
||||||
await _tradeDataProvider.LogPrice(message, "maw_small", closings.smallWindowAv);
|
await _tradeDataProvider.LogPrice(message, "maw_small", closings.smallWindowAv);
|
||||||
await _tradeDataProvider.LogPrice(message, "maw_big", closings.bigWindowAv);
|
await _tradeDataProvider.LogPrice(message, "maw_big", closings.bigWindowAv);
|
||||||
}
|
}
|
||||||
//if ((re.events & TradingEvent.OpenShort) == TradingEvent.OpenShort)
|
|
||||||
//{
|
|
||||||
// res[TradingEvent.OpenShort] = Constants.PowerUppingCoefficient;
|
|
||||||
//}
|
|
||||||
//if ((re.events & TradingEvent.OpenLong) == TradingEvent.OpenLong)
|
|
||||||
//{
|
|
||||||
// res[TradingEvent.OpenLong] = Constants.PowerUppingCoefficient;
|
|
||||||
//}
|
|
||||||
if ((closings.events & TradingEvent.CloseShort) == TradingEvent.CloseShort)
|
if ((closings.events & TradingEvent.CloseShort) == TradingEvent.CloseShort)
|
||||||
{
|
{
|
||||||
res[TradingEvent.CloseShort] = Constants.PowerUppingCoefficient;
|
res[TradingEvent.CloseShort] = Constants.PowerUppingCoefficient;
|
||||||
|
@ -265,7 +350,24 @@ namespace KLHZ.Trader.Core.Exchange.Services
|
||||||
return res.ToImmutableDictionary();
|
return res.ToImmutableDictionary();
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<ImmutableDictionary<TradingEvent, decimal>> ProcessPirson(PirsonCalculatingResult pirson, ITradeDataItem message)
|
private ImmutableDictionary<TradingEvent, decimal> ProcessTradingMode(TradingMode mode)
|
||||||
|
{
|
||||||
|
var res = TraderUtils.GetInitDict(1);
|
||||||
|
if ((mode & TradingMode.Growing) == TradingMode.Growing)
|
||||||
|
{
|
||||||
|
res[TradingEvent.OpenShort] = Constants.BlockingCoefficient;
|
||||||
|
res[TradingEvent.OpenLong] = Constants.UppingCoefficient;
|
||||||
|
}
|
||||||
|
if ((mode & TradingMode.Falling) == TradingMode.Growing)
|
||||||
|
{
|
||||||
|
res[TradingEvent.OpenLong] = Constants.BlockingCoefficient;
|
||||||
|
res[TradingEvent.OpenShort] = Constants.UppingCoefficient;
|
||||||
|
}
|
||||||
|
|
||||||
|
return res.ToImmutableDictionary();
|
||||||
|
}
|
||||||
|
|
||||||
|
private ImmutableDictionary<TradingEvent, decimal> ProcessPirson(PirsonCalculatingResult pirson, ITradeDataItem message)
|
||||||
{
|
{
|
||||||
var res = TraderUtils.GetInitDict(Constants.BlockingCoefficient);
|
var res = TraderUtils.GetInitDict(Constants.BlockingCoefficient);
|
||||||
if (pirson.Success && _pirsonValues.TryGetValue(message.Figi, out var olddpirs))
|
if (pirson.Success && _pirsonValues.TryGetValue(message.Figi, out var olddpirs))
|
||||||
|
@ -282,6 +384,28 @@ namespace KLHZ.Trader.Core.Exchange.Services
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (pirson.Pirson > 0.5m && pirson.Pirson < 0.8m && (pirson.Pirson - olddpirs > 0.05m) && pirson.PriceDiff > 0 && (pirson.TradesDiffRelative > 0.25m))
|
||||||
|
{
|
||||||
|
res[TradingEvent.OpenLong] = Constants.PowerUppingCoefficient;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pirson.Pirson < -0.5m && pirson.Pirson > -0.8m && (pirson.Pirson - olddpirs < -0.05m) && pirson.PriceDiff < 0 && (pirson.TradesDiffRelative > 0.25m))
|
||||||
|
{
|
||||||
|
res[TradingEvent.OpenShort] = Constants.PowerUppingCoefficient;
|
||||||
|
}
|
||||||
|
|
||||||
|
//if (pirson.Pirson > 0.7m && (pirson.Pirson > olddpirs) && pirson.PriceDiff > 0 && (pirson.TradesDiffRelative > 0.25m))
|
||||||
|
//{
|
||||||
|
// res[TradingEvent.OpenLong] = Constants.UppingCoefficient;
|
||||||
|
//}
|
||||||
|
|
||||||
|
//if (pirson.Pirson < -0.7m && (pirson.Pirson < olddpirs) && pirson.PriceDiff < 0 && (pirson.TradesDiffRelative > 0.25m))
|
||||||
|
//{
|
||||||
|
// res[TradingEvent.OpenShort] = Constants.UppingCoefficient;
|
||||||
|
//}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if (olddpirs > 0.9m && pirson.Pirson <= 0.9m && pirson.TradesDiffRelative < -0.1m && pirson.TradesDiff <= 0)
|
if (olddpirs > 0.9m && pirson.Pirson <= 0.9m && pirson.TradesDiffRelative < -0.1m && pirson.TradesDiff <= 0)
|
||||||
{
|
{
|
||||||
res[TradingEvent.CloseLong] = Constants.PowerUppingCoefficient;
|
res[TradingEvent.CloseLong] = Constants.PowerUppingCoefficient;
|
||||||
|
@ -314,7 +438,7 @@ namespace KLHZ.Trader.Core.Exchange.Services
|
||||||
{
|
{
|
||||||
await _tradeDataProvider.LogPrice(message, "privcesDiff", pricesDiff);
|
await _tradeDataProvider.LogPrice(message, "privcesDiff", pricesDiff);
|
||||||
await _tradeDataProvider.LogPrice(message, "tradevolume_diff", tradesDiff);
|
await _tradeDataProvider.LogPrice(message, "tradevolume_diff", tradesDiff);
|
||||||
await _tradeDataProvider.AddData(message.Figi, "5min_diff", new Contracts.Declisions.Dtos.CachedValue()
|
await _tradeDataProvider.AddData(message.Figi, "5min_diff", new TradeDataItem()
|
||||||
{
|
{
|
||||||
Time = message.Time,
|
Time = message.Time,
|
||||||
Value2 = tradesDiff,
|
Value2 = tradesDiff,
|
||||||
|
@ -356,8 +480,16 @@ namespace KLHZ.Trader.Core.Exchange.Services
|
||||||
var state = ExchangeScheduler.GetCurrentState(message.Time);
|
var state = ExchangeScheduler.GetCurrentState(message.Time);
|
||||||
if (!message.IsHistoricalData && state == ExchangeState.ClearingTime)
|
if (!message.IsHistoricalData && state == ExchangeState.ClearingTime)
|
||||||
{
|
{
|
||||||
|
if (_marginCloses.TryGetValue(string.Empty, out var time))
|
||||||
|
{
|
||||||
|
if (message.Time - time < TimeSpan.FromHours(2))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
var futuresFigis = _portfolioWrapper.Accounts.Values.SelectMany(v => v.Assets.Values.Where(a => a.Type == AssetType.Futures)).ToArray();
|
var futuresFigis = _portfolioWrapper.Accounts.Values.SelectMany(v => v.Assets.Values.Where(a => a.Type == AssetType.Futures)).ToArray();
|
||||||
await ClosePositions(futuresFigis, message, false);
|
await ClosePositions(futuresFigis, message, false);
|
||||||
|
_marginCloses[string.Empty] = message.Time;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -453,6 +585,22 @@ namespace KLHZ.Trader.Core.Exchange.Services
|
||||||
}
|
}
|
||||||
|
|
||||||
SupportLevels[message.Figi] = finalLevels;
|
SupportLevels[message.Figi] = finalLevels;
|
||||||
|
if (SupportLevelsHistory.TryGetValue(message.Figi, out var list))
|
||||||
|
{
|
||||||
|
list.AddLast(finalLevels);
|
||||||
|
while (list.Last != null && list.First != null
|
||||||
|
&& list.Last.Value.Length > 0 && list.First.Value.Length > 0
|
||||||
|
&& (list.Last.Value[0].CalculatedAt - list.First.Value[0].CalculatedAt).TotalHours > 3)
|
||||||
|
{
|
||||||
|
list.RemoveFirst();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
SupportLevelsHistory[message.Figi] = new LinkedList<SupportLevel[]>();
|
||||||
|
SupportLevelsHistory[message.Figi].AddLast(finalLevels);
|
||||||
|
}
|
||||||
|
|
||||||
await _tradeDataProvider.LogPrice(message, "support_level_calc", message.Price);
|
await _tradeDataProvider.LogPrice(message, "support_level_calc", message.Price);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -514,7 +662,7 @@ namespace KLHZ.Trader.Core.Exchange.Services
|
||||||
var closingResult = await _portfolioWrapper.Accounts[asset.AccountId].ClosePosition(message.Figi);
|
var closingResult = await _portfolioWrapper.Accounts[asset.AccountId].ClosePosition(message.Figi);
|
||||||
if (closingResult.Success)
|
if (closingResult.Success)
|
||||||
{
|
{
|
||||||
var profitText = profit == 0 ? string.Empty : ", профит {profit}";
|
var profitText = profit == 0 ? string.Empty : $", профит {profit}";
|
||||||
mess = $"Закрываю позицию {asset.Figi} ({(asset.Count > 0 ? "лонг" : "шорт")}) на счёте {_portfolioWrapper.Accounts[asset.AccountId].AccountName}. Количество {(long)asset.Count}, цена ~{closingResult.ExecutedPrice / (settings?.PointPriceRub ?? 1)}, комиссия {closingResult.Comission}" + profitText;
|
mess = $"Закрываю позицию {asset.Figi} ({(asset.Count > 0 ? "лонг" : "шорт")}) на счёте {_portfolioWrapper.Accounts[asset.AccountId].AccountName}. Количество {(long)asset.Count}, цена ~{closingResult.ExecutedPrice / (settings?.PointPriceRub ?? 1)}, комиссия {closingResult.Comission}" + profitText;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -565,7 +713,7 @@ namespace KLHZ.Trader.Core.Exchange.Services
|
||||||
private async Task ExecuteDeclisions(ImmutableDictionary<TradingEvent, decimal> result, ITradeDataItem message, Stops st, int accountsForOpening = 1)
|
private async Task ExecuteDeclisions(ImmutableDictionary<TradingEvent, decimal> result, ITradeDataItem message, Stops st, int accountsForOpening = 1)
|
||||||
{
|
{
|
||||||
var state = ExchangeScheduler.GetCurrentState(message.Time);
|
var state = ExchangeScheduler.GetCurrentState(message.Time);
|
||||||
if (result[TradingEvent.OpenLong] >= Constants.UppingCoefficient
|
if (result[TradingEvent.OpenLong] > Constants.UppingCoefficient
|
||||||
&& state == ExchangeState.Open
|
&& state == ExchangeState.Open
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
|
@ -587,7 +735,7 @@ namespace KLHZ.Trader.Core.Exchange.Services
|
||||||
//await _tradeDataProvider.LogDeclision(DeclisionTradeAction.ResetStopsLong, valHigh, message.Time.AddMilliseconds(-RandomNumberGenerator.GetInt32(300, 1000)), message);
|
//await _tradeDataProvider.LogDeclision(DeclisionTradeAction.ResetStopsLong, valHigh, message.Time.AddMilliseconds(-RandomNumberGenerator.GetInt32(300, 1000)), message);
|
||||||
await _tradeDataProvider.LogDeclision(DeclisionTradeAction.ResetStopsLong, valLow, message.Time.AddMilliseconds(RandomNumberGenerator.GetInt32(300, 1000)), message);
|
await _tradeDataProvider.LogDeclision(DeclisionTradeAction.ResetStopsLong, valLow, message.Time.AddMilliseconds(RandomNumberGenerator.GetInt32(300, 1000)), message);
|
||||||
}
|
}
|
||||||
if (result[TradingEvent.OpenShort] >= Constants.UppingCoefficient
|
if (result[TradingEvent.OpenShort] > Constants.UppingCoefficient
|
||||||
&& state == ExchangeState.Open
|
&& state == ExchangeState.Open
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
|
@ -702,12 +850,15 @@ namespace KLHZ.Trader.Core.Exchange.Services
|
||||||
if (message.Price > nearestLevel.HighValue)
|
if (message.Price > nearestLevel.HighValue)
|
||||||
{
|
{
|
||||||
longStopLossShift = message.Price - nearestLevel.HighValue + additionalShift;
|
longStopLossShift = message.Price - nearestLevel.HighValue + additionalShift;
|
||||||
|
shortStopLossShift = message.Price - nearestLevel.HighValue + additionalShift;
|
||||||
}
|
}
|
||||||
|
|
||||||
nearestLevel = levelsByDiffForShort[0];
|
nearestLevel = levelsByDiffForShort[0];
|
||||||
if (message.Price < nearestLevel.LowValue)
|
if (message.Price < nearestLevel.LowValue)
|
||||||
{
|
{
|
||||||
shortStopLossShift = nearestLevel.LowValue - message.Price + additionalShift;
|
shortStopLossShift = nearestLevel.LowValue - message.Price + additionalShift;
|
||||||
|
longStopLossShift = nearestLevel.LowValue - message.Price + additionalShift;
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -751,6 +902,13 @@ namespace KLHZ.Trader.Core.Exchange.Services
|
||||||
var levelByTime = levelsByTime[0];
|
var levelByTime = levelsByTime[0];
|
||||||
if (message.Price >= levelByTime.LowValue && message.Price < levelByTime.HighValue)
|
if (message.Price >= levelByTime.LowValue && message.Price < levelByTime.HighValue)
|
||||||
{
|
{
|
||||||
|
var info = new AttachedInfo()
|
||||||
|
{
|
||||||
|
Key = Constants.PriceIsInSupportLevel,
|
||||||
|
Value1 = levelByTime.LowValue,
|
||||||
|
Value2 = levelByTime.HighValue,
|
||||||
|
};
|
||||||
|
message.SetAttachedInfo(info);
|
||||||
if (message.Price > levelByTime.Value)
|
if (message.Price > levelByTime.Value)
|
||||||
{
|
{
|
||||||
res[TradingEvent.OpenLong] = Constants.BlockingCoefficient;
|
res[TradingEvent.OpenLong] = Constants.BlockingCoefficient;
|
||||||
|
@ -785,35 +943,46 @@ namespace KLHZ.Trader.Core.Exchange.Services
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (_oldItems.TryGetValue(message.Figi, out var old))
|
else
|
||||||
{
|
{
|
||||||
if (old.Price >= levelByTime.LowValue && old.Price < levelByTime.HighValue)
|
var info = new AttachedInfo()
|
||||||
{
|
{
|
||||||
var islevelUsed = false;
|
Key = Constants.PriceIsNotInSupportLevel,
|
||||||
if (_usedSupportLevels.TryGetValue(message.Figi, out var time))
|
Value1 = levelByTime.LowValue,
|
||||||
|
Value2 = levelByTime.HighValue,
|
||||||
|
};
|
||||||
|
message.SetAttachedInfo(info);
|
||||||
|
if (_oldItems.TryGetValue(message.Figi, out var old))
|
||||||
|
{
|
||||||
|
if (old.Price >= levelByTime.LowValue && old.Price < levelByTime.HighValue)
|
||||||
{
|
{
|
||||||
if (time == levelByTime.CalculatedAt)
|
var islevelUsed = false;
|
||||||
|
if (_usedSupportLevels.TryGetValue(message.Figi, out var time))
|
||||||
{
|
{
|
||||||
islevelUsed = true;
|
if (time == levelByTime.CalculatedAt)
|
||||||
|
{
|
||||||
|
islevelUsed = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
if (!islevelUsed)
|
||||||
if (!islevelUsed)
|
|
||||||
{
|
|
||||||
if (message.Price < levelByTime.LowValue)
|
|
||||||
{
|
{
|
||||||
res[TradingEvent.OpenShort] = Constants.ForceExecuteCoefficient;
|
if (message.Price < levelByTime.LowValue)
|
||||||
res[TradingEvent.OpenLong] = Constants.ForceExecuteCoefficient;
|
{
|
||||||
_usedSupportLevels[message.Figi] = levelByTime.CalculatedAt;
|
res[TradingEvent.OpenShort] = Constants.UppingCoefficient;
|
||||||
}
|
res[TradingEvent.OpenLong] = Constants.UppingCoefficient;
|
||||||
else if (message.Price > levelByTime.HighValue)
|
_usedSupportLevels[message.Figi] = levelByTime.CalculatedAt;
|
||||||
{
|
}
|
||||||
res[TradingEvent.OpenShort] = Constants.ForceExecuteCoefficient;
|
else if (message.Price > levelByTime.HighValue)
|
||||||
res[TradingEvent.OpenLong] = Constants.ForceExecuteCoefficient;
|
{
|
||||||
_usedSupportLevels[message.Figi] = levelByTime.CalculatedAt;
|
res[TradingEvent.OpenShort] = Constants.UppingCoefficient;
|
||||||
|
res[TradingEvent.OpenLong] = Constants.UppingCoefficient;
|
||||||
|
_usedSupportLevels[message.Figi] = levelByTime.CalculatedAt;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return res.ToImmutableDictionary();
|
return res.ToImmutableDictionary();
|
||||||
|
|
|
@ -89,23 +89,6 @@ namespace KLHZ.Trader.Core.Exchange.Services
|
||||||
return ValueTask.FromResult(Array.Empty<ITradeDataItem>());
|
return ValueTask.FromResult(Array.Empty<ITradeDataItem>());
|
||||||
}
|
}
|
||||||
|
|
||||||
public ValueTask<ITradeDataItem[]> GetDataFrom20SecondsWindowCache2(string figi, string key)
|
|
||||||
{
|
|
||||||
return GetDataForTimeWindow(figi, TimeSpan.FromSeconds(20), key);
|
|
||||||
}
|
|
||||||
|
|
||||||
public ValueTask<ITradeDataItem[]> GetDataFrom1MinuteWindowCache2(string figi, string key)
|
|
||||||
{
|
|
||||||
return GetDataForTimeWindow(figi, TimeSpan.FromSeconds(60), key);
|
|
||||||
}
|
|
||||||
public ValueTask<ITradeDataItem[]> GetDataFrom5MinuteWindowCache2(string figi, string key)
|
|
||||||
{
|
|
||||||
return GetDataForTimeWindow(figi, TimeSpan.FromMinutes(5), key);
|
|
||||||
}
|
|
||||||
public ValueTask<ITradeDataItem[]> GetDataFrom15MinuteWindowCache2(string figi, string key)
|
|
||||||
{
|
|
||||||
return GetDataForTimeWindow(figi, TimeSpan.FromMinutes(15), key);
|
|
||||||
}
|
|
||||||
public async ValueTask AddOrderbook(IOrderbook orderbook)
|
public async ValueTask AddOrderbook(IOrderbook orderbook)
|
||||||
{
|
{
|
||||||
if (!_historyCash3.TryGetValue(orderbook.Figi, out var unit))
|
if (!_historyCash3.TryGetValue(orderbook.Figi, out var unit))
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
using KLHZ.Trader.Core.Exchange.Models.Trading;
|
using KLHZ.Trader.Core.Exchange.Models.Trading.Enums;
|
||||||
|
|
||||||
namespace KLHZ.Trader.Core.Exchange.Utils
|
namespace KLHZ.Trader.Core.Exchange.Utils
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in New Issue