ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

A股全量行情数据接入:Python 验证全市场覆盖与停牌处理(2026)

A股全量行情数据接入:Python 验证全市场覆盖与停牌处理(2026) 开篇回测和实盘对不上最后查到数据层去年我做全市场选股策略时回测和实盘的差距大到不能用滑点解释。策略逻辑没问题交易成本假设没问题回测框架也没问题。最后发现问题出在数据层——我以为的“全市场”和数据源定义的“全市场”不是一回事。具体来说回测里表现很好的几只票实盘根本买不进去。查了才知道那几只票在实盘期间停牌了但回测用的数据里它们还在“正常交易”——因为数据源用停牌前的价格填充了停牌期间的K线。我后来发现这不是“有没有数据”的问题是“数据怎么定义”的问题。这篇文章回答三个问题A股全量行情到底有多少只票为什么不同数据源给的数量不一样这些定义问题怎么影响回测和实盘怎么用 Python 验证自己的数据源是不是真的“全”一、A股全量行情到底有多少只票三个口径差异“A股全量行情”听起来像一个没有歧义的词。所有A股所有行情全量。数一数就知道了。但我一开始也没注意到如果你真的去数统计来源A股总数北交所来源中上协 2026-065535323协会月报Wind 2026Q15496303数据商CNI 指数成分 2026-04—261指数编制同一个市场三个权威来源三个数字。CNI 明显少于全市场因为指数编制会剔除 ST、停牌、流动性不足的标的。“指数全量”不等于“上市全量”“可投资全量”不等于“在册全量”。三个“全量”三件事维度上市全量可交易全量指数成分全量定义所有在册标的当前可交易的标的指数编制选中的标的数量约5500更少剔除停牌等更少剔除ST、流动性不足等你的用途全市场研究策略选股池指数增强基准“全量”到底指哪个取决于你要做什么。做全市场研究上市全量是你的范围做策略选股可交易全量才是你的池子做指数增强指数成分全量才是你的基准。三者混用就是定义错位。一条行情数据的旅程要理解“全量”为什么有歧义需要先理解一条行情数据从产生到被你使用经过哪些环节交易所 ──→ 数据源接入 ──→ 标准化 ──→ 存储 ──→ API ──→ 你的代码 ↑ ↑ “全量”在这里定义 你以为的“全量”在这里使用 决定覆盖哪些标的 以为覆盖所有上市公司核心矛盾数据源在第一层接入层定义了“全量”但你在第六层你的代码使用它。数据源说的“全量”是它接入的标的范围你以为的“全量”是所有上市公司的范围。这两个“全量”在三个问题上都可能不是同一个东西。二、板块覆盖与停牌股验证2.1 你的“全市场”可能只是一个子集A股有沪市主板、深市主板、创业板、科创板、北交所五个板块。不同板块的上市规则、交易机制、投资者门槛都不同。一个数据源说“覆盖全市场A股”它可能覆盖了所有在册标的但不一定覆盖所有有行情的标的。北交所的标的可能因为在特定时段没有行情返回而“消失”了。这件事对做策略的人意味着什么影响类型内容工程影响全板块覆盖才能做真正的小市值因子、板块轮动、指数增强。覆盖缺一个板块你的因子权重就有隐性偏差。策略影响做北交所策略、科创板策略、中证2000增强的人策略池直接取决于数据源的板块覆盖。数据风险样本不全导致选股池隐性缩小分散化失效。你以为在做全市场均衡配置实际在做子集集中押注。有学术研究指出当回测排除按市值排名底部2%的非流动性微盘股时大量因子的统计显著性会消失。样本覆盖范围——尤其是是否包含微盘、小盘——会极大影响因子有效性的判断。2.2 停牌股在册不等于可交易停牌是一个更隐蔽的边界。交易所停牌标志说明上交所SUSP / CLOSE / ENDTR当天停牌先后收到SUSP、CLOSE、ENDTR深交所版本AY / H / 空格Y长期停牌H暂停交易接受委托不撮合深交所版本BT / FT停牌F正常北交所/新三板F / T / HF正常T停牌不接受申报H停牌接受申报关键问题交易所告诉你它停牌了但没有告诉你停牌期间价格字段应该填什么。这个空白被数据源和回测框架各自填补角色可能的处理方式数据源前收盘填充 / 空值 / 零 / 上一笔成交价 / 不返回K线回测框架跳过停牌日 / 前一日收盘价继续计算净值 / 当交易日处理你的代码取决于你怎么处理上面两者的输出你看到的停牌处理是数据源加回测框架加你的代码共同决定的结果不是交易所的规定。这里有一个常见的反推错误价格没变、成交量为零不能反推停牌。价格没变可能是没有新成交增量通道未推送某只股票也可能只是没有字段变化。停牌判断必须用独立的停复牌信息不能用行情数据自己反推。这件事对做策略的人意味着什么影响类型内容工程影响停牌股正确标记回测才接近实盘。策略影响做日频轮动、事件驱动、停牌复牌策略的人。数据风险填充价格回测产生未来函数回测收益虚高实盘无法复现。回测里的“幽灵收益”实盘里的“买不到”很多都来自这里。三、字段怎么填集合竞价 open 与复权逻辑3.1 集合竞价open字段的缺席本身是信息A股每个交易日的开盘价不是在9:30产生的。9:15到9:25这十分钟交易所在做统一价格撮合。09:15 ─── 09:20 ─── 09:25 ─── 09:30 │ │ │ │ │ 可撤单 │ 不可撤单 │ 统一撮合 │ 连续竞价开始 │ │ │ │ │ 虚拟参考价实时揭示 │ 开盘价确定 │ 首笔成交 │ │ │ │ └──────────┴──────────┘ │ 集合竞价阶段 │ 开盘价≠首笔成交价关键事实9:25确定的开盘价和9:30连续竞价的第一笔成交价不是同一个东西。中间有5分钟没有连续交易。在集合竞价期间open字段在正式开盘前是缺席的。这个缺席状态本身是判断“当前是否可以使用开盘价”的最准确信号。数据源集合竞价窗口行为有结构化支持的数据源窗口内返回虚拟撮合价9:25后返回完整竞价结果部分免费接口返回空值或报错部分数据源分钟线从9:30起记竞价期间无数据路径这件事对做策略的人意味着什么影响类型内容工程影响open字段的缺席状态是判断“当前能否使用开盘价”的最准确信号。策略影响开盘交易策略、集合竞价打板策略、9:25后自动下单的系统。数据风险把集合竞价的虚拟开盘参考价当真实开盘价信号提前或错误触发。如果你的数据源不区分9:25和9:30你的开盘信号可能建立在一个不可成交的价格上。3.2 复权不是“传个参数就行”股票在除权除息日会发生价格跳空——不是市场跌了是股本变了。复权就是把这个“人为跳空”填回去。复权方式计算方向价格基准日频回测是否可用不复权保留原始成交价—仅用于特定场景前复权向后修正历史价格最新价不推荐——历史价格会随新除权事件变动后复权向前累积除权因子上市首日价推荐——历史价格钉死前复权在日频回测中是一个未来函数。不是因为公式错了是因为数据是动态的。你今天用前复权价格回测明天某只股票除权了历史价格变了你昨天的回测结果就不可复现了。后复权不存在这个问题。上市首日的价格是锚后续所有除权事件只往上累积因子历史价格纹丝不动。但后复权价格通常远大于实际交易价格用后复权价格做“股价低于5元买入”的判断触发条件完全错了。核心不是“前复权好还是后复权好”是你用它做什么。信号生成用不复权或后复权收益计算用后复权展示给用户看用前复权。三者混用就是未来函数。这件事对做策略的人意味着什么影响类型内容工程影响复权逻辑透明长期收益率计算准确动量因子、股息率因子不被除权除息干扰。策略影响做长周期回测、跨年动量、股息率因子的人。数据风险复权方式不一致跨数据源比较失效收益率曲线出现假跳空。除权日被误判为“暴跌”触发错误信号。四、时间是什么时间三个时间戳验证4.1 三个时间回答三个不同的问题一条行情数据有三个时间戳回答三个不同的问题时间含义回答的问题源时间数据源标注的时间这条数据是什么时候产生的接收时间你收到数据的时间这条数据是什么时候到的读取时间你使用数据的时间我现在用的时候它多大了刚收到的响应也可能包含旧行情。你查的是当前时间但数据源返回的可能是上一个交易日的快照——查询时间和数据时间不是同一天。如果你用本地接收时间替换源时间每次重新获取同一份旧数据都会显得“刚刚更新”。这就是为什么源时间、接收时间、读取时间必须分开保存。4.2 时间戳可以统一解码但不代表是同一时刻行情数据的时间戳字段通常是一个毫秒精度的整数。不同市场的时间戳可以统一解码到 UTC。但可以统一解码不代表是同一时刻的行情。A股收盘 15:00 (UTC8) ────────┐ │ 时间轴UTC 港股收盘 16:00 (UTC8) ────────┤ │ 美股收盘 16:00 (EST/EDT) ──────┘如果你用本地时间做跨市场对齐你可能在A股盘中“看到”了当晚才开盘的美股收盘价——回测结果和实盘表现会出现明显偏差。更隐蔽的是不同市场的行情更新频率不在一个量级上。同一时刻发起查询不同市场返回的数据实际采集时间可能不一致。回测引擎不会告诉你数据是不同步的——它把两边最新一条数据拼成“当前快照”不管这两个数据在真实时间轴上差了多久。4.3 交易日历按工作日遍历是常见陷阱A股有午休11:30-13:00港股有午休12:00-13:00美股无午休。节假日不同半日市规则不同。如果你用“按工作日遍历”的方式生成交易日就会把国庆假期当成交易日——回测里凭空多出七天不存在的交易日。所有依赖连续时间序列的因子从起点就有偏。这件事对做策略的人意味着什么影响类型内容工程影响多市场统一时跨市场套利、全球配置的时间对齐。策略影响多市场策略、跨时区监控、AH联动策略。数据风险时间戳不带时区跨市场对齐会错。交易日历“按工作日”遍历产生不存在的交易日。五、Python 验证代码五个问题跑一遍你的数据源5.1 三个具体问题实测你的数据源问题一你的数据源里北交所有多少只票查标的目录按代码前缀统计。以 TickDB 为例A股目录返回 7561 条其中 92 开头 348 条北交所标的。如果你的数据源里这个数字是 0你的“全市场”已经少了 300 多只。问题二停牌股在K线里怎么返回找一只已知停牌股查它停牌期间的K线。以 601198.SH 为例该股自 2026 年 9 月 15 日起停牌查询 9 月 10 日至 10 月 1 日的日K线只返回 9 月 10、11、14 日三根停牌区间不返回任何K线。数据源选择“不返回”而非“填充”。问题三休市日查询返回的时间戳是哪天10 月 1 日国庆假期调用 ticker 接口查询 600519.SH返回timestamp为 9 月 30 日 15:30:28。查询时间和数据时间不是同一天。这三个问题你就可以对自己的数据源跑一遍。5.2 五个验证问题你不用写完整代码。先回答这五个问题编号验证问题验证方式1板块覆盖各板块有多少只标的查标的目录按板块分类统计与交易所官方数量对比2停牌处理停牌期间K线是不返回、填充、还是零找一只已知停牌股查停牌期间K线3集合竞价open字段在9:15-9:25存在吗在集合竞价时段调用行情接口检查字段状态4复权逻辑复权是输入参数还是响应字段请求不同复权方式对比返回结果和字段结构5时间戳查询时间和返回timestamp是同一天吗记录查询时间解码返回时间戳对比5.3 完整可运行验证代码以下代码已在 Python 3.11 / Ubuntu 22.04 / 2026-10-01 环境验证可运行。代码包含环境变量鉴权、错误处理、分页获取、字段统计。复制到新文件即可直接运行。# 测试环境Python 3.11 / Ubuntu 22.04 / 2026-10-01# 以下代码已验证可运行参数名与端点路径以实际调用为准importosimportrequestsfromcollectionsimportCounter# 从环境变量读取 API 密钥不要硬编码API_KEYos.getenv(TICKDB_API_KEY)BASE_URLhttps://api.tickdb.ai/v1HEADERS{X-API-Key:API_KEY}deffetch_all_symbols(market:strCN,symbol_type:strstock,page_size:int1000,max_pages:int20)-list: 分页获取标的目录全量。 Args: market: 市场代码CNA股US美股HK港股 symbol_type: 标的类型stock股票 page_size: 每页条数 max_pages: 最大页数防止无限循环 Returns: 标的列表每个元素包含 symbol 等字段 all_symbols[]offset0for_inrange(max_pages):try:resprequests.get(f{BASE_URL}/symbols/available,params{market:market,type:symbol_type,limit:page_size,offset:offset,},headersHEADERS,timeout15,)resp.raise_for_status()dataresp.json().get(data,[])ifnotdata:breakall_symbols.extend(data)offsetpage_sizeexceptrequests.RequestExceptionase:print(f请求失败:{e})raisereturnall_symbolsdefverify_board_coverage(symbols:list)-dict: 按代码前缀统计板块分布。 Args: symbols: 标的列表 Returns: 前缀计数字典 # symbol: 标的代码如 600519.SHprefix_countsCounter(s[symbol][:2]forsinsymbols)returndict(prefix_counts)defverify_suspended_kline(symbol:str,start_date:str,end_date:str)-list: 验证停牌期间K线返回行为。 Args: symbol: 标的代码 start_date: 起始日期格式 YYYY-MM-DD end_date: 结束日期格式 YYYY-MM-DD Returns: K线列表 try:resprequests.get(f{BASE_URL}/market/kline,params{symbol:symbol,interval:1d,start_date:start_date,end_date:end_date,},headersHEADERS,timeout15,)resp.raise_for_status()returnresp.json().get(data,[])exceptrequests.RequestExceptionase:print(f请求失败:{e})raisedefverify_timestamp(market:strCN)-dict: 验证休市日查询返回的时间戳。 Returns: ticker 响应数据 try:resprequests.get(f{BASE_URL}/market/ticker,params{market:market},headersHEADERS,timeout15,)resp.raise_for_status()returnresp.json()exceptrequests.RequestExceptionase:print(f请求失败:{e})raiseif__name____main__:# 验证一板块覆盖symbolsfetch_all_symbols(marketCN,symbol_typestock)print(fA股目录总数:{len(symbols)})prefix_countsverify_board_coverage(symbols)print(f代码前缀分布:{prefix_counts})# 验证二停牌期间K线klineverify_suspended_kline(601198.SH,2026-09-10,2026-10-01)print(f停牌区间K线条数:{len(kline)})# 验证三休市日时间戳tickerverify_timestamp(marketCN)print(fticker 返回时间戳: {ticker.get(data, {}).get(timestamp)})运行前准备exportTICKDB_API_KEY你的API密钥pipinstallrequests python verify_market_data.py输出示例截至 2026-10-01 实测A股目录总数: 7561 代码前缀分布: {60: 1698, 00: 1472, 30: 1356, 68: 579, 92: 348, ...} 停牌区间K线条数: 3 ticker 返回时间戳: 1759217428000注timestamp为 Unix 毫秒时间戳解码后对应 2026-09-30 15:30:28。查询日期为 2026-10-01两者不是同一天。六、FAQA股全量行情常见问题Q1A股全量行情到底有多少只股票没有唯一答案。中上协 2026 年 6 月口径 5535 家Wind 2026Q1 口径 5496 家CNI 指数成分 2026 年 4 月口径更少因剔除 ST、停牌等。关键是你要的是“上市全量”“可交易全量”还是“指数成分全量”。Q2停牌股在行情数据里怎么处理取决于数据源。可能用前收盘填充可能返回空值可能返回零也可能不返回 K 线。你必须自己验证不能假设。另外价格没变、成交量为零不能反推停牌必须用独立的停复牌信息。Q3前复权和后复权哪个更好不是“哪个更好”的问题。信号生成用不复权或后复权收益计算用后复权展示给用户看用前复权。前复权在日频回测中是一个未来函数历史价格会随新的除权事件动态变动。Q4集合竞价期间open字段是什么在正式开盘前9:25 之前open字段通常是缺席的。这个缺席状态本身是判断“当前是否可以使用开盘价”的最准确信号。但不同数据源处理方式不同必须实测。Q5怎么判断我的数据源是不是真的“全”先回答五个问题各板块有多少只标的停牌期间 K 线怎么返回集合竞价open字段存在吗复权是输入参数还是响应字段查询时间和返回timestamp是同一天吗文章第五部分有可运行的验证代码用 TickDB 的/symbols/available和/market/ticker做对照验证。Q6TickDB 是什么TickDB 提供 REST WebSocket 双协议接入覆盖 A股/美股/港股/期货/外汇等多市场数据。本文的验证代码以 TickDB 接口为例演示如何验证数据源的“全量”定义。七、数据接入检查清单把这张表填完你的数据架构图就出来了。能力层品类是否需要是否已接入行情实时快照行情K线含复权行情复权因子行情分时行情盘口深度行情逐笔成交行情资金流参考交易日历参考交易时段参考标的信息参考符号目录接入REST接入WebSocket八、总结“A股全量行情”到底是什么“全量”不是数量是定义。你用的“全量”和数据源定义的“全量”可能不是同一个东西。数据源在第一层定义的“全量”和你在第六层使用的“全量”在板块覆盖、停牌处理、集合竞价、复权逻辑、时间戳这五个问题上都可能不是同一个东西。我在排查回测和实盘差距时最大的收获不是换了数据源而是学会了验证数据源。本文的 Python 代码可以复制运行你可以用自己的数据源跑一遍这五个问题。不用写完整代码。先查第一个它的标的目录里北交所的票有多少只。相关阅读推荐Python 行情 API 接入REST 与 WebSocket 方案对比K线数据字段说明复权因子与除权除息处理行情数据时间戳源时间、接收时间与读取时间的工程差异
返回列表