logo

A股量化交易本地Tick数据落盘方案

作者:公子世无双2026.11.07 02:52浏览量:1

简介:面向A股量化开发者,本文系统讲解如何突破行情接口的历史数据获取限制,通过本地化存储构建可持续复用的Tick数据库。内容涵盖全市场代码列表拉取、集合竞价数据订阅、字段含义解析、分区存储与增量更新策略,并给出可落地的代码示例与性能优化建议,帮助你为集合竞价与高频策略打下坚实的数据基础。

A股量化交易本地Tick数据落盘方案

对于个人开发者而言,参与A股量化交易时最先遇到的工程问题往往不是策略本身,而是数据从哪里来、如何稳定地留下来。多数行情接口对历史Tick数据的回溯窗口有明确限制,通常只能提供最近若干天的数据,超过窗口期的请求会被拒绝或返回空值。这意味着如果不在每个交易日及时落盘,一旦错过就再也无法补回。对于依赖集合竞价、盘口微结构、逐笔成交行为的策略而言,这种数据缺失是致命的。

本文围绕“如何把Tick数据稳定地保存到本地”这一核心问题展开,从接口限制、代码列表获取、订阅与读取、字段解析,到存储结构与增量更新策略,逐层给出可操作的方案。

一、为什么必须做本地化存储

行情接口的设计目标通常是“实时推送”而非“历史归档”。因此它一般具备两个特征:

  1. 回溯窗口有限:只能获取最近N个交易日的Tick数据,N通常较小。
  2. 订阅有状态:需要先订阅标的,才能通过接口读取对应数据。

这两个特征决定了本地存储不是可选项,而是必需项。Tick数据本身还包含集合竞价阶段(开盘前与收盘前的集中撮合)的委托与成交信息,这正是集合竞价策略的核心输入。若当天不保存,第二天再想复现当日的竞价结构,基本不可能。

因此推荐的做法是:每个交易日收盘后,自动把全市场Tick数据落盘一次,形成按日期分区的本地数据库。

二、获取全市场标的列表

第一步是拿到需要订阅的标的集合。A股市场包含沪深两市与北交所,接口一般通过“板块”来组织标的。常见做法是先下载板块信息,再从板块中取出成分股。

  1. SECTOR_A_SHARE = "沪深A股"
  2. SECTOR_BEIJING = "BJ"
  3. def get_a_share_list() -> list[str]:
  4. """获取沪深京A股列表。"""
  5. download_sector_info()
  6. stock_codes = set(xtdata.get_stock_list_in_sector(SECTOR_A_SHARE) or [])
  7. stock_codes.update(xtdata.get_stock_list_in_sector(SECTOR_BEIJING) or [])
  8. return sorted(stock_codes)

这里有几个细节值得注意:

  • 先下载板块信息:板块成分并非静态,新股上市、退市、指数调整都会改变列表,因此每次运行前重新拉取是必要的。
  • 使用集合去重:沪深与北交所板块可能存在交叉,用 set 合并再去重可以避免重复订阅。
  • 排序输出:排序后的列表便于日志比对与增量更新时定位差异。

如果标的数量较大(A股全市场约五千余只),一次性订阅全部Tick可能给客户端带来较大压力。实践中可以分批处理,例如按每批200~500只分组,处理完一批再进入下一批。

三、订阅与读取Tick数据

拿到标的列表后,逐个(或分批)订阅并读取。核心流程是“先订阅,再读取指定时间区间”。

  1. xtdata.subscribe_quote(
  2. stock_code=stock_code,
  3. period="tick",
  4. count=-1,
  5. )
  6. data = xtdata.get_market_data_ex(
  7. stock_list=[stock_code],
  8. period="tick",
  9. start_time=f"{trading_day}091500",
  10. end_time=f"{trading_day}235959",
  11. )
  12. tick_df = data.get(stock_code)

几个关键点:

  • period="tick":指定获取的是Tick级别数据,而非分钟或日线。
  • count=-1:表示不限制推送数量,交由时间区间过滤。
  • 时间区间:起始时间设为 091500,可以覆盖集合竞价阶段(9:15开始接收申报)。结束时间设为当日 235959,确保收盘后的数据也被包含。
  • get_market_data_ex 返回结构:通常是一个字典,键为标的代码,值为对应的 DataFrame。取用时需要用 .get(stock_code) 判空,避免标的无数据时抛异常。

需要特别提醒的是:Tick数据的实际采样粒度约为3秒一次,并非逐笔成交。也就是说,它是按固定时间间隔对盘口与成交状态做的快照。这一点直接影响策略假设——如果你的策略需要真正的逐笔委托流,那么快照式Tick并不等价,需要重新评估数据适用性。

