Чистка кода
parent
50938f093e
commit
00de5bee2e
|
@ -3,7 +3,7 @@
|
||||||
public class CachedValue
|
public class CachedValue
|
||||||
{
|
{
|
||||||
public DateTime Time { get; init; }
|
public DateTime Time { get; init; }
|
||||||
public decimal Value { get; init; }
|
public decimal Count { get; init; }
|
||||||
public decimal Value2 { get; init; }
|
public decimal Price { get; init; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,12 +7,12 @@ namespace KLHZ.Trader.Core.Math.Declisions.Utils
|
||||||
|
|
||||||
public static decimal Mean(this CachedValue[] values)
|
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)
|
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)
|
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 tradevolume_diffMean = values.Mean();
|
||||||
var dprice_diffMean = values.Mean2();
|
var dprice_diffMean = values.Mean2();
|
||||||
var sum1 = (double)values.Sum(d => (d.Value - tradevolume_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.Value - tradevolume_diffMean) * (d.Value - tradevolume_diffMean));
|
var sum2 = values.Sum(d => (d.Count - tradevolume_diffMean) * (d.Count - tradevolume_diffMean));
|
||||||
var sum3 = values.Sum(d => (d.Value2 - dprice_diffMean) * (d.Value2 - dprice_diffMean));
|
var sum3 = values.Sum(d => (d.Price - dprice_diffMean) * (d.Price - dprice_diffMean));
|
||||||
if (sum2 != 0 && sum3 != 0)
|
if (sum2 != 0 && sum3 != 0)
|
||||||
{
|
{
|
||||||
result = (decimal)(sum1 / System.Math.Sqrt((double)(sum2 * sum3)));
|
result = (decimal)(sum1 / System.Math.Sqrt((double)(sum2 * sum3)));
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
namespace KLHZ.Trader.Core.DataLayer.Entities.Trades.Enums
|
namespace KLHZ.Trader.Core.Common
|
||||||
{
|
{
|
||||||
public enum TradeDirection
|
public enum TradeDirection
|
||||||
{
|
{
|
|
@ -1,10 +0,0 @@
|
||||||
namespace KLHZ.Trader.Core.DataLayer.Entities.Trades.Enums
|
|
||||||
{
|
|
||||||
public enum AssetType
|
|
||||||
{
|
|
||||||
Unknown = 0,
|
|
||||||
Common = 1,
|
|
||||||
Future = 2,
|
|
||||||
Currency = 3,
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,9 +0,0 @@
|
||||||
namespace KLHZ.Trader.Core.DataLayer.Entities.Trades.Enums
|
|
||||||
{
|
|
||||||
public enum PositionType
|
|
||||||
{
|
|
||||||
Unknown = 0,
|
|
||||||
Long = 1,
|
|
||||||
Short = 2
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,52 +0,0 @@
|
||||||
using KLHZ.Trader.Core.DataLayer.Entities.Trades.Enums;
|
|
||||||
using System.ComponentModel.DataAnnotations.Schema;
|
|
||||||
|
|
||||||
namespace KLHZ.Trader.Core.DataLayer.Entities.Trades
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Сделка, совершенная ботом.
|
|
||||||
/// </summary>
|
|
||||||
[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; }
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,7 +1,6 @@
|
||||||
using KLHZ.Trader.Core.DataLayer.Entities.Declisions;
|
using KLHZ.Trader.Core.DataLayer.Entities.Declisions;
|
||||||
using KLHZ.Trader.Core.DataLayer.Entities.Orders;
|
using KLHZ.Trader.Core.DataLayer.Entities.Orders;
|
||||||
using KLHZ.Trader.Core.DataLayer.Entities.Prices;
|
using KLHZ.Trader.Core.DataLayer.Entities.Prices;
|
||||||
using KLHZ.Trader.Core.DataLayer.Entities.Trades;
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
|
|
||||||
|
@ -9,7 +8,6 @@ namespace KLHZ.Trader.Core.DataLayer
|
||||||
{
|
{
|
||||||
public class TraderDbContext : DbContext
|
public class TraderDbContext : DbContext
|
||||||
{
|
{
|
||||||
public DbSet<Trade> Trades { get; set; }
|
|
||||||
public DbSet<Declision> Declisions { get; set; }
|
public DbSet<Declision> Declisions { get; set; }
|
||||||
public DbSet<PriceChange> PriceChanges { get; set; }
|
public DbSet<PriceChange> PriceChanges { get; set; }
|
||||||
public DbSet<ProcessedPrice> ProcessedPrices { get; set; }
|
public DbSet<ProcessedPrice> ProcessedPrices { get; set; }
|
||||||
|
@ -23,15 +21,6 @@ namespace KLHZ.Trader.Core.DataLayer
|
||||||
{
|
{
|
||||||
modelBuilder.UseSerialColumns();
|
modelBuilder.UseSerialColumns();
|
||||||
|
|
||||||
modelBuilder.Entity<Trade>(entity =>
|
|
||||||
{
|
|
||||||
entity.HasKey(e1 => e1.Id);
|
|
||||||
entity.Property(e => e.BoughtAt)
|
|
||||||
.HasConversion(
|
|
||||||
v => v.ToUniversalTime(),
|
|
||||||
v => DateTime.SpecifyKind(v, DateTimeKind.Utc));
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity<Declision>(entity =>
|
modelBuilder.Entity<Declision>(entity =>
|
||||||
{
|
{
|
||||||
entity.HasKey(e1 => e1.Id);
|
entity.HasKey(e1 => e1.Id);
|
||||||
|
|
|
@ -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; }
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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; }
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -2,16 +2,16 @@
|
||||||
{
|
{
|
||||||
public class ExchangeConfig
|
public class ExchangeConfig
|
||||||
{
|
{
|
||||||
public bool ExchangeDataRecievingEnabled { get; set; }
|
public bool ExchangeDataRecievingEnabled { get; init; }
|
||||||
public decimal StopBuyLengthMinuts { get; set; }
|
public decimal StopBuyLengthMinuts { get; init; }
|
||||||
public decimal FutureComission { get; set; }
|
public decimal FutureComission { get; init; }
|
||||||
public decimal ShareComission { get; set; }
|
public decimal ShareComission { get; init; }
|
||||||
public decimal AccountCashPart { get; set; }
|
public decimal AccountCashPart { get; init; }
|
||||||
public decimal AccountCashPartFutures { get; set; }
|
public decimal AccountCashPartFutures { get; init; }
|
||||||
public decimal DefaultBuyPartOfAccount { get; set; }
|
public decimal DefaultBuyPartOfAccount { get; init; }
|
||||||
public string[] DataRecievingInstrumentsFigis { get; set; } = [];
|
public string[] DataRecievingInstrumentsFigis { get; init; } = [];
|
||||||
public string[] TradingInstrumentsFigis { get; set; } = [];
|
public string[] TradingInstrumentsFigis { get; init; } = [];
|
||||||
public string[] ManagingAccountNamePatterns { get; set; } = [];
|
public string[] ManagingAccountNamePatterns { get; init; } = [];
|
||||||
public InstrumentSettings[] InstrumentsSettings { get; set; } = [];
|
public InstrumentSettings[] InstrumentsSettings { get; init; } = [];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,6 @@ using KLHZ.Trader.Core.DataLayer.Entities.Orders;
|
||||||
using KLHZ.Trader.Core.DataLayer.Entities.Prices;
|
using KLHZ.Trader.Core.DataLayer.Entities.Prices;
|
||||||
using KLHZ.Trader.Core.Exchange.Extentions;
|
using KLHZ.Trader.Core.Exchange.Extentions;
|
||||||
using KLHZ.Trader.Core.Exchange.Models.Configs;
|
using KLHZ.Trader.Core.Exchange.Models.Configs;
|
||||||
using KLHZ.Trader.Core.Exchange.Utils;
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Microsoft.Extensions.Hosting;
|
using Microsoft.Extensions.Hosting;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
@ -156,39 +155,7 @@ namespace KLHZ.Trader.Core.Exchange.Services
|
||||||
Count = response.Trade.Quantity,
|
Count = response.Trade.Quantity,
|
||||||
};
|
};
|
||||||
|
|
||||||
//await _tradeDataProvider.AddData(message, TimeSpan.FromHours(7));
|
|
||||||
await _eventBus.Broadcast(message);
|
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);
|
pricesBuffer.Add(message);
|
||||||
}
|
}
|
||||||
if (response.Orderbook != null)
|
if (response.Orderbook != null)
|
||||||
|
|
|
@ -50,13 +50,11 @@ namespace KLHZ.Trader.Core.Exchange.Services
|
||||||
|
|
||||||
public ImmutableDictionary<string, Asset> Assets => GetAssets();
|
public ImmutableDictionary<string, Asset> Assets => GetAssets();
|
||||||
|
|
||||||
|
|
||||||
private readonly InvestApiClient _investApiClient;
|
private readonly InvestApiClient _investApiClient;
|
||||||
private readonly IDbContextFactory<TraderDbContext> _dbContextFactory;
|
private readonly IDbContextFactory<TraderDbContext> _dbContextFactory;
|
||||||
private readonly ILogger<TraderDataProvider> _logger;
|
private readonly ILogger<TraderDataProvider> _logger;
|
||||||
private readonly IOptions<ExchangeConfig> _options;
|
private readonly IOptions<ExchangeConfig> _options;
|
||||||
|
|
||||||
|
|
||||||
private readonly Dictionary<string, Asset> _assets = new();
|
private readonly Dictionary<string, Asset> _assets = new();
|
||||||
private readonly ConcurrentDictionary<string, DateTime> _usedOrderIds = new();
|
private readonly ConcurrentDictionary<string, DateTime> _usedOrderIds = new();
|
||||||
|
|
||||||
|
@ -140,26 +138,20 @@ namespace KLHZ.Trader.Core.Exchange.Services
|
||||||
using var context = await _dbContextFactory.CreateDbContextAsync();
|
using var context = await _dbContextFactory.CreateDbContextAsync();
|
||||||
context.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking;
|
context.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking;
|
||||||
|
|
||||||
var trades = await context.Trades
|
|
||||||
.Where(t => t.ArchiveStatus == 0)
|
|
||||||
.OrderByDescending(t => t.BoughtAt)
|
|
||||||
.ToListAsync();
|
|
||||||
|
|
||||||
var oldAssets = _assets.ToDictionary();
|
var oldAssets = _assets.ToDictionary();
|
||||||
_assets.Clear();
|
_assets.Clear();
|
||||||
foreach (var position in portfolio.Positions)
|
foreach (var position in portfolio.Positions)
|
||||||
{
|
{
|
||||||
oldAssets.TryGetValue(position.Figi, out var oldAsset);
|
oldAssets.TryGetValue(position.Figi, out var oldAsset);
|
||||||
var newAssetId = oldAsset?.AssetId ?? Guid.NewGuid();
|
var newAssetId = oldAsset?.AssetId ?? Guid.NewGuid();
|
||||||
var trade = trades.FirstOrDefault(t => t.Figi == position.Figi && t.AssetId == newAssetId);
|
|
||||||
var asset = new Asset()
|
var asset = new Asset()
|
||||||
{
|
{
|
||||||
AssetId = newAssetId,
|
AssetId = newAssetId,
|
||||||
AccountId = AccountId,
|
AccountId = AccountId,
|
||||||
Figi = position.Figi,
|
Figi = position.Figi,
|
||||||
Ticker = position.Ticker,
|
Ticker = position.Ticker,
|
||||||
BoughtAt = trade?.BoughtAt ?? DateTime.UtcNow,
|
BoughtAt = oldAsset?.BoughtAt ?? DateTime.UtcNow,
|
||||||
BoughtPrice = trade?.Price ?? position.AveragePositionPrice,
|
BoughtPrice = oldAsset?.BoughtPrice ?? position.AveragePositionPrice,
|
||||||
Type = position.InstrumentType.ParseInstrumentType(),
|
Type = position.InstrumentType.ParseInstrumentType(),
|
||||||
Position = position.Quantity > 0 ? PositionType.Long : PositionType.Short,
|
Position = position.Quantity > 0 ? PositionType.Long : PositionType.Short,
|
||||||
BlockedItems = position.BlockedLots,
|
BlockedItems = position.BlockedLots,
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -257,7 +257,7 @@ namespace KLHZ.Trader.Core.Exchange.Services
|
||||||
{
|
{
|
||||||
if (ShapeAreaCalculator.TryGetAreasRelation(cachedData.timestamps, cachedData.prices, price.Value, Constants.AreasRelationWindow, out var rel))
|
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 });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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<TradingEvent, decimal> MergeResultsMult(Dictionary<TradingEvent, decimal> res, ImmutableDictionary<TradingEvent, decimal> data)
|
||||||
|
{
|
||||||
|
foreach (var k in res.Keys)
|
||||||
|
{
|
||||||
|
var valRes = res[k];
|
||||||
|
var valData = data[k];
|
||||||
|
res[k] = valRes * valData;
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static Dictionary<TradingEvent, decimal> MergeResultsMax(Dictionary<TradingEvent, decimal> res, ImmutableDictionary<TradingEvent, decimal> 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<TradingEvent, decimal> GetInitDict(decimal initValue)
|
||||||
|
{
|
||||||
|
var values = System.Enum.GetValues<TradingEvent>();
|
||||||
|
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<string, List<INewPrice>> pricesCache1)
|
||||||
|
{
|
||||||
|
if (!pricesCache1.TryGetValue(message.Figi, out var list))
|
||||||
|
{
|
||||||
|
list = new List<INewPrice>();
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue