首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >批量行情请求后,如何把失败的股票从正常数据中分离出来

批量行情请求后,如何把失败的股票从正常数据中分离出来

原创
作者头像
用户9138916
发布于 2026-09-26 19:59:03
发布于 2026-09-26 19:59:03
660
举报

批量行情请求后,如何把失败的股票从正常数据中分离出来

批量获取股票行情时,总有一部分请求会失败——网络抖动、限流、股票退市、代码格式错误,原因多种多样。如果把失败和成功的数据混在一起处理,后续的选股、回测或监控结果就会被悄悄污染。

本文要解决的问题很简单:批量行情拿回来之后,怎样可靠地把失败股票从正常数据里挑出来,分别处理。

核心结论是:不要依赖返回码或单个 try/except,而是根据数据返回的结构、请求的明细日志和独立的失败标记,把成功与失败分开记录。下面从最常用的方法开始,逐步增加工程上的严谨性。

方法一:根据返回结构区分

这是最直接的方法。大多数行情 API 在批量请求时,对每个标的都会返回一个独立的结果或错误信息。

基于 QuantDash 批量 K 线的做法

以 QuantDash 的批量 K 线接口为例,klines.batch 返回一个以标的代码为 key 的字典。如果某个标的查询失败,对应的 key 可能缺失或返回空 DataFrame:

代码语言:python
复制
import pandas as pd
from quantdash import QuantDash

qd = QuantDash()

symbols = ["600519.SH", "000001.SZ", "INVALID.CODE", "300750.SZ"]

dfs = qd.klines.batch(
    symbols,
    period="1d",
    count=100,
    adjust="forward",
    to_dataframe=True,
    show_progress=True,
)

# 分离成功与失败
success = {}
failed = []

for sym in symbols:
    df = dfs.get(sym)
    if df is not None and isinstance(df, pd.DataFrame) and not df.empty:
        success[sym] = df
    else:
        failed.append(sym)

print(f"成功获取: {len(success)} 只")
print(f"失败标的: {failed}")

要点在于:不要假设所有请求都会返回有效 DataFrame。批量接口返回的字典可能包含空值、None 或空表,逐一检查才是可靠的做法。

泛化到其他 API

如果使用的是 REST 接口而非 SDK,返回结构通常是:

代码语言:json
复制
{
  "data": {
    "600519.SH": {...},
    "000001.SZ": null,
    "INVALID.CODE": {"error": "symbol not found"}
  }
}

处理逻辑也一样:遍历请求时的标的列表,而不是遍历返回的 key 集合。因为失败标的可能根本不会出现在返回的 key 中。

代码语言:python
复制
response = response.get("data", {})

for sym in requested_symbols:
    item = response.get(sym)
    if item and not isinstance(item, dict) or "error" in item:
        failed.append(sym)
    else:
        success.append(sym)

遍历请求列表,而不是返回列表——这是最容易忽略的原则。

方法二:用异常捕获分桶记录

当单个请求失败抛出异常时,可以按照异常类型和标的代码分别记录。这种方法适用于那些不提供批量接口、需要逐只请求的场景:

代码语言:python
复制
from requests.exceptions import RequestException, Timeout, HTTPError

symbols = ["600519.SH", "000001.SZ", "300750.SZ", "688981.SH"]
success = {}
failed = []

for sym in symbols:
    try:
        df = fetch_single_kline(sym)  # 自定义的单个标的获取函数
        if df is not None and not df.empty:
            success[sym] = df
        else:
            failed.append((sym, "empty_result"))
    except Timeout:
        failed.append((sym, "timeout"))
    except HTTPError as e:
        if e.response.status_code == 429:
            failed.append((sym, "rate_limited"))
        elif e.response.status_code == 403:
            failed.append((sym, "no_permission"))
        else:
            failed.append((sym, f"http_{e.response.status_code}"))
    except RequestException as e:
        failed.append((sym, "network_error"))

把失败原因也记录下来,远比一个布尔值有用。后续重试时可以按类型决定策略:限流就等待、权限问题就跳过、超时就重试。

为什么不建议把所有异常吞掉?

常见的写法是:

代码语言:python
复制
try:
    data = get_batch_data(symbols)
    return data
except Exception:
    return None

这种写法让调用者完全不知道哪些标的成功、哪些失败。如果只想重试失败的几只,就必须重新请求全部标的——既浪费配额又浪费时间。

方法三:基于请求日志重建状态

