diff --git a/KLHZ.Trader.Core.Contracts/Declisions/Dtos/Enums/TradingEvent.cs b/KLHZ.Trader.Core.Contracts/Declisions/Dtos/Enums/TradingEvent.cs index ed4ab8d..4f8d20e 100644 --- a/KLHZ.Trader.Core.Contracts/Declisions/Dtos/Enums/TradingEvent.cs +++ b/KLHZ.Trader.Core.Contracts/Declisions/Dtos/Enums/TradingEvent.cs @@ -11,5 +11,6 @@ ShortOpen = 16, UptrendEnd = 32, UptrendStart = 64, + HorisontTrend = 128, } } diff --git a/KLHZ.Trader.Core.Contracts/Declisions/Interfaces/IPriceHistoryCacheUnit.cs b/KLHZ.Trader.Core.Contracts/Declisions/Interfaces/IPriceHistoryCacheUnit.cs index 446767c..2826943 100644 --- a/KLHZ.Trader.Core.Contracts/Declisions/Interfaces/IPriceHistoryCacheUnit.cs +++ b/KLHZ.Trader.Core.Contracts/Declisions/Interfaces/IPriceHistoryCacheUnit.cs @@ -7,7 +7,7 @@ namespace KLHZ.Trader.Core.Contracts.Declisions.Interfaces public string Figi { get; } public int Length { get; } public ValueTask AddData(INewPrice priceChange); - public ValueTask<(DateTime[] timestamps, decimal[] prices)> GetData(); + public ValueTask<(DateTime[] timestamps, decimal[] prices)> GetData(int? length = null); public ValueTask AddOrderbook(IOrderbook orderbook); /// diff --git a/KLHZ.Trader.Core.Math/Declisions/Dtos/PeriodPricesInfoDto.cs b/KLHZ.Trader.Core.Math/Declisions/Dtos/PeriodPricesInfoDto.cs deleted file mode 100644 index b1d7e58..0000000 --- a/KLHZ.Trader.Core.Math/Declisions/Dtos/PeriodPricesInfoDto.cs +++ /dev/null @@ -1,28 +0,0 @@ -namespace KLHZ.Trader.Core.Math.Declisions.Dtos -{ - internal readonly struct PeriodPricesInfoDto - { - public readonly int Start; - public readonly int End; - public readonly decimal LastPrice; - public readonly decimal FirstPrice; - public readonly decimal PeriodDiff; - public readonly decimal PeriodMax; - public readonly decimal PeriodMin; - public readonly bool Success; - public readonly TimeSpan Period; - - public PeriodPricesInfoDto(bool success, decimal firstPrice, decimal lastPrice, decimal periodDiff, decimal periodMin, decimal periodMax, TimeSpan period, int start, int end) - { - Success = success; - LastPrice = lastPrice; - FirstPrice = firstPrice; - PeriodDiff = periodDiff; - PeriodMax = periodMax; - PeriodMin = periodMin; - Period = period; - Start = start; - End = end; - } - } -} diff --git a/KLHZ.Trader.Core.Contracts/Declisions/Dtos/TwoPeriodsResultDto.cs b/KLHZ.Trader.Core.Math/Declisions/Dtos/TwoLocalTrendsResultDto.cs similarity index 72% rename from KLHZ.Trader.Core.Contracts/Declisions/Dtos/TwoPeriodsResultDto.cs rename to KLHZ.Trader.Core.Math/Declisions/Dtos/TwoLocalTrendsResultDto.cs index d16f12f..0d3b251 100644 --- a/KLHZ.Trader.Core.Contracts/Declisions/Dtos/TwoPeriodsResultDto.cs +++ b/KLHZ.Trader.Core.Math/Declisions/Dtos/TwoLocalTrendsResultDto.cs @@ -1,6 +1,6 @@ -namespace KLHZ.Trader.Core.Contracts.Declisions.Dtos +namespace KLHZ.Trader.Core.Math.Declisions.Dtos { - public readonly struct TwoPeriodsResultDto + internal readonly struct TwoLocalTrendsResultDto { public readonly int Start; public readonly int Bound; @@ -11,7 +11,7 @@ public readonly TimeSpan PeriodStart; public readonly TimeSpan PeriodEnd; - public TwoPeriodsResultDto(bool success, decimal diffStart, decimal diffEnd, int start, int bound, int end, + public TwoLocalTrendsResultDto(bool success, decimal diffStart, decimal diffEnd, int start, int bound, int end, TimeSpan periodStart, TimeSpan periodEnd) { Success = success; diff --git a/KLHZ.Trader.Core.Math/Declisions/Services/Cache/PriceHistoryCacheUnit.cs b/KLHZ.Trader.Core.Math/Declisions/Services/Cache/PriceHistoryCacheUnit.cs index 6d222da..de90df6 100644 --- a/KLHZ.Trader.Core.Math/Declisions/Services/Cache/PriceHistoryCacheUnit.cs +++ b/KLHZ.Trader.Core.Math/Declisions/Services/Cache/PriceHistoryCacheUnit.cs @@ -47,7 +47,7 @@ namespace KLHZ.Trader.Core.Math.Declisions.Services.Cache return ValueTask.CompletedTask; } - public ValueTask<(DateTime[] timestamps, decimal[] prices)> GetData() + public ValueTask<(DateTime[] timestamps, decimal[] prices)> GetData(int? length = null) { lock (_locker) { diff --git a/KLHZ.Trader.Core.Math/Declisions/Services/Cache/PriceHistoryCacheUnit2.cs b/KLHZ.Trader.Core.Math/Declisions/Services/Cache/PriceHistoryCacheUnit2.cs index 1fcb33f..dcad0f0 100644 --- a/KLHZ.Trader.Core.Math/Declisions/Services/Cache/PriceHistoryCacheUnit2.cs +++ b/KLHZ.Trader.Core.Math/Declisions/Services/Cache/PriceHistoryCacheUnit2.cs @@ -76,7 +76,7 @@ namespace KLHZ.Trader.Core.Math.Declisions.Services.Cache return ValueTask.CompletedTask; } - public ValueTask<(DateTime[] timestamps, decimal[] prices)> GetData() + public ValueTask<(DateTime[] timestamps, decimal[] prices)> GetData(int? length = null) { lock (_locker) { @@ -86,10 +86,12 @@ namespace KLHZ.Trader.Core.Math.Declisions.Services.Cache } else { - var prices = new decimal[_length]; - var timestamps = new DateTime[_length]; - Array.Copy(Prices, 1 + _pointer - _length, prices, 0, prices.Length); - Array.Copy(Timestamps, 1 + _pointer - _length, timestamps, 0, timestamps.Length); + 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)); } } diff --git a/KLHZ.Trader.Core.Math/Declisions/Utils/LocalTrends.cs b/KLHZ.Trader.Core.Math/Declisions/Utils/LocalTrends.cs index def5810..3d3aecd 100644 --- a/KLHZ.Trader.Core.Math/Declisions/Utils/LocalTrends.cs +++ b/KLHZ.Trader.Core.Math/Declisions/Utils/LocalTrends.cs @@ -1,8 +1,6 @@ -using KLHZ.Trader.Core.Contracts.Declisions.Dtos; -using KLHZ.Trader.Core.Contracts.Declisions.Dtos.Enums; +using KLHZ.Trader.Core.Contracts.Declisions.Dtos.Enums; using KLHZ.Trader.Core.Contracts.Declisions.Interfaces; using KLHZ.Trader.Core.Math.Declisions.Dtos; -using KLHZ.Trader.Core.Math.Declisions.Services.Cache; namespace KLHZ.Trader.Core.Math.Declisions.Utils { @@ -11,199 +9,48 @@ namespace KLHZ.Trader.Core.Math.Declisions.Utils /// public static class LocalTrends { - internal static PeriodPricesInfoDto GetPriceDiffForTimeSpan(this IPriceHistoryCacheUnit unit, TimeSpan timeShift, TimeSpan timeSpan, int? pointsShift = null) + public static TradingEvent CheckByLocalTrends(DateTime[] times, decimal[] prices, TimeSpan firstPeriod, TimeSpan secondPeriod, decimal meanfullDiff, int boundIndex) { - var res = new PeriodPricesInfoDto(false, 0, 0, 0, 0, 0, timeSpan, 0, 0); - var data = unit.GetData().Result; - var times = data.timestamps; - var prices = data.prices; - if (times.Length < 2) return res; - var lastPriceTime = times[times.Length - 1]; - var intervalEnd = lastPriceTime - timeShift; - var intervalStart = intervalEnd - timeSpan; - var max = decimal.MinValue; - var min = decimal.MaxValue; - - var intervaEndIndex = -1; - var intervaStartIndex = -1; - - int count = 0; - for (int i = times.Length - 1; i > -1; i--) - { - if ((times[i] <= intervalEnd || pointsShift.HasValue && count < pointsShift.Value) && intervaEndIndex < 0) - { - intervaEndIndex = i; - } - - if (prices[i] > max && intervaEndIndex >= 0) - { - max = prices[i]; - } - if (prices[i] < min && intervaEndIndex >= 0) - { - min = prices[i]; - } - if (times[i] <= intervalStart && intervaStartIndex < 0) - { - intervaStartIndex = i; - if (intervaStartIndex != intervaEndIndex && intervaEndIndex >= 0) - break; - } - count++; - } - - if (intervaStartIndex >= 0 && intervaEndIndex >= 0) - { - res = new PeriodPricesInfoDto( - true, - prices[intervaStartIndex], - prices[intervaEndIndex], - prices[intervaEndIndex] - prices[intervaStartIndex], - min, - max, - timeSpan, - intervaStartIndex, - intervaEndIndex); - } + var res = TradingEvent.None; + res |= CheckUptrendStart(times, prices, firstPeriod, secondPeriod, meanfullDiff, boundIndex); + res |= CheckUptrendEnd(times, prices, firstPeriod, secondPeriod, meanfullDiff, boundIndex); return res; } - internal static bool CheckStable(this PeriodPricesInfoDto data, decimal meanfullDiff) + internal static TradingEvent CheckUptrendStart(DateTime[] times, decimal[] prices, TimeSpan firstPeriod, TimeSpan secondPeriod, decimal meanfullDiff, int boundIndex) { - meanfullDiff = System.Math.Abs(meanfullDiff); - return data.Success && System.Math.Abs(data.PeriodDiff) < 1.5m * meanfullDiff && System.Math.Abs(data.PeriodMax - data.PeriodMin) < 2 * meanfullDiff; + var periodStat = GetTwoPeriodsProcessingData(times, prices, firstPeriod, secondPeriod, boundIndex, meanfullDiff); + var isStartOk = periodStat.Success && periodStat.DiffStart < -meanfullDiff; + var isEndOk = periodStat.Success && periodStat.DiffEnd >= meanfullDiff; + return isStartOk && isEndOk && prices[periodStat.Start] - prices[periodStat.End] >= meanfullDiff ? TradingEvent.UptrendStart : TradingEvent.None; } - internal static bool CheckGrowing(this PeriodPricesInfoDto data, decimal meanfullDiff) + internal static TradingEvent CheckUptrendEnd(DateTime[] times, decimal[] prices, TimeSpan firstPeriod, TimeSpan secondPeriod, decimal meanfullDiff, int boundIndex) { - return meanfullDiff > 0 && data.Success && data.PeriodDiff > meanfullDiff && System.Math.Abs(data.PeriodMax - data.PeriodMin) < 3 * System.Math.Abs(data.PeriodDiff); + var periodStat = GetTwoPeriodsProcessingData(times, prices, firstPeriod, secondPeriod, boundIndex, meanfullDiff); + var isStartOk = periodStat.Success && periodStat.DiffStart > 0 && periodStat.DiffStart > 1.5m * meanfullDiff; + var isEndOk = periodStat.Success && periodStat.DiffEnd < meanfullDiff; + return isStartOk && isEndOk && prices[periodStat.End] - prices[periodStat.Start] >= meanfullDiff ? TradingEvent.UptrendEnd : TradingEvent.None; ; } - internal static bool CheckFalling(this PeriodPricesInfoDto data, decimal meanfullDiff) + internal static TwoLocalTrendsResultDto GetTwoPeriodsProcessingData(DateTime[] times, decimal[] prices, TimeSpan firstPeriod, TimeSpan lastPeriod, int boundIndex, decimal meanfullDiff) { - meanfullDiff = -meanfullDiff; - return meanfullDiff < 0 && data.Success && data.PeriodDiff < meanfullDiff && System.Math.Abs(data.PeriodMax - data.PeriodMin) < 3 * System.Math.Abs(data.PeriodDiff); - } - - internal static float CalcTrendRelationAbs(PeriodPricesInfoDto first, PeriodPricesInfoDto second) - { - var k1 = System.Math.Abs(first.PeriodDiff) / System.Math.Abs((decimal)first.Period.TotalSeconds); - var k2 = System.Math.Abs(second.PeriodDiff) / System.Math.Abs((decimal)second.Period.TotalSeconds); - if (k2 == 0 && k1 != 0) return 1000; - return (float)(k1 / k2); - } - internal static float CalcTrendRelationAbs(TwoPeriodsResultDto data) - { - var k1 = System.Math.Abs(data.DiffStart) / System.Math.Abs((decimal)data.PeriodStart.TotalSeconds); - var k2 = System.Math.Abs(data.DiffEnd) / System.Math.Abs((decimal)data.PeriodEnd.TotalSeconds); - if (k2 == 0 && k1 != 0) return 1000; - return (float)(k1 / k2); - } - - internal static bool CheckDowntrendEnding(this IPriceHistoryCacheUnit unit, TimeSpan firstPeriod, TimeSpan secondPeriod, decimal meanfullDiff) - { - var totalDiff = unit.GetPriceDiffForTimeSpan(TimeSpan.Zero, firstPeriod + secondPeriod); - var startDiff = unit.GetPriceDiffForTimeSpan(secondPeriod, firstPeriod); - var endDiff = unit.GetPriceDiffForTimeSpan(TimeSpan.Zero, secondPeriod); - var isEndStable = endDiff.CheckStable(meanfullDiff); - var isEndGrown = endDiff.CheckGrowing(meanfullDiff); - var isStartFalls = startDiff.CheckFalling(meanfullDiff); - var isTotalFalls = totalDiff.CheckFalling(meanfullDiff); - - var trendRelation = CalcTrendRelationAbs(startDiff, endDiff); - var res = totalDiff.Success && isStartFalls && (isEndStable || isEndGrown) && trendRelation >= 2; - - if (startDiff.Success) - { - - } - return res; - } - - internal static bool CheckUptrendEnding(this IPriceHistoryCacheUnit unit, TimeSpan firstPeriod, TimeSpan secondPeriod, decimal meanfullDiff) - { - var totalDiff = unit.GetPriceDiffForTimeSpan(TimeSpan.Zero, firstPeriod + secondPeriod); - var startDiff = unit.GetPriceDiffForTimeSpan(secondPeriod, firstPeriod); - var endDiff = unit.GetPriceDiffForTimeSpan(TimeSpan.Zero, secondPeriod); - var isEndStable = endDiff.CheckStable(meanfullDiff); - var isEndFalls = endDiff.CheckFalling(meanfullDiff); - var isStartGrows = startDiff.CheckGrowing(meanfullDiff); - - var trendRelation = CalcTrendRelationAbs(startDiff, endDiff); - var isEndLocal = endDiff.PeriodDiff == 0; - var res = totalDiff.Success && isStartGrows && (isEndStable || isEndFalls) && trendRelation >= 2 && !isEndLocal; - - if (res) - { - - } - return res; - } - - internal static bool CheckDowntrendStarting(this PriceHistoryCacheUnit unit, TimeSpan firstPeriod, TimeSpan secondPeriod, decimal meanfullDiff) - { - var totalDiff = unit.GetPriceDiffForTimeSpan(TimeSpan.Zero, firstPeriod + secondPeriod); - var startDiff = unit.GetPriceDiffForTimeSpan(secondPeriod, firstPeriod); - var endDiff = unit.GetPriceDiffForTimeSpan(TimeSpan.Zero, secondPeriod); - - - var isEndFalls = endDiff.CheckFalling(meanfullDiff); - var isStartStable = startDiff.CheckStable(meanfullDiff); - var isStartGrows = startDiff.CheckGrowing(meanfullDiff); - - var trendRelation = CalcTrendRelationAbs(endDiff, startDiff); - return totalDiff.Success && (isStartStable || isStartGrows) && isEndFalls && trendRelation >= 2; - } - - internal static bool CheckUptrendStarting(this PriceHistoryCacheUnit unit, TimeSpan firstPeriod, TimeSpan secondPeriod, decimal meanfullDiff) - { - var totalDiff = unit.GetPriceDiffForTimeSpan(TimeSpan.Zero, firstPeriod + secondPeriod); - var startDiff = unit.GetPriceDiffForTimeSpan(secondPeriod, firstPeriod); - var endDiff = unit.GetPriceDiffForTimeSpan(TimeSpan.Zero, secondPeriod); - - - var isEndGrows = endDiff.CheckGrowing(meanfullDiff); - var isStartStable = startDiff.CheckStable(meanfullDiff); - var isStartFalls = startDiff.CheckStable(meanfullDiff); - - var trendRelation = CalcTrendRelationAbs(endDiff, startDiff); - var res = totalDiff.Success && (isStartStable || isStartFalls) && isEndGrows && endDiff.PeriodDiff > meanfullDiff; - - if (isStartStable) - { - res &= trendRelation >= 2; - } - else - { - - } - if (res) - { - - } - return res; - } - - internal static TwoPeriodsResultDto GetTwoPeriodsProcessingData(this (DateTime[] timestamps, decimal[] prices) data, TimeSpan shift, int shiftPointsStart, int shiftPointsEnd, TimeSpan firstPeriod, decimal meanfullDiff) - { - var res = new TwoPeriodsResultDto(success: false, 0, 0, 0, 0, 0, TimeSpan.Zero, TimeSpan.Zero); - var time = data.timestamps; - var prices = data.prices; + var res = new TwoLocalTrendsResultDto(success: false, 0, 0, 0, 0, 0, TimeSpan.Zero, TimeSpan.Zero); int count = -1; - var lastTime = time[time.Length - 1]; + var lastTime = times[times.Length - 1]; var bound = -1; var start = -1; - var end = time.Length - 1; + var end = times.Length - 1; - for (int i = time.Length - 1; i > -1; i--) + for (int i = times.Length - 1; i > -1; i--) { - if (count > 0 && bound < 0 && (count == shiftPointsEnd || lastTime - time[i] >= shift)) + if (count > 0 && bound < 0 && (count == boundIndex || lastTime - times[i] >= lastPeriod)) { bound = i; - shift = lastTime - time[i]; + lastPeriod = lastTime - times[i]; } - if (lastTime - time[i] >= shift + firstPeriod) + if (lastTime - times[i] >= lastPeriod + firstPeriod) { start = i; @@ -217,105 +64,37 @@ namespace KLHZ.Trader.Core.Math.Declisions.Utils { var diff1 = prices[bound] - prices[start]; var diff2 = prices[end] - prices[bound]; - res = new TwoPeriodsResultDto(true, diff1, diff2, start, bound, end, time[bound] - time[start], time[end] - time[bound]); + res = new TwoLocalTrendsResultDto(true, diff1, diff2, start, bound, end, times[bound] - times[start], times[end] - times[bound]); } return res; } - - public static bool CheckLongClose(this IPriceHistoryCacheUnit unit, TimeSpan firstPeriod, TimeSpan secondPeriod, decimal meanfullDiff, int pointsStart, int pointsEnd) + internal static bool CheckLongOpen(this IPriceHistoryCacheUnit unit, TimeSpan firstPeriod, TimeSpan secondPeriod, decimal meanfullDiff, int boundIndex) { var data = unit.GetData().Result; - var periodStat = data.GetTwoPeriodsProcessingData(secondPeriod, pointsStart, pointsEnd, firstPeriod, meanfullDiff); - var trendRelation = CalcTrendRelationAbs(periodStat); - var isStartOk = periodStat.Success && periodStat.DiffStart > 0 && periodStat.DiffStart > 1.5m * meanfullDiff; - var isEndOk = periodStat.Success && periodStat.DiffEnd < meanfullDiff; - - if (isEndOk) - { - - } - - if (isStartOk) - { - - } - - if (isEndOk && isStartOk) - { - - } - return isStartOk && isEndOk && data.prices[periodStat.End] - data.prices[periodStat.Start] >= meanfullDiff; - } - - internal static bool CheckUptrendStarting2(this IPriceHistoryCacheUnit unit, TimeSpan firstPeriod, TimeSpan secondPeriod, decimal meanfullDiff) - { - var data = unit.GetData().Result; - var periodStat = data.GetTwoPeriodsProcessingData(secondPeriod, 15, 2, firstPeriod, meanfullDiff); - var trendRelation = CalcTrendRelationAbs(periodStat); - var isStartOk = periodStat.Success && periodStat.DiffStart < -meanfullDiff; - var isEndOk = periodStat.Success && periodStat.DiffEnd >= meanfullDiff; - - if (isEndOk) - { - - } - - if (isStartOk) - { - - } - - if (isEndOk && isStartOk) - { - - } - return isStartOk && isEndOk; - } - - internal static bool _CheckUptrendStarting2(this IPriceHistoryCacheUnit unit, TimeSpan firstPeriod, TimeSpan secondPeriod, decimal meanfullDiff) - { - var data = unit.GetData().Result; - var periodStat = data.GetTwoPeriodsProcessingData(secondPeriod, 15, 1, firstPeriod, meanfullDiff); - var trendRelation = CalcTrendRelationAbs(periodStat); - var isStartOk = periodStat.Success && periodStat.DiffStart < -meanfullDiff; - var isEndOk = periodStat.Success && periodStat.DiffEnd >= meanfullDiff; - - if (isEndOk) - { - - } - - if (isStartOk) - { - - } - - if (isEndOk && isStartOk) - { - - } - return isStartOk && isEndOk; - } - - - public static bool CheckLongOpen(this IPriceHistoryCacheUnit unit, TimeSpan firstPeriod, TimeSpan secondPeriod, decimal meanfullDiff, int pointsStart, int pointsEnd) - { - var data = unit.GetData().Result; - var periodStat = data.GetTwoPeriodsProcessingData(secondPeriod, pointsStart, pointsEnd, firstPeriod, meanfullDiff); + var periodStat = GetTwoPeriodsProcessingData(data.timestamps, data.prices, firstPeriod, secondPeriod, boundIndex, meanfullDiff); var isStartOk = periodStat.Success && periodStat.DiffStart < -meanfullDiff; var isEndOk = periodStat.Success && periodStat.DiffEnd >= meanfullDiff; return isStartOk && isEndOk && data.prices[periodStat.Start] - data.prices[periodStat.End] >= meanfullDiff; } - public static TradingEvent Detect(IPriceHistoryCacheUnit data) + internal static bool CheckLongClose(this IPriceHistoryCacheUnit unit, TimeSpan firstPeriod, TimeSpan secondPeriod, decimal meanfullDiff, int boundIndex) + { + var data = unit.GetData().Result; + var periodStat = GetTwoPeriodsProcessingData(data.timestamps, data.prices, firstPeriod, secondPeriod, boundIndex, meanfullDiff); + var isStartOk = periodStat.Success && periodStat.DiffStart > 0 && periodStat.DiffStart > 1.5m * meanfullDiff; + var isEndOk = periodStat.Success && periodStat.DiffEnd < meanfullDiff; + return isStartOk && isEndOk && data.prices[periodStat.End] - data.prices[periodStat.Start] >= meanfullDiff; + } + + internal static TradingEvent Detect(IPriceHistoryCacheUnit data) { decimal meanfullDiff = 1m; var res = TradingEvent.None; //var downtrendStarts = data.CheckDowntrendStarting(TimeSpan.FromSeconds(30), TimeSpan.FromSeconds(7), meanfullDiff); - var uptrendStarts = data.CheckLongOpen(TimeSpan.FromSeconds(60), TimeSpan.FromSeconds(7), meanfullDiff, 8, 3); - var uptrendStarts2 = data.CheckLongOpen(TimeSpan.FromSeconds(60), TimeSpan.FromSeconds(3), meanfullDiff, 15, 2); - var downtrendEnds = data.CheckLongOpen(TimeSpan.FromSeconds(120), TimeSpan.FromSeconds(10), meanfullDiff, 15, 5); + var uptrendStarts = data.CheckLongOpen(TimeSpan.FromSeconds(60), TimeSpan.FromSeconds(7), meanfullDiff, 3); + var uptrendStarts2 = data.CheckLongOpen(TimeSpan.FromSeconds(60), TimeSpan.FromSeconds(3), meanfullDiff, 2); + var downtrendEnds = data.CheckLongOpen(TimeSpan.FromSeconds(120), TimeSpan.FromSeconds(10), meanfullDiff, 5); uptrendStarts |= downtrendEnds; uptrendStarts |= uptrendStarts2; if (uptrendStarts) @@ -324,8 +103,8 @@ namespace KLHZ.Trader.Core.Math.Declisions.Utils } //var downtrendEnds = data.CheckDowntrendEnding(TimeSpan.FromSeconds(60), TimeSpan.FromSeconds(15), meanfullDiff); - var uptrendEnds = data.CheckLongClose(TimeSpan.FromSeconds(15), TimeSpan.FromSeconds(20), meanfullDiff * 1.5m, 8, 8); - var uptrendEnds2 = data.CheckLongClose(TimeSpan.FromSeconds(120), TimeSpan.FromSeconds(30), meanfullDiff, 15, 8); + var uptrendEnds = data.CheckLongClose(TimeSpan.FromSeconds(15), TimeSpan.FromSeconds(20), meanfullDiff * 1.5m, 8); + var uptrendEnds2 = data.CheckLongClose(TimeSpan.FromSeconds(120), TimeSpan.FromSeconds(30), meanfullDiff, 8); uptrendEnds |= uptrendEnds2; if (uptrendEnds) { diff --git a/KLHZ.Trader.Core.Tests/HistoryCacheUnit2Tests.cs b/KLHZ.Trader.Core.Tests/HistoryCacheUnit2Tests.cs index 64906df..3049e0a 100644 --- a/KLHZ.Trader.Core.Tests/HistoryCacheUnit2Tests.cs +++ b/KLHZ.Trader.Core.Tests/HistoryCacheUnit2Tests.cs @@ -219,5 +219,28 @@ namespace KLHZ.Trader.Core.Tests } } } + + [Test] + public void Test10() + { + var length = 77; + var shift = 334; + var count = PriceHistoryCacheUnit.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().Value); + Assert.That(data.timestamps.Last() == hist.Last().Time); + 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].Time, Is.EqualTo(data.timestamps[data.prices.Length - i])); + } + } } } \ No newline at end of file diff --git a/KLHZ.Trader.Core.Tests/HistoryProcessingInstrumentsTests.cs b/KLHZ.Trader.Core.Tests/HistoryProcessingInstrumentsTests.cs deleted file mode 100644 index f2d6a6c..0000000 --- a/KLHZ.Trader.Core.Tests/HistoryProcessingInstrumentsTests.cs +++ /dev/null @@ -1,275 +0,0 @@ -using KLHZ.Trader.Core.DataLayer.Entities.Prices; -using KLHZ.Trader.Core.Math.Declisions.Services.Cache; -using KLHZ.Trader.Core.Math.Declisions.Utils; - -namespace KLHZ.Trader.Core.Tests -{ - public class HistoryProcessingInstrumentsTests - { - private static PriceChange[] GetHistory(int count, string figi, DateTime startDt, decimal startValue, decimal step) - { - var res = new PriceChange[count]; - if (count != 0) - { - for (int i = 0; i < count; i++) - { - startValue += step; - startDt = startDt.AddSeconds(1); - res[i] = new PriceChange() - { - Figi = figi, - Ticker = figi + "_ticker", - Id = i, - Time = startDt, - Value = startValue, - }; - } - } - return res; - } - - [Test] - public void Test0() - { - var figi = "figi"; - var startDate = new DateTime(2020, 1, 1, 1, 0, 0, DateTimeKind.Utc); - var count = 100; - var step = 0; - var startValue = 10; - var unit = new PriceHistoryCacheUnit(figi, GetHistory(count, figi, startDate, startValue, step)); - - var data = unit.GetData().Result; - var endDate = startDate.AddSeconds(count); - Assert.IsTrue(data.timestamps.Last() == endDate); - Assert.IsTrue(data.prices.Last() == startValue + step * count); - - var periodLength = 4; - var shift = 0; - var result = LocalTrends.GetPriceDiffForTimeSpan(unit, TimeSpan.FromSeconds(shift), TimeSpan.FromSeconds(periodLength)); - - var maxValue = startValue + (step > 0 ? (step * count) - step * shift : (step * count) - (step * (shift + periodLength))); - var minValue = startValue + (step > 0 ? (step * count) - (step * (shift + periodLength)) : (step * count) - step * shift); - var firstPrice = startValue + (step * count) - (step * (shift + periodLength)); - var lastPrice = startValue + (step * count) - step * shift; - - var diff = firstPrice - lastPrice; - - - Assert.IsTrue(result.LastPrice == lastPrice); - Assert.IsTrue(result.FirstPrice == firstPrice); - Assert.IsTrue(result.PeriodMax == maxValue); - Assert.IsTrue(result.PeriodMin == minValue); - Assert.IsTrue(result.PeriodDiff == diff); - } - - [Test] - public void Test1() - { - var figi = "figi"; - var startDate = new DateTime(2020, 1, 1, 1, 0, 0, DateTimeKind.Utc); - var count = 100; - var step = 0.5m; - var startValue = 10; - var unit = new PriceHistoryCacheUnit(figi, GetHistory(count, figi, startDate, startValue, step)); - - var data = unit.GetData().Result; - var endDate = startDate.AddSeconds(count); - Assert.IsTrue(data.timestamps.Last() == endDate); - Assert.IsTrue(data.prices.Last() == startValue + step * count); - - var periodLength = 4; - var shift = 0; - var result = LocalTrends.GetPriceDiffForTimeSpan(unit, TimeSpan.FromSeconds(shift), TimeSpan.FromSeconds(periodLength)); - - var maxValue = startValue + (step > 0 ? (step * count) - step * shift : (step * count) - (step * (shift + periodLength))); - var minValue = startValue + (step > 0 ? (step * count) - (step * (shift + periodLength)) : (step * count) - step * shift); - var firstPrice = startValue + (step * count) - (step * (shift + periodLength)); - var lastPrice = startValue + (step * count) - step * shift; - - var diff = lastPrice - firstPrice; - - - Assert.IsTrue(result.LastPrice == lastPrice); - Assert.IsTrue(result.FirstPrice == firstPrice); - Assert.IsTrue(result.PeriodMax == maxValue); - Assert.IsTrue(result.PeriodMin == minValue); - Assert.IsTrue(result.PeriodDiff == diff); - } - - [Test] - public void Test2() - { - var figi = "figi"; - var startDate = new DateTime(2020, 1, 1, 1, 0, 0, DateTimeKind.Utc); - var count = 100; - var step = 0.5m; - var startValue = 10; - - var unit = new PriceHistoryCacheUnit(figi, GetHistory(count, figi, startDate, startValue, step)); - - var data = unit.GetData().Result; - var endDate = startDate.AddSeconds(count); - Assert.IsTrue(data.timestamps.Last() == endDate); - Assert.IsTrue(data.prices.Last() == startValue + step * count); - - var periodLength = 4; - var shift = 1; - var result = LocalTrends.GetPriceDiffForTimeSpan(unit, TimeSpan.FromSeconds(shift), TimeSpan.FromSeconds(periodLength)); - - var maxValue = startValue + (step > 0 ? (step * count) - step * shift : (step * count) - (step * (shift + periodLength))); - var minValue = startValue + (step > 0 ? (step * count) - (step * (shift + periodLength)) : (step * count) - step * shift); - var firstPrice = startValue + (step * count) - (step * (shift + periodLength)); - var lastPrice = startValue + (step * count) - step * shift; - - var diff = lastPrice - firstPrice; - - - Assert.IsTrue(result.LastPrice == lastPrice); - Assert.IsTrue(result.FirstPrice == firstPrice); - Assert.IsTrue(result.PeriodMax == maxValue); - Assert.IsTrue(result.PeriodMin == minValue); - Assert.IsTrue(result.PeriodDiff == diff); - } - - [Test] - public void Test3() - { - var figi = "figi"; - var startDate = new DateTime(2020, 1, 1, 1, 0, 0, DateTimeKind.Utc); - var count = 100; - var step = -0.5m; - var startValue = 10; - - var unit = new PriceHistoryCacheUnit(figi, GetHistory(count, figi, startDate, startValue, step)); - - var data = unit.GetData().Result; - var endDate = startDate.AddSeconds(count); - Assert.IsTrue(data.timestamps.Last() == endDate); - Assert.IsTrue(data.prices.Last() == startValue + step * count); - - var periodLength = 4; - var shift = 0; - var result = LocalTrends.GetPriceDiffForTimeSpan(unit, TimeSpan.FromSeconds(shift), TimeSpan.FromSeconds(periodLength)); - - var maxValue = startValue + (step > 0 ? (step * count) - step * shift : (step * count) - (step * (shift + periodLength))); - var minValue = startValue + (step > 0 ? (step * count) - (step * (shift + periodLength)) : (step * count) - step * shift); - var firstPrice = startValue + (step * count) - (step * (shift + periodLength)); - var lastPrice = startValue + (step * count) - step * shift; - - var diff = lastPrice - firstPrice; - - - Assert.IsTrue(result.LastPrice == lastPrice); - Assert.IsTrue(result.FirstPrice == firstPrice); - Assert.IsTrue(result.PeriodMax == maxValue); - Assert.IsTrue(result.PeriodMin == minValue); - Assert.IsTrue(result.PeriodDiff == diff); - } - - [Test] - public void Test4() - { - var figi = "figi"; - var startDate = new DateTime(2020, 1, 1, 1, 0, 0, DateTimeKind.Utc); - var count = 100; - var step = -0.5m; - var startValue = 10; - - var unit = new PriceHistoryCacheUnit(figi, GetHistory(count, figi, startDate, startValue, step)); - - var data = unit.GetData().Result; - var endDate = startDate.AddSeconds(count); - Assert.IsTrue(data.timestamps.Last() == endDate); - Assert.IsTrue(data.prices.Last() == startValue + step * count); - - var periodLength = 4; - var shift = 3; - var result = LocalTrends.GetPriceDiffForTimeSpan(unit, TimeSpan.FromSeconds(shift), TimeSpan.FromSeconds(periodLength)); - - var maxValue = startValue + (step > 0 ? (step * count) - step * shift : (step * count) - (step * (shift + periodLength))); - var minValue = startValue + (step > 0 ? (step * count) - (step * (shift + periodLength)) : (step * count) - step * shift); - var firstPrice = startValue + (step * count) - (step * (shift + periodLength)); - var lastPrice = startValue + (step * count) - step * shift; - - var diff = lastPrice - firstPrice; - - - Assert.IsTrue(result.LastPrice == lastPrice); - Assert.IsTrue(result.FirstPrice == firstPrice); - Assert.IsTrue(result.PeriodMax == maxValue); - Assert.IsTrue(result.PeriodMin == minValue); - Assert.IsTrue(result.PeriodDiff == diff); - } - - [Test] - public void Test5() - { - var figi = "figi"; - var startDate = new DateTime(2020, 1, 1, 1, 0, 0, DateTimeKind.Utc); - var count = 100; - var step = -0.5m; - var startValue = 10; - - var unit = new PriceHistoryCacheUnit(figi, GetHistory(count, figi, startDate, startValue, step)); - - var data = unit.GetData().Result; - var endDate = startDate.AddSeconds(count); - Assert.IsTrue(data.timestamps.Last() == endDate); - Assert.IsTrue(data.prices.Last() == startValue + step * count); - - var periodLength = 4; - var shift = 3; - var result = LocalTrends.GetPriceDiffForTimeSpan(unit, TimeSpan.FromSeconds(shift), TimeSpan.FromSeconds(periodLength)); - - var maxValue1 = startValue + (step > 0 ? (step * count) - step * shift : (step * count) - (step * (shift + periodLength))); - var minValue1 = startValue + (step > 0 ? (step * count) - (step * (shift + periodLength)) : (step * count) - step * shift); - var firstPrice1 = startValue + (step * count) - (step * (shift + periodLength)); - var lastPrice1 = startValue + (step * count) - step * shift; - - var diff1 = lastPrice1 - firstPrice1; - - - Assert.IsTrue(result.LastPrice == lastPrice1); - Assert.IsTrue(result.FirstPrice == firstPrice1); - Assert.IsTrue(result.PeriodMax == maxValue1); - Assert.IsTrue(result.PeriodMin == minValue1); - Assert.IsTrue(result.PeriodDiff == diff1); - - - - var unit2 = new PriceHistoryCacheUnit(figi); - var data2 = unit.GetData().Result; - for (int i = 0; i < data2.prices.Length; i++) - { - var value = (decimal)data2.prices[i]; - if (i == data2.prices.Length - 5) - { - value = 100; - } - else if (i == data2.prices.Length - 6) - { - value = -100; - } - unit2.AddData(new PriceChange() - { - Figi = figi, - Ticker = figi, - Time = data2.timestamps[i], - Value = value - }); - - } - - var result2 = LocalTrends.GetPriceDiffForTimeSpan(unit2, TimeSpan.FromSeconds(shift), TimeSpan.FromSeconds(periodLength)); - - var maxValue2 = 100; - var minValue2 = -100; - - Assert.IsTrue(result2.LastPrice == result.LastPrice); - Assert.IsTrue(result2.FirstPrice == result.FirstPrice); - Assert.IsTrue(result2.PeriodMax == maxValue2); - Assert.IsTrue(result2.PeriodMin == minValue2); - Assert.IsTrue(result2.PeriodDiff == result.PeriodDiff); - } - } -} diff --git a/KLHZ.Trader.Core/Exchange/Extentions/StringExtensions.cs b/KLHZ.Trader.Core/Exchange/Extentions/StringExtensions.cs index a0bcda0..625ce00 100644 --- a/KLHZ.Trader.Core/Exchange/Extentions/StringExtensions.cs +++ b/KLHZ.Trader.Core/Exchange/Extentions/StringExtensions.cs @@ -1,4 +1,4 @@ -using KLHZ.Trader.Core.Exchange.Models; +using KLHZ.Trader.Core.Exchange.Models.Assets; namespace KLHZ.Trader.Core.Exchange.Extentions { diff --git a/KLHZ.Trader.Core/Exchange/Models/Asset.cs b/KLHZ.Trader.Core/Exchange/Models/Assets/Asset.cs similarity index 88% rename from KLHZ.Trader.Core/Exchange/Models/Asset.cs rename to KLHZ.Trader.Core/Exchange/Models/Assets/Asset.cs index fec382d..e42f69a 100644 --- a/KLHZ.Trader.Core/Exchange/Models/Asset.cs +++ b/KLHZ.Trader.Core/Exchange/Models/Assets/Asset.cs @@ -1,4 +1,4 @@ -namespace KLHZ.Trader.Core.Exchange.Models +namespace KLHZ.Trader.Core.Exchange.Models.Assets { public class Asset { diff --git a/KLHZ.Trader.Core/Exchange/Models/AssetType.cs b/KLHZ.Trader.Core/Exchange/Models/Assets/AssetType.cs similarity index 66% rename from KLHZ.Trader.Core/Exchange/Models/AssetType.cs rename to KLHZ.Trader.Core/Exchange/Models/Assets/AssetType.cs index 1a58b9d..9000294 100644 --- a/KLHZ.Trader.Core/Exchange/Models/AssetType.cs +++ b/KLHZ.Trader.Core/Exchange/Models/Assets/AssetType.cs @@ -1,4 +1,4 @@ -namespace KLHZ.Trader.Core.Exchange.Models +namespace KLHZ.Trader.Core.Exchange.Models.Assets { public enum AssetType { diff --git a/KLHZ.Trader.Core/Exchange/Models/PositionType.cs b/KLHZ.Trader.Core/Exchange/Models/Assets/PositionType.cs similarity index 62% rename from KLHZ.Trader.Core/Exchange/Models/PositionType.cs rename to KLHZ.Trader.Core/Exchange/Models/Assets/PositionType.cs index 1f68c6f..549c1e0 100644 --- a/KLHZ.Trader.Core/Exchange/Models/PositionType.cs +++ b/KLHZ.Trader.Core/Exchange/Models/Assets/PositionType.cs @@ -1,4 +1,4 @@ -namespace KLHZ.Trader.Core.Exchange.Models +namespace KLHZ.Trader.Core.Exchange.Models.Assets { public enum PositionType { diff --git a/KLHZ.Trader.Core/Exchange/Models/DeferredTrade.cs b/KLHZ.Trader.Core/Exchange/Models/DeferredTrade.cs new file mode 100644 index 0000000..15aabb5 --- /dev/null +++ b/KLHZ.Trader.Core/Exchange/Models/DeferredTrade.cs @@ -0,0 +1,9 @@ +namespace KLHZ.Trader.Core.Exchange.Models +{ + internal class DeferredTrade + { + public required string Figi { get; set; } + public decimal Price { get; set; } + public DateTime Time { get; set; } + } +} diff --git a/KLHZ.Trader.Core/Exchange/Services/ManagedAccount.cs b/KLHZ.Trader.Core/Exchange/Services/ManagedAccount.cs index 5afcdb8..38b1f9f 100644 --- a/KLHZ.Trader.Core/Exchange/Services/ManagedAccount.cs +++ b/KLHZ.Trader.Core/Exchange/Services/ManagedAccount.cs @@ -10,7 +10,7 @@ using System.Collections.Concurrent; using System.Threading.Channels; using Tinkoff.InvestApi; using Tinkoff.InvestApi.V1; -using PositionType = KLHZ.Trader.Core.Exchange.Models.PositionType; +using PositionType = KLHZ.Trader.Core.Exchange.Models.Assets.PositionType; namespace KLHZ.Trader.Core.Exchange.Services { @@ -58,7 +58,7 @@ namespace KLHZ.Trader.Core.Exchange.Services } } - internal readonly ConcurrentDictionary Assets = new(); + internal readonly ConcurrentDictionary Assets = new(); #endregion @@ -126,7 +126,7 @@ namespace KLHZ.Trader.Core.Exchange.Services price = position.AveragePositionPrice; } #pragma warning disable CS0612 // Тип или член устарел - var asset = new Models.Asset() + var asset = new Models.Assets.Asset() { AccountId = AccountId, Figi = position.Figi, diff --git a/KLHZ.Trader.Core/Exchange/Services/Trader.cs b/KLHZ.Trader.Core/Exchange/Services/Trader.cs index 49dea9b..f3dc518 100644 --- a/KLHZ.Trader.Core/Exchange/Services/Trader.cs +++ b/KLHZ.Trader.Core/Exchange/Services/Trader.cs @@ -10,6 +10,7 @@ using KLHZ.Trader.Core.DataLayer.Entities.Declisions.Enums; using KLHZ.Trader.Core.DataLayer.Entities.Prices; using KLHZ.Trader.Core.Exchange.Extentions; using KLHZ.Trader.Core.Exchange.Models; +using KLHZ.Trader.Core.Exchange.Models.Assets; using KLHZ.Trader.Core.Math.Declisions.Services.Cache; using KLHZ.Trader.Core.Math.Declisions.Utils; using Microsoft.EntityFrameworkCore; @@ -20,7 +21,7 @@ using Microsoft.Extensions.Options; using System.Collections.Concurrent; using System.Threading.Channels; using Tinkoff.InvestApi; -using AssetType = KLHZ.Trader.Core.Exchange.Models.AssetType; +using AssetType = KLHZ.Trader.Core.Exchange.Models.Assets.AssetType; namespace KLHZ.Trader.Core.Exchange.Services { @@ -31,6 +32,8 @@ namespace KLHZ.Trader.Core.Exchange.Services private readonly IDataBus _dataBus; private readonly BotModeSwitcher _botModeSwitcher; private readonly IDbContextFactory _dbContextFactory; + private readonly ConcurrentDictionary DeferredLongOpens = new(); + private readonly ConcurrentDictionary DeferredLongCloses = new(); private readonly ConcurrentDictionary OpeningStops = new(); private readonly ConcurrentDictionary Accounts = new(); private readonly ConcurrentDictionary _historyCash = new(); @@ -125,7 +128,37 @@ namespace KLHZ.Trader.Core.Exchange.Services { if (message.Figi == "FUTIMOEXF000") { - var data = await unit.GetData(); + + DeferredTrade? longOpen; + DeferredLongOpens.TryGetValue(message.Figi, out longOpen); + if (longOpen != null) + { + if (longOpen.Time <= (message.IsHistoricalData ? message.Time : DateTime.UtcNow)) + { + DeferredLongOpens.TryRemove(message.Figi, out _); + if (message.Value - longOpen.Price < 1) + { + LogDeclision(declisionsForSave, DeclisionTradeAction.OpenLong, message); + } + } + } + + DeferredTrade? longClose; + DeferredLongCloses.TryGetValue(message.Figi, out longClose); + if (longClose != null) + { + if (longClose.Time <= (message.IsHistoricalData ? message.Time : DateTime.UtcNow)) + { + DeferredLongCloses.TryRemove(message.Figi, out _); + if (longClose.Price - message.Value < 1) + { + LogDeclision(declisionsForSave, DeclisionTradeAction.CloseLong, message); + } + } + } + + var windowMaxSize = 100; + var data = await unit.GetData(windowMaxSize); if (OpeningStops.TryGetValue(message.Figi, out var dt)) { if (dt < (message.IsHistoricalData ? message.Time : DateTime.UtcNow)) @@ -137,34 +170,59 @@ namespace KLHZ.Trader.Core.Exchange.Services if ((unit.BidsCount / unit.AsksCount) < 0.5m || (unit.BidsCount / unit.AsksCount) > 2m) { var stopTo = (message.IsHistoricalData ? message.Time : DateTime.UtcNow).AddMinutes(3); - OpeningStops.AddOrUpdate(message.Figi, stopTo, (k, v) => stopTo); - LogDeclision(declisionsForSave, DeclisionTradeAction.StopBuyShortTime, message); + //OpeningStops.AddOrUpdate(message.Figi, stopTo, (k, v) => stopTo); + //LogDeclision(declisionsForSave, DeclisionTradeAction.StopBuyShortTime, message); } - var resultLongOpen = MovingAverage.CheckByWindowAverageMean(data.timestamps, data.prices, 100, 45, 180, 2.5m); - var resultLongClose = MovingAverage.CheckByWindowAverageMean(data.timestamps, data.prices, 100, 15, 120, 2.5m); + var res = TradingEvent.None; + var resultMoveAvFull = MovingAverage.CheckByWindowAverageMean(data.timestamps, data.prices, windowMaxSize, 45, 180, 2.5m); + var resultLongClose = MovingAverage.CheckByWindowAverageMean(data.timestamps, data.prices, windowMaxSize, 15, 120, 2.5m).events; - if (resultLongOpen.bigWindowAv != 0) + var uptrendStarts = LocalTrends.CheckByLocalTrends(data.timestamps, data.prices, TimeSpan.FromSeconds(60), TimeSpan.FromSeconds(7), 1.5m, 3); + var uptrendStarts2 = LocalTrends.CheckByLocalTrends(data.timestamps, data.prices, TimeSpan.FromSeconds(60), TimeSpan.FromSeconds(3), 1.5m, 2); + var downtrendEnds = LocalTrends.CheckByLocalTrends(data.timestamps, data.prices, TimeSpan.FromSeconds(120), TimeSpan.FromSeconds(10), 1.5m, 5); + + + res |= uptrendStarts; + res |= uptrendStarts2; + //res |= downtrendEnds; + res |= resultLongClose; + res |= resultMoveAvFull.events; + if (resultMoveAvFull.bigWindowAv != 0) { - LogPrice(processedPrices, message, bigWindowProcessor, resultLongOpen.bigWindowAv); - LogPrice(processedPrices, message, smallWindowProcessor, resultLongOpen.smallWindowAv); + LogPrice(processedPrices, message, bigWindowProcessor, resultMoveAvFull.bigWindowAv); + LogPrice(processedPrices, message, smallWindowProcessor, resultMoveAvFull.smallWindowAv); } - if ((resultLongClose.events & TradingEvent.StopBuy) == TradingEvent.StopBuy) + if ((resultLongClose & TradingEvent.StopBuy) == TradingEvent.StopBuy) { var stopTo = (message.IsHistoricalData ? message.Time : DateTime.UtcNow).AddMinutes(_buyStopLength); OpeningStops.AddOrUpdate(message.Figi, stopTo, (k, v) => stopTo); //LogDeclision(declisionsForSave, DeclisionTradeAction.StopBuy, message); } - if ((resultLongOpen.events & TradingEvent.UptrendStart) == TradingEvent.UptrendStart - && !OpeningStops.TryGetValue(message.Figi, out _)) + if ((res & TradingEvent.UptrendStart) == TradingEvent.UptrendStart + && !OpeningStops.TryGetValue(message.Figi, out _) + && data.timestamps.Length > 1 + && (data.timestamps[data.timestamps.Length - 1] - data.timestamps[data.timestamps.Length - 2] < TimeSpan.FromMinutes(1))) { - LogDeclision(declisionsForSave, DeclisionTradeAction.OpenLong, message); + var trade = new DeferredTrade() + { + Figi = message.Figi, + Price = message.Value, + Time = message.Time.AddSeconds(15) + }; + DeferredLongOpens[message.Figi] = trade; } - if ((resultLongClose.events & TradingEvent.UptrendEnd) == TradingEvent.UptrendEnd) + if ((res & TradingEvent.UptrendEnd) == TradingEvent.UptrendEnd) { - LogDeclision(declisionsForSave, DeclisionTradeAction.CloseLong, message); + var trade = new DeferredTrade() + { + Figi = message.Figi, + Price = message.Value, + Time = message.Time.AddSeconds(15) + }; + DeferredLongCloses[message.Figi] = trade; } //if ((resultLongOpen.events & TradingEvent.ShortOpen) == TradingEvent.ShortOpen