四、Tick字段含义解析

落盘前必须理解每个字段的语义,否则存下来的只是一堆无意义的列名。常见字段及含义如下:

字段 含义
time 时间戳
lastPrice 最新价
open 开盘价
high 最高价
low 最低价
lastClose 前收盘价
amount 成交总额
volume 成交总量
pvolume 原始成交总量
stockStatus 证券状态
openInt 持仓量
lastSettlementPrice 前结算
askPrice 委卖价
bidPrice 委买价
askVol 委卖量
bidVol 委买量
transactionNum 成交笔数

几点解读:

  • askPrice / bidPrice 通常是数组:一般包含多档(如五档或十档)委卖委买价,落盘时需要决定是展平为多列,还是以序列化形式存入单列。
  • stockStatus:用于判断标的当前是否处于可交易、停牌、涨跌停等状态,对过滤异常数据非常重要。
  • pvolume 与 volume:前者为原始成交量(可能含未成交或特殊口径),后者为已成交量,两者在不同场景下用途不同。
  • transactionNum:成交笔数,可用于估算成交活跃度与平均单笔规模。

建议在落盘时同时保存字段字典或注释文件,避免日后回溯时忘记语义。

五、落盘:从DataFrame到本地文件

最直接的保存方式是利用 DataFrame 的导出能力:

  1. tick_df.to_csv(output_path, index=True, encoding="utf-8-sig")

utf-8-sig 编码可以保证在常见表格工具中打开时不出现中文乱码。index=True 会保留行索引,便于后续按序读取。

但在生产环境中,直接存CSV会带来几个问题:

  1. 体积膨胀:Tick数据列多、行数大,CSV文本格式占用空间远高于二进制格式。
  2. 读取缓慢:全市场Tick按日落盘后,CSV读取速度难以满足回测需求。
  3. 类型丢失:CSV读回后数值类型需要重新推断,容易出错。

因此更推荐的分层存储策略是:

  • 热数据(近期):使用列式二进制格式(如 Parquet)保存,压缩率高、读取快、类型保留完整。
  • 冷数据(历史):可归档到对象存储,按日期分区,需要时再拉取。
  • 元数据:单独维护一份清单,记录每个交易日已落盘的标的、行数、时间范围,用于校验与增量补齐。

六、目录结构与增量更新

一个清晰、可扩展的目录结构能极大降低后续维护成本。推荐按“日期 + 标的”两级分区:

  1. tick_data/
  2. 2026-04-22/
  3. 600000.parquet
  4. 000001.parquet
  5. ...
  6. 2026-04-23/
  7. ...

对应代码:

  1. from pathlib import Path
  2. def build_output_path(base_dir: str, trading_day: str, stock_code: str) -> Path:
  3. day_dir = Path(base_dir) / trading_day
  4. day_dir.mkdir(parents=True, exist_ok=True)
  5. return day_dir / f"{stock_code}.parquet"

增量更新逻辑建议如下:

  1. 每日收盘后运行一次全量落盘任务。
  2. 落盘前检查目标文件是否已存在,若存在则跳过,避免重复写入。
  3. 维护一份当日清单文件,记录成功与失败的标的。
  4. 对失败的标的进行重试,最多重试若干次。
  5. 次日开盘前,可对前一日清单做完整性校验。

七、性能与稳定性建议

  • 分批订阅:全市场一次性订阅容易造成内存与连接压力,建议分批处理并在批次间释放引用。
  • 异常隔离:单只标的失败不应中断整个任务,用 try/except 包裹单标的处理逻辑。
  • 日志记录:记录每只标的的落盘行数与耗时,便于发现异常标的。
  • 时区与交易日:确保 trading_day 来自交易日历而非自然日,避免节假日产生空数据。
  • 数据校验:可对关键字段做空值率统计,若某字段空值率异常偏高,说明该标的当日数据可能不完整。

八、总结

个人开发者在A股做量化,数据自主可控是长期竞争力的基础。行情接口的历史回溯限制决定了Tick数据必须当天落盘,而集合竞价数据的不可复现性进一步放大了这件事的重要性。完整的落盘链路包括:拉取全市场标的、分批订阅、按时间区间读取、理解字段语义、以分区结构保存、并配合增量更新与校验机制。

把这套流程脚本化、定时化之后,你得到的不只是一堆文件,而是一个可以持续增长的本地行情数据库——它将成为后续策略研究、回测与实盘验证的共同地基。

发表评论

活动