Crypto Feeds That Survive a Reconnect, and a Rate Limiter That Keeps You Unbanned | eSeGeCe Blog

Crypto Feeds That Survive a Reconnect, and a Rate Limiter That Keeps You Unbanned

· Components
Crypto Feeds That Survive a Reconnect, and a Rate Limiter That Keeps You Unbanned

Every application that talks to a crypto exchange ends up writing the same two pieces of plumbing. The first restores your market data subscriptions after the socket drops. The second slows your REST calls down so the exchange does not throttle you. Neither is interesting code, both are easy to get subtly wrong, and both have been left to the application until now.

sgcWebSockets 2026.7 builds them in. The exchange API clients can replay their own subscriptions after a reconnect, and every ready-made REST client now has a client-side rate limiter that caps how many requests you send in a time window. Both are opt-in and off by default, so upgrading changes nothing until you switch them on.

The reconnect that leaves you subscribed to nothing

The WatchDog has always been good at getting the connection back. A cable is unplugged, a proxy times out, the exchange restarts a gateway, and a few seconds later the client is connected again. That part works.

What does not come back are your subscriptions. The exchange has no memory of them. A new socket is a blank session, so the trade and order book streams you asked for on the old socket simply do not exist any more. The symptom is nasty because nothing looks broken: the client reports Connected, no error is raised, and the message events just stop arriving. Your chart quietly goes stale.

The usual fix is to keep your own list of subscriptions and replay it from OnConnect, which means remembering every symbol, every channel and every stream you asked for, and remembering to update that list every time you subscribe or unsubscribe somewhere else in the codebase. It is bookkeeping that has nothing to do with your strategy.

Turning on automatic resubscribe

The API clients already know what you subscribed to, because you subscribed through them. In 2026.7 they can keep that list and replay it themselves after a reconnect. It is a single Boolean on the exchange options object, named Resubscribe.

Here is the hand-rolled version that most trading apps have today, and what replaces it:

uses
  sgcWebSocket, sgcWebSocket_API_Binance;

// Before: keep your own list and replay it on every reconnect.
procedure TForm1.OnBinanceConnect(Connection: TsgcWSConnection);
begin
  sgcWSAPI_Binance1.SubscribeTrades('btcusdt');
  sgcWSAPI_Binance1.SubscribeTicker('btcusdt');
  sgcWSAPI_Binance1.SubscribeTrades('ethusdt');
  // ... and every other symbol you had open, in the right order.
end;

// After: the API client replays them for you.
sgcWSAPI_Binance1.Binance.Resubscribe := True;

With Resubscribe enabled, the client records each subscription as you make it, and after the WatchDog brings the socket back it sends them again on the fresh session. Unsubscribing removes the entry, so a stream you deliberately closed does not reappear from the dead after the next reconnect.

It pairs with the WatchDog rather than replacing it. The WatchDog restores the transport, Resubscribe restores the session on top of it:

uses
  sgcWebSocket, sgcWebSocket_API_Kraken;

begin
  sgcWebSocketClient1.WatchDog.Enabled := True;
  sgcWebSocketClient1.WatchDog.Timeout := 5;   // retry the socket every 5 seconds

  sgcWSAPI_Kraken1.Client := sgcWebSocketClient1;
  sgcWSAPI_Kraken1.Kraken.Resubscribe := True; // and restore the streams on top

  sgcWebSocketClient1.Active := True;
  sgcWSAPI_Kraken1.SubscribeTrade(['XBT/USD', 'ETH/USD']);
end;

The default is False. Existing applications that already resubscribe by hand keep working exactly as they did, with no duplicate subscriptions, until you delete your own code and set the flag.

Which exchanges

Resubscribe is on the options object of each exchange client. It is available on Binance, Bitfinex, Bitget, BitMEX, Bitstamp, Bybit, CEX, CEX Plus, Coinbase, Crypto.com, Deribit, Gate.io, Huobi, Kraken, KuCoin, MEXC and OKX, and on the session-based XTB streaming feed. The property is always spelled the same way, so the line reads Coinbase.Resubscribe, Bybit.Resubscribe, OKX.Resubscribe and so on, depending on which component you dropped on the form.

The Lightstreamer client, which is used by IG and other brokers, does the same thing internally. After a session rebind the server has no record of the prior subscriptions, so the client replays them from its local collection without you asking.

Rate limits, from the client side

The other half of a trading app is the REST side: place an order, cancel an order, load balances, load the symbol list. Every exchange publishes a request budget for those endpoints, usually a number of requests or a weight per minute per IP or per key. Go over it and you get a 429, and if you keep going over it you get a temporary ban, which is a much worse day.

