diff --git a/KLHZ.Trader.Core.Math/Declisions/Utils/MovingAverage.cs b/KLHZ.Trader.Core.Math/Declisions/Utils/MovingAverage.cs index c82feee..575c746 100644 --- a/KLHZ.Trader.Core.Math/Declisions/Utils/MovingAverage.cs +++ b/KLHZ.Trader.Core.Math/Declisions/Utils/MovingAverage.cs @@ -18,78 +18,86 @@ namespace KLHZ.Trader.Core.Math.Declisions.Utils return (startTime, sum / count); } - public static TradingEvent CheckByWindowAverageMean(DateTime[] timestamps, decimal[] prices, int size, int smallWindow, int bigWindow, decimal meanfullStep = 3m) + public static (TradingEvent events, decimal bigWindowAv, decimal smallWindowAv) CheckByWindowAverageMean(DateTime[] timestamps, decimal[] prices, int size, int smallWindow, int bigWindow, decimal meanfullStep = 3m) { var res = TradingEvent.None; - if (timestamps.Length < size) + var bigWindowAv = 0m; + var smallWindowAv = 0m; + + try { - return res; - } - var pricesForFinalComparison = new decimal[size]; - var twavss = new decimal[size]; - var twavbs = new decimal[size]; - var times = new DateTime[size]; + var pricesForFinalComparison = new decimal[size]; + var twavss = new decimal[size]; + var twavbs = new decimal[size]; + var times = new DateTime[size]; - - for (int shift = 0; shift < size; shift++) - { - var twavs = CalcTimeWindowAverageValue(timestamps, prices, smallWindow, shift); - var twavb = CalcTimeWindowAverageValue(timestamps, prices, bigWindow, shift); - pricesForFinalComparison[size - 1 - shift] = prices[prices.Length - 1 - shift]; - twavss[size - 1 - shift] = twavs.value; - twavbs[size - 1 - shift] = twavb.value; - times[size - 1 - shift] = twavb.time; - if (System.Math.Abs(twavb.value - prices[prices.Length - 1]) > 2 * meanfullStep) + for (int shift = 0; shift < size; shift++) { - res |= TradingEvent.StopBuy; - return res; - } - if (shift > 0) - { - var i1 = size - 1 - shift; - var i2 = size - shift; - var isCrossing = Lines.IsLinesCrossing( - times[i1], - times[i2], - twavss[i1], - twavss[i2], - twavbs[i1], - twavbs[i2]); - - if (shift == 1 && !isCrossing) //если нет пересечения скользящих средний с окном 120 и 15 секунд между - //текущей и предыдущей точкой - можно не продолжать выполнение. + var twavs = CalcTimeWindowAverageValue(timestamps, prices, smallWindow, shift); + var twavb = CalcTimeWindowAverageValue(timestamps, prices, bigWindow, shift); + pricesForFinalComparison[size - 1 - shift] = prices[prices.Length - 1 - shift]; + if (shift == 0) { - break; + bigWindowAv = twavb.value; + smallWindowAv = twavs.value; } - if (shift > 1 && isCrossing) + twavss[size - 1 - shift] = twavs.value; + twavbs[size - 1 - shift] = twavb.value; + times[size - 1 - shift] = twavb.time; + if (System.Math.Abs(twavb.value - prices[prices.Length - 1]) > 2 * meanfullStep) { - // если фильтрация окном 120 наползает на окно 15 сверху, потенциальное время открытия лонга и закрытия шорта - if (twavbs[size - 1] <= twavss[size - 1] && twavbs[size - 2] > twavss[size - 2]) - { - if (pricesForFinalComparison[size - 1 - shift] - pricesForFinalComparison[size - 1] >= meanfullStep) - { - res |= TradingEvent.LongOpen; - res |= TradingEvent.ShortClose; - break; - } - } + res |= TradingEvent.StopBuy; + return (res, bigWindowAv, smallWindowAv); + } + if (shift > 0) + { + var i1 = size - 1 - shift; + var i2 = size - shift; + var isCrossing = Lines.IsLinesCrossing( + times[i1], + times[i2], + twavss[i1], + twavss[i2], + twavbs[i1], + twavbs[i2]); - // если фильтрация окном 15 наползает на окно 120 сверху, потенциальное время закрытия лонга и возможно открытия шорта - if (twavss[size - 1] <= twavbs[size - 1] && twavss[size - 2] > twavbs[size - 2]) + if (shift == 1 && !isCrossing) //если нет пересечения скользящих средний с окном 120 и 15 секунд между + //текущей и предыдущей точкой - можно не продолжать выполнение. { - if (pricesForFinalComparison[size - 1 - shift] - pricesForFinalComparison[size - 1] <= -meanfullStep) + break; + } + if (shift > 1 && isCrossing) + { + // если фильтрация окном 120 наползает на окно 15 сверху, потенциальное время открытия лонга и закрытия шорта + if (twavbs[size - 1] <= twavss[size - 1] && twavbs[size - 2] > twavss[size - 2]) { - res |= TradingEvent.LongClose; - res |= TradingEvent.ShortOpen; - break; + if (pricesForFinalComparison[size - 1 - shift] - pricesForFinalComparison[size - 1] >= meanfullStep) + { + res |= TradingEvent.LongOpen; + res |= TradingEvent.ShortClose; + break; + } + } + + // если фильтрация окном 15 наползает на окно 120 сверху, потенциальное время закрытия лонга и возможно открытия шорта + if (twavss[size - 1] <= twavbs[size - 1] && twavss[size - 2] > twavbs[size - 2]) + { + if (pricesForFinalComparison[size - 1 - shift] - pricesForFinalComparison[size - 1] <= -meanfullStep) + { + res |= TradingEvent.LongClose; + res |= TradingEvent.ShortOpen; + break; + } } } } } } + catch (Exception ex) + { - return res; + } + return (res, bigWindowAv, smallWindowAv); } - } } diff --git a/KLHZ.Trader.Core/DataLayer/Entities/Declisions/Declision.cs b/KLHZ.Trader.Core/DataLayer/Entities/Declisions/Declision.cs index a3db8c6..de0e760 100644 --- a/KLHZ.Trader.Core/DataLayer/Entities/Declisions/Declision.cs +++ b/KLHZ.Trader.Core/DataLayer/Entities/Declisions/Declision.cs @@ -23,6 +23,10 @@ namespace KLHZ.Trader.Core.DataLayer.Entities.Declisions [Column("price")] public decimal Price { get; set; } + + [Column("value")] + public decimal? Value { get; set; } + [Column("action")] public DeclisionTradeAction Action { get; set; } } diff --git a/KLHZ.Trader.Core/Exchange/Services/Trader.cs b/KLHZ.Trader.Core/Exchange/Services/Trader.cs index 7493050..c529ef9 100644 --- a/KLHZ.Trader.Core/Exchange/Services/Trader.cs +++ b/KLHZ.Trader.Core/Exchange/Services/Trader.cs @@ -7,6 +7,7 @@ using KLHZ.Trader.Core.Contracts.Messaging.Interfaces; using KLHZ.Trader.Core.DataLayer; using KLHZ.Trader.Core.DataLayer.Entities.Declisions; using KLHZ.Trader.Core.DataLayer.Entities.Declisions.Enums; +using KLHZ.Trader.Core.DataLayer.Entities.Prices; using KLHZ.Trader.Core.Exchange.Extentions; using KLHZ.Trader.Core.Exchange.Models; using KLHZ.Trader.Core.Math.Declisions.Services.Cache; @@ -115,6 +116,8 @@ namespace KLHZ.Trader.Core.Exchange.Services private async Task ProcessPrices() { + var declisionsForSave = new List(); + var processedPrices = new List(); while (await _pricesChannel.Reader.WaitToReadAsync()) { var message = await _pricesChannel.Reader.ReadAsync(); @@ -132,7 +135,7 @@ namespace KLHZ.Trader.Core.Exchange.Services try { var data = await unit.GetData(); - var declisionsForSave = new List(); + if (message.Figi == "FUTIMOEXF000") { if (unit.Length < 100) @@ -140,9 +143,58 @@ namespace KLHZ.Trader.Core.Exchange.Services continue; } + if (BuyStops.TryGetValue(message.Figi, out var dt)) + { + if (dt > (message.IsHistoricalData ? message.Time : DateTime.UtcNow)) + { + continue; + } + else + { + BuyStops.TryRemove(message.Figi, out _); + } + } + + if ((unit.BidsCount / unit.AsksCount) < 0.5m || (unit.BidsCount / unit.AsksCount) > 2m) + { + var stopTo = (message.IsHistoricalData ? message.Time : DateTime.UtcNow).AddMinutes(3); + BuyStops.AddOrUpdate(message.Figi, stopTo, (k, v) => stopTo); + declisionsForSave.Add(new Declision() + { + AccountId = string.Empty, + Figi = message.Figi, + Ticker = message.Ticker, + Price = message.Value, + Value = 3, + Time = message.IsHistoricalData ? message.Time : DateTime.UtcNow, + Action = DeclisionTradeAction.StopBuy, + }); + } var result = MovingAverage.CheckByWindowAverageMean(data.timestamps, data.prices, 100, 15, 120); - if ((result & TradingEvent.StopBuy) == TradingEvent.StopBuy) + if (result.bigWindowAv != 0) + { + var priceb = new ProcessedPrice() + { + Figi = message.Figi, + Ticker = message.Ticker, + Processor = nameof(Trader) + "_big", + Time = message.Time, + Value = result.bigWindowAv, + }; + + var prices = new ProcessedPrice() + { + Figi = message.Figi, + Ticker = message.Ticker, + Processor = nameof(Trader) + "_small", + Time = message.Time, + Value = result.smallWindowAv, + }; + processedPrices.Add(priceb); + processedPrices.Add(prices); + } + if ((result.events & TradingEvent.StopBuy) == TradingEvent.StopBuy) { var stopTo = (message.IsHistoricalData ? message.Time : DateTime.UtcNow).AddMinutes(_buyStopLength); BuyStops.AddOrUpdate(message.Figi, stopTo, (k, v) => stopTo); @@ -152,26 +204,15 @@ namespace KLHZ.Trader.Core.Exchange.Services Figi = message.Figi, Ticker = message.Ticker, Price = message.Value, + Value = (decimal)_buyStopLength, Time = message.IsHistoricalData ? message.Time : DateTime.UtcNow, Action = DeclisionTradeAction.StopBuy, }); } - if ((result & TradingEvent.LongOpen) == TradingEvent.LongOpen + if ((result.events & TradingEvent.LongOpen) == TradingEvent.LongOpen && ((unit.BidsCount / unit.AsksCount) > 0.5m)) { - if (BuyStops.TryGetValue(message.Figi, out var dt)) - { - if (dt > (message.IsHistoricalData ? message.Time : DateTime.UtcNow)) - { - continue; - } - else - { - BuyStops.TryRemove(message.Figi, out _); - } - } - declisionsForSave.Add(new Declision() { AccountId = string.Empty, @@ -183,7 +224,7 @@ namespace KLHZ.Trader.Core.Exchange.Services }); } - if ((result & TradingEvent.LongClose) == TradingEvent.LongClose) + if ((result.events & TradingEvent.LongClose) == TradingEvent.LongClose) { declisionsForSave.Add(new Declision() { @@ -196,7 +237,7 @@ namespace KLHZ.Trader.Core.Exchange.Services }); } - if ((result & TradingEvent.ShortOpen) == TradingEvent.ShortOpen && (unit.BidsCount / unit.AsksCount < 2)) + if ((result.events & TradingEvent.ShortOpen) == TradingEvent.ShortOpen && (unit.BidsCount / unit.AsksCount < 2)) { declisionsForSave.Add(new Declision() { @@ -209,7 +250,7 @@ namespace KLHZ.Trader.Core.Exchange.Services }); } - if ((result & TradingEvent.ShortClose) == TradingEvent.ShortClose) + if ((result.events & TradingEvent.ShortClose) == TradingEvent.ShortClose) { declisionsForSave.Add(new Declision() { @@ -222,14 +263,23 @@ namespace KLHZ.Trader.Core.Exchange.Services }); } - if (declisionsForSave.Count > 0) + if ((!message.IsHistoricalData && (processedPrices.Count > 0 || declisionsForSave.Count > 0)) + || (message.IsHistoricalData && ((processedPrices.Count + declisionsForSave.Count > 10000) || _pricesChannel.Reader.Count == 0))) { using var context = await _dbContextFactory.CreateDbContextAsync(); context.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking; - await context.AddRangeAsync(declisionsForSave); + if (processedPrices.Count > 0) + { + await context.ProcessedPrices.AddRangeAsync(processedPrices); + processedPrices.Clear(); + } + if (declisionsForSave.Count > 0) + { + await context.Declisions.AddRangeAsync(declisionsForSave); + declisionsForSave.Clear(); + } await context.SaveChangesAsync(); - declisionsForSave.Clear(); } } } @@ -238,6 +288,11 @@ namespace KLHZ.Trader.Core.Exchange.Services } } + + if (_pricesChannel.Reader.Count == 0) + { + + } } } diff --git a/KLHZ.Trader.Infrastructure/postgres/migration4.sql b/KLHZ.Trader.Infrastructure/postgres/migration4.sql new file mode 100644 index 0000000..60adb29 --- /dev/null +++ b/KLHZ.Trader.Infrastructure/postgres/migration4.sql @@ -0,0 +1 @@ +alter table declisions add column value decimal default null; \ No newline at end of file diff --git a/KLHZ.Trader.Service/appsettings.json b/KLHZ.Trader.Service/appsettings.json index aec7101..3894cfb 100644 --- a/KLHZ.Trader.Service/appsettings.json +++ b/KLHZ.Trader.Service/appsettings.json @@ -12,7 +12,7 @@ "ExchangeDataRecievingEnabled": true, "Token": "", "ManagingAccountNamePatterns": [ "автотрейд 1" ], - "DataRecievingInstrumentsFigis": [ "BBG004730N88", "FUTIMOEXF000", "FUTGMKN09250", "FUTBR1025000" ], + "DataRecievingInstrumentsFigis": [ "BBG004730N88", "FUTIMOEXF000", "FUTGMKN09250", "FUTBR1025000", "FUTNG0925000" ], "TradingInstrumentsFigis": [ "FUTIMOEXF000" ], "FutureComission": 0.0025, "ShareComission": 0.0004,