Ecng.Net.SocketIO 1.0.488

There is a newer version of this package available.
See the version list below for details.
dotnet add package Ecng.Net.SocketIO --version 1.0.488
                    
NuGet\Install-Package Ecng.Net.SocketIO -Version 1.0.488
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Ecng.Net.SocketIO" Version="1.0.488" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Ecng.Net.SocketIO" Version="1.0.488" />
                    
Directory.Packages.props
<PackageReference Include="Ecng.Net.SocketIO" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Ecng.Net.SocketIO --version 1.0.488
                    
#r "nuget: Ecng.Net.SocketIO, 1.0.488"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Ecng.Net.SocketIO@1.0.488
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Ecng.Net.SocketIO&version=1.0.488
                    
Install as a Cake Addin
#tool nuget:?package=Ecng.Net.SocketIO&version=1.0.488
                    
Install as a Cake Tool

Ecng.Net.SocketIO

A high-performance .NET client library for WebSocket communication with automatic reconnection, message resending, and comprehensive connection state management.

Features

  • WebSocket Client Implementation - Full-featured WebSocket client with async/await support
  • Automatic Reconnection - Configurable reconnection attempts with exponential backoff
  • Command Resending - Automatic resend of commands after reconnection
  • Connection State Tracking - Track and aggregate connection states across multiple connections
  • JSON Serialization - Built-in JSON serialization for object messages
  • Flexible Logging - Customizable logging hooks for info, error, and verbose messages
  • RestSharp Integration - Helper methods for REST API calls with authentication
  • Thread-Safe - Safe to use in multi-threaded environments

Installation

Add the package reference to your project:

<PackageReference Include="Ecng.Net.SocketIO" Version="x.x.x" />

Quick Start

Basic WebSocket Connection

using Ecng.Net;

var socket = new WebSocketClient(
    url: "wss://example.com/socket",
    stateChanged: state => Console.WriteLine($"State: {state}"),
    error: ex => Console.WriteLine($"Error: {ex.Message}"),
    process: (msg, ct) =>
    {
        Console.WriteLine($"Received: {msg.AsString()}");
        return ValueTask.CompletedTask;
    },
    infoLog: (fmt, args) => Console.WriteLine(fmt, args),
    errorLog: (fmt, args) => Console.Error.WriteLine(fmt, args),
    verboseLog: (fmt, args) => Console.WriteLine($"[VERBOSE] {fmt}", args)
);

await socket.ConnectAsync(CancellationToken.None);

Sending Messages

// Send JSON object
await socket.SendAsync(new { type = "subscribe", channel = "trades" });

// Send string
await socket.SendAsync("Hello, WebSocket!");

// Send raw bytes
byte[] data = Encoding.UTF8.GetBytes("Raw message");
await socket.SendAsync(data, WebSocketMessageType.Text);

Core Components

WebSocketClient

The main class for WebSocket communication.

Constructor Parameters
public WebSocketClient(
    string url,                              // WebSocket URL (ws:// or wss://)
    Action<ConnectionStates> stateChanged,   // Connection state change callback
    Action<Exception> error,                 // Error handler
    Func<WebSocketMessage, CancellationToken, ValueTask> process,  // Message processor
    Action<string, object> infoLog,          // Info log handler
    Action<string, object> errorLog,         // Error log handler
    Action<string, object> verboseLog        // Verbose log handler (can be null)
)
Configuration Properties
// Reconnection settings
socket.ReconnectAttempts = 10;              // -1 for infinite, 0 for no reconnect
socket.ReconnectInterval = TimeSpan.FromSeconds(5);
socket.ResendInterval = TimeSpan.FromSeconds(2);
socket.ResendTimeout = TimeSpan.FromMilliseconds(500);

// Message encoding
socket.Encoding = Encoding.UTF8;

// Buffer sizes
socket.BufferSize = 1024 * 1024;            // 1MB for compressed data
socket.BufferSizeUncompress = 10 * 1024 * 1024; // 10MB for uncompressed

// JSON serialization
socket.Indent = true;
socket.SendSettings = new JsonSerializerSettings { ... };

// Auto-resend control
socket.DisableAutoResend = false;
Connection Management
// Connect
await socket.ConnectAsync(cancellationToken);
socket.Connect(); // Synchronous version

// Disconnect
socket.Disconnect();

// Check connection state
bool isConnected = socket.IsConnected;
ConnectionStates currentState = socket.State;

// Abort connection immediately
socket.Abort();
Sending Messages with Subscription Tracking
// Send with subscription ID for automatic resend after reconnect
long subscriptionId = 12345;
await socket.SendAsync(
    obj: new { action = "subscribe", symbol = "BTCUSD" },
    subId: subscriptionId
);

// Unsubscribe (negative ID removes from resend queue)
await socket.SendAsync(
    obj: new { action = "unsubscribe", symbol = "BTCUSD" },
    subId: -subscriptionId
);

