diff --git a/KLHZ.Trader.Core.Math/Common/Lines.cs b/KLHZ.Trader.Core.Math/Common/Lines.cs index 7b4b7ca..4584703 100644 --- a/KLHZ.Trader.Core.Math/Common/Lines.cs +++ b/KLHZ.Trader.Core.Math/Common/Lines.cs @@ -10,10 +10,10 @@ return (x, y); } - public static bool IsLinesCrossing(DateTime time1, DateTime time2, decimal val1_1, decimal val1_2, decimal val2_1, decimal val2_2) + public static (bool res,DateTime x, decimal y) IsLinesCrossing(DateTime time1, DateTime time2, decimal val1_1, decimal val1_2, decimal val2_1, decimal val2_2) { var dtime = (decimal)(time2 - time1).TotalSeconds; - if (dtime == 0) return false; + if (dtime == 0) return (false, DateTime.MinValue,0); var dval1 = val1_2 - val1_1; var k1 = dval1 / dtime; var b1 = val1_1; @@ -28,10 +28,10 @@ if (cross.x >= 0 && cross.x <= dtime) { var crossingTimestamp = time1.AddSeconds((double)cross.x); - return crossingTimestamp >= time1 && crossingTimestamp <= time2; + return (crossingTimestamp >= time1 && crossingTimestamp <= time2, crossingTimestamp, cross.y); } } - return false; + return (false, DateTime.MinValue, 0); } } } diff --git a/KLHZ.Trader.Core.Math/Declisions/Utils/MovingAverage.cs b/KLHZ.Trader.Core.Math/Declisions/Utils/MovingAverage.cs index ac509ea..ea7ac40 100644 --- a/KLHZ.Trader.Core.Math/Declisions/Utils/MovingAverage.cs +++ b/KLHZ.Trader.Core.Math/Declisions/Utils/MovingAverage.cs @@ -25,7 +25,6 @@ namespace KLHZ.Trader.Core.Math.Declisions.Utils var res = TradingEvent.None; var bigWindowAv = 0m; var smallWindowAv = 0m; - var s = 0; try { var pricesForFinalComparison = new decimal[size]; @@ -33,10 +32,12 @@ namespace KLHZ.Trader.Core.Math.Declisions.Utils var twavss = new decimal[size]; var twavbs = new decimal[size]; var times = new DateTime[size]; - + var areas = new List(); + var sign = 1d; + var areaValue = 0d; + var crossingsCounter = 1; for (int shift = 0; shift < size - 1 && shift < prices.Length - 1; shift++) { - s = shift; var i2 = size - 1 - shift; var i1 = size - 2 - shift; @@ -49,9 +50,14 @@ namespace KLHZ.Trader.Core.Math.Declisions.Utils bigWindowAv = twavb.value; smallWindowAv = twavs.value; } + twavss[i2] = twavs.value; twavbs[i2] = twavb.value; times[i2] = twavb.time; + + var debugArrayB = twavbs.Reverse().ToArray(); + var debugArrayS = twavss.Reverse().ToArray(); + var debugArrayT = times.Reverse().ToArray(); if (System.Math.Abs(twavb.value - prices[prices.Length - 1]) > 2 * meanfullStep) { res |= TradingEvent.StopBuy; @@ -67,12 +73,40 @@ namespace KLHZ.Trader.Core.Math.Declisions.Utils twavbs[i1 + 1], twavbs[i2 + 1]); - if (shift == 1 && !isCrossing) //если нет пересечения скользящих средний с окном 120 и 15 секунд между + if (shift == 1 && !isCrossing.res) //если нет пересечения скользящих средний с окном 120 и 15 секунд между //текущей и предыдущей точкой - можно не продолжать выполнение. { break; } - if (shift > 1 && isCrossing) + + if (shift == 1 && isCrossing.res)//обработка ситуации когда одна из точек - совпадение + { + // если фильтрация окном 120 наползает на окно 15 сверху, потенциальное время открытия лонга и закрытия шорта + //if (twavbs[size - 1] <= twavss[size - 1] && twavbs[size - 2] > twavss[size - 2]) + //{ + // sign = -1d; + //} + //// если фильтрация окном 15 наползает на окно 120 сверху, потенциальное время закрытия лонга и возможно открытия шорта + //if (twavss[size - 1] <= twavbs[size - 1] && twavss[size - 2] > twavbs[size - 2]) + //{ + // sign = 1d; + //} + // если фильтрация окном 120 наползает на окно 15 сверху, потенциальное время открытия лонга и закрытия шорта + if ((debugArrayS[0] == debugArrayB[0] && debugArrayB[1] > debugArrayS[1]) + || (debugArrayB[0] < debugArrayS[0] && debugArrayB[1] > debugArrayS[1])) + { + sign = -1d; + } + + // если фильтрация окном 15 наползает на окно 120 снизу, потенциальное время закрытия лонга и возможно открытия шорта + if ((debugArrayS[0] == debugArrayB[0] && debugArrayS[1] < debugArrayB[1]) + || (debugArrayS[0] > debugArrayB[0] && debugArrayS[1] < debugArrayB[1])) + { + sign = 1d; + } + } + + if (shift > 1 && isCrossing.res) { // если фильтрация окном 120 наползает на окно 15 сверху, потенциальное время открытия лонга и закрытия шорта if (twavbs[size - 1] <= twavss[size - 1] && twavbs[size - 2] > twavss[size - 2]) @@ -86,7 +120,8 @@ namespace KLHZ.Trader.Core.Math.Declisions.Utils } // если фильтрация окном 15 наползает на окно 120 сверху, потенциальное время закрытия лонга и возможно открытия шорта - if (twavss[size - 1] <= twavbs[size - 1] && twavss[size - 2] > twavbs[size - 2]) + if ((debugArrayS[0] == debugArrayB[0] && debugArrayS[1] < debugArrayB[1]) + || (debugArrayS[0] > debugArrayB[0] && debugArrayS[1] < debugArrayB[1])) { if (pricesForFinalComparison[i2 + 1] - pricesForFinalComparison[size - 1] <= -meanfullStep && timesForFinalComparison[size - 1] - timesForFinalComparison[i2 + 1] >= timeForUptreandStart) @@ -96,6 +131,26 @@ namespace KLHZ.Trader.Core.Math.Declisions.Utils break; } } + + if (shift > 1) + { + if (isCrossing.res) + { + var ar = ShapeAreaCalculator.CalculateTriangleArea( + isCrossing.x, isCrossing.y, times[i1 + 1], twavss[i1 + 1], times[i1 + 1], twavbs[i1 + 1]); + areas.Add(ar); + areaValue += sign* areas.Sum(); + sign *= -1; + areas.Clear(); + crossingsCounter++; + } + else + { + var ar = ShapeAreaCalculator.CalculateQuadrilateralArea( + times[i1 + 1], twavss[i1 + 1], times[i1 + 1], twavbs[i1 + 1], times[i2 + 1], twavss[i2 + 1], times[i2 + 1], twavbs[i2 + 1]); + areas.Add(ar); + } + } } } } diff --git a/KLHZ.Trader.Core.Math/Declisions/Utils/ShapeAreaCalculator.cs b/KLHZ.Trader.Core.Math/Declisions/Utils/ShapeAreaCalculator.cs new file mode 100644 index 0000000..712ca8a --- /dev/null +++ b/KLHZ.Trader.Core.Math/Declisions/Utils/ShapeAreaCalculator.cs @@ -0,0 +1,69 @@ +using System; + +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)); + } + } +} diff --git a/KLHZ.Trader.Core.Tests/LinesProcessingTest.cs b/KLHZ.Trader.Core.Tests/LinesProcessingTest.cs index 888e5d3..e31c80c 100644 --- a/KLHZ.Trader.Core.Tests/LinesProcessingTest.cs +++ b/KLHZ.Trader.Core.Tests/LinesProcessingTest.cs @@ -16,7 +16,7 @@ namespace KLHZ.Trader.Core.Tests var val2_1 = -0.5m; var val2_2 = 0.5m; - Assert.IsTrue(Lines.IsLinesCrossing(time1, time2, val1_1, val1_2, val2_1, val2_2)); + Assert.IsTrue(Lines.IsLinesCrossing(time1, time2, val1_1, val1_2, val2_1, val2_2).res); } [Test] @@ -31,7 +31,7 @@ namespace KLHZ.Trader.Core.Tests var val2_1 = 0.5m; var val2_2 = -0.5m; - Assert.IsFalse(Lines.IsLinesCrossing(time1, time2, val1_1, val1_2, val2_1, val2_2)); + Assert.IsFalse(Lines.IsLinesCrossing(time1, time2, val1_1, val1_2, val2_1, val2_2).res); } } } \ No newline at end of file diff --git a/KLHZ.Trader.Core/Exchange/Services/Trader.cs b/KLHZ.Trader.Core/Exchange/Services/Trader.cs index 36a0f9e..849fdfc 100644 --- a/KLHZ.Trader.Core/Exchange/Services/Trader.cs +++ b/KLHZ.Trader.Core/Exchange/Services/Trader.cs @@ -91,8 +91,8 @@ namespace KLHZ.Trader.Core.Exchange.Services try { await _tradeDataProvider.AddData(message, TimeSpan.FromHours(7)); - await ProcessDeferredLongOpens(message, currentTime); - await ProcessDeferredLongCloses(message, currentTime); + //await ProcessDeferredLongOpens(message, currentTime); + //await ProcessDeferredLongCloses(message, currentTime); if (message.Figi == "FUTIMOEXF000") { var windowMaxSize = 1000; @@ -146,7 +146,7 @@ namespace KLHZ.Trader.Core.Exchange.Services INewPrice message, int windowMaxSize) { var res = TradingEvent.None; - var resultMoveAvFull = MovingAverage.CheckByWindowAverageMean(data.timestamps, data.prices, windowMaxSize, 45, 180, TimeSpan.FromSeconds(30), 1m); + var resultMoveAvFull = MovingAverage.CheckByWindowAverageMean(data.timestamps, data.prices, windowMaxSize, 25, 120, TimeSpan.FromSeconds(20), 1m); //var resultLongClose = MovingAverage.CheckByWindowAverageMean(data.timestamps, data.prices, windowMaxSize, 15, 120, 1.5m).events; //ar uptrendStarts = LocalTrends.CheckByLocalTrends(data.timestamps, data.prices, TimeSpan.FromSeconds(120), TimeSpan.FromSeconds(20), 1.5m, 15); diff --git a/KLHZ.Trader.Core/Exchange/Services/TraderDataProvider.cs b/KLHZ.Trader.Core/Exchange/Services/TraderDataProvider.cs index 9a8cb14..94a8ae1 100644 --- a/KLHZ.Trader.Core/Exchange/Services/TraderDataProvider.cs +++ b/KLHZ.Trader.Core/Exchange/Services/TraderDataProvider.cs @@ -108,7 +108,7 @@ namespace KLHZ.Trader.Core.Exchange.Services public async Task Init() { - await _initSemaphore.WaitAsync(TimeSpan.FromSeconds(15)); + await _initSemaphore.WaitAsync(TimeSpan.FromSeconds(3)); try { var shares = await _investApiClient.Instruments.SharesAsync();