From 00de5bee2e8f66338b213e9f570c7ea607b13b8c Mon Sep 17 00:00:00 2001 From: vlad zverzhkhovskiy Date: Tue, 7 Oct 2025 12:26:25 +0300 Subject: [PATCH] =?UTF-8?q?=D0=A7=D0=B8=D1=81=D1=82=D0=BA=D0=B0=20=D0=BA?= =?UTF-8?q?=D0=BE=D0=B4=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Declisions/Dtos/CachedValue.cs | 4 +- .../Declisions/Utils/Statistics.cs | 10 +- .../Trades/Enums => Common}/TradeDirection.cs | 2 +- .../Entities/Trades/Enums/AssetType.cs | 10 - .../Entities/Trades/Enums/PositionType.cs | 9 - .../DataLayer/Entities/Trades/Trade.cs | 52 - KLHZ.Trader.Core/DataLayer/TraderDbContext.cs | 11 - .../Models/AssetsAccounting/DealResult.cs | 12 - .../Exchange/Models/AssetsAccounting/Order.cs | 15 - .../Exchange/Models/Configs/ExchangeConfig.cs | 22 +- .../Exchange/Services/ExchangeDataReader.cs | 33 - .../Exchange/Services/ManagedAccount.cs | 12 +- KLHZ.Trader.Core/Exchange/Services/Trader.cs | 993 +----------------- .../Exchange/Services/TraderDataProvider.cs | 2 +- .../Exchange/Utils/TraderUtils.cs | 93 ++ 15 files changed, 157 insertions(+), 1123 deletions(-) rename KLHZ.Trader.Core/{DataLayer/Entities/Trades/Enums => Common}/TradeDirection.cs (58%) delete mode 100644 KLHZ.Trader.Core/DataLayer/Entities/Trades/Enums/AssetType.cs delete mode 100644 KLHZ.Trader.Core/DataLayer/Entities/Trades/Enums/PositionType.cs delete mode 100644 KLHZ.Trader.Core/DataLayer/Entities/Trades/Trade.cs delete mode 100644 KLHZ.Trader.Core/Exchange/Models/AssetsAccounting/DealResult.cs delete mode 100644 KLHZ.Trader.Core/Exchange/Models/AssetsAccounting/Order.cs create mode 100644 KLHZ.Trader.Core/Exchange/Utils/TraderUtils.cs diff --git a/KLHZ.Trader.Core.Contracts/Declisions/Dtos/CachedValue.cs b/KLHZ.Trader.Core.Contracts/Declisions/Dtos/CachedValue.cs index 815cc70..52d6872 100644 --- a/KLHZ.Trader.Core.Contracts/Declisions/Dtos/CachedValue.cs +++ b/KLHZ.Trader.Core.Contracts/Declisions/Dtos/CachedValue.cs @@ -3,7 +3,7 @@ public class CachedValue { public DateTime Time { get; init; } - public decimal Value { get; init; } - public decimal Value2 { get; init; } + public decimal Count { get; init; } + public decimal Price { get; init; } } } diff --git a/KLHZ.Trader.Core.Math/Declisions/Utils/Statistics.cs b/KLHZ.Trader.Core.Math/Declisions/Utils/Statistics.cs index 6778ea8..850002c 100644 --- a/KLHZ.Trader.Core.Math/Declisions/Utils/Statistics.cs +++ b/KLHZ.Trader.Core.Math/Declisions/Utils/Statistics.cs @@ -7,12 +7,12 @@ namespace KLHZ.Trader.Core.Math.Declisions.Utils public static decimal Mean(this CachedValue[] values) { - return values.Sum(x => x.Value) / values.Length; + return values.Sum(x => x.Count) / values.Length; } public static decimal Mean2(this CachedValue[] values) { - return values.Sum(x => x.Value2) / values.Length; + return values.Sum(x => x.Price) / values.Length; } private static (decimal mean, decimal std) CaclSigma(decimal[] values) @@ -94,9 +94,9 @@ namespace KLHZ.Trader.Core.Math.Declisions.Utils { var tradevolume_diffMean = values.Mean(); var dprice_diffMean = values.Mean2(); - var sum1 = (double)values.Sum(d => (d.Value - tradevolume_diffMean) * (d.Value2 - dprice_diffMean)); - var sum2 = values.Sum(d => (d.Value - tradevolume_diffMean) * (d.Value - tradevolume_diffMean)); - var sum3 = values.Sum(d => (d.Value2 - dprice_diffMean) * (d.Value2 - dprice_diffMean)); + var sum1 = (double)values.Sum(d => (d.Count - tradevolume_diffMean) * (d.Price - dprice_diffMean)); + var sum2 = values.Sum(d => (d.Count - tradevolume_diffMean) * (d.Count - tradevolume_diffMean)); + var sum3 = values.Sum(d => (d.Price - dprice_diffMean) * (d.Price - dprice_diffMean)); if (sum2 != 0 && sum3 != 0) { result = (decimal)(sum1 / System.Math.Sqrt((double)(sum2 * sum3))); diff --git a/KLHZ.Trader.Core/DataLayer/Entities/Trades/Enums/TradeDirection.cs b/KLHZ.Trader.Core/Common/TradeDirection.cs similarity index 58% rename from KLHZ.Trader.Core/DataLayer/Entities/Trades/Enums/TradeDirection.cs rename to KLHZ.Trader.Core/Common/TradeDirection.cs index 2042e4a..b48ec27 100644 --- a/KLHZ.Trader.Core/DataLayer/Entities/Trades/Enums/TradeDirection.cs +++ b/KLHZ.Trader.Core/Common/TradeDirection.cs @@ -1,4 +1,4 @@ -namespace KLHZ.Trader.Core.DataLayer.Entities.Trades.Enums +namespace KLHZ.Trader.Core.Common { public enum TradeDirection { diff --git a/KLHZ.Trader.Core/DataLayer/Entities/Trades/Enums/AssetType.cs b/KLHZ.Trader.Core/DataLayer/Entities/Trades/Enums/AssetType.cs deleted file mode 100644 index 5da2b5a..0000000 --- a/KLHZ.Trader.Core/DataLayer/Entities/Trades/Enums/AssetType.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace KLHZ.Trader.Core.DataLayer.Entities.Trades.Enums -{ - public enum AssetType - { - Unknown = 0, - Common = 1, - Future = 2, - Currency = 3, - } -} diff --git a/KLHZ.Trader.Core/DataLayer/Entities/Trades/Enums/PositionType.cs b/KLHZ.Trader.Core/DataLayer/Entities/Trades/Enums/PositionType.cs deleted file mode 100644 index 3b90439..0000000 --- a/KLHZ.Trader.Core/DataLayer/Entities/Trades/Enums/PositionType.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace KLHZ.Trader.Core.DataLayer.Entities.Trades.Enums -{ - public enum PositionType - { - Unknown = 0, - Long = 1, - Short = 2 - } -} diff --git a/KLHZ.Trader.Core/DataLayer/Entities/Trades/Trade.cs b/KLHZ.Trader.Core/DataLayer/Entities/Trades/Trade.cs deleted file mode 100644 index de797ca..0000000 --- a/KLHZ.Trader.Core/DataLayer/Entities/Trades/Trade.cs +++ /dev/null @@ -1,52 +0,0 @@ -using KLHZ.Trader.Core.DataLayer.Entities.Trades.Enums; -using System.ComponentModel.DataAnnotations.Schema; - -namespace KLHZ.Trader.Core.DataLayer.Entities.Trades -{ - /// - /// Сделка, совершенная ботом. - /// - [Table("trades")] - public class Trade - { - [Column("trade_id")] - public long Id { get; set; } - - [Column("bought_at")] - public DateTime BoughtAt { get; set; } - - [Column("account_id")] - public required string AccountId { get; set; } - - [Column("figi")] - public required string Figi { get; set; } - - [Column("ticker")] - public required string Ticker { get; set; } - - [Column("price")] - - public decimal Price { get; set; } - - [Column("count")] - public decimal Count { get; set; } - - [Column("count_lots")] - public decimal CountLots { get; set; } - - [Column("archive_status")] - public int ArchiveStatus { get; set; } - - [Column("direction")] - public TradeDirection Direction { get; set; } - - [Column("position_type")] - public PositionType Position { get; set; } - - [Column("asset_type")] - public AssetType Asset { get; set; } - - [Column("asset_id")] - public Guid AssetId { get; set; } - } -} diff --git a/KLHZ.Trader.Core/DataLayer/TraderDbContext.cs b/KLHZ.Trader.Core/DataLayer/TraderDbContext.cs index 245beb5..f8dd9a0 100644 --- a/KLHZ.Trader.Core/DataLayer/TraderDbContext.cs +++ b/KLHZ.Trader.Core/DataLayer/TraderDbContext.cs @@ -1,7 +1,6 @@ using KLHZ.Trader.Core.DataLayer.Entities.Declisions; using KLHZ.Trader.Core.DataLayer.Entities.Orders; using KLHZ.Trader.Core.DataLayer.Entities.Prices; -using KLHZ.Trader.Core.DataLayer.Entities.Trades; using Microsoft.EntityFrameworkCore; @@ -9,7 +8,6 @@ namespace KLHZ.Trader.Core.DataLayer { public class TraderDbContext : DbContext { - public DbSet Trades { get; set; } public DbSet Declisions { get; set; } public DbSet PriceChanges { get; set; } public DbSet ProcessedPrices { get; set; } @@ -23,15 +21,6 @@ namespace KLHZ.Trader.Core.DataLayer { modelBuilder.UseSerialColumns(); - modelBuilder.Entity(entity => - { - entity.HasKey(e1 => e1.Id); - entity.Property(e => e.BoughtAt) - .HasConversion( - v => v.ToUniversalTime(), - v => DateTime.SpecifyKind(v, DateTimeKind.Utc)); - }); - modelBuilder.Entity(entity => { entity.HasKey(e1 => e1.Id); diff --git a/KLHZ.Trader.Core/Exchange/Models/AssetsAccounting/DealResult.cs b/KLHZ.Trader.Core/Exchange/Models/AssetsAccounting/DealResult.cs deleted file mode 100644 index 407c400..0000000 --- a/KLHZ.Trader.Core/Exchange/Models/AssetsAccounting/DealResult.cs +++ /dev/null @@ -1,12 +0,0 @@ -namespace KLHZ.Trader.Core.Exchange.Models.AssetsAccounting -{ - public class DealResult - { - public required string AccountId { get; set; } - public required string Figi { get; set; } - public decimal Price { get; set; } - public decimal Count { get; set; } - public bool Success { get; set; } - public DealDirection Direction { get; set; } - } -} diff --git a/KLHZ.Trader.Core/Exchange/Models/AssetsAccounting/Order.cs b/KLHZ.Trader.Core/Exchange/Models/AssetsAccounting/Order.cs deleted file mode 100644 index 4beef7c..0000000 --- a/KLHZ.Trader.Core/Exchange/Models/AssetsAccounting/Order.cs +++ /dev/null @@ -1,15 +0,0 @@ -namespace KLHZ.Trader.Core.Exchange.Models.AssetsAccounting -{ - public class Order - { - public required string AccountId { get; init; } - public required string Figi { get; init; } - public required string Ticker { get; init; } - public required string OrderId { get; init; } - public decimal Price { get; init; } - public long Count { get; init; } - public DateTime ExpirationTime { get; init; } - public DateTime OpenDate { get; init; } - public DealDirection Direction { get; init; } - } -} diff --git a/KLHZ.Trader.Core/Exchange/Models/Configs/ExchangeConfig.cs b/KLHZ.Trader.Core/Exchange/Models/Configs/ExchangeConfig.cs index 2546973..c467f43 100644 --- a/KLHZ.Trader.Core/Exchange/Models/Configs/ExchangeConfig.cs +++ b/KLHZ.Trader.Core/Exchange/Models/Configs/ExchangeConfig.cs @@ -2,16 +2,16 @@ { public class ExchangeConfig { - public bool ExchangeDataRecievingEnabled { get; set; } - public decimal StopBuyLengthMinuts { get; set; } - public decimal FutureComission { get; set; } - public decimal ShareComission { get; set; } - public decimal AccountCashPart { get; set; } - public decimal AccountCashPartFutures { get; set; } - public decimal DefaultBuyPartOfAccount { get; set; } - public string[] DataRecievingInstrumentsFigis { get; set; } = []; - public string[] TradingInstrumentsFigis { get; set; } = []; - public string[] ManagingAccountNamePatterns { get; set; } = []; - public InstrumentSettings[] InstrumentsSettings { get; set; } = []; + public bool ExchangeDataRecievingEnabled { get; init; } + public decimal StopBuyLengthMinuts { get; init; } + public decimal FutureComission { get; init; } + public decimal ShareComission { get; init; } + public decimal AccountCashPart { get; init; } + public decimal AccountCashPartFutures { get; init; } + public decimal DefaultBuyPartOfAccount { get; init; } + public string[] DataRecievingInstrumentsFigis { get; init; } = []; + public string[] TradingInstrumentsFigis { get; init; } = []; + public string[] ManagingAccountNamePatterns { get; init; } = []; + public InstrumentSettings[] InstrumentsSettings { get; init; } = []; } } diff --git a/KLHZ.Trader.Core/Exchange/Services/ExchangeDataReader.cs b/KLHZ.Trader.Core/Exchange/Services/ExchangeDataReader.cs index c0a9bb2..dddfb8a 100644 --- a/KLHZ.Trader.Core/Exchange/Services/ExchangeDataReader.cs +++ b/KLHZ.Trader.Core/Exchange/Services/ExchangeDataReader.cs @@ -6,7 +6,6 @@ using KLHZ.Trader.Core.DataLayer.Entities.Orders; using KLHZ.Trader.Core.DataLayer.Entities.Prices; using KLHZ.Trader.Core.Exchange.Extentions; using KLHZ.Trader.Core.Exchange.Models.Configs; -using KLHZ.Trader.Core.Exchange.Utils; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; @@ -156,39 +155,7 @@ namespace KLHZ.Trader.Core.Exchange.Services Count = response.Trade.Quantity, }; - //await _tradeDataProvider.AddData(message, TimeSpan.FromHours(7)); await _eventBus.Broadcast(message); - - var exchangeState = ExchangeScheduler.GetCurrentState(); - if (exchangeState == Models.Trading.ExchangeState.ClearingTime - && lastUpdateDict.TryGetValue(message.Figi, out var pri) - && (DateTime.UtcNow - pri.Time).Minutes > 3) - { - var assets = _portfolioWrapper.Accounts.Values.SelectMany(a => a.Assets.Values).Where(a => a.Figi == message.Figi).ToArray(); - - foreach (var a in assets) - { - using var context = await _dbContextFactory.CreateDbContextAsync(); - context.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking; - await context.Trades.AddAsync(new DataLayer.Entities.Trades.Trade() - { - AssetId = a.AssetId, - AccountId = string.Empty, - Figi = message.Figi, - Ticker = string.Empty, - ArchiveStatus = 0, - Asset = (KLHZ.Trader.Core.DataLayer.Entities.Trades.Enums.AssetType)(int)a.Type, - BoughtAt = DateTime.UtcNow, - Count = 0, - Direction = a.Count > 0 ? DataLayer.Entities.Trades.Enums.TradeDirection.Buy : DataLayer.Entities.Trades.Enums.TradeDirection.Sell, - Position = a.Count > 0 ? DataLayer.Entities.Trades.Enums.PositionType.Long : DataLayer.Entities.Trades.Enums.PositionType.Short, - Price = message.Value, - }); - await context.SaveChangesAsync(); - } - } - lastUpdateDict[message.Figi] = message; - pricesBuffer.Add(message); } if (response.Orderbook != null) diff --git a/KLHZ.Trader.Core/Exchange/Services/ManagedAccount.cs b/KLHZ.Trader.Core/Exchange/Services/ManagedAccount.cs index dc31586..406db63 100644 --- a/KLHZ.Trader.Core/Exchange/Services/ManagedAccount.cs +++ b/KLHZ.Trader.Core/Exchange/Services/ManagedAccount.cs @@ -50,13 +50,11 @@ namespace KLHZ.Trader.Core.Exchange.Services public ImmutableDictionary Assets => GetAssets(); - private readonly InvestApiClient _investApiClient; private readonly IDbContextFactory _dbContextFactory; private readonly ILogger _logger; private readonly IOptions _options; - private readonly Dictionary _assets = new(); private readonly ConcurrentDictionary _usedOrderIds = new(); @@ -140,26 +138,20 @@ namespace KLHZ.Trader.Core.Exchange.Services using var context = await _dbContextFactory.CreateDbContextAsync(); context.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking; - var trades = await context.Trades - .Where(t => t.ArchiveStatus == 0) - .OrderByDescending(t => t.BoughtAt) - .ToListAsync(); - var oldAssets = _assets.ToDictionary(); _assets.Clear(); foreach (var position in portfolio.Positions) { oldAssets.TryGetValue(position.Figi, out var oldAsset); var newAssetId = oldAsset?.AssetId ?? Guid.NewGuid(); - var trade = trades.FirstOrDefault(t => t.Figi == position.Figi && t.AssetId == newAssetId); var asset = new Asset() { AssetId = newAssetId, AccountId = AccountId, Figi = position.Figi, Ticker = position.Ticker, - BoughtAt = trade?.BoughtAt ?? DateTime.UtcNow, - BoughtPrice = trade?.Price ?? position.AveragePositionPrice, + BoughtAt = oldAsset?.BoughtAt ?? DateTime.UtcNow, + BoughtPrice = oldAsset?.BoughtPrice ?? position.AveragePositionPrice, Type = position.InstrumentType.ParseInstrumentType(), Position = position.Quantity > 0 ? PositionType.Long : PositionType.Short, BlockedItems = position.BlockedLots, diff --git a/KLHZ.Trader.Core/Exchange/Services/Trader.cs b/KLHZ.Trader.Core/Exchange/Services/Trader.cs index 4f33283..5b397cb 100644 --- a/KLHZ.Trader.Core/Exchange/Services/Trader.cs +++ b/KLHZ.Trader.Core/Exchange/Services/Trader.cs @@ -11,7 +11,6 @@ using KLHZ.Trader.Core.Exchange.Interfaces; using KLHZ.Trader.Core.Exchange.Models.Configs; using KLHZ.Trader.Core.Exchange.Models.Trading; using KLHZ.Trader.Core.Exchange.Utils; -using KLHZ.Trader.Core.Math.Declisions.Dtos.FFT.Enums; using KLHZ.Trader.Core.Math.Declisions.Utils; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Hosting; @@ -33,36 +32,17 @@ namespace KLHZ.Trader.Core.Exchange.Services private readonly IDataBus _dataBus; private readonly TraderDataProvider _tradeDataProvider; private readonly PortfolioWrapper _portfolioWrapper; - + private readonly ExchangeConfig _exchangeConfig; private readonly ILogger _logger; - private readonly ConcurrentDictionary LastTradingEvents = new(); private readonly ConcurrentDictionary TradingModes = new(); - private readonly ConcurrentDictionary OrderBooks = new(); - private readonly ConcurrentDictionary privces = new(); - private readonly ConcurrentDictionary privcesDiffDiff = new(); - private readonly ConcurrentDictionary privcesDiffDiffPrev = new(); - private readonly ConcurrentDictionary correlations = new(); - private readonly ConcurrentDictionary correlationsPrev = new(); - private readonly ConcurrentDictionary PirsonZeroCrossings = new(); private readonly ConcurrentDictionary DPirsonValues = new(); - private readonly ConcurrentDictionary PricesZeroCrossingsUp = new(); - private readonly ConcurrentDictionary PricesZeroCrossingsDown = new(); - private readonly ConcurrentDictionary LongOpeningStops = new(); private readonly ConcurrentDictionary ShortOpeningStops = new(); private readonly ConcurrentDictionary LongClosingStops = new(); private readonly ConcurrentDictionary ShortClosingStops = new(); - private readonly ConcurrentDictionary Leverages = new(); - - - private readonly decimal _futureComission; - private readonly decimal _shareComission; - private readonly decimal _accountCashPart; - private readonly decimal _accountCashPartFutures; - private readonly string[] _tradingInstrumentsFigis = []; private readonly Channel _pricesChannel = Channel.CreateUnbounded(); private readonly Channel _commands = Channel.CreateUnbounded(); @@ -79,20 +59,11 @@ namespace KLHZ.Trader.Core.Exchange.Services _tradeDataProvider = tradeDataProvider; _logger = logger; _dataBus = dataBus; - _futureComission = options.Value.FutureComission; - _shareComission = options.Value.ShareComission; - _accountCashPart = options.Value.AccountCashPart; - _accountCashPartFutures = options.Value.AccountCashPartFutures; - _tradingInstrumentsFigis = options.Value.TradingInstrumentsFigis; - foreach (var f in _tradingInstrumentsFigis) + _exchangeConfig = options.Value; + foreach (var f in _exchangeConfig.TradingInstrumentsFigis) { TradingModes[f] = TradingMode.None; } - - foreach (var lev in options.Value.InstrumentsSettings) - { - Leverages.TryAdd(lev.Figi, lev); - } } public Task StartAsync(CancellationToken cancellationToken) @@ -106,122 +77,6 @@ namespace KLHZ.Trader.Core.Exchange.Services return Task.CompletedTask; } - 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)); - //if (!data2.isFullIntervalExists) - //{ - // data2 = await _tradeDataProvider.GetData(message.Figi, TimeSpan.FromHours(0.75)); - //} - return data2; - } - - private async ValueTask<(decimal smallWindow, decimal bigWindow)> GetWindowsSizes(INewPrice message) - { - var fftFull = await _tradeDataProvider.GetFFtResult(message.Figi + "_full"); - if (!fftFull.IsEmpty) - { - var harms = fftFull.Harmonics.Skip(1).Take(fftFull.Harmonics.Length - 3).ToArray(); - var sum = harms.Sum(h => h.Magnitude); - var sumtmp = 0f; - foreach (var h in harms) - { - sumtmp += h.Magnitude; - if (sumtmp / sum > 0.7f) - { - return ((decimal)(h.Period.TotalSeconds / 4), (decimal)(h.Period.TotalSeconds)); - } - } - } - return (30m, 180m); - } - - private async ValueTask CheckHarmonicPosition((DateTime[] timestamps, decimal[] prices, bool isFullIntervalExists) data, INewPrice message) - { - var currentTime = message.IsHistoricalData ? message.Time : DateTime.UtcNow; - var position = ValueAmplitudePosition.None; - var fft = await _tradeDataProvider.GetFFtResult(message.Figi); - var fftFull = await _tradeDataProvider.GetFFtResult(message.Figi + "_full"); - //var highFreq = await _tradeDataProvider.GetFFtResult(message.Figi + "_high_freq"); - //var lowFreq = await _tradeDataProvider.GetFFtResult(message.Figi + "_low_freq"); - var step = message.IsHistoricalData ? 40 : 5; - if (fft.IsEmpty || (currentTime - fft.LastTime).TotalSeconds > step) - { - 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)); - - //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)); - //lowFreq = FFT.ReAnalyze(fftFull, message.Figi + "_high_freq", TimeSpan.FromMinutes(3), TimeSpan.FromMinutes(20)); - //var tmp = FFT.GetMainHarmonic(interpolatedData.timestamps, interpolatedData.values, "mainHarm", TimeSpan.FromMinutes(20)); - //await _tradeDataProvider.SetFFtResult(fft); - await _tradeDataProvider.SetFFtResult(fftFull); - //await _tradeDataProvider.SetFFtResult(lowFreq); - //await _tradeDataProvider.SetFFtResult(highFreq); - - } - - //var highFreqData = await GetData(message, TimeSpan.FromMinutes(120)); - - //if (highFreqData.isFullIntervalExists) - //{ - // var interpolatehighFreqData = SignalProcessing.InterpolateData(data.timestamps, data.prices, TimeSpan.FromSeconds(5)); - // highFreq = FFT.Analyze(interpolatehighFreqData.timestamps, interpolatehighFreqData.values, message.Figi + "_high_freq", TimeSpan.FromSeconds(20), TimeSpan.FromMinutes(120)); - // await _tradeDataProvider.SetFFtResult(highFreq); - //} - } - - - - position = FFT.Check(fftFull, message.Time); - if (position == Math.Declisions.Dtos.FFT.Enums.ValueAmplitudePosition.UpperThen30Decil) - { - await LogPrice(message, "upper30percent", message.Value); - } - if (position == Math.Declisions.Dtos.FFT.Enums.ValueAmplitudePosition.LowerThenMedianaGrowing) - { - await LogPrice(message, "lower30percent", message.Value); - } - - - - //var hposition = FFT.CheckExtremums(highFreq, message.Time); - //if (hposition == Math.Declisions.Dtos.FFT.Enums.ValueAmplitudePosition.UpperThen30Decil) - //{ - // await LogPrice(message, "high_freq_high", message.Value); - //} - //if (hposition == Math.Declisions.Dtos.FFT.Enums.ValueAmplitudePosition.LowerThen30Decil) - //{ - // await LogPrice(message, "high_freq_low", message.Value); - //} - - - //var gposition = FFT.CheckSign(highFreq, message.Time); - //if (gposition == Math.Declisions.Dtos.FFT.Enums.ValueAmplitudePosition.Growing) - //{ - // await LogPrice(message, "growing", message.Value); - //} - //if (gposition == Math.Declisions.Dtos.FFT.Enums.ValueAmplitudePosition.Falling) - //{ - // await LogPrice(message, "falling", message.Value); - //} - - - //var lposition = FFT.CheckExtremums(lowFreq, message.Time); - //if (lposition == Math.Declisions.Dtos.FFT.Enums.ValueAmplitudePosition.UpperThen30Decil) - //{ - // await LogPrice(message, "low_freq_high", message.Value); - //} - //if (lposition == Math.Declisions.Dtos.FFT.Enums.ValueAmplitudePosition.LowerThen30Decil) - //{ - // await LogPrice(message, "low_freq_low", message.Value); - //} - - return position; - } - private async Task ProcessCommands() { while (await _commands.Reader.WaitToReadAsync()) @@ -265,37 +120,10 @@ namespace KLHZ.Trader.Core.Exchange.Services { var message = await _orderbooks.Reader.ReadAsync(); await _tradeDataProvider.AddOrderbook(message); - - //if (OrderBooks.TryGetValue(message.Figi, out var oldMessage)) - //{ - // if (message.Bids.Length > 0 && oldMessage.Bids.Length>0) - // { - // var val = message.Bids[0].Count - oldMessage.Bids[0].Count; - // await _tradeDataProvider.AddDataTo20SecondsWindowCache(message.Figi, "dbids0", new Contracts.Declisions.Dtos.CachedValue() - // { - // Time = message.Time, - // Value = val - // }); - // if (val!=0) - // await LogPrice(message.Figi, message.Ticker, message.Time, val, "bids0"); - // } - // if (message.Asks.Length > 0 && oldMessage.Asks.Length > 0) - // { - // var val = message.Asks[0].Count - oldMessage.Asks[0].Count; - // await _tradeDataProvider.AddDataTo20SecondsWindowCache(message.Figi, "dasks0", new Contracts.Declisions.Dtos.CachedValue() - // { - // Time = message.Time, - // Value = val - // }); - // if (val != 0) - // await LogPrice(message.Figi, message.Ticker, message.Time, val, "asks0"); - // } - - //} - OrderBooks[message.Figi] = message; } } + private async Task ProcessPrices() { var pricesCache1 = new Dictionary>(); @@ -308,107 +136,41 @@ namespace KLHZ.Trader.Core.Exchange.Services { continue; } - var changeMods = GetInitDict(0); + var changeMods = TraderUtils.GetInitDict(0); try { - #region Ускорение обработки исторических данных при отладке - if (message.Direction == 1) - { - if (!pricesCache1.TryGetValue(message.Figi, out var list)) - { - list = new List(); - pricesCache1[message.Figi] = list; - } - list.Add(message); + message = TraderUtils.FilterHighFreqValues(message, message.Direction == 1 ? pricesCache1 : pricesCache2); - if ((list.Last().Time - list.First().Time).TotalSeconds < 0.5) - { - list.Add(message); - continue; - } - else - { - message = new PriceChange() - { - Figi = message.Figi, - Ticker = message.Ticker, - Count = message.Count, - Direction = message.Direction, - IsHistoricalData = message.IsHistoricalData, - Time = message.Time, - Value = list.Sum(l => l.Value) / list.Count - }; - list.Clear(); - } - } - if (message.Direction == 2) - { - - if (!pricesCache2.TryGetValue(message.Figi, out var list)) - { - list = new List(); - pricesCache2[message.Figi] = list; - } - list.Add(message); - - if ((list.Last().Time - list.First().Time).TotalSeconds < 0.5) - { - list.Add(message); - continue; - } - else - { - message = new PriceChange() - { - Figi = message.Figi, - Ticker = message.Ticker, - Count = message.Count, - Direction = message.Direction, - IsHistoricalData = message.IsHistoricalData, - Time = message.Time, - Value = list.Sum(l => l.Value) / list.Count - }; - list.Clear(); - } - } - - - - - #endregion - - #region Подсчёт торгового баланса по сберу и IMOEXF + #region Добавление данных в кеши. if (message.Figi == "BBG004730N88" || message.Figi == "FUTIMOEXF000") { if (message.Direction == 1) { - - await _tradeDataProvider.AddDataTo20SecondsWindowCache(message.Figi, "1", new Contracts.Declisions.Dtos.CachedValue() { Time = message.Time, - Value = message.Count, - Value2 = message.Value, + Count = message.Count, + Price = message.Value, }); await _tradeDataProvider.AddDataTo5MinuteWindowCache(message.Figi, Constants._5minBuyCacheKey, new Contracts.Declisions.Dtos.CachedValue() { Time = message.Time, - Value = (decimal)message.Count, - Value2 = message.Value, + Count = (decimal)message.Count, + Price = message.Value, }); await _tradeDataProvider.AddDataTo15MinuteWindowCache(message.Figi, Constants._15minBuyCacheKey, new Contracts.Declisions.Dtos.CachedValue() { Time = message.Time, - Value = (decimal)message.Count, - Value2 = message.Value, + Count = (decimal)message.Count, + Price = message.Value, }); await _tradeDataProvider.AddDataTo1MinuteWindowCache(message.Figi, Constants._1minBuyCacheKey, new Contracts.Declisions.Dtos.CachedValue() { Time = message.Time, - Value = (decimal)message.Count, - Value2 = message.Value, + Count = (decimal)message.Count, + Price = message.Value, }); } if (message.Direction == 2) @@ -416,32 +178,32 @@ namespace KLHZ.Trader.Core.Exchange.Services await _tradeDataProvider.AddDataTo5MinuteWindowCache(message.Figi, Constants._5minSellCacheKey, new Contracts.Declisions.Dtos.CachedValue() { Time = message.Time, - Value = (decimal)message.Count, - Value2 = message.Value, + Count = (decimal)message.Count, + Price = message.Value, }); await _tradeDataProvider.AddDataTo15MinuteWindowCache(message.Figi, Constants._15minSellCacheKey, new Contracts.Declisions.Dtos.CachedValue() { Time = message.Time, - Value = (decimal)message.Count, - Value2 = message.Value, + Count = (decimal)message.Count, + Price = message.Value, }); await _tradeDataProvider.AddDataTo1MinuteWindowCache(message.Figi, Constants._1minSellCacheKey, new Contracts.Declisions.Dtos.CachedValue() { Time = message.Time, - Value = (decimal)message.Count, - Value2 = message.Value, + Count = (decimal)message.Count, + Price = message.Value, }); await _tradeDataProvider.AddDataTo20SecondsWindowCache(message.Figi, "2", new Contracts.Declisions.Dtos.CachedValue() { Time = message.Time, - Value = message.Count, - Value2 = message.Value, + Count = message.Count, + Price = message.Value, }); } } #endregion - if (_tradingInstrumentsFigis.Contains(message.Figi) && (message.Figi == "FUTIMOEXF000" || message.Figi == "BBG004730N88") && message.Direction == 1) + if (_exchangeConfig.TradingInstrumentsFigis.Contains(message.Figi) && (message.Figi == "FUTIMOEXF000" || message.Figi == "BBG004730N88") && message.Direction == 1) { var smallWindow = TimeSpan.FromSeconds(120); var bigWindow = TimeSpan.FromSeconds(240); @@ -454,29 +216,29 @@ namespace KLHZ.Trader.Core.Exchange.Services var trades = buys.ToList(); trades.AddRange(sells); var trades2 = trades.OrderBy(t => t.Time).ToArray(); - if (trades2.TryCalcTimeWindowsDiff(bigWindow, smallWindow, v => v.Value, false, out var tradesDiff) - && buys.TryCalcTimeWindowsDiff(bigWindow, smallWindow, v => v.Value2, true, out var pricesDiff)) + if (trades2.TryCalcTimeWindowsDiff(bigWindow, smallWindow, v => v.Count, false, out var tradesDiff) + && buys.TryCalcTimeWindowsDiff(bigWindow, smallWindow, v => v.Price, true, out var pricesDiff)) { await LogPrice(message, "privcesDiff", pricesDiff); await LogPrice(message, "tradevolume_diff", tradesDiff); await _tradeDataProvider.AddDataTo15MinuteWindowCache(message.Figi, "5min_diff", new Contracts.Declisions.Dtos.CachedValue() { Time = message.Time, - Value = tradesDiff, - Value2 = pricesDiff, + Count = tradesDiff, + Price = pricesDiff, }); var diffs = await _tradeDataProvider.GetDataFrom15MinuteWindowCache(message.Figi, "5min_diff"); - if (diffs.TryCalcTimeWindowsDiff(bigWindow, smallWindow, (c) => c.Value2, true, out var resdp) - && diffs.TryCalcTimeWindowsDiff(bigWindow, smallWindow, (c) => c.Value, true, out var resv)) + if (diffs.TryCalcTimeWindowsDiff(bigWindow, smallWindow, (c) => c.Price, true, out var resdp) + && diffs.TryCalcTimeWindowsDiff(bigWindow, smallWindow, (c) => c.Count, true, out var resv)) { await LogPrice(message, "privcesDiffDiff", (decimal)resdp); await LogPrice(message, "tradevolume_diff_diff", (decimal)resv); await _tradeDataProvider.AddDataTo15MinuteWindowCache(message.Figi, "5min_diff_diff", new Contracts.Declisions.Dtos.CachedValue() { Time = message.Time, - Value = resv, - Value2 = resdp, + Count = resv, + Price = resdp, }); if (diffs.TryCalcPirsonCorrelation(meanWindow, out var pirson)) { @@ -484,16 +246,10 @@ namespace KLHZ.Trader.Core.Exchange.Services await _tradeDataProvider.AddDataTo15MinuteWindowCache(message.Figi, "diffs_pirson", new Contracts.Declisions.Dtos.CachedValue() { Time = message.Time, - Value = (decimal)pirson, + Count = (decimal)pirson, }); - if (pirson < 0) - { - //PirsonZeroCrossings["FUTIMOEXF000"] = message.Time; - PirsonZeroCrossings[message.Figi] = message.Time; - // await SetEvents(message); - } var diffs_pirson = await _tradeDataProvider.GetDataFrom15MinuteWindowCache(message.Figi, "diffs_pirson"); - if (diffs_pirson.TryCalcTimeWindowsDiff(bigWindow, smallWindow, (c) => c.Value, true, out var res)) + if (diffs_pirson.TryCalcTimeWindowsDiff(bigWindow, smallWindow, (c) => c.Count, true, out var res)) { await LogPrice(message, "diffs_pirson_diff", (decimal)res); if (DPirsonValues.TryGetValue(message.Figi, out var olddpirs)) @@ -514,57 +270,7 @@ namespace KLHZ.Trader.Core.Exchange.Services DPirsonValues[message.Figi] = res; } } - - - var diffdiff = await _tradeDataProvider.GetDataFrom15MinuteWindowCache(message.Figi, "5min_diff_diff"); - - - //resdp = diffdiff.Where(d => message.Time - d.Time < TimeSpan.FromSeconds(45)).ToArray().Mean2(); - - if (privcesDiffDiff.TryGetValue(message.Figi, out var priceOld)) - { - privcesDiffDiffPrev[message.Figi] = priceOld; - } - privcesDiffDiff[message.Figi] = pricesDiff; - - if (privcesDiffDiff.TryGetValue(message.Figi, out var dprice2) - && privcesDiffDiffPrev.TryGetValue(message.Figi, out var dprice2_prev)) - { - if (((dprice2 < 0 && dprice2_prev > 0) || (dprice2 > 0 && dprice2_prev < 0))) - { - if (PirsonZeroCrossings.TryGetValue(message.Figi, out var pir) - && message.Time - pir < TimeSpan.FromSeconds(60)) - { - if (pricesDiff < 0) - { - PricesZeroCrossingsDown["FUTIMOEXF000"] = message.Time; - //await SetEvents(message); - await LogPrice(message, "price_diff2_point_short", message.Value); - } - if (pricesDiff > 0) - { - PricesZeroCrossingsUp["FUTIMOEXF000"] = message.Time; - //await SetEvents(message); - await LogPrice(message, "price_diff2_point_long", message.Value); - } - } - } - } - - if (diffdiff.TryCalcPirsonCorrelation(meanWindow, out var pirson2)) - { - if (correlations.TryGetValue(message.Figi, out var corr)) - { - correlationsPrev[message.Figi] = corr; - } - await LogPrice(message, "diffs2_pirson", (decimal)pirson2); - correlations[message.Figi] = pirson2; - } } - - - - } if (timesCache.TryGetValue(message.Figi, out var dt)) @@ -661,308 +367,6 @@ namespace KLHZ.Trader.Core.Exchange.Services } } - - // - private async Task SetEvents(INewPrice message) - { - DateTime pz = DateTime.MinValue; - if (PirsonZeroCrossings.TryGetValue(message.Figi, out var pir)) - { - if (PricesZeroCrossingsDown.TryGetValue(message.Figi, out pz)) - { - if (message.Time - pir < TimeSpan.FromSeconds(120) && message.Time - pz < TimeSpan.FromSeconds(120)) - { - await LogPrice(message, "price_diff2_point_short", message.Value); - } - } - if (PricesZeroCrossingsUp.TryGetValue(message.Figi, out pz)) - { - if (message.Time - pir < TimeSpan.FromSeconds(120) && message.Time - pz < TimeSpan.FromSeconds(120)) - { - await LogPrice(message, "price_diff2_point_long", message.Value); - } - } - } - } - private async Task> CheckDivergency(DateTime[] timestamps, decimal[] values, INewPrice message) - { - var res = GetInitDict(1); - var time1 = message.Time - TimeSpan.FromMinutes(4); - var time2 = message.Time - TimeSpan.FromMinutes(2); - - var data1 = FFT.TrimValues(timestamps, values, TimeSpan.FromMinutes(4), TimeSpan.FromMinutes(2)); - var data2 = FFT.TrimValues(timestamps, values, TimeSpan.FromMinutes(2), TimeSpan.FromSeconds(-1)); - var p1 = data1.values.Sum() / data1.values.Length; - var p2 = data2.values.Sum() / data2.values.Length; - var dp = p2 - p1; - - var buys = await _tradeDataProvider.GetDataFrom5MinuteWindowCache(message.Figi, Constants._5minBuyCacheKey); - var sells = await _tradeDataProvider.GetDataFrom5MinuteWindowCache(message.Figi, Constants._5minSellCacheKey); - - var buysOld = buys.Where(b => b.Time < time2 && b.Time >= time1).ToArray(); - 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) - { - var dpriceNew = buysNew.Sum(b => b.Value); - var dpriceOld = buysOld.Sum(b => b.Value); - } - - - return res.ToImmutableDictionary(); - } - - 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) / 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 / 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 / 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", changeModIndicator); - } - - - - //var sells20 = await _tradeDataProvider.GetDataFrom20SecondsWindowCache(message.Figi, "2"); - //var buys20 = await _tradeDataProvider.GetDataFrom20SecondsWindowCache(message.Figi, "1"); - - //if (sells20.Length>0) - // await LogPrice(message.Figi, message.Ticker, message.Time, sells20.Sum(v => v.Value), "sells20"); - //if (buys20.Length > 0) - // await LogPrice(message.Figi, message.Ticker, message.Time, buys20.Sum(v => v.Value), "buys20"); - //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; - await LogPrice(message, "changemode2", diff2.Value); - } - } - if (diff5min < -0.2m || (diff2.HasValue && diff2 < -0.3m)) - { - res[TradingEvent.UptrendStart] = Constants.BlockingCoefficient; - } - if (diff5min > 0.2m || (diff2.HasValue && diff2 > 0.3m)) - { - res[TradingEvent.DowntrendStart] = Constants.BlockingCoefficient; - } - if (diff5min > 2m) - { - res[TradingEvent.UptrendEnd] = Constants.BlockingCoefficient; - } - 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 = null, decimal? uptrendEndingDetectionMeanfullStep = null) - { - var resultMoveAvFull = MovingAverage.CheckByWindowAverageMean2(data.timestamps, data.prices, - windowMaxSize, smallWindow, bigWindow, uptrendStartingDetectionMeanfullStep, uptrendEndingDetectionMeanfullStep); - if (resultMoveAvFull.bigWindowAv != 0) - { - await LogPrice(message, Constants.BigWindowCrossingAverageProcessor, resultMoveAvFull.bigWindowAv); - await LogPrice(message, Constants.SmallWindowCrossingAverageProcessor, resultMoveAvFull.smallWindowAv); - } - var res = GetInitDict(0); - res[TradingEvent.DowntrendEnd] = Constants.PowerLowingCoefficient; - res[TradingEvent.UptrendEnd] = Constants.PowerLowingCoefficient; - if ((resultMoveAvFull.events & TradingEvent.UptrendStart) == TradingEvent.UptrendStart) - { - res[TradingEvent.UptrendStart] = Constants.UppingCoefficient; - //res[TradingEvent.DowntrendEnd] = Constants.UppingCoefficient; - } - if ((resultMoveAvFull.events & TradingEvent.UptrendEnd) == TradingEvent.UptrendEnd) - { - //res[TradingEvent.UptrendEnd] = Constants.UppingCoefficient; - res[TradingEvent.DowntrendStart] = Constants.UppingCoefficient; - } - return res; - } - - private async Task> GetPriceDiff2Mods((DateTime[] timestamps, decimal[] prices) data, int smallWindow, int bigWindow, -INewPrice message, int windowMaxSize, decimal? uptrendStartingDetectionMeanfullStep = null, decimal? uptrendEndingDetectionMeanfullStep = null) - { - var res = GetInitDict(0); - - if (privcesDiffDiff.TryGetValue(message.Figi, out var dprice2) && privcesDiffDiffPrev.TryGetValue(message.Figi, out var dprice2_prev)) - { - if ((dprice2 < 0 && dprice2_prev > 0) || (dprice2 > 0 && dprice2_prev < 0)) - { - res[TradingEvent.DowntrendStart] = Constants.UppingCoefficient; - } - } - return res; - //res[TradingEvent.DowntrendEnd] = Constants.PowerLowingCoefficient; - //res[TradingEvent.UptrendEnd] = Constants.PowerLowingCoefficient; - //if ((resultMoveAvFull.events & TradingEvent.UptrendStart) == TradingEvent.UptrendStart) - //{ - // res[TradingEvent.UptrendStart] = Constants.UppingCoefficient; - // //res[TradingEvent.DowntrendEnd] = Constants.UppingCoefficient; - //} - //if ((resultMoveAvFull.events & TradingEvent.UptrendEnd) == TradingEvent.UptrendEnd) - //{ - // //res[TradingEvent.UptrendEnd] = Constants.UppingCoefficient; - // res[TradingEvent.DowntrendStart] = Constants.UppingCoefficient; - //} - //return res; - } - - - private async Task> GetWindowAverageStartData2((DateTime[] timestamps, decimal[] prices) data, int smallWindow, int bigWindow, -INewPrice message, int windowMaxSize, decimal? uptrendStartingDetectionMeanfullStep = null, decimal? uptrendEndingDetectionMeanfullStep = null) - { - var res = GetInitDict(0); - var resultMoveAvFull = MovingAverage.CheckByWindowAverageMean2(data.timestamps, data.prices, - windowMaxSize, smallWindow, bigWindow, uptrendStartingDetectionMeanfullStep, uptrendEndingDetectionMeanfullStep); - if (resultMoveAvFull.bigWindowAv != 0) - { - await LogPrice(message, Constants.BigWindowCrossingAverageProcessor, resultMoveAvFull.bigWindowAv); - await LogPrice(message, Constants.SmallWindowCrossingAverageProcessor, resultMoveAvFull.smallWindowAv); - } - - if (correlationsPrev.TryGetValue(message.Figi, out var oldC) && correlations.TryGetValue(message.Figi, out var c)) - { - if ((oldC < 0 && c > 0) || (oldC > 0 && c < 0)) - { - if (resultMoveAvFull.bigWindowAv - resultMoveAvFull.smallWindowAv > 0.5m) - { - res[TradingEvent.DowntrendStart] = Constants.UppingCoefficient; - } - - if (resultMoveAvFull.smallWindowAv - resultMoveAvFull.bigWindowAv > 0.5m) - { - res[TradingEvent.UptrendStart] = Constants.UppingCoefficient; - } - - } - } - return res; - //res[TradingEvent.DowntrendEnd] = Constants.PowerLowingCoefficient; - //res[TradingEvent.UptrendEnd] = Constants.PowerLowingCoefficient; - //if ((resultMoveAvFull.events & TradingEvent.UptrendStart) == TradingEvent.UptrendStart) - //{ - // res[TradingEvent.UptrendStart] = Constants.UppingCoefficient; - // //res[TradingEvent.DowntrendEnd] = Constants.UppingCoefficient; - //} - //if ((resultMoveAvFull.events & TradingEvent.UptrendEnd) == TradingEvent.UptrendEnd) - //{ - // //res[TradingEvent.UptrendEnd] = Constants.UppingCoefficient; - // res[TradingEvent.DowntrendStart] = Constants.UppingCoefficient; - //} - //return res; - } - - private async Task GetAreasRelation((DateTime[] timestamps, decimal[] prices) data, INewPrice message) - { - var areasRel = -1m; - if (ShapeAreaCalculator.TryGetAreasRelation(data.timestamps, data.prices, message.Value, Constants.AreasRelationWindow, out var rel)) - { - await _tradeDataProvider.AddDataTo1MinuteWindowCache(message.Figi, Constants._1minCacheKey, new Contracts.Declisions.Dtos.CachedValue() - { - Time = message.Time, - Value = (decimal)rel - }); - var areas = await _tradeDataProvider.GetDataFrom1MinuteWindowCache(message.Figi, Constants._1minCacheKey); - - areasRel = (decimal)areas.Sum(a => a.Value) / areas.Length; - await LogPrice(message, Constants.AreasRelationProcessor, areasRel); - return areasRel > 0 ? areasRel : null; - } - return null; - } - - private async Task CheckHarmonicPosition(INewPrice message) - { - var data2 = await GetData(message); - var position = await CheckHarmonicPosition(data2, message); - return position; - } - private async Task ClosePositions(Asset[] assets, INewPrice message, bool withProfitOnly = true) { var loggedDeclisions = 0; @@ -1031,7 +435,7 @@ INewPrice message, int windowMaxSize, decimal? uptrendStartingDetectionMeanfullS var sign = positionType == PositionType.Long ? 1 : 1; foreach (var acc in accounts) { - if (IsOperationAllowed(acc, message.Value, 1, _accountCashPartFutures, _accountCashPart)) + if (TraderUtils.IsOperationAllowed(acc, message.Value, 1, _exchangeConfig.AccountCashPartFutures, _exchangeConfig.AccountCashPart)) { await acc.OpenPosition(message.Figi, positionType, stopLossShift, takeProfitShift, count); await _dataBus.Broadcast(new MessageForAdmin() @@ -1052,55 +456,14 @@ INewPrice message, int windowMaxSize, decimal? uptrendStartingDetectionMeanfullS } } - private async Task ProcessNewPriceIMOEXF3((DateTime[] timestamps, decimal[] prices) data, - ExchangeState state, - INewPrice message, int windowMaxSize, ImmutableDictionary changeModeData) + private async Task ExecuteDeclisions(INewPrice message, ImmutableDictionary result) { - if (data.timestamps.Length <= 4) - { - return; - } - - var steps = await GetSteps(message); - var windows = await GetWindowsSizes(message); - //var resTask1 = GetWindowAverageStartData(data, 30, 180, message, windowMaxSize, -2m, 2m,3); - var resTask1 = GetWindowAverageStartData2(data, (int)windows.smallWindow, (int)windows.bigWindow, message, windowMaxSize, steps.downtrendEndStep, steps.uptrendEndStep); - - var corrModsTask = GetCorrelationsMods(message); - ////var resTask3 = GetWindowAverageStartData(data, 30, 180, message, windowMaxSize, 0, 0,0.7m); - //var getFFTModsTask = GetFFTMods(message); - //var getLocalTrendsModsTask = GetLocalTrendsMods(data, message); - //var getSellsDiffsModsTask = GetSellsDiffsMods(message); - //var getTradingModeModsTask = GetTradingModeMods(message); - var getSpeedResultantModsTask = GetSpeedResultantMods(message); - - await Task.WhenAll(resTask1, getSpeedResultantModsTask); - //var assetType = _tradeDataProvider.GetAssetTypeByFigi(message.Figi); - //if (resTask1.Result[TradingEvent.UptrendStart] >= 1) - //{ - - //} - - var result = resTask1.Result; - - //result = MergeResults(result, resTask2.Result.ToImmutableDictionary()); - //result = MergeResults(result, resTask3.Result.ToImmutableDictionary()); - //result = MergeResultsMax(result, changeModeData); - //result = MergeResultsMult(result, corrModsTask.Result); - // result = MergeResultsMax(result, getLocalTrendsModsTask.Result); - //result = MergeResultsMult(result, getFFTModsTask.Result); - //result = MergeResultsMult(result, getSellsDiffsModsTask.Result); - //result = MergeResultsMult(result, getTradingModeModsTask.Result); - //result = MergeResultsMult(result, getSpeedResultantModsTask.Result); - - - + var state = ExchangeScheduler.GetCurrentState(); if (result[TradingEvent.UptrendStart] >= Constants.UppingCoefficient && !LongOpeningStops.ContainsKey(message.Figi) && state == ExchangeState.Open ) { - LastTradingEvents[message.Figi] = TradingEvent.UptrendStart; var stops = GetStops(message, PositionType.Long); if (!message.IsHistoricalData && BotModeSwitcher.CanPurchase()) { @@ -1121,7 +484,6 @@ INewPrice message, int windowMaxSize, decimal? uptrendStartingDetectionMeanfullS && state == ExchangeState.Open ) { - LastTradingEvents[message.Figi] = TradingEvent.DowntrendStart; var stops = GetStops(message, PositionType.Short); if (!message.IsHistoricalData && BotModeSwitcher.CanPurchase()) { @@ -1260,11 +622,11 @@ INewPrice message, int windowMaxSize, decimal? uptrendStartingDetectionMeanfullS { if (assetType == AssetType.Common) { - return _shareComission; + return _exchangeConfig.ShareComission; } else if (assetType == AssetType.Futures) { - return _futureComission; + return _exchangeConfig.FutureComission; } else { @@ -1275,53 +637,14 @@ INewPrice message, int windowMaxSize, decimal? uptrendStartingDetectionMeanfullS private decimal GetLeverage(string figi, bool isShort) { var res = 1m; - if (Leverages.TryGetValue(figi, out var leverage)) + var leverage = _exchangeConfig.InstrumentsSettings.FirstOrDefault(l => l.Figi == figi); + if (leverage != null) { res = isShort ? leverage.ShortLeverage : leverage.LongLeverage; } return res; } - private async ValueTask CalcTradingMode(INewPrice message) - { - var res = TradingMode.None; - 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) - { - if (LocalTrends.TryCalcTrendDiff(largeData.timestamps, largeData.prices, out var largeDataRes) - && LocalTrends.TryCalcTrendDiff(smallData.timestamps, smallData.prices, out var smallDataRes)) - { - if (largeDataRes > 0 && largeDataRes <= 4 && System.Math.Abs(smallDataRes) < 3) - { - res = TradingMode.Stable; - } - if (largeDataRes < 0 && largeDataRes >= -5 && smallDataRes < 1) - { - res = TradingMode.SlowDropping; - } - if ((largeDataRes > 5 && smallDataRes > 0)) - { - res = TradingMode.Growing; - } - if ((largeDataRes < -5 && smallDataRes < 0)) - { - res = TradingMode.Dropping; - } - if (smallDataRes > 7) - { - res = TradingMode.Growing; - } - if (smallDataRes < -7) - { - res = TradingMode.Dropping; - } - } - } - return res; - } - private async ValueTask CalcTradingMode2(INewPrice message) { var res = TradingMode.None; @@ -1356,38 +679,6 @@ INewPrice message, int windowMaxSize, decimal? uptrendStartingDetectionMeanfullS 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, 0.5m); ; - } - case TradingMode.SlowDropping: - { - return (-1m, 0.5m); - } - case TradingMode.SlowGrowing: - { - return (-0.5m, 1m); - } - case TradingMode.Growing: - { - return (-0.5m, 1.5m); - } - default: - { - return (-0.5m, 0.5m); - } - } - } - private (decimal stopLoss, decimal takeProfit) GetStops(INewPrice message, PositionType type) { var mode = TradingModes[message.Figi]; @@ -1442,161 +733,9 @@ INewPrice message, int windowMaxSize, decimal? uptrendStartingDetectionMeanfullS return (stopLossShift, takeProfitShift); } - private async Task> GetFFTMods(INewPrice message) - { - var res = GetInitDict(1); - var position = await CheckHarmonicPosition(message); - - if (position == ValueAmplitudePosition.LowerThenMedianaGrowing) - { - //res[TradingEvent.UptrendStart] = Constants.UppingCoefficient; - res[TradingEvent.DowntrendEnd] = Constants.UppingCoefficient; - //res[TradingEvent.UptrendEnd] = Constants.LowingCoefficient; - res[TradingEvent.DowntrendStart] = Constants.LowingCoefficient; - } - if (position == ValueAmplitudePosition.UpperThen30Decil) - { - res[TradingEvent.UptrendStart] = Constants.LowingCoefficient; - //res[TradingEvent.DowntrendEnd] = Constants.LowingCoefficient; - //res[TradingEvent.UptrendEnd] = Constants.UppingCoefficient; - res[TradingEvent.DowntrendEnd] = Constants.UppingCoefficient; - } - return res.ToImmutableDictionary(); - } - - private async Task> GetAreasMods((DateTime[] timestamps, decimal[] prices) data, INewPrice message) - { - var res = GetInitDict(1); - var areas = await GetAreasRelation(data, message); - if (areas.HasValue && areas.Value > 0.2m && areas.Value <= 0.8m) - { - //res[TradingEvent.UptrendStart] = Constants.PowerLowingCoefficient; - } - if (areas.HasValue && areas.Value > 0.8m) - { - //res[TradingEvent.DowntrendStart] = Constants.UppingCoefficient; - //res[TradingEvent.DowntrendEnd] = Constants.UppingCoefficient; - } - return res.ToImmutableDictionary(); - } - - private Task> GetLocalTrendsMods((DateTime[] timestamps, decimal[] prices) data, INewPrice message) - { - var res = GetInitDict(0); - if (LocalTrends.TryGetLocalTrends(data.timestamps, data.prices, TimeSpan.FromSeconds(15), TimeSpan.FromSeconds(15), 0.5, out var localTrends)) - { - if ((localTrends & TradingEvent.UptrendEnd) == TradingEvent.UptrendEnd) - { - res[TradingEvent.UptrendEnd] = Constants.PowerUppingCoefficient; - } - if ((localTrends & TradingEvent.DowntrendEnd) == TradingEvent.DowntrendEnd) - { - res[TradingEvent.DowntrendEnd] = Constants.PowerUppingCoefficient; - } - } - return Task.FromResult(res.ToImmutableDictionary()); - } - - - private async Task> GetCorrelationsMods(INewPrice message) - { - var res = GetInitDict(1); - - return res.ToImmutableDictionary(); - if (PirsonZeroCrossings.TryGetValue(message.Figi, out var pir)) - { - if (PricesZeroCrossingsUp.TryGetValue(message.Figi, out var prU)) - { - var dt1 = message.Time - prU; - var dt2 = message.Time - pir; - - if (dt1 < TimeSpan.FromMinutes(0.5) && dt2 < TimeSpan.FromMinutes(0.5)) - { - //res[TradingEvent.DowntrendStart] = Constants.UppingCoefficient; - res[TradingEvent.UptrendStart] = Constants.UppingCoefficient; - } - } - if (PricesZeroCrossingsDown.TryGetValue(message.Figi, out var prD)) - { - var dt1 = message.Time - prD; - var dt2 = message.Time - pir; - - if (dt1 < TimeSpan.FromMinutes(0.5) && dt2 < TimeSpan.FromMinutes(0.5)) - { - res[TradingEvent.DowntrendStart] = Constants.UppingCoefficient; - //res[TradingEvent.UptrendStart] = Constants.UppingCoefficient; - } - } - } - return res.ToImmutableDictionary(); - } - - private async Task> GetSellsDiffsMods(INewPrice message) - { - var res = GetInitDict(1); - var sberSells5min = await _tradeDataProvider.GetDataFrom5MinuteWindowCache("BBG004730N88", Constants._5minSellCacheKey); - var sberBuys5min = await _tradeDataProvider.GetDataFrom5MinuteWindowCache("BBG004730N88", Constants._5minBuyCacheKey); - - var sberSells1min = await _tradeDataProvider.GetDataFrom1MinuteWindowCache("BBG004730N88", Constants._1minSellCacheKey); - var sberBuys1min = await _tradeDataProvider.GetDataFrom1MinuteWindowCache("BBG004730N88", Constants._1minBuyCacheKey); - - var sells5min = sberSells5min.Sum(s => s.Value); - var buys5min = sberBuys5min.Sum(s => s.Value); - var sells1min = sberSells1min.Sum(s => s.Value); - var buys1min = sberBuys1min.Sum(s => s.Value); - - var su = sells5min + buys5min; - var uptrendEndMode = 1m; - var downtrendEndMode = 1m; - var downstartMode = 1m; - var uptrendStartMode = 1m; - - - if (su != 0) - { - var trades_rel = (sells5min / su - 0.5m) * 2; - var bys_rel = buys1min / su; - var sells_rel = sells1min / su; - await LogPrice(message, "trades_rel", trades_rel); - await LogPrice(message, "bys_rel", bys_rel); - await LogPrice(message, "sells_rel", sells_rel); - - if (trades_rel > 0.7m) - { - //uptrendStartMode *= Constants.LowingCoefficient; - } - - if (System.Math.Abs(bys_rel) > 0.6m || System.Math.Abs(sells_rel) > 0.6m) - { - uptrendStartMode *= Constants.PowerUppingCoefficient; - downstartMode *= Constants.PowerUppingCoefficient; - uptrendEndMode *= Constants.BlockingCoefficient; - downtrendEndMode *= Constants.BlockingCoefficient; - } - else if (System.Math.Abs(bys_rel) > 0.3m || System.Math.Abs(sells_rel) > 0.3m) - { - uptrendStartMode *= Constants.UppingCoefficient; - downstartMode *= Constants.UppingCoefficient; - uptrendEndMode *= Constants.LowingCoefficient; - downtrendEndMode *= Constants.LowingCoefficient; - } - //else if (System.Math.Abs(bys_rel) <= 0.2m && System.Math.Abs(sells_rel) <= 0.2m) - //{ - // uptrendEndMode *= Constants.UppingCoefficient; - // downtrendEndMode *= Constants.UppingCoefficient; - //} - - res[TradingEvent.UptrendStart] = uptrendStartMode; - res[TradingEvent.UptrendEnd] = uptrendEndMode; - res[TradingEvent.DowntrendEnd] = downtrendEndMode; - res[TradingEvent.DowntrendStart] = downstartMode; - } - return res.ToImmutableDictionary(); - } - private Task> GetTradingModeMods(INewPrice message) { - var res = GetInitDict(1); + var res = TraderUtils.GetInitDict(1); var mode = TradingModes[message.Figi]; if (mode == TradingMode.None) { @@ -1636,53 +775,5 @@ INewPrice message, int windowMaxSize, decimal? uptrendStartingDetectionMeanfullS return Task.FromResult(res.ToImmutableDictionary()); } - internal static bool IsOperationAllowed(IManagedAccount account, decimal boutPrice, decimal count, - decimal accountCashPartFutures, decimal accountCashPart) - { - if (!BotModeSwitcher.CanPurchase()) return false; - - var balance = account.Balance; - var total = account.Total; - - var futures = account.Assets.Values.FirstOrDefault(v => v.Type == AssetType.Futures); - if (futures != null) - { - if ((balance - boutPrice * count) / total < accountCashPartFutures) return false; - } - else - { - if ((balance - boutPrice * count) / total < accountCashPart) return false; - } - - return true; - } - - private static Dictionary GetInitDict(decimal initValue) - { - var values = System.Enum.GetValues(); - return values.ToDictionary(v => v, v => initValue); - } - - private static Dictionary MergeResultsMult(Dictionary res, ImmutableDictionary data) - { - foreach (var k in res.Keys) - { - var valRes = res[k]; - var valData = data[k]; - res[k] = valRes * valData; - } - return res; - } - - private static Dictionary MergeResultsMax(Dictionary res, ImmutableDictionary data) - { - foreach (var k in res.Keys) - { - var valRes = res[k]; - var valData = data[k]; - res[k] = System.Math.Max(valRes, valData); - } - return res; - } } } diff --git a/KLHZ.Trader.Core/Exchange/Services/TraderDataProvider.cs b/KLHZ.Trader.Core/Exchange/Services/TraderDataProvider.cs index eea3fa3..cdd5658 100644 --- a/KLHZ.Trader.Core/Exchange/Services/TraderDataProvider.cs +++ b/KLHZ.Trader.Core/Exchange/Services/TraderDataProvider.cs @@ -257,7 +257,7 @@ namespace KLHZ.Trader.Core.Exchange.Services { if (ShapeAreaCalculator.TryGetAreasRelation(cachedData.timestamps, cachedData.prices, price.Value, Constants.AreasRelationWindow, out var rel)) { - await AddDataTo1MinuteWindowCache(price.Figi, Constants._1minCacheKey, new CachedValue() { Time = price.Time, Value = (decimal)rel }); + await AddDataTo1MinuteWindowCache(price.Figi, Constants._1minCacheKey, new CachedValue() { Time = price.Time, Count = (decimal)rel }); } } } diff --git a/KLHZ.Trader.Core/Exchange/Utils/TraderUtils.cs b/KLHZ.Trader.Core/Exchange/Utils/TraderUtils.cs new file mode 100644 index 0000000..4c4920c --- /dev/null +++ b/KLHZ.Trader.Core/Exchange/Utils/TraderUtils.cs @@ -0,0 +1,93 @@ +using KLHZ.Trader.Core.Common; +using KLHZ.Trader.Core.Contracts.Declisions.Dtos.Enums; +using KLHZ.Trader.Core.Contracts.Messaging.Dtos.Interfaces; +using KLHZ.Trader.Core.DataLayer.Entities.Prices; +using KLHZ.Trader.Core.Exchange.Interfaces; +using KLHZ.Trader.Core.Exchange.Models.AssetsAccounting; +using System.Collections.Immutable; + +namespace KLHZ.Trader.Core.Exchange.Utils +{ + internal static class TraderUtils + { + internal static Dictionary MergeResultsMult(Dictionary res, ImmutableDictionary data) + { + foreach (var k in res.Keys) + { + var valRes = res[k]; + var valData = data[k]; + res[k] = valRes * valData; + } + return res; + } + + internal static Dictionary MergeResultsMax(Dictionary res, ImmutableDictionary data) + { + foreach (var k in res.Keys) + { + var valRes = res[k]; + var valData = data[k]; + res[k] = System.Math.Max(valRes, valData); + } + return res; + } + internal static Dictionary GetInitDict(decimal initValue) + { + var values = System.Enum.GetValues(); + return values.ToDictionary(v => v, v => initValue); + } + + internal static bool IsOperationAllowed(IManagedAccount account, decimal boutPrice, decimal count, + decimal accountCashPartFutures, decimal accountCashPart) + { + if (!BotModeSwitcher.CanPurchase()) return false; + + var balance = account.Balance; + var total = account.Total; + + var futures = account.Assets.Values.FirstOrDefault(v => v.Type == AssetType.Futures); + if (futures != null) + { + if ((balance - boutPrice * count) / total < accountCashPartFutures) return false; + } + else + { + if ((balance - boutPrice * count) / total < accountCashPart) return false; + } + + return true; + } + + internal static INewPrice FilterHighFreqValues(INewPrice message, Dictionary> pricesCache1) + { + if (!pricesCache1.TryGetValue(message.Figi, out var list)) + { + list = new List(); + pricesCache1[message.Figi] = list; + } + list.Add(message); + + if ((list.Last().Time - list.First().Time).TotalSeconds < 0.5) + { + list.Add(message); + return message; + } + else + { + message = new PriceChange() + { + Figi = message.Figi, + Ticker = message.Ticker, + Count = list.Sum(l => l.Count), + Direction = message.Direction, + IsHistoricalData = message.IsHistoricalData, + Time = message.Time, + Value = list.Sum(l => l.Value) / list.Count + }; + list.Clear(); + return message; + } + } + + } +}