// Manual resend management
socket.RemoveResend(subscriptionId);  // Remove specific subscription
socket.RemoveResend();                 // Remove all subscriptions
Advanced Features
// Custom initialization
socket.Init += ws =>
{
    ws.Options.SetRequestHeader("X-Custom-Header", "value");
    ws.Options.KeepAliveInterval = TimeSpan.FromSeconds(30);
};

// Post-connect hook
socket.PostConnect += async (isReconnect, ct) =>
{
    if (isReconnect)
        Console.WriteLine("Reconnected! Resubscribing...");

    await Task.CompletedTask;
};

// Pre-process received data (e.g., decompression)
socket.PreProcess2 = (input, output) =>
{
    // Decompress or transform data
    input.CopyTo(output);
    return input.Length;
};

// Send ping frame
await socket.SendOpCode(0x9);

WebSocketMessage

Represents an incoming message from the WebSocket.

// In your message processor
Func<WebSocketMessage, CancellationToken, ValueTask> process = (msg, ct) =>
{
    // Get as string
    string text = msg.AsString();

    // Deserialize to object
    var trade = msg.AsObject<TradeData>();

    // Deserialize to dynamic
    dynamic data = msg.AsObject();

    // Get JSON reader for streaming
    using var reader = msg.AsReader();

    // Access raw bytes
    ReadOnlyMemory<byte> bytes = msg.Memory;

    return ValueTask.CompletedTask;
};

Connection States

The ConnectionStates enum represents the current state of the connection:

public enum ConnectionStates
{
    Disconnected,   // Not connected
    Disconnecting,  // In process of disconnecting
    Connecting,     // In process of connecting
    Connected,      // Successfully connected
    Reconnecting,   // Attempting to reconnect
    Restored,       // Connection restored after reconnect
    Failed          // Connection failed
}

ConnectionStateTracker

Track and aggregate states across multiple connections.

var tracker = new ConnectionStateTracker();

// Add connections
tracker.Add(socket1);
tracker.Add(socket2);

// Monitor overall state
tracker.StateChanged += state =>
    Console.WriteLine($"Overall state: {state}");

// Connect all
await tracker.ConnectAsync(CancellationToken.None);

// Disconnect all
tracker.Disconnect();

// Remove connections
tracker.Remove(socket1);

The tracker aggregates states with the following logic:

  • Connected: All connections are connected
  • Reconnecting: Any connection is reconnecting
  • Restored: All connections are connected or restored
  • Failed: All connections have failed
  • Disconnected: All connections are disconnected or failed

IConnection Interface

Standard interface for connection management:

public interface IConnection
{
    event Action<ConnectionStates> StateChanged;
    ValueTask ConnectAsync(CancellationToken cancellationToken);
    void Disconnect();
}

Both WebSocketClient and ConnectionStateTracker implement this interface.

RestSharp Integration

The library includes helper methods for REST API calls, often used alongside WebSocket connections.

Basic REST Request

using Ecng.Net;
using RestSharp;

var request = new RestRequest(Method.Get);
request.AddQueryParameter("symbol", "BTCUSD");

var response = await request.InvokeAsync<PriceData>(
    url: new Uri("https://api.example.com/price"),
    caller: this,
    logVerbose: (fmt, args) => Console.WriteLine(fmt, args),
    token: CancellationToken.None
);

Console.WriteLine($"Price: {response.Price}");

Authentication

// Bearer token authentication
request.SetBearer(secureToken);

// Custom authenticator
var authenticator = new MyCustomAuthenticator();
var response = await request.InvokeAsync2<Data>(
    url: apiUrl,
    caller: this,
    logVerbose: logger,
    token: cancellationToken,
    auth: authenticator
);

Error Handling

try
{
    var response = await request.InvokeAsync<Data>(url, this, logger, token);
}
catch (RestSharpException ex)
{
    Console.WriteLine($"HTTP {ex.Response.StatusCode}: {ex.Response.Content}");
    Console.WriteLine($"Error: {ex.Message}");
}

Advanced REST Features

// Custom content converter
var response = await request.InvokeAsync<Data>(
    url: apiUrl,
    caller: this,
    logVerbose: logger,
    token: cancellationToken,
    contentConverter: content => content.Replace("null", "\"\"")
);

// Handle specific error status codes
var response = await request.InvokeAsync3<Data>(
    url: apiUrl,
    caller: this,
    logVerbose: logger,
    token: cancellationToken,
    handleErrorStatus: statusCode =>
    {
        if (statusCode == HttpStatusCode.TooManyRequests)
        {
            // Custom handling
            return true; // Handled
        }
        return false; // Not handled, will throw
    }
);

// Add body as string
request.AddBodyAsStr("{\"key\": \"value\"}");

// Remove parameters
request.RemoveWhere(p => p.Name == "old_param");

// Convert parameters to query string
string queryString = request.Parameters.ToQueryString();

