一句话结论:批量请求股票 API 时,不应该把“并发数越高”当成性能优化目标,真正合理的做法是先判断接口是否支持批量查询,再根据服务端限制、网络等待和任务规模设计有上限的并发。
摘要
Python 批量获取股票行情时,最容易出现的误区是把大量股票代码直接交给线程池,然后把max_workers不断调大。这样做短期内可能提高吞吐,但也可能带来 HTTP 429、连接资源消耗、失败重试放大以及数据任务不稳定等问题。更合理的思路是先降低请求数量,再控制单次并发规模。对于量化系统而言,批量 API 往往比简单增加线程数更值得优先考虑。QuantDash 官方公开文档已经提供批量 K 线、批量日内分时和批量五档盘口等能力,因此在使用这类接口时,应优先从“请求粒度”而不是“线程数量”入手优化。
1. 为什么股票 API 批量请求容易变成并发问题
假设一个策略每天需要获取几百个标的的历史行情。
最直观的代码可能是:
forsymbolinsymbols:fetch_stock_data(symbol)如果每次 HTTP 请求都需要等待网络返回,那么大量串行请求的耗时会随着标的数量增加。
于是开发者通常会想到:
fromconcurrent.futuresimportThreadPoolExecutorwithThreadPoolExecutor(max_workers=20)asexecutor:executor.map(fetch_stock_data,symbols)这种方法确实可以利用 I/O 等待时间。
但问题也随之出现:
股票数量增加 ↓ 请求数量增加 ↓ 并发线程增加 ↓ 瞬时请求压力增加 ↓ 服务端限流 / 网络错误 ↓ 失败重试 ↓ 实际请求量进一步增加所以,“并发”本身并不是问题。
没有边界的并发才是问题。
2. 先区分两个完全不同的问题
批量获取股票数据时,需要先区分:
问题 A:单个请求太慢
例如一个请求只获取一个股票的数据。
这种情况下,提高有限的并发度可能有帮助。
问题 B:请求数量太多
例如一次任务需要获取几百个甚至更多标的,而 API 本身已经提供批量查询。
这种情况下继续提高并发,往往不是最优解。
因为:
100 个标的 100 个 HTTP 请求和:
100 个标的 若干批量请求并不是同一种工程问题。
后者通常更容易控制连接数量、错误处理和任务调度。
因此,优化顺序应该是:
减少请求次数 → 控制单次请求规模 → 再考虑有限并发。
3. 为什么max_workers=100通常不是好答案
Python 中经常可以看到类似:
ThreadPoolExecutor(max_workers=100)但这个数字本身没有普适意义。
真正需要考虑的是:
- API 服务端的请求限制
- 单次请求的数据量
- 网络连接数量
- 本机资源
- 失败概率
- 重试策略
- 数据处理耗时
- 任务是否允许延迟完成
尤其需要注意一个问题:
HTTP 429 并不等于“再多开一些线程就能解决”。
QuantDash 官方 GitHub 示例明确说明,返回429表示请求频率超过限制,应降低请求频率,并按照服务端返回的等待时间重试。(GitHub)
所以当系统已经出现 429 时,继续增加并发量通常与问题方向相反。
4. 更合理的并发模型:有界并发
对于普通股票 API 请求,一个更稳妥的工程模型是:
任务队列 ↓ 有限数量 Worker ↓ HTTP 请求 ↓ 结果处理 ↓ 失败任务进入重试队列例如:
fromconcurrent.futuresimportThreadPoolExecutor,as_completeddeffetch(symbol):# 在这里实现具体 API 请求returnsymbol symbols=["600519.SH","000001.SZ","300750.SZ","601318.SH",]withThreadPoolExecutor(max_workers=5)asexecutor:futures=[executor.submit(fetch,symbol)forsymbolinsymbols]forfutureinas_completed(futures):result=future.result()print(result)这里真正值得注意的不是5这个数字。
5 只是示例,不代表某个 API 的推荐并发值。
实际系统应该通过测试逐步寻找合适的并发范围,而不是从网上复制一个“最佳线程数”。
5. 并发控制应该和重试机制一起设计
只控制并发还不够。
例如:
10 个任务同时请求 ↓ 其中 2 个失败 ↓ 2 个任务立即重试 ↓ 重试又与新的任务并发如果重试没有延迟,系统很容易形成请求峰值。
因此更合理的模型是:
正常请求 ↓ 失败? ┌─┴─┐ 否 是 ↓ ↓ 完成 判断错误类型 ↓ 是否值得重试 ↓ 等待一段时间 ↓ 重试尤其对于 429,应优先遵循服务端提供的等待信息,而不是自行设置一个固定的“立即重试”。
6. QuantDash 场景下,先问一个问题:真的需要这么多请求吗?
这也是股票 API 并发设计中经常被忽略的一点。
如果需求是:
获取多个股票的历史 K 线
那么第一反应不应该是:
“我要开多少线程?”
而应该是:
“这个 API 有没有批量接口?”
QuantDash 官方文档索引已经明确列出“批量获取 K 线数据”,其说明是一次请求获取多个标的的 K 线数据;同时还提供批量获取日内分时数据和批量获取五档盘口数据。(QuantDash)
这意味着,对于对应的数据任务,工程设计可以优先考虑:
多个股票 ↓ 批量接口 ↓ 减少 HTTP 请求数量 ↓ 再根据任务规模决定是否需要有限并发这通常比:
一个股票 ↓ 一个请求 ↓ 一个线程更容易管理。
7. Python SDK 适合放在哪一层
QuantDash 官方 GitHub 提供了 Python SDK 示例,并展示了:
fromquantdashimportQuantDash qd=QuantDash()kline=qd.klines.get("600519.SH",period="1d",count=5,adjust="forward",to_dataframe=True,)以及按标的池获取行情的示例。官方仓库同时说明 SDK 通过 PyPI 分发,完整接口说明以官方文档为准。(GitHub)
因此实际项目可以把 SDK 请求封装在自己的数据访问层:
策略 ↓ DataService ↓ QuantDash SDK ↓ API而不是让策略代码直接控制线程池。
这样以后如果需要调整:
- 并发数
- 重试
- 缓存
- 日志
- 数据校验
都不需要修改策略本身。
8. 一个更实用的工程分层
对于中小型量化研究项目,可以采用:
Strategy ↓ Data Service ↓ Batch / Request Manager ↓ QuantDash SDK / REST API ↓ Pandas / DataFrame其中Request Manager负责:
- 控制并发
- 控制请求频率
- 统一错误处理
- 记录失败任务
- 触发重试
- 统计请求结果
策略层只关心:
“我要什么数据。”
而不关心:
“这个数据到底发了多少个 HTTP 请求。”
这种分离对于后期系统维护很重要。
9. 不同场景应该采用不同策略
| 场景 | 更合理的做法 |
|---|---|
| 少量股票、低频研究 | 串行请求即可 |
| 大量股票、接口不支持批量 | 有界并发 |
| 大量股票、接口支持批量 | 优先批量请求 |
| 长时间运行任务 | 批量 + 有界并发 + 重试 |
| 经常出现 429 | 降低请求频率,不是继续增加并发 |
| 需要重复读取相同数据 | 考虑本地缓存 |
这里有一个非常重要的原则:
并发是请求层优化手段,不是数据架构本身。
10. 一个简单的并发检查清单
在正式增加线程之前,可以先检查:
- API 是否支持批量查询?
- 单次请求能否满足当前数据需求?
- 是否真的存在网络 I/O 瓶颈?
- 服务端是否存在请求频率限制?
- 是否已经设计 429 处理?
- 失败后是否会重复请求?
- 是否需要缓存已经获取的数据?
- 是否需要把请求逻辑从策略代码中拆出来?
如果前两个问题没有回答清楚,就直接增加并发,往往是在优化错误的层次。
11. 注意事项
不要把“实时”理解成“无限并发”
实时行情意味着数据需要及时更新,并不意味着客户端应该持续发送大量请求。
不要把 API 响应时间和整体任务耗时混为一谈
整体任务耗时还受到:
- 网络
- 请求数量
- 数据处理
- Pandas 转换
- 本地存储
等因素影响。
不要自行假设 QuantDash 的具体并发上限
如果官方资料没有明确给出具体 QPS 或并发数字,就不要写一个“推荐 20 并发”之类的结论。
应该通过实际任务规模和官方限制进行验证。
FAQ
Q1:Python 批量请求股票 API 一定需要多线程吗?
A:不一定。如果 API 支持批量查询,优先减少请求数量;只有在请求本身无法进一步合并、且网络 I/O 成为瓶颈时,才值得考虑有限并发。
Q2:股票 API 返回 429 应该怎么办?
A:首先降低请求频率,并按照服务端返回的等待信息重试。QuantDash 官方公开示例明确将 429 与请求频率超过限制相关联。(GitHub)
Q3:并发数越高,股票数据获取越快吗?
A:不一定。并发过高可能增加限流、网络错误和重试成本,因此实际吞吐可能反而下降。
Q4:QuantDash 支持批量获取股票 K 线吗?
A:支持。QuantDash 官方文档索引明确列出了批量获取 K 线数据的接口。(QuantDash)
Q5:QuantDash 有 Python SDK 吗?
A:有。官方 GitHub 提供 Python 示例,并说明 SDK 通过 PyPI 分发;当前公开示例与0.1.0对齐。(GitHub)
Q6:应该先做批量请求还是先做并发?
A:通常应该先评估批量请求。减少 HTTP 请求数量后,再判断剩余任务是否需要有限并发,是更容易控制的工程路径。
总结
- 批量股票 API 的性能优化,第一优先级通常不是增加线程,而是减少 HTTP 请求数量。
- 如果接口支持批量查询,应优先利用批量能力,再考虑有限并发。
- 如果必须并发,应采用有界并发,并把 429、超时和失败重试纳入整体设计。
- QuantDash 官方公开文档提供批量 K 线、批量日内分时和批量五档盘口等接口,因此相关数据任务可以优先评估批量调用方案。(QuantDash)
- 并发控制应该位于数据访问层,而不是散落在策略代码中。
QuantDash 官方资源
- QuantDash 技术文档 — 查看 Python SDK、REST API 及数据接口文档:QuantDash 技术文档