mcpField
A
alpaca-broker-rate-limits-resilience

alpaca-broker-rate-limits-resilience

github.com/alpacahq開発157130 回インストール1 週間前に更新
リポジトリ

alpaca-broker-rate-limits-resilience について

Alpaca API クライアントを堅牢に実装するための、言語非依存のスキルです。バッチ処理やバックフィル、照合作業に向けて、レート制限に応じたリトライ、並行数の制御、ページングなどを扱います。

主な機能

  1. 01レート制限ヘッダーの処理
  2. 02ジッター付きバックオフ
  3. 03ワーカープールの並行数制御
  4. 04ページングの反復処理
  5. 05タイムアウトと接続の再利用
  6. 06バッチ処理の堅牢性チェック

活用シーン

  1. 01Alpaca のデータバックフィル
  2. 02多数の口座データの照合
  3. 03レート制限された API の処理
  4. 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。

コメント