diff --git a/Kraken.Net.UnitTests/KrakenRestIntegrationTests.cs b/Kraken.Net.UnitTests/KrakenRestIntegrationTests.cs index b00dde9..7b249c5 100644 --- a/Kraken.Net.UnitTests/KrakenRestIntegrationTests.cs +++ b/Kraken.Net.UnitTests/KrakenRestIntegrationTests.cs @@ -11,7 +11,7 @@ namespace Kraken.Net.UnitTests { [NonParallelizable] - internal class KrakenRestIntegrationTests : RestIntergrationTest + internal class KrakenRestIntegrationTests : RestIntegrationTest { public override bool Run { get; set; } diff --git a/Kraken.Net/Clients/FuturesApi/KrakenRestClientFuturesApiShared.cs b/Kraken.Net/Clients/FuturesApi/KrakenRestClientFuturesApiShared.cs index e471776..c7a19fc 100644 --- a/Kraken.Net/Clients/FuturesApi/KrakenRestClientFuturesApiShared.cs +++ b/Kraken.Net/Clients/FuturesApi/KrakenRestClientFuturesApiShared.cs @@ -49,7 +49,16 @@ async Task>> IBalanceRestClient.Get #region Klines client - GetKlinesOptions IKlineRestClient.GetKlinesOptions { get; } = new GetKlinesOptions(SharedPaginationSupport.Ascending, true, 5000, false); + GetKlinesOptions IKlineRestClient.GetKlinesOptions { get; } = new GetKlinesOptions(SharedPaginationSupport.Ascending, true, 5000, false, + SharedKlineInterval.OneMinute, + SharedKlineInterval.FiveMinutes, + SharedKlineInterval.FifteenMinutes, + SharedKlineInterval.ThirtyMinutes, + SharedKlineInterval.OneHour, + SharedKlineInterval.FourHours, + SharedKlineInterval.TwelveHours, + SharedKlineInterval.OneDay, + SharedKlineInterval.OneWeek); async Task>> IKlineRestClient.GetKlinesAsync(GetKlinesRequest request, INextPageToken? pageToken, CancellationToken ct) { var interval = (Enums.FuturesKlineInterval)request.Interval; diff --git a/Kraken.Net/Clients/FuturesApi/KrakenSocketClientFuturesApi.cs b/Kraken.Net/Clients/FuturesApi/KrakenSocketClientFuturesApi.cs index dc94a54..8163cbc 100644 --- a/Kraken.Net/Clients/FuturesApi/KrakenSocketClientFuturesApi.cs +++ b/Kraken.Net/Clients/FuturesApi/KrakenSocketClientFuturesApi.cs @@ -85,7 +85,7 @@ protected override AuthenticationProvider CreateAuthenticationProvider(ApiCreden /// public async Task> SubscribeToHeartbeatUpdatesAsync(Action> handler, CancellationToken ct = default) { - var subscription = new KrakenFuturesSubscription(_logger, "heartbeat", null, handler); + var subscription = new KrakenFuturesSubscription(_logger, "heartbeat", null, x => handler(x.WithDataTimestamp(x.Data.Timestamp))); return await SubscribeAsync(BaseAddress.AppendPath("ws/v1"), subscription, ct).ConfigureAwait(false); } @@ -100,7 +100,7 @@ public Task> SubscribeToTickerUpdatesAsync(string /// public async Task> SubscribeToTickerUpdatesAsync(IEnumerable symbols, Action> handler, CancellationToken ct = default) { - var subscription = new KrakenFuturesSubscription(_logger, "ticker", symbols.ToList(), handler); + var subscription = new KrakenFuturesSubscription(_logger, "ticker", symbols.ToList(), x => handler(x.WithDataTimestamp(x.Data.Timestamp))); return await SubscribeAsync(BaseAddress.AppendPath("ws/v1"), subscription, ct).ConfigureAwait(false); } @@ -118,7 +118,7 @@ public async Task> SubscribeToTradeUpdatesAsync( Action>> handler, CancellationToken ct = default) { - var subscription = new KrakenFuturesTradesSubscription(_logger, symbols.ToList(), handler); + var subscription = new KrakenFuturesTradesSubscription(_logger, symbols.ToList(), x => handler(x.WithDataTimestamp(x.Data.Max(x => x.Timestamp)))); return await SubscribeAsync(BaseAddress.AppendPath("ws/v1"), subscription, ct).ConfigureAwait(false); } @@ -133,7 +133,7 @@ public Task> SubscribeToMiniTickerUpdatesAsync(st /// public async Task> SubscribeToMiniTickerUpdatesAsync(IEnumerable symbols, Action> handler, CancellationToken ct = default) { - var subscription = new KrakenFuturesSubscription(_logger, "ticker_lite", symbols.ToList(), handler); + var subscription = new KrakenFuturesSubscription(_logger, "ticker_lite", symbols.ToList(), x => handler(x)); return await SubscribeAsync(BaseAddress.AppendPath("ws/v1"), subscription, ct).ConfigureAwait(false); } diff --git a/Kraken.Net/Clients/FuturesApi/KrakenSocketClientFuturesApiShared.cs b/Kraken.Net/Clients/FuturesApi/KrakenSocketClientFuturesApiShared.cs index 441244d..dd49f85 100644 --- a/Kraken.Net/Clients/FuturesApi/KrakenSocketClientFuturesApiShared.cs +++ b/Kraken.Net/Clients/FuturesApi/KrakenSocketClientFuturesApiShared.cs @@ -173,7 +173,7 @@ async Task> IPositionSocketClient.SubscribeTo return; handler(update.AsExchangeEvent>(Exchange, update.Data.Positions.Select( - x => new SharedPosition(x.Symbol, Math.Abs(x.Balance), update.Timestamp) + x => new SharedPosition(x.Symbol, Math.Abs(x.Balance), update.DataTime ?? update.ReceiveTime) { AverageOpenPrice = x.EntryPrice, PositionSide = x.Balance > 0 ? SharedPositionSide.Long : SharedPositionSide.Short, diff --git a/Kraken.Net/Clients/SpotApi/KrakenRestClientSpotApiShared.cs b/Kraken.Net/Clients/SpotApi/KrakenRestClientSpotApiShared.cs index a710f0f..14a8096 100644 --- a/Kraken.Net/Clients/SpotApi/KrakenRestClientSpotApiShared.cs +++ b/Kraken.Net/Clients/SpotApi/KrakenRestClientSpotApiShared.cs @@ -15,7 +15,15 @@ internal partial class KrakenRestClientSpotApi : IKrakenRestClientSpotApiShared #region Kline client - GetKlinesOptions IKlineRestClient.GetKlinesOptions { get; } = new GetKlinesOptions(SharedPaginationSupport.NotSupported, true, 720, false); + GetKlinesOptions IKlineRestClient.GetKlinesOptions { get; } = new GetKlinesOptions(SharedPaginationSupport.NotSupported, true, 720, false, + SharedKlineInterval.OneMinute, + SharedKlineInterval.FiveMinutes, + SharedKlineInterval.FifteenMinutes, + SharedKlineInterval.ThirtyMinutes, + SharedKlineInterval.OneHour, + SharedKlineInterval.FourHours, + SharedKlineInterval.OneDay, + SharedKlineInterval.OneWeek); async Task>> IKlineRestClient.GetKlinesAsync(GetKlinesRequest request, INextPageToken? pageToken, CancellationToken ct) { diff --git a/Kraken.Net/Clients/SpotApi/KrakenSocketClientSpotApi.cs b/Kraken.Net/Clients/SpotApi/KrakenSocketClientSpotApi.cs index 4100036..4ab4074 100644 --- a/Kraken.Net/Clients/SpotApi/KrakenSocketClientSpotApi.cs +++ b/Kraken.Net/Clients/SpotApi/KrakenSocketClientSpotApi.cs @@ -126,8 +126,8 @@ public async Task> SubscribeToSystemStatusUpdates public async Task> SubscribeToTickerUpdatesAsync(string symbol, Action> handler, CancellationToken ct = default) { var subscription = new KrakenSubscriptionV2>(_logger, "ticker", [symbol], null, null, null, null, - x => handler(x.As(x.Data.First()).WithSymbol(x.Data.First().Symbol)) - ); + x => handler(x.As(x.Data.First()) + .WithSymbol(x.Data.First().Symbol))); return await SubscribeAsync(BaseAddress.AppendPath("v2"), subscription, ct).ConfigureAwait(false); } @@ -188,7 +188,7 @@ public async Task> SubscribeToAggregatedOrderBook depth.ValidateIntValues(nameof(depth), 10, 25, 100, 500, 1000); var subscription = new KrakenSubscriptionV2>(_logger, "book", symbols.ToArray(), null, snapshot, depth, null, - x => handler(x.As(x.Data.First()).WithSymbol(x.Data.First().Symbol)) + x => handler(x.As(x.Data.First()).WithSymbol(x.Data.First().Symbol).WithDataTimestamp(x.Data.First().Timestamp)) ); return await SubscribeAsync(BaseAddress.AppendPath("v2"), subscription, ct).ConfigureAwait(false); } diff --git a/Kraken.Net/Clients/SpotApi/KrakenSocketClientSpotApiShared.cs b/Kraken.Net/Clients/SpotApi/KrakenSocketClientSpotApiShared.cs index eabfb25..d000b11 100644 --- a/Kraken.Net/Clients/SpotApi/KrakenSocketClientSpotApiShared.cs +++ b/Kraken.Net/Clients/SpotApi/KrakenSocketClientSpotApiShared.cs @@ -64,7 +64,16 @@ async Task> IBookTickerSocketClient.Subscribe #endregion #region Kline client - SubscribeKlineOptions IKlineSocketClient.SubscribeKlineOptions { get; } = new SubscribeKlineOptions(false); + SubscribeKlineOptions IKlineSocketClient.SubscribeKlineOptions { get; } = new SubscribeKlineOptions(false, + SharedKlineInterval.OneMinute, + SharedKlineInterval.ThreeMinutes, + SharedKlineInterval.FiveMinutes, + SharedKlineInterval.FifteenMinutes, + SharedKlineInterval.ThirtyMinutes, + SharedKlineInterval.OneHour, + SharedKlineInterval.FourHours, + SharedKlineInterval.OneDay, + SharedKlineInterval.OneWeek); async Task> IKlineSocketClient.SubscribeToKlineUpdatesAsync(SubscribeKlineRequest request, Action> handler, CancellationToken ct) { var interval = (Enums.KlineInterval)request.Interval; diff --git a/Kraken.Net/ExtensionMethods/ServiceCollectionExtensions.cs b/Kraken.Net/ExtensionMethods/ServiceCollectionExtensions.cs index c9a3c9d..3063e64 100644 --- a/Kraken.Net/ExtensionMethods/ServiceCollectionExtensions.cs +++ b/Kraken.Net/ExtensionMethods/ServiceCollectionExtensions.cs @@ -110,8 +110,8 @@ private static IServiceCollection AddKrakenCore( handler.AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate; handler.DefaultProxyCredentials = CredentialCache.DefaultCredentials; } - catch (PlatformNotSupportedException) - { } + catch (PlatformNotSupportedException) { } + catch (NotImplementedException) { } // Mono runtime throws NotImplementedException for DefaultProxyCredentials setting var options = serviceProvider.GetRequiredService>().Value; if (options.Proxy != null) diff --git a/Kraken.Net/Kraken.Net.csproj b/Kraken.Net/Kraken.Net.csproj index ac2a3c3..d1ee42f 100644 --- a/Kraken.Net/Kraken.Net.csproj +++ b/Kraken.Net/Kraken.Net.csproj @@ -51,7 +51,7 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/Kraken.Net/Kraken.Net.xml b/Kraken.Net/Kraken.Net.xml index d785068..9103b34 100644 --- a/Kraken.Net/Kraken.Net.xml +++ b/Kraken.Net/Kraken.Net.xml @@ -8871,6 +8871,11 @@ Checksum + + + Data timestamp + + Order book entry diff --git a/Kraken.Net/Objects/Internal/KrakenSocketUpdateV2.cs b/Kraken.Net/Objects/Internal/KrakenSocketUpdateV2.cs index fbd2f29..43cb27d 100644 --- a/Kraken.Net/Objects/Internal/KrakenSocketUpdateV2.cs +++ b/Kraken.Net/Objects/Internal/KrakenSocketUpdateV2.cs @@ -6,6 +6,8 @@ internal class KrakenSocketUpdateV2 public string Channel { get; set; } = string.Empty; [JsonPropertyName("type")] public string Type { get; set; } = string.Empty; + [JsonPropertyName("timestamp")] + public DateTime? Timestamp { get; set; } [JsonPropertyName("data")] public T Data { get; set; } = default!; } diff --git a/Kraken.Net/Objects/Models/Socket/KrakenBookUpdate.cs b/Kraken.Net/Objects/Models/Socket/KrakenBookUpdate.cs index b6b2f63..f1b8489 100644 --- a/Kraken.Net/Objects/Models/Socket/KrakenBookUpdate.cs +++ b/Kraken.Net/Objects/Models/Socket/KrakenBookUpdate.cs @@ -25,6 +25,11 @@ public record KrakenBookUpdate /// [JsonPropertyName("checksum")] public long Checksum { get; set; } + /// + /// Data timestamp + /// + [JsonPropertyName("timestamp")] + public DateTime Timestamp { get; set; } } /// diff --git a/Kraken.Net/Objects/Sockets/Subscriptions/Futures/KrakenFuturesAccountLogSubscription.cs b/Kraken.Net/Objects/Sockets/Subscriptions/Futures/KrakenFuturesAccountLogSubscription.cs index 4642e65..5a5b46a 100644 --- a/Kraken.Net/Objects/Sockets/Subscriptions/Futures/KrakenFuturesAccountLogSubscription.cs +++ b/Kraken.Net/Objects/Sockets/Subscriptions/Futures/KrakenFuturesAccountLogSubscription.cs @@ -51,12 +51,12 @@ public override CallResult DoHandleMessage(SocketConnection connection, DataEven { if (message.Data is KrakenFuturesAccountLogsSnapshotUpdate snapshot) { - _snapshotHandler.Invoke(message.As(snapshot, snapshot.Feed, null, SocketUpdateType.Snapshot)); + _snapshotHandler.Invoke(message.As(snapshot, snapshot.Feed, null, SocketUpdateType.Snapshot).WithDataTimestamp(snapshot.Logs.Any() ? snapshot.Logs.Max(x => x.Timestamp) : null)); return new CallResult(null); } else if (message.Data is KrakenFuturesAccountLogsUpdate update) { - _updateHandler.Invoke(message.As(update, update.Feed, null, SocketUpdateType.Update)); + _updateHandler.Invoke(message.As(update, update.Feed, null, SocketUpdateType.Update).WithDataTimestamp(update.NewEntry.Timestamp)); return new CallResult(null); } diff --git a/Kraken.Net/Objects/Sockets/Subscriptions/Futures/KrakenFuturesBalanceSubscription.cs b/Kraken.Net/Objects/Sockets/Subscriptions/Futures/KrakenFuturesBalanceSubscription.cs index 3603f98..5f3df26 100644 --- a/Kraken.Net/Objects/Sockets/Subscriptions/Futures/KrakenFuturesBalanceSubscription.cs +++ b/Kraken.Net/Objects/Sockets/Subscriptions/Futures/KrakenFuturesBalanceSubscription.cs @@ -45,7 +45,8 @@ public KrakenFuturesBalanceSubscription(ILogger logger, Action message) { var data = (KrakenFuturesBalancesUpdate)message.Data; - _handler.Invoke(message.As(data, data.Feed, null, string.Equals(data.Feed, "balances_snapshot", StringComparison.Ordinal) ? SocketUpdateType.Snapshot : SocketUpdateType.Update)); + _handler.Invoke(message.As(data, data.Feed, null, string.Equals(data.Feed, "balances_snapshot", StringComparison.Ordinal) ? SocketUpdateType.Snapshot : SocketUpdateType.Update) + .WithDataTimestamp(data.Timestamp)); return new CallResult(null); } diff --git a/Kraken.Net/Objects/Sockets/Subscriptions/Futures/KrakenFuturesOrderbookSubscription.cs b/Kraken.Net/Objects/Sockets/Subscriptions/Futures/KrakenFuturesOrderbookSubscription.cs index c0c90a8..e28bc10 100644 --- a/Kraken.Net/Objects/Sockets/Subscriptions/Futures/KrakenFuturesOrderbookSubscription.cs +++ b/Kraken.Net/Objects/Sockets/Subscriptions/Futures/KrakenFuturesOrderbookSubscription.cs @@ -68,12 +68,12 @@ public override CallResult DoHandleMessage(SocketConnection connection, DataEven { if (message.Data is KrakenFuturesBookSnapshotUpdate snapshot) { - _snapshotHandler.Invoke(message.As(snapshot, snapshot.Feed, snapshot.Symbol, SocketUpdateType.Snapshot)); + _snapshotHandler.Invoke(message.As(snapshot, snapshot.Feed, snapshot.Symbol, SocketUpdateType.Snapshot).WithDataTimestamp(snapshot.Timestamp)); return new CallResult(null); } else if (message.Data is KrakenFuturesBookUpdate update) { - _updateHandler.Invoke(message.As(update, update.Feed, update.Symbol, SocketUpdateType.Update)); + _updateHandler.Invoke(message.As(update, update.Feed, update.Symbol, SocketUpdateType.Update).WithDataTimestamp(update.Timestamp)); return new CallResult(null); } diff --git a/Kraken.Net/Objects/Sockets/Subscriptions/Futures/KrakenFuturesOrdersSubscription.cs b/Kraken.Net/Objects/Sockets/Subscriptions/Futures/KrakenFuturesOrdersSubscription.cs index 5a41e64..15d112b 100644 --- a/Kraken.Net/Objects/Sockets/Subscriptions/Futures/KrakenFuturesOrdersSubscription.cs +++ b/Kraken.Net/Objects/Sockets/Subscriptions/Futures/KrakenFuturesOrdersSubscription.cs @@ -68,12 +68,12 @@ public override CallResult DoHandleMessage(SocketConnection connection, DataEven { if (message.Data is KrakenFuturesOpenOrdersSnapshotUpdate snapshot) { - _snapshotHandler.Invoke(message.As(snapshot, snapshot.Feed, snapshot.Symbol, SocketUpdateType.Snapshot)); + _snapshotHandler.Invoke(message.As(snapshot, snapshot.Feed, snapshot.Symbol, SocketUpdateType.Snapshot).WithDataTimestamp(snapshot.Orders.Any() ? snapshot.Orders.Max(x => x.LastUpdateTime) : null)); return new CallResult(null); } else if (message.Data is KrakenFuturesOpenOrdersUpdate update) { - _updateHandler.Invoke(message.As(update, update.Feed, update.Symbol, SocketUpdateType.Update)); + _updateHandler.Invoke(message.As(update, update.Feed, update.Symbol, SocketUpdateType.Update).WithDataTimestamp(update.Order?.LastUpdateTime)); return new CallResult(null); } diff --git a/Kraken.Net/Objects/Sockets/Subscriptions/Futures/KrakenFuturesUserTradeSubscription.cs b/Kraken.Net/Objects/Sockets/Subscriptions/Futures/KrakenFuturesUserTradeSubscription.cs index 0a993c2..181afbf 100644 --- a/Kraken.Net/Objects/Sockets/Subscriptions/Futures/KrakenFuturesUserTradeSubscription.cs +++ b/Kraken.Net/Objects/Sockets/Subscriptions/Futures/KrakenFuturesUserTradeSubscription.cs @@ -46,7 +46,8 @@ public KrakenFuturesUserTradeSubscription(ILogger logger, Action message) { var data = (KrakenFuturesUserTradesUpdate)message.Data; - _handler.Invoke(message.As(data, data.Feed, null, string.Equals(data.Feed, "fills_snapshot", StringComparison.Ordinal) ? SocketUpdateType.Snapshot : SocketUpdateType.Update)); + _handler.Invoke(message.As(data, data.Feed, null, string.Equals(data.Feed, "fills_snapshot", StringComparison.Ordinal) ? SocketUpdateType.Snapshot : SocketUpdateType.Update) + .WithDataTimestamp(data.Trades.Any() ? data.Trades.Max(x => x.Timestamp) : null)); return new CallResult(null); } } diff --git a/Kraken.Net/Objects/Sockets/Subscriptions/Spot/KrakenBalanceSubscription.cs b/Kraken.Net/Objects/Sockets/Subscriptions/Spot/KrakenBalanceSubscription.cs index 59d5d18..20975e1 100644 --- a/Kraken.Net/Objects/Sockets/Subscriptions/Spot/KrakenBalanceSubscription.cs +++ b/Kraken.Net/Objects/Sockets/Subscriptions/Spot/KrakenBalanceSubscription.cs @@ -73,9 +73,9 @@ public KrakenBalanceSubscription(ILogger logger, bool? snapshot, string token, A public override CallResult DoHandleMessage(SocketConnection connection, DataEvent message) { if (message.Data is KrakenSocketUpdateV2> snapshot) - _snapshotHandler?.Invoke(message.As(snapshot.Data, "balances", null, SocketUpdateType.Snapshot)); + _snapshotHandler?.Invoke(message.As(snapshot.Data, "balances", null, SocketUpdateType.Snapshot).WithDataTimestamp(snapshot.Timestamp)); else if (message.Data is KrakenSocketUpdateV2> update) - _updateHandler?.Invoke(message.As(update.Data, "balances", null, SocketUpdateType.Update)); + _updateHandler?.Invoke(message.As(update.Data, "balances", null, SocketUpdateType.Update).WithDataTimestamp(update.Timestamp)); return new CallResult(null); } } diff --git a/Kraken.Net/Objects/Sockets/Subscriptions/Spot/KrakenOrderSubscription.cs b/Kraken.Net/Objects/Sockets/Subscriptions/Spot/KrakenOrderSubscription.cs index 338d00b..9977028 100644 --- a/Kraken.Net/Objects/Sockets/Subscriptions/Spot/KrakenOrderSubscription.cs +++ b/Kraken.Net/Objects/Sockets/Subscriptions/Spot/KrakenOrderSubscription.cs @@ -69,7 +69,7 @@ public KrakenOrderSubscription(ILogger logger, bool? snapshotOrder, bool? snapsh public override CallResult DoHandleMessage(SocketConnection connection, DataEvent message) { var data = (KrakenSocketUpdateV2>)message.Data; - _updateHandler.Invoke(message.As(data.Data, "executions", null, data.Type == "snapshot" ? SocketUpdateType.Snapshot : SocketUpdateType.Update)); + _updateHandler.Invoke(message.As(data.Data, "executions", null, data.Type == "snapshot" ? SocketUpdateType.Snapshot : SocketUpdateType.Update).WithDataTimestamp(data.Timestamp)); return new CallResult(null); } } diff --git a/Kraken.Net/Objects/Sockets/Subscriptions/Spot/KrakenSubscriptionV2.cs b/Kraken.Net/Objects/Sockets/Subscriptions/Spot/KrakenSubscriptionV2.cs index e09f62c..65f5d6b 100644 --- a/Kraken.Net/Objects/Sockets/Subscriptions/Spot/KrakenSubscriptionV2.cs +++ b/Kraken.Net/Objects/Sockets/Subscriptions/Spot/KrakenSubscriptionV2.cs @@ -89,7 +89,7 @@ public KrakenSubscriptionV2(ILogger logger, string topic, IEnumerable? s public override CallResult DoHandleMessage(SocketConnection connection, DataEvent message) { var data = (KrakenSocketUpdateV2)message.Data!; - _handler.Invoke(message.As(data.Data, data.Channel, null, data.Type == "snapshot" ? SocketUpdateType.Snapshot : SocketUpdateType.Update)); + _handler.Invoke(message.As(data.Data, data.Channel, null, data.Type == "snapshot" ? SocketUpdateType.Snapshot : SocketUpdateType.Update).WithDataTimestamp(data.Timestamp)); return new CallResult(null); } diff --git a/Kraken.Net/Objects/Sockets/Subscriptions/Spot/KrakenSystemStatusSubscription.cs b/Kraken.Net/Objects/Sockets/Subscriptions/Spot/KrakenSystemStatusSubscription.cs index a9de313..4233664 100644 --- a/Kraken.Net/Objects/Sockets/Subscriptions/Spot/KrakenSystemStatusSubscription.cs +++ b/Kraken.Net/Objects/Sockets/Subscriptions/Spot/KrakenSystemStatusSubscription.cs @@ -23,7 +23,7 @@ public KrakenSystemStatusSubscription(ILogger logger, Action message) { var data = (KrakenSocketUpdateV2>)message.Data!; - _handler.Invoke(message.As(data.Data.First(), data.Channel, null, SocketUpdateType.Update)); + _handler.Invoke(message.As(data.Data.First(), data.Channel, null, SocketUpdateType.Update).WithDataTimestamp(data.Timestamp)); return new CallResult(null); }