alpaca-broker-rate-limits-resilience について
Alpaca API クライアントを堅牢に実装するための、言語非依存のスキルです。バッチ処理やバックフィル、照合作業に向けて、レート制限に応じたリトライ、並行数の制御、ページングなどを扱います。
主な機能
- 01レート制限ヘッダーの処理
- 02ジッター付きバックオフ
- 03ワーカープールの並行数制御
- 04ページングの反復処理
- 05タイムアウトと接続の再利用
- 06バッチ処理の堅牢性チェック
活用シーン
- 01Alpaca のデータバックフィル
- 02多数の口座データの照合
- 03レート制限された API の処理
- 04堅牢な定期ジョブの運用
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ alpacahq/ alpaca-skills --skill alpaca-broker-rate-limits-resilienceインストール後、Claude Code で/alpaca-broker-rate-limits-resilienceを入力して読み込まれたか確認します。
同梱ファイルreference.md
詳細
Alpaca — レート制限とレジリエンス
Alpaca の API にはレート制限があり、負荷がかかると不安定になることがあります。数回を超える API 呼び出しを行うクライアント、特に一括処理、バックフィル、照合処理を実行するクライアントには、規律ある再試行、バックオフ、同時実行数の制御が必要です。これらはトランスポートレベルのパターンであり、どの言語でも適用できます。
まず
alpaca-broker-integrationをお読みください。
1. レート制限ヘッダー — すべてのレスポンスで読み取る
Alpaca は標準ヘッダーを返します。
| ヘッダー | 意味 |
|---|---|
X-RateLimit-Limit | ウィンドウ内で許可されるリクエスト数 |
X-RateLimit-Remaining | 現在のウィンドウで残っているリクエスト数 |
X-RateLimit-Reset | ウィンドウがリセットされるUNIX タイムスタンプ(秒) |
エラー時だけでなく、すべてのレスポンスで解析してください。 用途は次の 2 つです。
- 事前対応: スロットリングされるのを待つのではなく、
Remainingがゼロに近づいたら速度を落とします。設定する警告しきい値は、Limitから導き出す独自の開始点です。Alpaca はしきい値を公開していません。 - 事後対応:
429の場合はResetを使い、ウィンドウが開くまで正確に待機します。
ブローカーの制限はコルレスポンデント単位で適用されます。エンドユーザーアカウントごとではなく、連携全体が対象となるため、実行するすべてのプロセスが 1 つの上限を共有します。制限はパートナーごとに設定され、公開されていません。ヘッダーに応じて対応してください。
サンドボックスの制限は本番環境より大幅に低く、本番環境の余力を示す信頼できる指標にはなりません。Alpaca はサンドボックスに対して負荷テストを行わないよう明示しています。代わりに Alpaca への呼び出しをモックし、実際の本番環境の呼び出しからサンプリングしたレイテンシーをスタブでシミュレートしてください。
2. 再試行ループ(擬似コード)
MAX_ATTEMPTS = 10
INITIAL_DELAY_MS = 1000
MAX_DELAY_MS = 60000
backoff(n) = min(INITIAL_DELAY_MS * 2^(n-1), MAX_DELAY_MS) + random(0, 500)
for attempt in 1..MAX_ATTEMPTS:
res = http(request) # with a sane timeout (see §5)
limit, remaining, reset_at = parse_rate_headers(res.headers)
if remaining < low_water(limit): log_warn("approaching rate limit", reset_at)
if res.status == 429:
# wait until the window resets, plus a small buffer
wait = (reset_at - now()) if reset_at else backoff(attempt)
sleep(max(0, wait) + 1000 + random(0, 500))
continue
if res.status in (500, 502, 503, 504) or network_error:
sleep(backoff(attempt))
continue
return res # success or non-retryable 4xx
raise last_error
要点:
429の場合は、X-RateLimit-Resetの時刻に約 1 秒の余裕を加えた時点まで待機し、さらにその下で指数バックオフを続けます。Resetが示すのはウィンドウの切り替わる時刻であり、その後に一斉に再試行するクライアント群によって再び上限に達しないという保証ではありません。- ネットワークエラーと 5xx には指数バックオフ(
base * 2^(attempt-1))を使い、上限を設定します(ドキュメントの例では 60 秒を上限としています)。base が 1 秒、試行回数が 10 回の場合、最後の待機時間は数分になります。バックグラウンドジョブには適していますが、ユーザー向けの呼び出しには遅すぎるため、試行回数を減らしてください。 - 待機ごとにジッターを加えます。 これは任意の追加策ではなく、ドキュメントで推奨されている方法です。コルレスポンデント全体に適用される制限のため、すべてのワーカーが同時にスロットリングされ、そうしないと同じタイミングで再試行することになります。
- 再試行できない 4xx(
400/403/422)は再試行しないでください。 それらは自然に解決することはないため、エラーを通知します。
3. 同時実行数の制限
並列処理は一括処理を高速化しますが、レート制限に達する最も速い方法でもあります。制限のないファンアウトではなく、固定サイズのワーカープールを使用してください。
- プールのサイズは固定値ではなく、
X-RateLimit-Limitを基準に決めます。また、制限はコルレスポンデント単位であることを忘れないでください。実行するすべてのワーカー、cron ジョブ、Web プロセスが同じ上限を消費します。観測されたRemainingをもとに調整してください。多くの場合、下流のストアのほうがより厳しい制約になります。 - 実行中はエンティティごとの読み取り(例:アカウントの購買余力やアカウントごとの送金一覧)をキャッシュし、バッチ内の項目間で同じデータを再取得しないようにします。
- 項目ごとのスロットリングでは、プールを簡単に調整できない場合、呼び出しの間に短い固定時間のスリープ(例:100ms)を入れるのが、粗削りながら効果的な下限となります。
4. ページネーションループ
一覧エンドポイントはトークンを使って次のページに進みます。1 回のレスポンスですべて取得できると想定しないでください。
- アクティビティ(
/v1/accounts/activities):レスポンスはページネーション用ヘッダーのないJSON 配列そのものです。ページ内の最後のアクティビティのidをpage_tokenとして渡し、ページの件数が少ない場合に終了します。page_sizeのデフォルト値と最大値は 100 です(dateを設定すると最大値はなく、すべての結果が返されます)。directionはasc/descで、デフォルトはdescです。 - マーケットデータのバー(
/v2/stocks/bars):本文のnext_page_tokenを使ってページを進め、それをpage_tokenとして渡します。limitは全シンボルを通じた件数であり、結果はシンボル、次に時刻の順に並ぶため、1 ページに最初の一部のシンボルしか含まれないことがあります。ページネーションを続けてください。 - 各ページの取得を §2 の再試行ループで囲んでください。
token = null
loop:
page = fetch(url + (token ? "&page_token="+token : "")) # via retry loop
accumulate(page.items)
if body_token_endpoint:
token = page.next_page_token
if not token: break
else: # activities: bare array, no token
if len(page.items) < page_size: break
token = page.items[-1].id
5. タイムアウトとバッチサイズ
- 必ず HTTP タイムアウトを設定してください(15~30 秒は妥当な初期値ですが、Alpaca 固有の数値ではありません)。タイムアウトなしで接続がハングすると、ワーカープール全体が停止します。(SSE ストリームは例外で、接続を開いたままにすることを想定しています。
alpaca-broker-sse-eventsを参照してください。) - リクエストごとにクライアントを構築するのではなく、共有 HTTP クライアント/接続プールを使用し、キープアライブと接続の再利用が機能するようにしてください。
- **マーケットデータの WebSocket で無通信状態を検知し、再接続して再購読してください。**遅いクライアントは切断され、
407 slow clientエラーは「切断前に必ず届くとは限らない」ため、ソケットが停止していても、単に静かな状態と見分けがつかない場合があります(alpaca-broker-market-dataを参照してください)。 - 照合結果を独自のストアに書き込む場合は、一括挿入をチャンクに分割して、DB のステートメントサイズ制限を超えないようにし、トランザクションを適切な規模に保ってください。チャンクサイズは、使用するデータベースの動作に基づいて決めてください。
6. 一括処理/cron ジョブのレジリエンスチェックリスト
- すべてのレスポンスでレート制限ヘッダーを解析し、上限に近づいたら事前に警告する。
-
429→X-RateLimit-Resetの時刻までバッファを設けて待機する。 - 5xx/ネットワークエラーに対してジッター付きの指数バックオフを行い、待機時間と試行回数に上限を設ける。
- 再試行できない 4xx は再試行せず、エラーとして通知する。
- ワーカープールに上限を設け、同じ読み取り処理は実行ごとにキャッシュする。
- トークンが空になるまでページネーションを繰り返す。
- すべての呼び出しに HTTP タイムアウトを設定し、クライアントを共有する。
- DB への一括書き込みはチャンクに分割し、冪等性を確保する(upsert)。
alpaca-broker-reconciliation-idempotencyを参照してください。 - 部分的な失敗のデバッグに備え、アイテムごとのトレース ID/相関 ID を含む構造化ログを記録する。
**関連スキル:**ジョブの安全な再実行 → alpaca-broker-reconciliation-idempotency;これらのパターンを使用する修復/ポーリングジョブ → alpaca-broker-reconciliation-idempotency;マーケットデータのページネーションの詳細 → alpaca-broker-market-data。