JWT Decoding

string token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...";
var parts = token.DecodeJWT();

foreach (var part in parts)
{
    Console.WriteLine(part);
}

Complete Examples

Crypto Exchange WebSocket Client

public class CryptoExchangeClient : IDisposable
{
    private readonly WebSocketClient _socket;
    private long _subscriptionCounter;

    public CryptoExchangeClient(string wsUrl)
    {
        _socket = new WebSocketClient(
            url: wsUrl,
            stateChanged: OnStateChanged,
            error: OnError,
            process: ProcessMessage,
            infoLog: (fmt, args) => Console.WriteLine($"[INFO] {fmt}", args),
            errorLog: (fmt, args) => Console.Error.WriteLine($"[ERROR] {fmt}", args),
            verboseLog: null
        )
        {
            ReconnectAttempts = -1,  // Infinite reconnection
            ReconnectInterval = TimeSpan.FromSeconds(5),
            ResendTimeout = TimeSpan.FromSeconds(1)
        };

        _socket.Init += ws =>
        {
            ws.Options.KeepAliveInterval = TimeSpan.FromSeconds(20);
        };

        _socket.PostConnect += async (isReconnect, ct) =>
        {
            if (isReconnect)
            {
                Console.WriteLine("Reconnected! Subscriptions will be restored automatically.");
            }
        };
    }

    public async Task ConnectAsync()
    {
        await _socket.ConnectAsync(CancellationToken.None);
    }

    public async Task SubscribeToTradesAsync(string symbol)
    {
        var subId = ++_subscriptionCounter;

        await _socket.SendAsync(
            obj: new
            {
                type = "subscribe",
                channel = "trades",
                symbol = symbol
            },
            subId: subId
        );

        Console.WriteLine($"Subscribed to {symbol} trades (ID: {subId})");
    }

    public async Task UnsubscribeFromTradesAsync(long subscriptionId, string symbol)
    {
        await _socket.SendAsync(
            obj: new
            {
                type = "unsubscribe",
                channel = "trades",
                symbol = symbol
            },
            subId: -subscriptionId  // Negative to remove from resend queue
        );
    }

    private void OnStateChanged(ConnectionStates state)
    {
        Console.WriteLine($"Connection state: {state}");

        if (state == ConnectionStates.Connected)
        {
            // Connection established
        }
        else if (state == ConnectionStates.Restored)
        {
            // Connection restored after disconnect
        }
        else if (state == ConnectionStates.Failed)
        {
            // Connection failed after all retry attempts
        }
    }

    private void OnError(Exception ex)
    {
        Console.Error.WriteLine($"WebSocket error: {ex}");
    }

    private async ValueTask ProcessMessage(WebSocketMessage msg, CancellationToken ct)
    {
        try
        {
            var message = msg.AsObject<dynamic>();

            if (message.type == "trade")
            {
                Console.WriteLine($"Trade: {message.symbol} @ {message.price}");
            }
            else if (message.type == "error")
            {
                Console.Error.WriteLine($"Server error: {message.message}");
            }
        }
        catch (Exception ex)
        {
            Console.Error.WriteLine($"Error processing message: {ex.Message}");
        }

        await ValueTask.CompletedTask;
    }

    public void Dispose()
    {
        _socket?.Disconnect();
        _socket?.Dispose();
    }
}

// Usage
await using var client = new CryptoExchangeClient("wss://api.exchange.com/ws");
await client.ConnectAsync();
await client.SubscribeToTradesAsync("BTCUSD");

// Keep running
await Task.Delay(Timeout.Infinite);

Multi-Connection Manager

public class MultiExchangeClient
{
    private readonly ConnectionStateTracker _tracker;
    private readonly WebSocketClient _exchangeA;
    private readonly WebSocketClient _exchangeB;

    public MultiExchangeClient()
    {
        _tracker = new ConnectionStateTracker();

        _exchangeA = CreateClient("wss://exchange-a.com/ws", "Exchange A");
        _exchangeB = CreateClient("wss://exchange-b.com/ws", "Exchange B");

        _tracker.Add(_exchangeA);
        _tracker.Add(_exchangeB);

        _tracker.StateChanged += state =>
        {
            Console.WriteLine($"Overall connection state: {state}");

            if (state == ConnectionStates.Connected)
            {
                Console.WriteLine("All exchanges connected!");
            }
        };
    }

    private WebSocketClient CreateClient(string url, string name)
    {
        return new WebSocketClient(
            url: url,
            stateChanged: state => Console.WriteLine($"{name}: {state}"),
            error: ex => Console.Error.WriteLine($"{name} error: {ex.Message}"),
            process: (msg, ct) =>
            {
                Console.WriteLine($"{name}: {msg.AsString()}");
                return ValueTask.CompletedTask;
            },
            infoLog: (fmt, args) => Console.WriteLine($"[{name}] {fmt}", args),
            errorLog: (fmt, args) => Console.Error.WriteLine($"[{name}] {fmt}", args),
            verboseLog: null
        )
        {
            ReconnectAttempts = 5,
            ReconnectInterval = TimeSpan.FromSeconds(3)
        };
    }

