仮想通貨取引所と通信するアプリケーションは、結局どれも同じ 2 つの配管を書くことになります。1 つはソケットが切れたあとにマーケットデータの購読を復元するもの。もう 1 つは、取引所からスロットリングされないように REST 呼び出しの速度を落とすものです。どちらも面白いコードではなく、どちらも微妙に間違えやすく、そしてどちらも、これまではアプリケーション側に任されてきました。
sgcWebSockets 2026.7 は、その両方を組み込みました。取引所 API クライアントは再接続後に自分の購読を再生でき、既製の REST クライアントはすべて、一定時間内に送信するリクエスト数を制限するクライアント側レートリミッターを備えます。どちらもオプトインで既定はオフなので、アップグレードしても、有効にするまで動作は何も変わりません。
何も購読していない状態で戻ってくる再接続
WatchDog は接続を復旧させるのが昔から得意です。ケーブルが抜かれても、プロキシがタイムアウトしても、取引所がゲートウェイを再起動しても、数秒後にはクライアントが再び接続されます。その部分は問題なく動きます。
戻ってこないのは購読です。取引所はそれを覚えていません。新しいソケットは真っさらなセッションなので、古いソケットで要求した約定やオーダーブックのストリームは、もはや存在しないのです。厄介なのは、症状として何も壊れて見えないことです。クライアントは接続済みと報告し、エラーも発生せず、ただメッセージイベントが届かなくなります。チャートは静かに古びていきます。
よくある対処は、購読リストを自分で保持して OnConnect から再生することです。つまり、要求したすべてのシンボル、すべてのチャンネル、すべてのストリームを覚えておき、コードベースのどこかで購読や購読解除をするたびに、そのリストを更新することを忘れないようにする、ということです。あなたの戦略とは何の関係もない帳簿付けです。
自動再購読を有効にする
API クライアントは、あなたが何を購読したかをすでに知っています。あなたはクライアント経由で購読したからです。2026.7 では、クライアントがそのリストを保持し、再接続後に自分で再生できるようになりました。取引所オプションオブジェクト上の 1 つの Boolean、その名も Resubscribe です。
今日の多くのトレーディングアプリが手書きしているコードと、それに取って代わるものを並べてみます。
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;
Resubscribe を有効にすると、クライアントは購読するたびにそれを記録し、WatchDog がソケットを復旧させたあと、新しいセッションで再送します。購読解除するとエントリは削除されるので、意図的に閉じたストリームが次の再接続で亡霊のようによみがえることはありません。
これは WatchDog を置き換えるものではなく、組み合わせて使うものです。WatchDog がトランスポートを復元し、Resubscribe がその上のセッションを復元します。
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;
既定値は False です。すでに手作業で再購読している既存のアプリケーションは、これまでとまったく同じように、購読が重複することもなく動き続けます。自分のコードを削除してフラグを設定するまでは、そのままです。
対応する取引所
Resubscribe は各取引所クライアントのオプションオブジェクトにあります。Binance、Bitfinex、Bitget、BitMEX、Bitstamp、Bybit、CEX、CEX Plus、Coinbase、Crypto.com、Deribit、Gate.io、Huobi、Kraken、KuCoin、MEXC、OKX、そしてセッションベースの XTB ストリーミングフィードで利用できます。プロパティ名はつねに同じ綴りなので、フォームに配置したコンポーネントに応じて Coinbase.Resubscribe、Bybit.Resubscribe、OKX.Resubscribe といった具合に書けます。
IG などのブローカーで使われる Lightstreamer クライアントは、内部で同じことを行います。セッションの再バインド後、サーバーには以前の購読の記録がないため、クライアントは何も言われなくてもローカルのコレクションからそれらを再生します。
クライアント側から見たレート制限
トレーディングアプリのもう半分は REST 側です。注文の発注、注文のキャンセル、残高の取得、銘柄一覧の取得。取引所はこれらのエンドポイントに対してリクエストの予算を公開しており、たいていは IP ごと、あるいはキーごとの 1 分あたりのリクエスト数またはウェイトです。それを超えると 429 が返り、超え続ければ一時的な BAN を受けます。そちらのほうがはるかに悲惨な一日になります。
2026.7 は TsgcHTTPAPI_client にクライアント側レートリミッターを追加したので、既製の REST クライアントはすべてそれを継承します。方式はスライディングウィンドウで、任意の IntervalMs のウィンドウ内で最大 MaxRequests 件です。取引所が公開しているルールに合わせて設定します。重要なのは取引所の制限だけだからです。
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;
スロットリングを食らう典型的な方法は、起動時のバースト送信です。アプリケーションが起動し、銘柄一覧、残高、未約定注文、40 銘柄分の直近ローソク足を、最初の 1 秒ですべて並行して読み込みに行きます。それ自体は何も不合理ではありませんが、取引所は 1 秒間に 40 リクエストと数えます。リミッターは、まさにその形のトラフィックを平準化します。初期化コードに Sleep 呼び出しをばらまく必要はありません。
リミッターは遅延生成されます。RateLimit に一度も触れなければ、オブジェクトは確保されず、リクエスト経路はこれまでとバイト単位で同一です。使わない機能のためのコストはありません。
待つか、拒否するか
現在のウィンドウの予算を使い切ったとき、次の呼び出しをどうするかを選べます。それが Behavior で、チューニングのつまみというより、本質的な意思決定です。
既定の rlbWait は、スロットが空くまで呼び出しをブロックし、その後で送信します。履歴を読み込んだりポジションを照合したりするバックグラウンドワーカーには、これが望ましい挙動です。作業に少し時間はかかりますが、すべて完了します。
rlbReject は、ただちに EsgcHTTPRateLimit を発生させ、何も送信しません。遅れて届くリクエストが、まったく送らないよりも悪い場合には、こちらが望ましい挙動です。送ろうと決めてから 3 秒後に届く注文は、もはや出したくない注文かもしれません。動いている市場にそれを投げ込むのは、はっきり失敗するよりも悪いことです。
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;
よくある構成は、コンポーネントを 2 つ用意して両方を使うことです。一括読み込みを行うコンポーネントには rlbWait、注文を発注するコンポーネントには rlbReject。それぞれに独自の予算を割り当てます。
これはサーバー側レートリミッターではありません
名前が似ているので、はっきりさせておく価値があります。sgcWebSockets 2026.5 は TsgcWSRateLimiter を出荷しました。あれはサーバー側のリミッターです。受信接続が送ってよい量に上限を設けることで、あなたのクライアントからあなたのサーバーを守ります。この記事のリミッターは逆方向を向いています。クライアント側のリミッターであり、あなたのアプリケーションが取引所へ送り出す量に上限を設けることで、他人の制限からあなたを守ります。両者は独立しており、サーバーであると同時に取引所の利用者でもあるアプリケーションは、両方を問題なく使えます。
仮想通貨だけではありません
RateLimit は HTTP API クライアントの基底クラスにあるので、取引所だけでなく、ライブラリ内の既製の REST クライアントすべてに継承されます。AI クライアントもまったく同じ種類のリクエスト制限を持つため、OpenAI、Anthropic、Gemini にも適用され、Stripe やその他の API 連携も同様です。プロパティは同じ場所にあり、同じ 4 つの設定を持ちます。
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;
アカウントが 1 分あたりのリクエスト上限が低いティアにある場合、並列バッチジョブが上限に引っかかるのを防ぐには、これがもっとも安上がりな方法です。
提供状況
自動再購読とクライアント側レートリミッターは、sgcWebSockets 2026.7 の Delphi 7 から 13 および C++Builder で、Win32/Win64、Linux64、macOS、Android、iOS 向けに利用できます。どちらも既定はオフなので、アップグレードはそのまま差し替えるだけです。
有効なサブスクリプションをお持ちのお客様は、カスタマーエリアから新しいビルドをダウンロードできます。トライアルユーザーの方は、esegece.com/products/websockets/download から更新されたインストーラーを入手できます。
ご質問、フィードバック、トレーディングアプリへの組み込みのお手伝いが必要ですか。お問い合わせください。コードを書いた本人から返信が届きます。
