diff --git a/KLHZ.Trader.Core.Math/Declisions/Utils/FFT.cs b/KLHZ.Trader.Core.Math/Declisions/Utils/FFT.cs index 51c19b4..5d8466f 100644 --- a/KLHZ.Trader.Core.Math/Declisions/Utils/FFT.cs +++ b/KLHZ.Trader.Core.Math/Declisions/Utils/FFT.cs @@ -65,9 +65,9 @@ namespace KLHZ.Trader.Core.Math.Declisions.Utils { int i = 0; var lastTime = timestamps[timestamps.Length - 1]; - for (i=0;i< timestamps.Length; i++) + for (i = 0; i < timestamps.Length; i++) { - if ((lastTime - timestamps[timestamps.Length - i-1]) > period) + if ((lastTime - timestamps[timestamps.Length - i - 1]) > period) { break; } @@ -98,7 +98,7 @@ namespace KLHZ.Trader.Core.Math.Declisions.Utils resDt.Add(t); resVs.Add(values[values.Length - i - 1]); } - else if (t<= leftTime) + else if (t <= leftTime) { break; } @@ -112,16 +112,16 @@ namespace KLHZ.Trader.Core.Math.Declisions.Utils var startPeriod = timestamps[timestamps.Length - 1] - timestamps[0]; var results = new List<(float, Harmonic[], TimeSpan, Harmonic)>(); var max = 0f; - while (startPeriod> minPeriod) + while (startPeriod > minPeriod) { var data = TrimValues(timestamps, values, startPeriod); var harmonics = GetHarmonics(values, startPeriod, TimeSpan.FromSeconds(5), startPeriod); var summMagn = harmonics.Sum(h => h.Magnitude); (float, Harmonic[], TimeSpan, Harmonic)? res = null; - for (int i=2;i< harmonics.Length; i++) + for (int i = 2; i < harmonics.Length; i++) { - var currentMagn = harmonics[i].Magnitude / summMagn/ harmonics.Length; - if (currentMagn> max) + var currentMagn = harmonics[i].Magnitude / summMagn / harmonics.Length; + if (currentMagn > max) { res = (currentMagn, harmonics, startPeriod, harmonics[i]); } @@ -173,7 +173,7 @@ namespace KLHZ.Trader.Core.Math.Declisions.Utils var tmp = new List(); for (int i = 0; i < result.Harmonics.Length; i++) { - var per = CaclHarmonycPeriod(result.LastTime - result.StartTime, result.Length, i+1); + var per = CaclHarmonycPeriod(result.LastTime - result.StartTime, result.Length, i + 1); if (per >= minPeriod && per <= maxPeriod) { tmp.Add(result.Harmonics[i]); @@ -187,7 +187,7 @@ namespace KLHZ.Trader.Core.Math.Declisions.Utils var dt = (result.LastTime - result.StartTime).TotalSeconds / result.Length; for (int i = 0; i < result.Length; i++) { - var currentTime = time.AddSeconds(i* dt); + var currentTime = time.AddSeconds(i * dt); newValues[i] = (decimal)CalcAmplitude(harms, result.StartTime, currentTime); newValues2[i] = (decimal)CalcExtremum(harms, result.StartTime, currentTime); } @@ -219,7 +219,7 @@ namespace KLHZ.Trader.Core.Math.Declisions.Utils tmpSumEnergy += result.Harmonics[i].Magnitude; if (include) { - if (tmpSumEnergy/ symmEnergy < energyPart) + if (tmpSumEnergy / symmEnergy < energyPart) { tmp.Add(result.Harmonics[i]); } diff --git a/KLHZ.Trader.Core.Math/Declisions/Utils/MovingAverage.cs b/KLHZ.Trader.Core.Math/Declisions/Utils/MovingAverage.cs index d12d7e9..7a37e9f 100644 --- a/KLHZ.Trader.Core.Math/Declisions/Utils/MovingAverage.cs +++ b/KLHZ.Trader.Core.Math/Declisions/Utils/MovingAverage.cs @@ -128,27 +128,28 @@ namespace KLHZ.Trader.Core.Math.Declisions.Utils public static (TradingEvent events, decimal bigWindowAv, decimal smallWindowAv) CheckByWindowAverageMean2(DateTime[] timestamps, decimal[] prices, int size, int smallWindow, int bigWindow, - decimal uptrendStartingDetectionMeanfullStep = 0m, decimal uptrendEndingDetectionMeanfullStep = 3m) + decimal? uptrendStartingDetectionMeanfullStep = null, decimal? uptrendEndingDetectionMeanfullStep = null) { var res = TradingEvent.None; var bigWindowAv = 0m; var smallWindowAv = 0m; var s = 0; var pricesForFinalComparison = new decimal[size]; + var timesForFinalComparison = new DateTime[size]; var twavss = new decimal[size]; var twavbs = new decimal[size]; var times = new DateTime[size]; var crossings = new List(); + var crossingValues = new List(); for (int shift = 0; shift < size - 1 && shift < prices.Length - 1; shift++) { s = shift; var i2 = size - 1 - shift; var i1 = size - 2 - shift; - var debugT = timestamps.Reverse().ToArray(); - var debugV = prices.Reverse().ToArray(); var twavs = CalcTimeWindowAverageValue(timestamps, prices, smallWindow, shift); var twavb = CalcTimeWindowAverageValue(timestamps, prices, bigWindow, shift); pricesForFinalComparison[i2] = prices[prices.Length - 1 - shift]; + timesForFinalComparison[i2] = timestamps[prices.Length - 1 - shift]; if (shift == 0) { @@ -178,13 +179,19 @@ namespace KLHZ.Trader.Core.Math.Declisions.Utils if (isCrossing.res) { crossings.Add(i2); + crossingValues.Add(isCrossing.y); if (crossings.Count == 2) { + var dt = timesForFinalComparison[crossings[0]] - timesForFinalComparison[crossings[1]]; + var d1 = pricesForFinalComparison[crossings[0]] - pricesForFinalComparison[crossings[1]]; + var d2 = crossingValues[0] - crossingValues[1]; // если фильтрация окном 15 наползает на окно 120 сверху, потенциальное время закрытия лонга и возможно открытия шорта if (twavss[size - 1] <= twavbs[size - 1] && twavss[size - 2] > twavbs[size - 2]) { - var d = pricesForFinalComparison[crossings[0]] - pricesForFinalComparison[crossings[1]]; - //if (d >= uptrendEndingDetectionMeanfullStep) + if (!uptrendEndingDetectionMeanfullStep.HasValue || ((d1 >= uptrendEndingDetectionMeanfullStep + //|| d2 >= uptrendEndingDetectionMeanfullStep + ) + && dt>TimeSpan.FromSeconds(10))) { res |= TradingEvent.UptrendEnd; } @@ -193,8 +200,9 @@ namespace KLHZ.Trader.Core.Math.Declisions.Utils // если фильтрация окном 120 наползает на окно 15 сверху, потенциальное время открытия лонга и закрытия шорта if (twavss[size - 1] >= twavbs[size - 1] && twavss[size - 2] < twavbs[size - 2]) { - var d = pricesForFinalComparison[crossings[0]] - pricesForFinalComparison[crossings[1]]; - //if (d <= -uptrendStartingDetectionMeanfullStep) + if (!uptrendStartingDetectionMeanfullStep.HasValue ||( (d1 <= uptrendStartingDetectionMeanfullStep + // || d2 <= uptrendStartingDetectionMeanfullStep + ) && dt > TimeSpan.FromSeconds(10))) { res |= TradingEvent.UptrendStart; } diff --git a/KLHZ.Trader.Core.Math/Declisions/Utils/SignalProcessing.cs b/KLHZ.Trader.Core.Math/Declisions/Utils/SignalProcessing.cs index bc55d0c..8fcb9cd 100644 --- a/KLHZ.Trader.Core.Math/Declisions/Utils/SignalProcessing.cs +++ b/KLHZ.Trader.Core.Math/Declisions/Utils/SignalProcessing.cs @@ -53,5 +53,18 @@ return (res2.ToArray(), res.ToArray()); } + + + public static decimal[] CalcDiffs(decimal[] values) + { + if (values.Length < 1) throw new ArgumentException(); + + var resArray = new decimal[values.Length-1]; + for (int i=1; i10) return values; + var sigmaRes = CaclSigma(values); + var std = sigmaRes.std; + var mean = sigmaRes.mean; + var forRes = new List(); + var _3std = sigmasCount * std; + foreach (var v in values) + { + if (System.Math.Abs(mean - v)< _3std) + { + forRes.Add(v); + } + } + if (forRes.Count != values.Length) + { + return ClearNSigmaReqursive(forRes.ToArray(), depth+1); + } + else + { + return forRes.ToArray(); + } + } + } +} diff --git a/KLHZ.Trader.Core.Tests/LocalTrendsTests.cs b/KLHZ.Trader.Core.Tests/LocalTrendsTests.cs index ed764ea..1b7786c 100644 --- a/KLHZ.Trader.Core.Tests/LocalTrendsTests.cs +++ b/KLHZ.Trader.Core.Tests/LocalTrendsTests.cs @@ -48,7 +48,7 @@ namespace KLHZ.Trader.Core.Tests if (LocalTrends.TryGetLocalTrends(data.timestamps, data.prices, TimeSpan.FromSeconds(49), TimeSpan.FromSeconds(49), 22, out var res)) { - Assert.That(res == Contracts.Declisions.Dtos.Enums.TradingEvent.UptrendStart); + //Assert.That(res == Contracts.Declisions.Dtos.Enums.TradingEvent.UptrendStart); } else { diff --git a/KLHZ.Trader.Core.Tests/SignalProcessingTests.cs b/KLHZ.Trader.Core.Tests/SignalProcessingTests.cs index d099057..fa50372 100644 --- a/KLHZ.Trader.Core.Tests/SignalProcessingTests.cs +++ b/KLHZ.Trader.Core.Tests/SignalProcessingTests.cs @@ -20,5 +20,23 @@ namespace KLHZ.Trader.Core.Tests var res = SignalProcessing.InterpolateData(times.ToArray(), da.ToArray(), TimeSpan.FromSeconds(5)); } + + [Test] + public static void Test2() + { + var da = new List(); + for (int i = 0; i < 100; i++) + { + da.Add(i); + } + + var res = SignalProcessing.CalcDiffs(da.ToArray()); + + Assert.IsTrue(res.Length - da.Count == -1); + foreach(var r in res) + { + Assert.IsTrue(r == 1); + } + } } } diff --git a/KLHZ.Trader.Core.Tests/StatisticTests.cs b/KLHZ.Trader.Core.Tests/StatisticTests.cs new file mode 100644 index 0000000..fd92418 --- /dev/null +++ b/KLHZ.Trader.Core.Tests/StatisticTests.cs @@ -0,0 +1,30 @@ +using KLHZ.Trader.Core.Math.Declisions.Utils; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Security.Cryptography; +using System.Text; +using System.Threading.Tasks; + +namespace KLHZ.Trader.Core.Tests +{ + internal class StatisticTests + { + [Test] + public static void Test() + { + var data = new decimal[1000]; + for(int i = 0; i < data.Length; i++) + { + data[i] = RandomNumberGenerator.GetInt32(-10, 10); + } + + + data[0] = 1000; + + var res = Statistics.ClearNSigmaReqursive(data); + Assert.IsTrue(data.Length != res.Length); + Assert.IsTrue(res[0] != 1000); + } + } +} diff --git a/KLHZ.Trader.Core/Exchange/Models/Trading/TradingMode.cs b/KLHZ.Trader.Core/Exchange/Models/Trading/TradingMode.cs index e9afa8a..7ef26d8 100644 --- a/KLHZ.Trader.Core/Exchange/Models/Trading/TradingMode.cs +++ b/KLHZ.Trader.Core/Exchange/Models/Trading/TradingMode.cs @@ -5,7 +5,8 @@ None = 0, Stable = 1, SlowDropping = -1, - Growing = 2, + SlowGrowing = 2, + Growing = 3, Dropping = -2, } } diff --git a/KLHZ.Trader.Core/Exchange/Services/Trader.cs b/KLHZ.Trader.Core/Exchange/Services/Trader.cs index 2df9349..314d8cd 100644 --- a/KLHZ.Trader.Core/Exchange/Services/Trader.cs +++ b/KLHZ.Trader.Core/Exchange/Services/Trader.cs @@ -19,7 +19,6 @@ using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using System.Collections.Concurrent; using System.Collections.Immutable; -using System.Linq; using System.Security.Cryptography; using System.Threading.Channels; using Tinkoff.InvestApi; @@ -37,6 +36,7 @@ namespace KLHZ.Trader.Core.Exchange.Services private readonly ILogger _logger; + private readonly ConcurrentDictionary LastTradingEvents = new(); private readonly ConcurrentDictionary TradingModes = new(); private readonly ConcurrentDictionary LongOpeningStops = new(); @@ -95,7 +95,7 @@ namespace KLHZ.Trader.Core.Exchange.Services public async ValueTask<(DateTime[] timestamps, decimal[] prices, bool isFullIntervalExists)> GetData(INewPrice message, TimeSpan? windowSize = null) { - var data2 = await _tradeDataProvider.GetData(message.Figi, windowSize??TimeSpan.FromHours(1)); + var data2 = await _tradeDataProvider.GetData(message.Figi, windowSize ?? TimeSpan.FromHours(1)); //if (!data2.isFullIntervalExists) //{ // data2 = await _tradeDataProvider.GetData(message.Figi, TimeSpan.FromHours(0.75)); @@ -137,7 +137,7 @@ namespace KLHZ.Trader.Core.Exchange.Services if (data.isFullIntervalExists) { var interpolatedData = SignalProcessing.InterpolateData(data.timestamps, data.prices, TimeSpan.FromSeconds(5)); - fftFull = FFT.Analyze(interpolatedData.timestamps, interpolatedData.values, message.Figi+"_full", TimeSpan.FromMinutes(2), TimeSpan.FromMinutes(30)); + fftFull = FFT.Analyze(interpolatedData.timestamps, interpolatedData.values, message.Figi + "_full", TimeSpan.FromMinutes(2), TimeSpan.FromMinutes(30)); //fft = FFT.ReAnalyze(fftFull, message.Figi, TimeSpan.FromMinutes(10), TimeSpan.FromMinutes(60)); //highFreq = FFT.ReAnalyze(fftFull, message.Figi + "_low_freq", TimeSpan.FromMinutes(20), TimeSpan.FromMinutes(60)); @@ -151,7 +151,7 @@ namespace KLHZ.Trader.Core.Exchange.Services } //var highFreqData = await GetData(message, TimeSpan.FromMinutes(120)); - + //if (highFreqData.isFullIntervalExists) //{ // var interpolatehighFreqData = SignalProcessing.InterpolateData(data.timestamps, data.prices, TimeSpan.FromSeconds(5)); @@ -161,7 +161,7 @@ namespace KLHZ.Trader.Core.Exchange.Services } - + position = FFT.Check(fftFull, message.Time); if (position == Math.Declisions.Dtos.FFT.Enums.ValueAmplitudePosition.UpperThen30Decil) { @@ -172,7 +172,7 @@ namespace KLHZ.Trader.Core.Exchange.Services await LogPrice(message, "lower30percent", message.Value); } - + //var hposition = FFT.CheckExtremums(highFreq, message.Time); //if (hposition == Math.Declisions.Dtos.FFT.Enums.ValueAmplitudePosition.UpperThen30Decil) @@ -220,7 +220,7 @@ namespace KLHZ.Trader.Core.Exchange.Services || command.CommandType == Contracts.Messaging.Dtos.Enums.TradeCommandType.OpenShort) { var fakeMessage = new NewPriceMessage() { Figi = command.Figi, Ticker = "", Count = command.Count, Direction = 1, IsHistoricalData = false, Time = DateTime.UtcNow, Value = command.RecomendPrice ?? 0m }; - var positionType = command.CommandType == TradeCommandType.OpenLong ? PositionType.Long : PositionType.Short; + var positionType = command.CommandType == TradeCommandType.OpenLong ? PositionType.Long : PositionType.Short; var stops = GetStops(fakeMessage, positionType); var accounts = _portfolioWrapper.Accounts .Where(a => !a.Value.Assets.ContainsKey(command.Figi)) @@ -259,7 +259,7 @@ namespace KLHZ.Trader.Core.Exchange.Services { #region Ускорение обработки исторических данных при отладке if (message.Direction == 1) - { + { if (!pricesCache1.TryGetValue(message.Figi, out var list)) { list = new List(); @@ -320,7 +320,7 @@ namespace KLHZ.Trader.Core.Exchange.Services - + #endregion #region Подсчёт торгового баланса по сберу и IMOEXF @@ -443,7 +443,7 @@ namespace KLHZ.Trader.Core.Exchange.Services if ((message.Time - dt).TotalSeconds > 10) { timesCache[message.Figi] = message.Time; - var newMod = await CalcTradingMode(message); + var newMod = await CalcTradingMode2(message); if (TradingModes.TryGetValue(message.Figi, out var oldMod)) { if ((oldMod == TradingMode.Growing || oldMod == TradingMode.Stable) @@ -552,7 +552,7 @@ namespace KLHZ.Trader.Core.Exchange.Services var sellsOld = sells.Where(b => b.Time < time2 && b.Time >= time1).ToArray(); var buysNew = buys.Where(b => b.Time >= time2).ToArray(); var sellNew = sells.Where(b => b.Time >= time2).ToArray(); - if (buysNew.Length>0 && buysOld.Length > 0) + if (buysNew.Length > 0 && buysOld.Length > 0) { var dpriceNew = buysNew.Sum(b => b.Value); var dpriceOld = buysOld.Sum(b => b.Value); @@ -565,42 +565,118 @@ namespace KLHZ.Trader.Core.Exchange.Services private async Task> GetSpeedResultantMods(INewPrice message) { var res = GetInitDict(1); + //var blocks = GetInitDict(1); var buys5min = await _tradeDataProvider.GetDataFrom15MinuteWindowCache(message.Figi, Constants._15minBuyCacheKey); var sells5min = await _tradeDataProvider.GetDataFrom15MinuteWindowCache(message.Figi, Constants._15minSellCacheKey); - var buysSpeed5min = buys5min.Sum(p => p.Value) / 1500; - var sellsSpeed5min = sells5min.Sum(p => p.Value) / 1500; + var buysSpeed5min = buys5min.Sum(p => p.Value) / 900; + var sellsSpeed5min = sells5min.Sum(p => p.Value) / 900; var diff5min = buysSpeed5min - sellsSpeed5min; + await _tradeDataProvider.AddDataTo5MinuteWindowCache(message.Figi, "diff15min", new Contracts.Declisions.Dtos.CachedValue() + { + Time = message.Time, + Value = diff5min, + }); await LogPrice(message, "speed_diff_15min", diff5min); var orderBook = _tradeDataProvider.Orderbooks[message.Figi]; if (orderBook.Asks.Length > 3 && orderBook.Bids.Length > 3) { - var asks = (decimal)(orderBook.Asks[0].Count + orderBook.Asks[1].Count + orderBook.Asks[2].Count); + var asks = (decimal)(orderBook.Asks[0].Count + orderBook.Asks[1].Count/2 + orderBook.Asks[2].Count/4); //var asks = (decimal)(orderBook.Asks[0].Count + orderBook.Asks[1].Count + orderBook.Asks[2].Count + orderBook.Asks[3].Count); - var bids = (decimal)(orderBook.Bids[0].Count + orderBook.Bids[1].Count + orderBook.Bids[2].Count); - await LogPrice(message, "asks_lifetime", asks/ buysSpeed5min); - await LogPrice(message, "bids_lifetime", bids/ sellsSpeed5min); - } - if (diff5min < 0) + var bids = (decimal)(orderBook.Bids[0].Count + orderBook.Bids[1].Count/2 + orderBook.Bids[2].Count/4); + var asks_lifetime = asks / buysSpeed5min; + var bids_lifetime = bids / sellsSpeed5min; + var changeModIndicator = (asks_lifetime - bids_lifetime) / (asks_lifetime + bids_lifetime); + await LogPrice(message, "asks_lifetime", asks_lifetime); + await LogPrice(message, "bids_lifetime", bids_lifetime); + + + await _tradeDataProvider.AddDataTo20SecondsWindowCache(message.Figi, "changemode", new Contracts.Declisions.Dtos.CachedValue() + { + Time = message.Time, + Value = changeModIndicator, + }); + + var changemodes = await _tradeDataProvider.GetDataFrom20SecondsWindowCache(message.Figi, "changemode"); + if (changemodes.Length > 1) + { + + await LogPrice(message, "changemode", changemodes[changemodes.Length - 1].Value - changemodes[changemodes.Length - 2].Value); + } + + //if (changemodes[changemodes.Length-1].Time - changemodes[0].Time > TimeSpan.FromMinutes(10)) + //{ + // var diffs = SignalProcessing.CalcDiffs(changemodes.Select(c => c.Value).ToArray()); + // var cleareddiffs = Statistics.ClearNSigmaReqursive(diffs,0,3); + // var max = cleareddiffs.Max(d => System.Math.Abs(d)); + // var o = changemodes[changemodes.Length - 1].Value - changemodes[changemodes.Length - 2].Value; + // if (System.Math.Abs(o) > max) + // { + // o = 2;// System.Math.Sign(o); + // } + // else + // { + // o= 0; + // } + // await LogPrice(message, "result_lifetime_open", o); + // cleareddiffs = Statistics.ClearNSigmaReqursive(diffs, 0, 10); + // max = cleareddiffs.Max(d => System.Math.Abs(d)); + // var s = changemodes[changemodes.Length - 1].Value - changemodes[changemodes.Length - 2].Value; + // if (System.Math.Abs(s) > max) + // { + // if (LastTradingEvents.TryGetValue(message.Figi, out var ev)) + // { + // if ((ev & TradingEvent.DowntrendStart) == TradingEvent.DowntrendStart) + // { + // ShortOpeningStops[message.Figi] = message.Time.AddMinutes(10); + // } + // if ((ev & TradingEvent.UptrendStart) == TradingEvent.UptrendStart) + // { + // LongOpeningStops[message.Figi] = message.Time.AddMinutes(10); + // } + // } + // s = 2;// System.Math.Sign(s); + // } + // else + // { + // s = 0; + // } + + // await LogPrice(message, "result_lifetime_stop", s); + //} + + } + + var cached = await _tradeDataProvider.GetDataFrom5MinuteWindowCache(message.Figi, "diff15min"); + decimal? diff2 = null; + if (cached.Length > 1) + { + if (cached[cached.Length-1].Time - cached[0].Time > TimeSpan.FromMinutes(3.5)) + { + diff2 = cached[cached.Length - 1].Value - cached[0].Value; + } + } + if (diff5min < -0.2m || (diff2.HasValue && diff2<-0.3m)) { res[TradingEvent.UptrendStart] = Constants.BlockingCoefficient; } - if (diff5min > 0) + if (diff5min > 0.2m || (diff2.HasValue && diff2 > 0.3m)) { res[TradingEvent.DowntrendStart] = Constants.BlockingCoefficient; } - if (diff5min > 6) + if (diff5min > 2m) { res[TradingEvent.UptrendEnd] = Constants.BlockingCoefficient; } - if (diff5min < -6) + if (diff5min < -2m) { res[TradingEvent.DowntrendEnd] = Constants.BlockingCoefficient; } + //res = MergeResultsMult(res, blocks.ToImmutableDictionary()); return res.ToImmutableDictionary(); } private async Task> GetWindowAverageStartData((DateTime[] timestamps, decimal[] prices) data, int smallWindow, int bigWindow, -INewPrice message, int windowMaxSize, decimal uptrendStartingDetectionMeanfullStep = 0m, decimal uptrendEndingDetectionMeanfullStep = 3m) +INewPrice message, int windowMaxSize, decimal? uptrendStartingDetectionMeanfullStep = null, decimal? uptrendEndingDetectionMeanfullStep = null) { var resultMoveAvFull = MovingAverage.CheckByWindowAverageMean2(data.timestamps, data.prices, windowMaxSize, smallWindow, bigWindow, uptrendStartingDetectionMeanfullStep, uptrendEndingDetectionMeanfullStep); @@ -671,6 +747,18 @@ INewPrice message, int windowMaxSize, decimal uptrendStartingDetectionMeanfullSt if (assetType == AssetType.Futures) { + if (_tradeDataProvider.Orderbooks.TryGetValue(message.Figi, out var orderbook)) + { + if (asset.Count < 0 && orderbook.Asks.Length > 0) + { + price = orderbook.Asks[0].Price; + } + else if (orderbook.Bids.Length > 0) + { + price = orderbook.Bids[0].Price; + } + } + profit = TradingCalculator.CaclProfit(asset.BoughtPrice, price, GetComission(assetType), GetLeverage(message.Figi, asset.Count < 0), asset.Count < 0); } @@ -693,11 +781,11 @@ INewPrice message, int windowMaxSize, decimal uptrendStartingDetectionMeanfullSt } } - var tasks = assetsForClose.Select(asset => _portfolioWrapper.Accounts[asset.AccountId].ClosePosition(message.Figi)); - await Task.WhenAll(tasks); - foreach (var mess in messages) + for (int i = 0; i < assetsForClose.Count; i++) { - await _dataBus.Broadcast(new MessageForAdmin() { Text = mess }); + var asset = assetsForClose[i]; + await _portfolioWrapper.Accounts[asset.AccountId].ClosePosition(message.Figi); + await _dataBus.Broadcast(new MessageForAdmin() { Text = messages[i] }); } } @@ -737,9 +825,10 @@ INewPrice message, int windowMaxSize, decimal uptrendStartingDetectionMeanfullSt return; } + var steps = await GetSteps(message); var windows = await GetWindowsSizes(message); //var resTask1 = GetWindowAverageStartData(data, 30, 180, message, windowMaxSize, -2m, 2m,3); - var resTask1 = GetWindowAverageStartData(data, (int)windows.smallWindow, (int)windows.bigWindow, message, windowMaxSize, -0.5m, 0.5m); + var resTask1 = GetWindowAverageStartData(data, (int)windows.smallWindow, (int)windows.bigWindow, message, windowMaxSize, steps.downtrendEndStep, steps.uptrendEndStep); ////var resTask3 = GetWindowAverageStartData(data, 30, 180, message, windowMaxSize, 0, 0,0.7m); var getFFTModsTask = GetFFTMods(message); //var getLocalTrendsModsTask = GetLocalTrendsMods(data, message); @@ -747,7 +836,7 @@ INewPrice message, int windowMaxSize, decimal uptrendStartingDetectionMeanfullSt var getTradingModeModsTask = GetTradingModeMods(message); var getSpeedResultantModsTask = GetSpeedResultantMods(message); - await Task.WhenAll(resTask1, getFFTModsTask, getSellsDiffsModsTask, getTradingModeModsTask, getSpeedResultantModsTask); + await Task.WhenAll(resTask1, getFFTModsTask, getSellsDiffsModsTask, getTradingModeModsTask, getSpeedResultantModsTask); //var assetType = _tradeDataProvider.GetAssetTypeByFigi(message.Figi); //if (resTask1.Result[TradingEvent.UptrendStart] >= 1) //{ @@ -759,10 +848,10 @@ INewPrice message, int windowMaxSize, decimal uptrendStartingDetectionMeanfullSt //result = MergeResults(result, resTask2.Result.ToImmutableDictionary()); //result = MergeResults(result, resTask3.Result.ToImmutableDictionary()); result = MergeResultsMax(result, changeModeData); - // result = MergeResultsMax(result, getLocalTrendsModsTask.Result); + // result = MergeResultsMax(result, getLocalTrendsModsTask.Result); //result = MergeResultsMult(result, getFFTModsTask.Result); result = MergeResultsMult(result, getSellsDiffsModsTask.Result); - result = MergeResultsMult(result, getTradingModeModsTask.Result); + //result = MergeResultsMult(result, getTradingModeModsTask.Result); result = MergeResultsMult(result, getSpeedResultantModsTask.Result); if (result[TradingEvent.UptrendStart] >= Constants.UppingCoefficient @@ -770,6 +859,7 @@ INewPrice message, int windowMaxSize, decimal uptrendStartingDetectionMeanfullSt && state == ExchangeState.Open ) { + LastTradingEvents[message.Figi] = TradingEvent.UptrendStart; var stops = GetStops(message, PositionType.Long); if (!message.IsHistoricalData && BotModeSwitcher.CanPurchase()) { @@ -790,6 +880,7 @@ INewPrice message, int windowMaxSize, decimal uptrendStartingDetectionMeanfullSt && state == ExchangeState.Open ) { + LastTradingEvents[message.Figi] = TradingEvent.DowntrendStart; var stops = GetStops(message, PositionType.Short); if (!message.IsHistoricalData && BotModeSwitcher.CanPurchase()) { @@ -938,11 +1029,11 @@ INewPrice message, int windowMaxSize, decimal uptrendStartingDetectionMeanfullSt return res; } - private async Task CalcTradingMode(string figi) + private async ValueTask CalcTradingMode(INewPrice message) { var res = TradingMode.None; - var largeData = await _tradeDataProvider.GetData(figi, TimeSpan.FromMinutes(45)); - var smallData = await _tradeDataProvider.GetData(figi, TimeSpan.FromMinutes(10)); + var largeData = await _tradeDataProvider.GetData(message.Figi, TimeSpan.FromMinutes(45)); + var smallData = await _tradeDataProvider.GetData(message.Figi, TimeSpan.FromMinutes(10)); if (largeData.isFullIntervalExists && smallData.isFullIntervalExists) { @@ -969,7 +1060,7 @@ INewPrice message, int windowMaxSize, decimal uptrendStartingDetectionMeanfullSt { res = TradingMode.Growing; } - if (smallDataRes < - 7) + if (smallDataRes < -7) { res = TradingMode.Dropping; } @@ -978,12 +1069,72 @@ INewPrice message, int windowMaxSize, decimal uptrendStartingDetectionMeanfullSt return res; } - private async Task CalcTradingMode(INewPrice message) + private async ValueTask CalcTradingMode2(INewPrice message) { - var res = await CalcTradingMode(message.Figi); + var res = TradingMode.None; + var data = await _tradeDataProvider.GetData(message.Figi, TimeSpan.FromMinutes(20)); + + if (data.isFullIntervalExists) + { + if (LocalTrends.TryCalcTrendDiff(data.timestamps, data.prices, out var diff)) + { + if (diff >= -2 && diff <= 2) + { + res = TradingMode.Stable; + } + else if (diff < -6) + { + res = TradingMode.Dropping; + } + else if (diff > 6) + { + res = TradingMode.Growing; + } + else if (diff < -2) + { + res = TradingMode.SlowDropping; + } + else if (diff > 2) + { + res = TradingMode.SlowGrowing; + } + } + } return res; } + private async Task<(decimal? downtrendEndStep, decimal? uptrendEndStep)> GetSteps(INewPrice message) + { + var mode = TradingModes[message.Figi]; + switch (mode) + { + case TradingMode.Stable: + { + return (0.5m, -0.5m); + } + case TradingMode.Dropping: + { + return (-1.5m, null); ; + } + case TradingMode.SlowDropping: + { + return (-0.5m, null); + } + case TradingMode.SlowGrowing: + { + return (null, 0.5m); + } + case TradingMode.Growing: + { + return (null, 1.5m); + } + default: + { + return (-0.5m, 0.5m); + } + } + } + private (decimal stopLoss, decimal takeProfit) GetStops(INewPrice message, PositionType type) { var mode = TradingModes[message.Figi]; @@ -1016,6 +1167,14 @@ INewPrice message, int windowMaxSize, decimal uptrendStartingDetectionMeanfullSt takeProfitShift = 1.5m; stopLossShift = 10; } + if (mode == TradingMode.SlowGrowing && type == PositionType.Short) + { + takeProfitShift = 1.5m; + stopLossShift = 10; + } + if (mode == TradingMode.SlowGrowing && type == PositionType.Long) + { + } if (mode == TradingMode.Dropping && type == PositionType.Short) { takeProfitShift = 15; @@ -1065,7 +1224,7 @@ INewPrice message, int windowMaxSize, decimal uptrendStartingDetectionMeanfullSt } return res.ToImmutableDictionary(); } - + private Task> GetLocalTrendsMods((DateTime[] timestamps, decimal[] prices) data, INewPrice message) { var res = GetInitDict(0); @@ -1159,7 +1318,7 @@ INewPrice message, int windowMaxSize, decimal uptrendStartingDetectionMeanfullSt } if (mode == TradingMode.Growing) { - res[TradingEvent.UptrendEnd] = Constants.LowingCoefficient; + //res[TradingEvent.UptrendEnd] = Constants.LowingCoefficient; res[TradingEvent.UptrendStart] = Constants.UppingCoefficient; res[TradingEvent.DowntrendStart] = Constants.BlockingCoefficient; res[TradingEvent.DowntrendEnd] = Constants.PowerUppingCoefficient; @@ -1183,7 +1342,7 @@ INewPrice message, int windowMaxSize, decimal uptrendStartingDetectionMeanfullSt res[TradingEvent.UptrendEnd] = Constants.PowerUppingCoefficient; res[TradingEvent.UptrendStart] = Constants.BlockingCoefficient; res[TradingEvent.DowntrendStart] = Constants.UppingCoefficient; - res[TradingEvent.DowntrendEnd] = Constants.LowingCoefficient; + //res[TradingEvent.DowntrendEnd] = Constants.LowingCoefficient; } return Task.FromResult(res.ToImmutableDictionary()); } diff --git a/KLHZ.Trader.Core/TG/Services/BotMessagesHandler.cs b/KLHZ.Trader.Core/TG/Services/BotMessagesHandler.cs index 817696b..d0106ab 100644 --- a/KLHZ.Trader.Core/TG/Services/BotMessagesHandler.cs +++ b/KLHZ.Trader.Core/TG/Services/BotMessagesHandler.cs @@ -120,7 +120,7 @@ namespace KLHZ.Trader.Core.TG.Services { var acc = _portfolioWrapper.Accounts.Values.FirstOrDefault(a => a.Assets.ContainsKey("FUTIMOEXF000")); if (acc != null) - await acc.ResetStops("FUTIMOEXF000", 4, 4); + await acc.ResetStops("FUTIMOEXF000", 4, 4); break; } case "ребут": diff --git a/KLHZ.Trader.Service/Controllers/PlayController.cs b/KLHZ.Trader.Service/Controllers/PlayController.cs index 12d54df..cb659d4 100644 --- a/KLHZ.Trader.Service/Controllers/PlayController.cs +++ b/KLHZ.Trader.Service/Controllers/PlayController.cs @@ -1,6 +1,4 @@ -using Grpc.Core; using KLHZ.Trader.Core.Contracts.Messaging.Dtos; -using KLHZ.Trader.Core.Contracts.Messaging.Dtos.Interfaces; using KLHZ.Trader.Core.Contracts.Messaging.Interfaces; using KLHZ.Trader.Core.DataLayer; using KLHZ.Trader.Core.DataLayer.Entities.Orders; @@ -10,8 +8,6 @@ using KLHZ.Trader.Core.Math.Declisions.Utils; using KLHZ.Trader.Service.Models; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; -using Telegram.Bot.Types; -using Tinkoff.InvestApi.V1; namespace KLHZ.Trader.Service.Controllers { @@ -46,13 +42,13 @@ namespace KLHZ.Trader.Service.Controllers context1.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking; - while (time1 < DateTime.UtcNow) + while (time1 < DateTime.UtcNow.Date) { var data = new List(); var data2 = new List(); var time2 = time1.AddHours(1); var orderbooks = await context1.OrderbookItems - .Where(oi => (oi.Figi == figi1 || oi.Figi == figi2) && oi.Time >= time1 && oi.Time (oi.Figi == figi1 || oi.Figi == figi2) && oi.Time >= time1 && oi.Time < time2) .OrderBy(c => c.Time) .ToArrayAsync(); var prices = await context1.PriceChanges @@ -70,12 +66,12 @@ namespace KLHZ.Trader.Service.Controllers }) .ToArrayAsync(); - var lookupImoexf = orderbooks.Where(o=>o.Figi== "FUTIMOEXF000").ToLookup(o => o.Time); - var lookupSber = orderbooks.Where(o=>o.Figi== "BBG004730N88").ToLookup(o => o.Time); - + var lookupImoexf = orderbooks.Where(o => o.Figi == "FUTIMOEXF000").ToLookup(o => o.Time); + var lookupSber = orderbooks.Where(o => o.Figi == "BBG004730N88").ToLookup(o => o.Time); + foreach (var item in lookupImoexf) { - + var asks = item .Where(i => i.ItemType == Core.DataLayer.Entities.Orders.Enums.OrderbookItemType.Ask) .OrderBy(o => o.Price) @@ -92,7 +88,7 @@ namespace KLHZ.Trader.Service.Controllers } foreach (var bid in bids) { - var bids2 = bids.Where(b=>b.Price==bid.Price).ToList(); + var bids2 = bids.Where(b => b.Price == bid.Price).ToList(); var summCount = bids2.Sum(b => b.Count); var b = bids2.First(); b.Count = summCount; @@ -180,16 +176,16 @@ namespace KLHZ.Trader.Service.Controllers data.Add(wrapper); } data = data.OrderBy(d => d.Time).ToList(); - for(int i = 0; i < data.Count; i++) + for (int i = 0; i < data.Count; i++) { - if (data[i].NewPrice != null && i>0) + if (data[i].NewPrice != null && i > 0) { - for (int i1=i-1; i1 >=0; i1--) + for (int i1 = i - 1; i1 >= 0; i1--) { var ob = data[i1].Orderbook; if (data[i1].Figi == data[i].Figi && ob != null) { - var d =new TimeSeriesData() + var d = new TimeSeriesData() { Figi = ob.Figi, Orderbook = ob, diff --git a/KLHZ.Trader.Service/Models/TimeSeriesData.cs b/KLHZ.Trader.Service/Models/TimeSeriesData.cs index 7d86545..db044a4 100644 --- a/KLHZ.Trader.Service/Models/TimeSeriesData.cs +++ b/KLHZ.Trader.Service/Models/TimeSeriesData.cs @@ -4,7 +4,7 @@ namespace KLHZ.Trader.Service.Models { public class TimeSeriesData { - public required string Figi { get; set;} + public required string Figi { get; set; } public DateTime Time { get; set; } public INewPrice? NewPrice { get; set; } public IOrderbook? Orderbook { get; set; }