    public async Task ConnectAllAsync()
    {
        await _tracker.ConnectAsync(CancellationToken.None);
    }

    public void DisconnectAll()
    {
        _tracker.Disconnect();
    }
}

WebSocket with REST API Integration

public class TradingClient
{
    private readonly WebSocketClient _wsClient;
    private readonly Uri _restApiUrl;

    public TradingClient(string wsUrl, string restUrl)
    {
        _restApiUrl = new Uri(restUrl);
        _wsClient = new WebSocketClient(
            url: wsUrl,
            stateChanged: state => Console.WriteLine($"WS State: {state}"),
            error: ex => Console.Error.WriteLine($"WS Error: {ex}"),
            process: ProcessWebSocketMessage,
            infoLog: (fmt, args) => Console.WriteLine(fmt, args),
            errorLog: (fmt, args) => Console.Error.WriteLine(fmt, args),
            verboseLog: null
        );
    }

    public async Task<AccountInfo> GetAccountInfoAsync()
    {
        var request = new RestRequest("/account", Method.Get);
        request.SetBearer(GetAuthToken());

        try
        {
            var account = await request.InvokeAsync<AccountInfo>(
                url: new Uri(_restApiUrl, request.Resource),
                caller: this,
                logVerbose: (fmt, args) => Console.WriteLine(fmt, args),
                token: CancellationToken.None
            );

            return account;
        }
        catch (RestSharpException ex)
        {
            Console.Error.WriteLine($"REST API Error: {ex.Message}");
            Console.Error.WriteLine($"Status: {ex.Response.StatusCode}");
            Console.Error.WriteLine($"Content: {ex.Response.Content}");
            throw;
        }
    }

    public async Task PlaceOrderAsync(string symbol, decimal price, decimal quantity)
    {
        var request = new RestRequest("/orders", Method.Post);
        request.SetBearer(GetAuthToken());
        request.AddBodyAsStr(new
        {
            symbol = symbol,
            price = price,
            quantity = quantity
        }.ToJson());

        var order = await request.InvokeAsync<Order>(
            url: new Uri(_restApiUrl, request.Resource),
            caller: this,
            logVerbose: (fmt, args) => Console.WriteLine(fmt, args),
            token: CancellationToken.None
        );

        Console.WriteLine($"Order placed: {order.Id}");
    }

    private async ValueTask ProcessWebSocketMessage(WebSocketMessage msg, CancellationToken ct)
    {
        var data = msg.AsObject<dynamic>();

        if (data.type == "order_update")
        {
            Console.WriteLine($"Order {data.orderId} status: {data.status}");
        }

        await ValueTask.CompletedTask;
    }

    private SecureString GetAuthToken()
    {
        // Return your authentication token
        throw new NotImplementedException();
    }
}

Best Practices

  1. Always handle errors: Provide error handlers to catch and log exceptions.

  2. Configure reconnection: Set appropriate reconnection attempts and intervals based on your use case.

  3. Use subscription IDs: Track subscriptions with IDs for automatic resend after reconnection.

  4. Monitor connection states: React to state changes to update your UI or trigger business logic.

  5. Dispose properly: Always dispose of WebSocketClient when done to clean up resources.

  6. Use async/await: Prefer async methods for better scalability.

  7. Implement backoff: Use increasing reconnection intervals to avoid overwhelming the server.

  8. Log appropriately: Use different log levels (info, error, verbose) for debugging and monitoring.

Thread Safety

The WebSocketClient class is designed to be thread-safe for the following operations:

  • Sending messages
  • Connection/disconnection
  • State management
  • Subscription tracking

However, you should not share a single WebSocketMessage instance across threads, as it contains read-only memory references.

Performance Considerations

  • Buffer Sizes: Adjust BufferSize and BufferSizeUncompress based on your message sizes.
  • Resend Interval: Lower intervals increase network traffic; higher intervals delay recovery.
  • Reconnect Attempts: Balance between reliability and resource usage.
  • Verbose Logging: Disable in production for better performance.

License

This library is part of the Ecng framework.

Support

For issues, questions, or contributions, please refer to the main StockSharp repository.

Product Compatible and additional computed target framework versions.
.NET net6.0 is compatible.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 was computed.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 was computed.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages (6)

Showing the top 5 NuGet packages that depend on Ecng.Net.SocketIO:

Package Downloads
StockSharp.AlphaVantage

AlphaVantage

StockSharp.IEX

Trading and algorithmic trading platform (stock markets, forex, bitcoins and options). .NET API for InteractiveBrokers, GainCapital, OANDA, FIX/FAST, Binance etc. More info on web site https://stocksharp.com/store/api/