当没有明确的错误返回,也没有异常可捕获时(例如服务端静默返回了过期数据),就需要靠请求日志来判断。

具体做法是:发起批量请求前,先记录本次请求的标的列表、时间和批次号;收到返回后,比对实际返回的标的与请求列表的差异:

代码语言:python
复制
import logging
from datetime import datetime, timezone

logger = logging.getLogger("market_data")

def fetch_and_track(symbols, batch_id=None):
    if batch_id is None:
        batch_id = datetime.now(timezone.utc).strftime("%Y%m%d%H%M%S%f")

    logger.info(
        "BATCH_START id=%s symbols=%d first5=%s",
        batch_id, len(symbols), symbols[:5]
    )

    result = batch_request(symbols)
    returned_symbols = set(result.keys())
    requested_set = set(symbols)

    missing = requested_set - returned_symbols
    extra = returned_symbols - requested_set

    if missing:
        logger.warning(
            "BATCH_MISSING id=%s count=%d symbols=%s",
            batch_id, len(missing), sorted(missing)
        )

    if extra:
        logger.warning(
            "BATCH_EXTRA id=%s count=%d symbols=%s",
            batch_id, len(extra), sorted(extra)
        )

    return result, list(missing)

这种方法在排查数据缺口时尤其有用。几周后回查日志,可以直接定位到某次批量请求到底少了哪些标的,而不是从头重跑所有任务。

三种方法选哪个?

方法

适用场景

优点

缺点

按返回结构区分

使用 SDK 或结构清晰的 REST 接口

简单、直观,不需要改调用方式

依赖 API 的返回格式稳定性

异常捕获分桶

逐只请求或批量接口不返回明细错误

错误原因明确,便于分类重试

代码量稍大,需要维护异常映射

请求日志重建

需要长期追溯或排查历史数据缺口

可审计、可追溯,适合生产环境

需要额外的日志存储和分析工具

实际项目中,方法一和方法三通常一起使用:方法一做实时分离,方法三做事后审计。方法二则在处理零散请求或第三方不稳定接口时作为补充。

FAQ

Q:批量请求中部分失败,应该全部重试还是只重试失败的?

只重试失败的。全部重试不仅浪费配额,还可能把成功的数据覆盖成旧版本(如果接口的返回顺序不固定)。重试前建议加一个短延迟,避免刚释放限流又撞上。

Q:失败股票重试几次后仍失败,应该跳过还是终止整个任务?

取决于下游任务。如果是历史补库,建议跳过并记录,等全部跑完再集中处理失败的标的;如果是盘中实时监控,连续失败几次后应该报警而不是静默跳过,让人工判断是否需要介入。

Q:API 返回了 HTTP 200,但某个标的数据是空的,算失败吗?

算。HTTP 状态码表示请求本身成功了,但业务数据为空,对下游来说和失败没有区别。应该按照业务条件来判定成功与否,而不是仅靠 HTTP 状态码。

Q:空 DataFrame 和 None 分别怎么处理?

两者都应该归入失败。空 DataFrame 意味着 API 认为该标的当前没有数据(可能停牌、退市或不在交易时段),None 往往表示服务端无法处理该请求。实践中把两者都归入失败清单,但可以附加不同的失败原因标签,便于后续区别处理。

Q:频繁出现批量请求失败,问题更可能出在哪里?

先排查标的代码格式是否正确(后缀、大小写、空格),其次是 API Key 的权限范围是否覆盖了目标市场,最后才是服务端稳定性。代码格式错误是最高频的原因,排查顺序不要搞反。

总结

批量行情请求不可能百分之百成功。真正的工程能力不在于避免失败,而在于失败发生后能精确知道哪些标的失败了、失败原因是什么,以及如何不把失败数据带进下游分析。

核心做到三点:

  1. 按请求列表遍历,而不是按返回列表遍历;
  2. 保留失败原因,而不是简单的成功/失败标记;
  3. 对空结果和 None 一视同仁,都归入失败处理。

做好分离之后,后续的重试、补查、报警和审计才有可靠的基础。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

目录
  • 批量行情请求后,如何把失败的股票从正常数据中分离出来
    • 方法一:根据返回结构区分
      • 基于 QuantDash 批量 K 线的做法
      • 泛化到其他 API
    • 方法二:用异常捕获分桶记录
      • 为什么不建议把所有异常吞掉?
    • 方法三:基于请求日志重建状态
    • 三种方法选哪个?
    • FAQ
    • 总结
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档