2026.7 adds a client-side rate limiter to TsgcHTTPAPI_client, so every ready-made REST client inherits it. It is a sliding window: at most MaxRequests requests in any IntervalMs window. You configure it from the exchange's published rules, because the exchange's limit is the only one that matters:

uses
  sgcHTTP_API, sgcHTTP_API_Binance;

var
  oBinance: TsgcHTTP_API_Binance_Rest;
begin
  oBinance := TsgcHTTP_API_Binance_Rest.Create(nil);
  try
    oBinance.RateLimit.Enabled     := True;
    oBinance.RateLimit.IntervalMs  := 60000;   // one minute window
    oBinance.RateLimit.MaxRequests := 1000;    // stay under the published budget
    oBinance.RateLimit.Behavior    := rlbWait; // default

    // Every request through this component now takes a slot first.
    oBinance.GetAccountInformation;
  finally
    oBinance.Free;
  end;
end;

The classic way to get yourself throttled is the burst at startup. The application launches, and it fans out to load the symbol list, the balances, the open orders and the last candles for forty instruments, all in the first second. Nothing about that is unreasonable, and the exchange still counts it as forty requests in one second. The limiter smooths exactly that shape of traffic without you having to sprinkle Sleep calls through your initialization code.

The limiter is created lazily. If you never touch RateLimit, no object is allocated and the request path is byte for byte what it was before, so there is nothing to pay for when you do not use it.

Wait or reject

When the budget for the current window is exhausted, you get to choose what happens to the next call. That is Behavior, and it is a genuine decision rather than a tuning knob.

rlbWait, the default, blocks the call until a slot frees up and then sends it. This is what you want in a background worker that is loading history or reconciling positions. The work takes a little longer and it all completes.

rlbReject raises EsgcHTTPRateLimit immediately and sends nothing. This is what you want when a late request is worse than no request at all. An order that arrives three seconds after you decided to send it may be an order you no longer want, and firing it into a moving market is worse than failing loudly:

uses
  SysUtils, sgcHTTP_API, sgcHTTP_API_Binance;

begin
  oBinance.RateLimit.Enabled     := True;
  oBinance.RateLimit.IntervalMs  := 1000;
  oBinance.RateLimit.MaxRequests := 10;
  oBinance.RateLimit.Behavior    := rlbReject;

  try
    oBinance.NewOrder('BTCUSDT', 'BUY', 'LIMIT', ...);
  except
    on E: EsgcHTTPRateLimit do
      // Budget exhausted. Drop the order rather than send it stale.
      LogWarning('rate limit hit, order not sent: ' + E.Message);
  end;
end;

A common setup is both, with two components: rlbWait on the component that does the bulk loading, and rlbReject on the component that places orders, each with its own budget.

This is not the server-side rate limiter

Worth being explicit, because the names are close. sgcWebSockets 2026.5 shipped TsgcWSRateLimiter, which is a server-side limiter: it protects your server from your clients, by capping what an inbound connection is allowed to send you. The one in this post points the other way. It is a client-side limiter that protects you from someone else's limits, by capping what your application sends out to an exchange. They are independent, and an application that is both a server and an exchange consumer will happily use both.

Not just crypto

RateLimit lives on the base HTTP API client, so it is inherited by every ready-made REST client in the library, not only the exchanges. The AI clients have request limits of exactly the same kind, so OpenAI, Anthropic and Gemini get it, and so do Stripe and the rest of the API integrations. The property is in the same place with the same four settings:

uses
  sgcHTTP_API, sgcHTTP_API_OpenAI;

begin
  oOpenAI.RateLimit.Enabled     := True;
  oOpenAI.RateLimit.IntervalMs  := 60000;
  oOpenAI.RateLimit.MaxRequests := 500;    // your tier's requests per minute
  oOpenAI.RateLimit.Behavior    := rlbWait;
end;

If your account is on a tier with a low requests-per-minute ceiling, this is the cheapest way to stop a parallel batch job from tripping it.

Availability

Automatic resubscribe and the client-side rate limiter are available in sgcWebSockets 2026.7 across Delphi 7 through 13 and C++Builder, on Win32/Win64, Linux64, macOS, Android and iOS. Both are off by default, so an upgrade is a drop-in.

Customers with an active subscription can download the new build from the customer area. Trial users can grab the updated installer at esegece.com/products/websockets/download.

Questions, feedback or help wiring this into your trading app? Get in touch, you will get a reply from the people who wrote the code.