关于 alpaca-broker-rate-limits-resilience
这是一项面向 Alpaca API 开发者的跨语言技能,适用于批处理、回填和对账任务。内容侧重按速率限制重试、控制并发、处理分页及保障请求可靠性。
主要功能
- 01处理速率限制响应头
- 02重试与随机退避
- 03限制工作池并发
- 04循环获取分页数据
- 05设置超时并复用连接
- 06批处理韧性检查清单
适用场景
- 01执行 Alpaca 数据回填
- 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 有速率限制,在高负载下偶尔也会不稳定。任何调用次数超过少量的客户端——尤其是批量任务、历史数据回填和对账扫描——都需要严格控制重试、退避和并发。这些模式属于传输层,适用于任何语言。
请先阅读
alpaca-broker-integration。
1. 速率限制标头——每次响应都要读取
Alpaca 会返回标准标头:
| 标头 | 含义 |
|---|---|
X-RateLimit-Limit | 时间窗口内允许的请求数 |
X-RateLimit-Remaining | 当前窗口剩余的请求数 |
X-RateLimit-Reset | 窗口重置时的 Unix 时间戳(秒) |
每次响应都要解析这些标头,而不只是出错时。 有两种用途:
- 主动控制: 随着
Remaining接近零而减慢请求速度,而不是等到被限流。你选择的任何预警阈值都只是自己的起始值,应根据Limit推导——Alpaca 没有公布相关阈值。 - 被动应对: 遇到
429时,使用Reset精确等待至窗口重新开放。
经纪商的限制在 对应方级别生效——覆盖整个集成,而非针对每个终端用户账户,因此你运行的每个进程都共用一个额度。限制按合作伙伴设置,且不会公布;请根据标头作出响应。
沙盒限制远低于生产环境,不能可靠反映生产环境的剩余容量。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)),并设置 上限(文档示例的上限为 60s)。base为 1s、尝试 10 次时,最后的等待时间会达到数分钟——适合后台任务,但对面向用户的调用来说太慢(应减少尝试次数)。 - 每次等待都加入抖动。 这属于文档建议的一部分,并非额外措施:对应方级别的限制意味着你的所有工作进程会同时遭到限流,否则就会同步重试。
- 不要重试不可重试的 4xx(
400/403/422)——这类错误不会自行消失;应将其反馈出来。
3. 有界并发
并行处理能加快批量任务,但也是触发限流最快的方法。应使用 固定工作进程池,而不是不受限制地扇出请求。
- 根据
X-RateLimit-Limit设置工作进程池大小,不要使用固定数字;并记住限制适用于对应方级别:你运行的每个工作进程、定时任务和 Web 进程都会消耗同一份额度。根据观测到的Remaining调低并发;通常你自己的下游存储才是更严格的限制因素。 - 在一次运行过程中缓存每个实体的读取结果(例如账户的购买力,或每个账户的转账列表),避免在批次中的各个项目重复获取相同数据。
- 若要按项目限制速率,而你又无法轻易协调工作进程池,可在调用之间加入较短的固定休眠时间(例如 100ms)。这种方法虽粗糙,但有效。
4. 分页循环
列表端点使用令牌向前分页——绝不要假设单次响应包含全部结果。
- 活动记录(
/v1/accounts/activities):响应是一个不带分页标头的原始 JSON 数组——将页面中最后一条活动记录的id作为page_token传回;当返回的页面条数不足时停止。page_size默认值和最大值均为 100(设置date时没有最大值——会返回所有结果);direction可设为asc/desc,默认值为desc。 - 市场数据 K 线(
/v2/stocks/bars):通过响应正文中的next_page_token翻页 → 将其作为page_token传回。请记住,limit计数涵盖所有标的,结果按标的再按时间排序,因此 单个页面可能只包含前几个标的——请继续翻页。 - 将每次页面获取都包装在 §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–30s 是合理的起始值,并非 Alpaca 给出的数值)。连接卡住且未设置超时,会阻塞整个工作线程池。(SSE 流是例外——它们本就应保持打开状态;参见
alpaca-broker-sse-events。) - 使用共享 HTTP 客户端/连接池,而不是每次请求都创建一个,这样才能使用 keep-alive 并复用连接。
- **检测市场数据 WebSocket 是否静默,并重新连接和重新订阅。**运行缓慢的客户端会被断开连接,而且
407 slow client错误“无法保证在断开连接前送达”——因此,已失效的套接字看起来可能与安静的套接字完全相同(参见alpaca-broker-market-data)。 - 将对账结果写入自有存储时,应将批量插入拆分成多个块,以避免超出数据库语句大小限制,并确保事务规模合理;应根据自有数据库的表现选择块大小。
6. 批处理/定时任务的弹性检查清单
- 每次响应都解析限流标头;接近限额时主动发出警告。
- 遇到
429→ 等待至X-RateLimit-Reset指定时间,并额外留出缓冲时间。 - 对 5xx/网络错误采用带抖动的指数退避;限制延迟时间和尝试次数。
- 显示不可重试的 4xx 错误,不要重试。
- 使用有界工作线程池;在每次运行期间缓存重复读取的数据。
- 持续翻页,直到令牌为空。
- 每次调用都设置 HTTP 超时;使用共享客户端。
- 对批量数据库写入进行分块,并确保幂等性(upsert)——参见
alpaca-broker-reconciliation-idempotency。 - 使用结构化日志,并为每个条目记录跟踪/关联 ID,以便调试部分失败。
**相关技能:**安全地重新运行任务 → alpaca-broker-reconciliation-idempotency;采用这些模式的修复/轮询任务 → alpaca-broker-reconciliation-idempotency;市场数据分页详情 → alpaca-broker-market-data。