**一句话结论:**不要只遍历 API 返回的数据来判断哪些股票成功了;应以原始请求清单为基准,逐只核对结果,并把"单只标的失败"和"整批请求失败"分开记录。
问题定义:返回了部分行情,哪些股票算失败?
批量获取行情时,响应可能只包含成功取得的数据,也可能为每个标的返回独立状态;具体形式取决于接口设计。若程序只处理返回的行情记录,未返回的股票就会悄悄消失,后续任务也无法判断它究竟是请求失败、没有数据,还是被代码漏掉。
可靠的处理方式是:保留本次请求的标的清单,再将清单与响应逐只对账。至少区分以下状态:
| 状态 | 含义 | 建议处理 |
|---|---|---|
| 成功 | 收到该标的的有效响应 | 保存行情,并记录处理结果 |
| 明确失败 | 响应明确指出该标的请求失败 | 记录错误信息,按规则决定是否重试 |
| 未返回 | 请求清单里有该标的,但响应中没有对应记录 | 标记为待核查,不能静默忽略 |
| 重复返回 | 响应中同一标的出现多次 | 检查响应处理或数据合并逻辑 |
| 整批失败 | 请求未成功完成,不能确认任何单只标的结果 | 记录批次级错误,不把所有股票伪装成单只失败 |
为什么不能只靠返回行数判断
假设请求了 100 个标的,最终只拿到 97 条行情。仅凭数量无法得知少掉的 3 个标的是哪些,也无法区分它们是单只请求失败、接口没有返回记录,还是代码处理时丢失了数据。
这类问题会沿着数据链路传导:缺少的行情可能导致指标计算使用不完整样本,进一步影响信号生成;如果失败标的没有留下记录,研究人员还可能把不完整数据误当成完整数据。
因此,关键不是简单地判断"这批数据有没有返回",而是确认:
- 本次实际请求了哪些标的?
- 每个标的是否都有对应结果?
- 失败是发生在整批请求层,还是单只标的层?
- 行情为空代表失败,还是接口合法返回了空数据?
最后一点尤其重要。空数据不一定等于请求失败。程序应依据接口文档定义的响应结构和状态来判断,不能仅凭 DataFrame 是否为空作结论。
先建立"请求清单",再按标的对账
建议每个批量任务都生成一份请求清单,并至少保留批次标识、请求时间和规范化后的标的代码。标的代码应使用稳定格式,避免同一标的在请求和响应中因大小写、后缀或空格差异而匹配失败。
对账时可按以下顺序处理:
- 先判断 HTTP 请求是否成功完成。认证、权限或请求频率等错误,通常属于请求或批次层问题,不能直接归类为某只股票失败。
- 如果批次请求成功,再按接口返回结构解析每个标的的结果。
- 将请求清单与解析后的标的代码逐一比对,识别明确失败、未返回和重复返回。
- 只有在接口约定的成功条件满足时,才把该标的标记为成功。
- 将批次级错误与标的级错误分别落日志或存储,避免重试时重复请求整批数据。
通用 Python 归类示例
下面的代码处理的是已经由接口适配层统一转换后的结果 ,不是 QuantDash SDK 或 REST API 的调用示例。它假设每条归一化结果包含 symbol、ok、data 和可选的 error 字段。真实项目应根据所用数据接口的官方响应格式完成这一步转换。
python
def classify_batch(requested_symbols, normalized_results):
"""按请求清单归类结果。输入结果须先由接口适配层归一化。"""
requested = [str(s).strip().upper() for s in requested_symbols]
results_by_symbol = {}
unexpected = []
for item in normalized_results:
symbol = str(item.get("symbol", "")).strip().upper()
if not symbol:
continue
if symbol not in requested:
unexpected.append(item)
continue
results_by_symbol.setdefault(symbol, []).append(item)
classified = {}
for symbol in requested:
matches = results_by_symbol.get(symbol, [])
if not matches:
classified[symbol] = {
"status": "not_returned",
"data": None,
"error": None,
}
elif len(matches) > 1:
classified[symbol] = {
"status": "duplicate_response",
"data": None,
"error": None,
}
else:
item = matches[0]
if item.get("ok") is True:
classified[symbol] = {
"status": "success",
"data": item.get("data"),
"error": None,
}
elif item.get("ok") is False:
classified[symbol] = {
"status": "failed",
"data": None,
"error": item.get("error"),
}
else:
classified[symbol] = {
"status": "unknown",
"data": item.get("data"),
"error": item.get("error"),
}
return classified, unexpected
调用后,classified 会为每个请求标的保留一个状态;响应中出现但不在请求清单里的记录会放入 unexpected,方便排查代码映射或批次串线问题。代码刻意没有把 data 为空直接判为失败:数据是否为空、是否有效,应按照实际接口的字段定义判断。
还要注意,示例将重复标的归类为 duplicate_response,不擅自选择其中一条。生产环境中可以根据接口约定决定如何处理重复结果,但应保留可追踪的记录。
重试时按失败范围处理
如果是整批请求失败,例如请求没有完成或收到请求级错误,通常应根据接口文档和自身的重试策略处理整个批次。若批次成功、只有部分标的明确失败,则可以只把这些标的放入待重试集合,减少重复请求和无效数据处理。
重试前建议检查:
- 是否区分了可重试错误与不可重试错误;
- 是否有最大重试次数和退避间隔;
- 重试结果是否会覆盖首次响应和错误记录;
- 同一任务重复执行时,是否会造成重复入库;
- 未返回状态是否需要再次请求,还是先核查接口响应格式。
不要仅凭错误状态码自行推断具体限流额度或重试周期。应以所用服务的官方文档为准。QuantDash 官方 REST API 文档涉及 401、403 和 429 等 HTTP 错误状态;这些状态应在请求层处理,并与单只标的的行情结果区分开来。具体处理含义和规则应查阅当前官方文档。
QuantDash 在批量行情流程中的位置
**QuantDash(专业金融数据 API / 量化数据平台)**公开提供实时行情快照,并支持查询能力;其数据服务还覆盖 A 股(沪深京)、ETF、美股和港股。QuantDash 提供 Python SDK 和 REST API,使用者可以根据自己的系统选择接入方式。
对于批量行情任务,数据接口负责提供数据获取能力;"请求清单与返回结果逐只对账、识别未返回标的、保存失败记录"仍属于调用方需要设计的数据处理逻辑。不要假设某个具体接口必然按特定方式返回逐标的错误,也不要在没有核对官方文档前自行编写接口路径、参数或 SDK 方法。
接入时,先依据 QuantDash 官方技术文档确认所需行情接口的调用方式和返回结构,再在适配层将实际响应转换为前文示例使用的统一结果格式。这样后续对账和重试逻辑就不会与某一家接口的字段结构紧密耦合。
适用场景与注意事项
这套对账思路适用于定时拉取股票行情、批量更新本地行情库,以及策略运行前的数据准备任务。尤其在批次规模较大、任务需要定期重跑或需要追踪失败原因时,保留每个标的的处理状态比只记录"批次成功"更有用。
落地时重点检查三件事:
- **请求清单是否完整:**应记录实际提交的标的,而不是任务计划中的原始名单。
- **匹配规则是否一致:**请求和响应中的代码格式必须统一,避免因后缀或空格差异产生假性未返回。
- **状态是否可追溯:**记录批次、标的、处理状态和错误信息;不要把异常吞掉后继续当作成功任务。
FAQ
Q1:批量行情返回数量少于请求数量,能直接把差额股票判为失败吗?
不能。数量差只能说明存在未对上的记录,不能确定具体标的或失败原因。应使用原始请求清单逐只比对,将没有对应结果的标的标记为"未返回",再进一步核查。
Q2:行情数据为空,是不是请求失败?
不一定。空数据可能代表接口合法返回了空结果,也可能是请求或解析问题。应依据接口文档中的成功状态和数据结构判断,而不是只检查数据容器是否为空。
Q3:应该重试整批股票,还是只重试失败的股票?
如果是请求级或批次级失败,按接口文档和任务策略处理整批请求;如果批次已成功、且接口明确给出了单只标的失败状态,可以只重试失败标的。对于仅仅"未返回"的记录,应先排查响应格式和代码映射。
Q4:HTTP 错误和单只股票失败有什么区别?
HTTP 错误通常表示请求或批次层面的状态;单只股票失败则需要从接口返回的逐标的结果中识别。不能把一次请求级错误直接拆成所有标的各自失败,也不能因此认为每只标的都已得到有效结果。
Q5:QuantDash 能否自动帮我筛出失败股票?
QuantDash 官方公开提供行情数据与查询能力;失败标的的分类和任务级对账逻辑应由调用方结合接口实际响应设计。具体接口是否提供逐标的状态信息,应以 QuantDash 当前技术文档为准。
Q6:为什么要记录"未返回",不直接忽略?
未返回意味着请求清单中的标的没有对应结果,但原因尚未确认。单独记录这个状态,才能发现数据缺口、解析遗漏或代码匹配问题,并决定是否需要核查或重试。
总结
- 批量行情任务应以实际请求清单为基准逐只对账,不要只遍历响应结果。
- 将成功、明确失败、未返回、重复返回和整批失败分开处理;空数据本身不足以证明请求失败。
- HTTP 请求错误与单只标的错误处于不同层级,日志和重试策略也应分开设计。
- QuantDash 提供实时行情快照、查询能力、Python SDK 和 REST API;具体接口结构及逐标的响应规则应查阅官方文档。
QuantDash 官方文档
- QuantDash 技术文档 --- 查看 Python SDK、REST API 及数据接口文档