diff --git a/KLHZ.Trader.Core.Contracts/Declisions/Interfaces/IPriceHistoryCacheUnit.cs b/KLHZ.Trader.Core.Contracts/Declisions/Interfaces/IPriceHistoryCacheUnit.cs index fdd930e..baaba9d 100644 --- a/KLHZ.Trader.Core.Contracts/Declisions/Interfaces/IPriceHistoryCacheUnit.cs +++ b/KLHZ.Trader.Core.Contracts/Declisions/Interfaces/IPriceHistoryCacheUnit.cs @@ -9,7 +9,15 @@ namespace KLHZ.Trader.Core.Contracts.Declisions.Interfaces public ValueTask AddData(INewPrice priceChange); public ValueTask<(DateTime[] timestamps, float[] prices)> GetData(); public ValueTask AddOrderbook(IOrderbook orderbook); + + /// + /// Число заявок на продаже в стакане. + /// public decimal AsksCount { get; } + + /// + /// Число заявок на покупку в стакане. + /// public decimal BidsCount { get; } } } diff --git a/KLHZ.Trader.Core.Math/Declisions/Utils/MovingAverage.cs b/KLHZ.Trader.Core.Math/Declisions/Utils/MovingAverage.cs index 61c21ed..5831302 100644 --- a/KLHZ.Trader.Core.Math/Declisions/Utils/MovingAverage.cs +++ b/KLHZ.Trader.Core.Math/Declisions/Utils/MovingAverage.cs @@ -38,13 +38,15 @@ namespace KLHZ.Trader.Core.Math.Declisions.Utils } if (shift > 0) { + var i1 = size - 1 - shift; + var i2 = size - 2 - shift; var isCrossing = Lines.IsLinesCrossing( - times[size - 1 - shift], - times[size - 2 - shift], - twav15s[size - 1 - shift], - twav15s[size - 2 - shift], - twav120s[size - 1 - shift], - twav120s[size - 2 - shift]); + times[i1], + times[i2], + twav15s[i1], + twav15s[i2], + twav120s[i1], + twav120s[i2]); if (shift == 1 && !isCrossing) //если нет пересечения скользящих средний с окном 120 и 15 секунд между //текущей и предыдущей точкой - можно не продолжать выполнение. { diff --git a/KLHZ.Trader.Core/Exchange/Services/ExchangeDataReader.cs b/KLHZ.Trader.Core/Exchange/Services/ExchangeDataReader.cs index 54039b4..038c827 100644 --- a/KLHZ.Trader.Core/Exchange/Services/ExchangeDataReader.cs +++ b/KLHZ.Trader.Core/Exchange/Services/ExchangeDataReader.cs @@ -142,14 +142,10 @@ namespace KLHZ.Trader.Core.Exchange.Services { SubscribeOrderBookRequest = bookRequest }); - using var context = await _dbContextFactory.CreateDbContextAsync(); - context.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking; + var pricesBuffer = new List(); var orderbookItemsBuffer = new List(); var tradesBuffer = new List(); - var lastWriteOrderbooks = DateTime.UtcNow; - var lastWriteTrades = DateTime.UtcNow; - var lastWritePrices = DateTime.UtcNow; var lastWrite = DateTime.UtcNow; await foreach (var response in stream.ResponseStream.ReadAllAsync()) { @@ -214,25 +210,34 @@ namespace KLHZ.Trader.Core.Exchange.Services await _eventBus.Broadcast(message); } - if (orderbookItemsBuffer.Count + pricesBuffer.Count + tradesBuffer.Count > 1000 || (DateTime.UtcNow - lastWrite).TotalSeconds > 10) + if (orderbookItemsBuffer.Count + pricesBuffer.Count + tradesBuffer.Count > 0 || (DateTime.UtcNow - lastWrite).TotalSeconds > 10) { - lastWrite = DateTime.UtcNow; - if (orderbookItemsBuffer.Count > 0) + try { - await context.OrderbookItems.AddRangeAsync(orderbookItemsBuffer); - orderbookItemsBuffer.Clear(); + using var context = await _dbContextFactory.CreateDbContextAsync(); + context.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking; + lastWrite = DateTime.UtcNow; + if (orderbookItemsBuffer.Count > 0) + { + await context.OrderbookItems.AddRangeAsync(orderbookItemsBuffer); + orderbookItemsBuffer.Clear(); + } + if (pricesBuffer.Count > 0) + { + await context.PriceChanges.AddRangeAsync(pricesBuffer); + pricesBuffer.Clear(); + } + if (tradesBuffer.Count > 0) + { + await context.InstrumentTrades.AddRangeAsync(tradesBuffer); + tradesBuffer.Clear(); + } + await context.SaveChangesAsync(); } - if (pricesBuffer.Count > 0) + catch (Exception ex) { - await context.PriceChanges.AddRangeAsync(pricesBuffer); - pricesBuffer.Clear(); + _logger.LogError(ex, "Ошибка при сохранении данных биржи."); } - if (tradesBuffer.Count > 0) - { - await context.InstrumentTrades.AddRangeAsync(tradesBuffer); - tradesBuffer.Clear(); - } - await context.SaveChangesAsync(); } } } diff --git a/KLHZ.Trader.Core/Exchange/Services/Trader.cs b/KLHZ.Trader.Core/Exchange/Services/Trader.cs index 076da5e..4629f8b 100644 --- a/KLHZ.Trader.Core/Exchange/Services/Trader.cs +++ b/KLHZ.Trader.Core/Exchange/Services/Trader.cs @@ -98,7 +98,6 @@ namespace KLHZ.Trader.Core.Exchange.Services _dataBus.AddChannel(nameof(Trader), _ordersbookChannel); _ = ProcessPrices(); _ = ProcessOrdersbooks(); - _ = BackgroundWorker(); } private async Task InitStops() @@ -119,86 +118,121 @@ namespace KLHZ.Trader.Core.Exchange.Services while (await _pricesChannel.Reader.WaitToReadAsync()) { var message = await _pricesChannel.Reader.ReadAsync(); - //if (_tradingInstrumentsFigis.Contains(message.Figi)) - //{ - // if (_historyCash.TryGetValue(message.Figi, out var unit)) - // { - // await unit.AddData(message); - // } - // else - // { - // unit = new PriceHistoryCacheUnit2(message.Figi, message); - // _historyCash.TryAdd(message.Figi, unit); - // } - // var data = await unit.GetData(); - // var declisionsForSave = new List(); - // if (message.Figi == "FUTIMOEXF000") - // { - // var result = MovingAverage.CheckByWindowAverageMean(data.timestamps, data.prices, 100, 3f); - // if ((result & TradingEvent.StopBuy) == TradingEvent.StopBuy) - // { - // var stopTo = DateTime.UtcNow.AddMinutes(_buyStopLength); - // BuyStops.AddOrUpdate(message.Figi, stopTo, (k, v) => stopTo); - // declisionsForSave.Add(new Declision() - // { - // AccountId = string.Empty, - // Figi = message.Figi, - // Ticker = message.Ticker, - // Price = message.Value, - // Time = message.IsHistoricalData ? message.Time : DateTime.UtcNow, - // Action = DeclisionTradeAction.StopBuy, - // }); - // } + if (_tradingInstrumentsFigis.Contains(message.Figi)) + { + if (_historyCash.TryGetValue(message.Figi, out var unit)) + { + await unit.AddData(message); + } + else + { + unit = new PriceHistoryCacheUnit2(message.Figi, message); + _historyCash.TryAdd(message.Figi, unit); + } + try + { + var data = await unit.GetData(); + var declisionsForSave = new List(); + if (message.Figi == "FUTIMOEXF000") + { + var result = MovingAverage.CheckByWindowAverageMean(data.timestamps, data.prices, 100, 3f); + if ((result & TradingEvent.StopBuy) == TradingEvent.StopBuy) + { + var stopTo = DateTime.UtcNow.AddMinutes(_buyStopLength); + BuyStops.AddOrUpdate(message.Figi, stopTo, (k, v) => stopTo); + declisionsForSave.Add(new Declision() + { + AccountId = string.Empty, + Figi = message.Figi, + Ticker = message.Ticker, + Price = message.Value, + Time = message.IsHistoricalData ? message.Time : DateTime.UtcNow, + Action = DeclisionTradeAction.StopBuy, + }); + } - // if ((result & TradingEvent.LongOpen) == TradingEvent.LongOpen - // && !BuyStops.TryGetValue(message.Figi, out _)) - // { - // var stopTo = DateTime.UtcNow.AddMinutes(_buyStopLength); - // BuyStops.AddOrUpdate(message.Figi, stopTo, (k, v) => stopTo); - // declisionsForSave.Add(new Declision() - // { - // AccountId = string.Empty, - // Figi = message.Figi, - // Ticker = message.Ticker, - // Price = message.Value, - // Time = message.IsHistoricalData ? message.Time : DateTime.UtcNow, - // Action = DeclisionTradeAction.OpenLong, - // }); - // } + if ((result & TradingEvent.LongOpen) == TradingEvent.LongOpen + && ((unit.BidsCount / unit.AsksCount) > 0.5m)) + { + if (BuyStops.TryGetValue(message.Figi, out var dt)) + { + if (dt > DateTime.UtcNow) + { + continue; + } + else + { + BuyStops.TryRemove(message.Figi, out _); + } + } + var stopTo = DateTime.UtcNow.AddMinutes(_buyStopLength); + BuyStops.AddOrUpdate(message.Figi, stopTo, (k, v) => stopTo); + declisionsForSave.Add(new Declision() + { + AccountId = string.Empty, + Figi = message.Figi, + Ticker = message.Ticker, + Price = message.Value, + Time = message.IsHistoricalData ? message.Time : DateTime.UtcNow, + Action = DeclisionTradeAction.OpenLong, + }); + } - // if ((result & TradingEvent.LongClose) == TradingEvent.LongClose) - // { - // declisionsForSave.Add(new Declision() - // { - // AccountId = string.Empty, - // Figi = message.Figi, - // Ticker = message.Ticker, - // Price = message.Value, - // Time = message.IsHistoricalData ? message.Time : DateTime.UtcNow, - // Action = DeclisionTradeAction.CloseLong, - // }); - // } + if ((result & TradingEvent.LongClose) == TradingEvent.LongClose) + { + declisionsForSave.Add(new Declision() + { + AccountId = string.Empty, + Figi = message.Figi, + Ticker = message.Ticker, + Price = message.Value, + Time = message.IsHistoricalData ? message.Time : DateTime.UtcNow, + Action = DeclisionTradeAction.CloseLong, + }); + } - // if ((result & TradingEvent.ShortOpen) == TradingEvent.ShortOpen && (unit.AsksCount/ unit.BidsCount>2 )) - // { - // declisionsForSave.Add(new Declision() - // { - // AccountId = string.Empty, - // Figi = message.Figi, - // Ticker = message.Ticker, - // Price = message.Value, - // Time = message.IsHistoricalData ? message.Time : DateTime.UtcNow, - // Action = DeclisionTradeAction.OpenShort, - // }); - // } + if ((result & TradingEvent.ShortOpen) == TradingEvent.ShortOpen && (unit.BidsCount / unit.AsksCount < 2)) + { + declisionsForSave.Add(new Declision() + { + AccountId = string.Empty, + Figi = message.Figi, + Ticker = message.Ticker, + Price = message.Value, + Time = message.IsHistoricalData ? message.Time : DateTime.UtcNow, + Action = DeclisionTradeAction.OpenShort, + }); + } - // using var context = await _dbContextFactory.CreateDbContextAsync(); - // context.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking; + if ((result & TradingEvent.ShortClose) == TradingEvent.ShortClose) + { + declisionsForSave.Add(new Declision() + { + AccountId = string.Empty, + Figi = message.Figi, + Ticker = message.Ticker, + Price = message.Value, + Time = message.IsHistoricalData ? message.Time : DateTime.UtcNow, + Action = DeclisionTradeAction.CloseShort, + }); + } - // await context.AddRangeAsync(declisionsForSave); - // await context.SaveChangesAsync(); - // } - //} + if (declisionsForSave.Count > 0) + { + using var context = await _dbContextFactory.CreateDbContextAsync(); + context.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking; + + await context.AddRangeAsync(declisionsForSave); + await context.SaveChangesAsync(); + declisionsForSave.Clear(); + } + } + } + catch (Exception ex) + { + + } + } } } @@ -216,27 +250,6 @@ namespace KLHZ.Trader.Core.Exchange.Services } } - private async Task BackgroundWorker() - { - var keysForRemove = new List(); - while (!_cts.IsCancellationRequested) - { - var time = DateTime.UtcNow; - foreach (var kvp in BuyStops) - { - if (kvp.Value > time) - { - keysForRemove.Add(kvp.Key); - } - } - foreach (var key in keysForRemove) - { - BuyStops.TryRemove(key, out _); - } - await Task.Delay(10000); - } - } - public Task StopAsync(CancellationToken cancellationToken) { _cts.Cancel();