ExchangeRestClient and ExchangeSocketClient expose methods that fan out a single request or subscription across multiple exchanges concurrently. This eliminates the boilerplate of managing parallel tasks, error handling per exchange, and result aggregation.
Every multi-exchange method is available in three forms:
Single exchange
Pass a named exchange string. Returns a single ExchangeWebResult<T>.
Task array
Pass an array of exchange names (or omit for all). await collects all results at once as ExchangeWebResult<T>[].
IAsyncEnumerable
Results are yield-returned as each exchange responds — ideal for showing progressive UI or processing results immediately without waiting for slow exchanges.
var client = new ExchangeRestClient();var symbol = new SharedSymbol(TradingMode.Spot, "ETH", "USDT");ExchangeWebResult<SharedSpotTicker> result = await client.GetSpotTickerAsync( "Binance", new GetTickerRequest(symbol));if (result.Success) Console.WriteLine($"Binance ETH/USDT: {result.Data.LastPrice}");
var client = new ExchangeRestClient();var symbol = new SharedSymbol(TradingMode.Spot, "ETH", "USDT");ExchangeWebResult<SharedSpotTicker>[] results = await client.GetSpotTickerAsync( new GetTickerRequest(symbol), ["Binance", "Bybit", "OKX", "Kraken"]);foreach (var result in results){ if (!result.Success) Console.WriteLine($"{result.Exchange} error: {result.Error}"); else Console.WriteLine($"{result.Exchange}: {result.Data.LastPrice}");}
var client = new ExchangeRestClient();var symbol = new SharedSymbol(TradingMode.Spot, "ETH", "USDT");await foreach (var result in client.GetSpotTickerAsyncEnumerable( new GetTickerRequest(symbol), ["Binance", "Bybit", "HyperLiquid", "OKX"])){ if (!result.Success) Console.WriteLine($"{result.Exchange} error: {result.Error}"); else Console.WriteLine($"{result.Exchange}: {result.Data.LastPrice}");}
The IAsyncEnumerable variant is the best choice for dashboards or CLIs where you want to display each exchange result as soon as it arrives rather than waiting for the slowest one.
All multi-exchange methods accept an optional IEnumerable<string>? exchanges parameter. When omitted or null, every exchange that supports the request type is queried.
// Query only these three exchangesvar results = await client.GetSpotTickerAsync( new GetTickerRequest(new SharedSymbol(TradingMode.Spot, "BTC", "USDT")), [Exchange.Binance, Exchange.Kucoin, Exchange.GateIo]);// Query all supported exchangesvar allResults = await client.GetSpotTickerAsync( new GetTickerRequest(new SharedSymbol(TradingMode.Spot, "BTC", "USDT")));
Use the Exchange static class for named string constants, or pass exchange name strings directly.
foreach (var result in results){ if (!result.Success) { // result.Error is non-null when Success is false Console.WriteLine($"[{result.Exchange}] Failed: {result.Error}"); continue; } // Safe to access result.Data Console.WriteLine($"[{result.Exchange}] Last price: {result.Data.LastPrice}");}
Always check result.Success before accessing result.Data. Individual exchanges can fail independently — a network error on one exchange does not prevent results from others.
The same fan-out pattern applies to ExchangeSocketClient. Passing a list of exchange names creates independent subscriptions and returns one ExchangeResult<UpdateSubscription> per exchange:
var socketClient = new ExchangeSocketClient();var symbol = new SharedSymbol(TradingMode.Spot, "ETH", "USDT");ExchangeResult<UpdateSubscription>[] subscriptions = await socketClient.SubscribeToTickerUpdatesAsync( new SubscribeTickerRequest(symbol), data => Console.WriteLine($"[{data.Exchange}] {data.Data.Symbol}: {data.Data.LastPrice}"), [Exchange.Binance, Exchange.Bybit, Exchange.OKX]);// Check which subscriptions succeededforeach (var sub in subscriptions){ if (!sub.Success) Console.WriteLine($"[{sub.Exchange}] Subscribe failed: {sub.Error}");}
See WebSocket Client for the full list of available subscription methods.