StockSharp.Binance

Binance

StockSharp.Okex

OKX connector

StockSharp.Bitmex

Bitmex

GitHub repositories (1)

Showing the top 1 popular GitHub repositories that depend on Ecng.Net.SocketIO:

Repository Stars
StockSharp/StockSharp
Algorithmic trading and quantitative trading open source platform to develop trading robots (stock markets, forex, crypto, bitcoins, and options).
Version Downloads Last Updated
1.0.490 0 12/23/2025
1.0.489 17 12/22/2025
1.0.488 20 12/22/2025
1.0.487 36 12/22/2025
1.0.486 39 12/21/2025
1.0.485 162 12/19/2025
1.0.484 181 12/19/2025
1.0.483 225 12/18/2025
1.0.482 247 12/17/2025
1.0.481 270 12/15/2025
1.0.480 244 12/15/2025
1.0.479 215 12/14/2025
1.0.478 147 12/14/2025
1.0.477 152 12/13/2025
1.0.476 159 12/13/2025
1.0.475 128 12/12/2025
1.0.474 110 12/12/2025
1.0.473 106 12/12/2025
1.0.472 108 12/12/2025
1.0.471 104 12/12/2025
1.0.470 110 12/12/2025
1.0.469 109 12/12/2025
1.0.468 712 12/2/2025
1.0.467 664 12/2/2025
1.0.466 665 12/2/2025
1.0.465 264 11/30/2025
1.0.464 131 11/29/2025
1.0.463 128 11/28/2025
1.0.462 136 11/28/2025
1.0.461 180 11/27/2025
1.0.460 219 11/24/2025
1.0.459 190 11/24/2025
1.0.458 185 11/23/2025
1.0.457 162 11/23/2025
1.0.456 217 11/22/2025
1.0.455 428 11/20/2025
1.0.454 401 11/20/2025
1.0.453 403 11/20/2025
1.0.452 422 11/18/2025
1.0.451 402 11/18/2025
1.0.450 331 11/13/2025
1.0.449 269 11/10/2025
1.0.448 1,089 11/1/2025
1.0.447 197 10/31/2025
1.0.446 207 10/28/2025
1.0.445 301 10/27/2025
1.0.444 194 10/27/2025
1.0.443 124 10/25/2025
1.0.442 151 10/24/2025
1.0.441 262 10/20/2025
1.0.440 297 10/12/2025
1.0.439 144 10/11/2025
1.0.438 287 10/7/2025
1.0.437 224 10/6/2025
1.0.436 267 10/3/2025
1.0.435 241 10/1/2025
1.0.434 205 10/1/2025
1.0.433 204 9/30/2025
1.0.432 209 9/28/2025
1.0.431 222 9/25/2025
1.0.430 2,970 9/5/2025
1.0.429 238 9/2/2025
1.0.428 593 8/30/2025
1.0.427 258 8/30/2025
1.0.426 261 8/20/2025
1.0.425 206 8/20/2025
1.0.424 217 8/19/2025
1.0.423 226 8/15/2025
1.0.422 318 8/10/2025
1.0.421 1,041 7/16/2025
1.0.420 273 7/14/2025
1.0.419 247 7/13/2025
1.0.418 218 7/13/2025
1.0.417 200 7/12/2025
1.0.416 683 7/8/2025
1.0.415 256 7/4/2025
1.0.414 251 7/2/2025
1.0.413 416 6/24/2025
1.0.412 1,026 6/16/2025
1.0.411 406 6/9/2025
1.0.410 284 6/8/2025
1.0.409 610 5/21/2025
1.0.408 256 5/21/2025
1.0.407 243 5/17/2025
1.0.406 645 5/12/2025
1.0.405 340 5/12/2025
1.0.404 309 5/12/2025
1.0.403 233 5/11/2025
1.0.402 241 5/11/2025
1.0.401 197 5/10/2025
1.0.400 162 5/10/2025
1.0.399 282 5/6/2025
1.0.398 209 5/3/2025
1.0.397 378 4/17/2025
1.0.396 328 4/15/2025
1.0.395 228 4/12/2025
1.0.394 300 4/9/2025
1.0.393 260 4/6/2025
1.0.392 213 4/5/2025
1.0.391 1,087 3/22/2025
1.0.390 282 3/20/2025
1.0.389 275 3/20/2025
1.0.388 258 3/19/2025
1.0.387 831 2/26/2025
1.0.386 239 2/26/2025
1.0.385 759 2/8/2025
1.0.384 244 2/8/2025
1.0.383 218 2/8/2025
1.0.382 220 2/6/2025
1.0.381 210 2/6/2025
1.0.380 228 2/6/2025
1.0.379 232 2/6/2025
1.0.378 210 2/6/2025
1.0.377 211 2/5/2025
1.0.376 216 2/5/2025
1.0.375 230 2/5/2025
1.0.374 255 2/3/2025
1.0.373 239 2/2/2025
1.0.372 255 2/1/2025
1.0.371 239 1/31/2025
1.0.370 241 1/30/2025
1.0.369 222 1/26/2025
1.0.368 260 1/21/2025
1.0.367 244 1/20/2025
1.0.366 211 1/20/2025
1.0.365 222 1/19/2025
1.0.364 212 1/19/2025
1.0.363 238 1/15/2025
1.0.362 210 1/15/2025
1.0.361 201 1/14/2025
1.0.360 195 1/12/2025
1.0.359 185 1/12/2025
1.0.358 199 1/12/2025
1.0.357 173 1/12/2025
1.0.356 217 1/10/2025
1.0.355 1,057 12/30/2024
1.0.354 246 12/27/2024
1.0.353 271 12/19/2024
1.0.352 729 11/20/2024
1.0.351 244 11/19/2024
1.0.350 228 11/19/2024
1.0.349 788 11/18/2024
1.0.348 459 11/15/2024
1.0.347 215 11/14/2024
1.0.346 236 11/14/2024
1.0.345 242 11/14/2024
1.0.344 193 11/14/2024
1.0.343 217 11/14/2024
1.0.342 260 11/7/2024
1.0.341 270 10/31/2024
1.0.340 329 10/20/2024
1.0.339 253 10/20/2024
1.0.338 263 10/20/2024
1.0.337 258 10/19/2024
1.0.336 269 10/19/2024
1.0.335 270 10/19/2024
1.0.334 267 10/19/2024
1.0.333 266 10/19/2024
1.0.332 247 10/19/2024
1.0.331 272 10/19/2024
1.0.330 303 10/18/2024
1.0.329 263 10/17/2024
1.0.328 220 10/17/2024
1.0.327 239 10/17/2024
1.0.326 796 10/14/2024
1.0.325 226 10/13/2024
1.0.324 223 10/13/2024
1.0.323 243 10/12/2024
1.0.322 437 10/9/2024
1.0.321 241 10/9/2024
1.0.320 439 10/5/2024
1.0.319 766 9/18/2024
1.0.318 225 9/18/2024
1.0.317 245 9/18/2024
1.0.316 241 9/17/2024
1.0.315 737 9/3/2024
1.0.314 282 9/1/2024
1.0.313 950 8/9/2024
1.0.312 243 8/9/2024
1.0.311 262 8/8/2024
1.0.310 696 7/25/2024
1.0.309 256 7/23/2024
1.0.308 284 7/17/2024
1.0.307 548 7/4/2024
1.0.306 608 6/12/2024
1.0.305 250 6/12/2024
1.0.304 246 6/12/2024
1.0.303 440 5/28/2024
1.0.302 605 5/4/2024
1.0.301 398 4/23/2024
1.0.300 263 4/21/2024
1.0.299 289 4/14/2024
1.0.298 555 3/28/2024
1.0.297 325 3/17/2024
1.0.296 531 3/9/2024
1.0.295 347 2/23/2024
1.0.294 278 2/23/2024
1.0.293 487 2/18/2024
1.0.292 278 2/18/2024
1.0.291 251 2/17/2024
1.0.290 269 2/16/2024
1.0.289 387 2/14/2024
1.0.288 269 2/13/2024
1.0.287 338 2/8/2024
1.0.286 343 2/5/2024
1.0.285 261 2/4/2024
1.0.284 430 1/23/2024
1.0.283 248 1/23/2024
1.0.282 316 1/12/2024
1.0.281 685 1/2/2024
1.0.280 300 12/29/2023
1.0.279 352 12/17/2023
1.0.278 508 12/15/2023
1.0.277 279 12/15/2023
1.0.276 260 12/15/2023
1.0.275 290 12/13/2023
1.0.274 288 12/13/2023
1.0.273 292 12/10/2023
1.0.272 681 11/18/2023
1.0.271 225 11/18/2023
1.0.270 251 11/18/2023
1.0.269 234 11/17/2023
1.0.268 209 11/12/2023
1.0.267 206 11/12/2023
1.0.266 229 11/10/2023
1.0.265 195 11/10/2023
1.0.264 235 11/9/2023
1.0.263 195 11/9/2023
1.0.262 214 11/9/2023
1.0.261 238 11/3/2023
1.0.260 225 11/1/2023
1.0.259 209 11/1/2023
1.0.258 1,471 9/8/2023
1.0.257 262 9/8/2023
1.0.256 281 9/3/2023
1.0.255 346 8/27/2023
1.0.254 274 8/24/2023
1.0.253 241 8/21/2023
1.0.252 313 8/15/2023
1.0.251 284 8/14/2023
1.0.250 266 8/14/2023
1.0.249 294 8/10/2023
1.0.248 1,014 7/29/2023
1.0.247 1,086 7/1/2023
1.0.246 307 6/29/2023
1.0.245 798 5/27/2023
1.0.244 349 5/21/2023
1.0.243 294 5/19/2023
1.0.242 1,079 5/8/2023
1.0.241 353 5/7/2023
1.0.240 339 5/7/2023
1.0.239 317 5/7/2023
1.0.238 365 5/1/2023
1.0.237 410 4/22/2023
1.0.236 371 4/21/2023
1.0.235 335 4/21/2023
1.0.234 1,224 4/13/2023
1.0.233 1,133 4/3/2023
1.0.232 485 3/27/2023
1.0.231 431 3/21/2023
1.0.230 431 3/17/2023
1.0.229 425 3/13/2023
1.0.228 1,183 3/6/2023
1.0.227 495 2/26/2023
1.0.226 937 2/21/2023
1.0.225 454 2/20/2023
1.0.224 466 2/16/2023
1.0.223 454 2/15/2023
1.0.222 428 2/14/2023
1.0.221 425 2/14/2023
1.0.220 1,352 2/9/2023
1.0.219 829 2/7/2023
1.0.218 483 2/4/2023
1.0.217 465 2/4/2023
1.0.216 479 2/3/2023
1.0.215 444 2/3/2023
1.0.214 438 2/3/2023
1.0.213 883 2/2/2023
1.0.212 831 1/30/2023
1.0.211 464 1/30/2023
1.0.210 507 1/25/2023
1.0.209 504 1/23/2023
1.0.208 450 1/23/2023
1.0.207 488 1/18/2023
1.0.206 513 1/15/2023
1.0.205 511 1/6/2023
1.0.204 1,416 1/1/2023
1.0.203 479 12/31/2022
1.0.202 968 12/30/2022
1.0.201 498 12/29/2022
1.0.200 507 12/23/2022
1.0.199 1,388 12/12/2022
1.0.198 1,068 12/8/2022
1.0.197 483 12/4/2022
1.0.196 487 12/4/2022
1.0.195 489 12/2/2022
1.0.194 515 11/30/2022
1.0.193 477 11/29/2022
1.0.192 474 11/28/2022
1.0.191 513 11/26/2022
1.0.190 511 11/26/2022
1.0.189 515 11/25/2022
1.0.188 510 11/25/2022
1.0.187 524 11/18/2022
1.0.186 1,593 11/11/2022
1.0.185 545 11/11/2022
1.0.184 504 11/10/2022
1.0.183 571 11/5/2022
1.0.182 545 11/4/2022
1.0.181 530 11/2/2022
1.0.180 520 11/2/2022
1.0.179 1,429 11/1/2022
1.0.178 1,655 10/16/2022
1.0.177 737 9/25/2022
1.0.176 665 9/10/2022
1.0.175 2,978 9/8/2022
1.0.174 651 9/8/2022
1.0.173 655 9/8/2022
1.0.172 631 9/4/2022
1.0.171 633 9/4/2022
1.0.170 5,352 8/24/2022
1.0.169 713 8/8/2022
1.0.168 633 8/8/2022
1.0.167 1,264 7/31/2022
1.0.166 651 7/31/2022
1.0.165 663 7/26/2022
1.0.164 630 7/26/2022
1.0.163 3,196 7/21/2022
1.0.162 682 7/19/2022
1.0.161 3,155 7/18/2022
1.0.160 688 7/13/2022
1.0.159 671 7/8/2022
1.0.158 687 6/30/2022
1.0.157 704 6/20/2022
1.0.156 654 6/18/2022
1.0.155 696 6/6/2022
1.0.154 742 5/27/2022
1.0.153 5,208 4/30/2022
1.0.152 683 4/20/2022
1.0.151 709 4/10/2022
1.0.150 674 4/7/2022
1.0.149 661 4/7/2022
1.0.148 717 4/2/2022
1.0.147 675 3/29/2022
1.0.146 662 3/27/2022
1.0.145 666 3/27/2022
1.0.144 3,917 3/24/2022
1.0.143 2,683 2/20/2022
1.0.142 644 2/20/2022
1.0.141 655 2/20/2022
1.0.140 684 2/20/2022
1.0.139 698 2/20/2022
1.0.138 663 2/20/2022
1.0.137 657 2/20/2022
1.0.136 672 2/20/2022
1.0.135 679 2/20/2022
1.0.134 665 2/19/2022
1.0.133 4,565 2/10/2022
1.0.132 779 1/27/2022
1.0.131 705 1/27/2022
1.0.130 3,567 1/24/2022
1.0.129 650 1/24/2022
1.0.128 691 1/23/2022
1.0.127 7,017 12/29/2021
1.0.126 540 12/27/2021
1.0.125 482 12/27/2021
1.0.124 501 12/27/2021
1.0.123 1,756 12/20/2021
1.0.122 544 12/17/2021
1.0.121 534 12/16/2021
1.0.120 518 12/15/2021
1.0.119 507 12/14/2021
1.0.118 529 12/14/2021
1.0.117 489 12/13/2021
1.0.116 650 12/12/2021
1.0.115 1,629 12/10/2021
1.0.114 540 12/7/2021
1.0.113 537 12/7/2021
1.0.112 1,973 12/6/2021
1.0.111 537 12/6/2021
1.0.110 539 12/5/2021
1.0.109 1,230 12/3/2021
1.0.108 1,040 12/3/2021
1.0.107 573 12/2/2021
1.0.106 2,453 11/29/2021
1.0.105 5,266 11/23/2021
1.0.104 525 11/23/2021
1.0.103 1,614 11/22/2021
1.0.102 618 11/17/2021
1.0.101 576 11/14/2021
1.0.100 1,742 11/13/2021
1.0.99 591 11/11/2021
1.0.98 574 11/11/2021
1.0.97 561 11/10/2021
1.0.96 564 11/9/2021
1.0.95 2,595 11/6/2021
1.0.94 602 11/6/2021
1.0.93 2,168 11/5/2021
1.0.92 627 11/5/2021
1.0.91 587 11/4/2021
1.0.90 561 11/4/2021
1.0.89 612 11/3/2021
1.0.88 680 10/30/2021
1.0.87 2,045 10/21/2021
1.0.86 650 10/17/2021
1.0.85 660 10/17/2021
1.0.84 3,044 10/14/2021
1.0.83 585 10/13/2021
1.0.82 606 10/13/2021
1.0.81 585 10/12/2021
1.0.80 2,138 10/11/2021
1.0.79 568 10/9/2021
1.0.78 1,950 10/7/2021
1.0.77 2,151 10/7/2021
1.0.76 566 10/7/2021
1.0.75 597 10/6/2021
1.0.74 653 9/28/2021
1.0.73 2,364 9/23/2021
1.0.72 702 9/11/2021
1.0.71 600 9/10/2021
1.0.70 667 9/9/2021
1.0.69 569 9/8/2021
1.0.68 628 9/8/2021
1.0.67 2,154 9/6/2021
1.0.66 697 8/31/2021
1.0.65 559 8/30/2021
1.0.64 2,609 7/31/2021
1.0.63 3,039 7/30/2021
1.0.62 681 7/26/2021
1.0.61 4,482 7/5/2021
1.0.60 639 7/1/2021
1.0.59 3,820 6/4/2021
1.0.58 5,069 4/26/2021
1.0.57 2,180 4/19/2021
1.0.56 5,720 4/8/2021
1.0.55 1,805 4/7/2021
1.0.54 617 4/7/2021
1.0.53 1,875 4/3/2021
1.0.52 7,983 3/22/2021
1.0.51 5,770 3/4/2021
1.0.50 2,158 2/26/2021
1.0.49 8,274 2/2/2021
1.0.48 3,228 1/26/2021
1.0.47 2,997 1/24/2021
1.0.46 655 1/24/2021
1.0.45 710 1/23/2021
1.0.44 3,898 1/20/2021
1.0.43 713 1/20/2021
1.0.42 2,082 1/18/2021
1.0.41 627 1/18/2021
1.0.40 2,060 1/16/2021
1.0.39 6,311 12/17/2020
1.0.38 694 12/16/2020
1.0.37 3,212 12/14/2020
1.0.36 2,121 12/9/2020
1.0.35 673 12/9/2020
1.0.34 679 12/7/2020
1.0.33 786 12/6/2020
1.0.32 736 12/2/2020
1.0.31 704 12/2/2020
1.0.30 2,196 12/1/2020
1.0.29 7,360 11/12/2020
1.0.29-atestpub 560 11/11/2020
1.0.28 3,334 10/11/2020
1.0.27 8,613 9/9/2020
1.0.26 2,625 9/3/2020
1.0.25 2,682 8/20/2020
1.0.24 6,157 8/9/2020
1.0.23 2,706 7/28/2020
1.0.22 2,640 7/19/2020
1.0.21 4,451 7/6/2020
1.0.20 6,623 6/6/2020
1.0.19 2,612 6/4/2020
1.0.18 4,381 5/29/2020
1.0.17 4,422 5/21/2020
1.0.16 764 5/17/2020
1.0.15 4,519 5/12/2020
1.0.14 8,236 5/4/2020
1.0.13 785 4/24/2020
1.0.12 764 4/22/2020
1.0.11 751 4/22/2020
1.0.10 771 4/21/2020
1.0.9 3,225 4/18/2020
1.0.8 2,575 4/16/2020
1.0.7 780 4/16/2020
1.0.6 2,251 4/15/2020
1.0.5 2,663 4/11/2020
1.0.4 2,555 4/3/2020
1.0.3 775 4/1/2020
1.0.2 2,483 3/27/2020
1.0.1 2,604 3/22/2020
1.0.0 887 3/22/2020

Removed async StateChanged events, reverted to sync-only API.