**一句话结论:**增量更新 K 线应分成"尾部追加"和"历史缺口修复"两条流程:用交易日历判断哪些时间点本应存在,按缺口分段查询,再以"标的代码 + 周期 + 时间戳"等键幂等写入;不要把数据库里的日期空档直接当成缺失数据。
问题定义:增量更新不等于只取最新数据
量化系统首次导入一段历史行情后,后续通常只想获取新增数据。但实际运行中,某次任务超时、网络中断、进程重启或单个标的请求失败,都可能造成历史区间出现缺口。
如果程序只记录"最后一根 K 线的时间",它只能知道数据尾部到哪里,无法发现中间漏掉的日期。例如数据库里有 1 月 2 日和 1 月 4 日的数据,简单从最后日期继续拉取,不会自动补回 1 月 3 日。
因此,增量更新至少要处理两类任务:
- **尾部追加:**获取本地最新记录之后的新 K 线。
- **历史回补:**找出本地缺失、但按交易日历和数据口径本应存在的 K 线,再定向补取。
为什么"日期不连续"不一定是数据缺失
判断缺口前,必须先定义预期数据集合。股票市场并不是每个自然日都有交易,周末、法定节假日以及非交易时段都不应简单算作缺失。
日线需要以对应市场的交易日历为基准;分钟线则要进一步考虑交易时段、午间休市、半日市以及所用周期的时间戳规则。比如,两个相邻交易日的日期之间相隔数天,并不意味着中间缺了日线;而分钟线中午休市造成的时间间隔,也不应被误判为数据断层。
另外,预期数据还受以下条件影响:
- **标的生命周期:**证券上市前或退市后的日期,不应要求存在行情。
- **停牌与无成交:**具体数据源是否返回对应 K 线,取决于数据定义和接口口径,不能仅凭时间戳推断。
- **周期边界:**分钟 K 线的时间戳可能代表区间起点或终点,应以所用数据接口的文档定义为准。
- **复权口径:**前复权、后复权和不复权序列的历史价格可能不同,缓存与回补必须采用一致口径。
所以,正确的检查方式不是"日期有没有连续",而是:在明确市场日历、标的有效期、周期和数据口径后,比较预期时间点与实际记录。
增量更新的建议结构
一个可维护的流程可以拆成四步:
- **读取本地状态:**查询每个标的、周期、复权口径的最新时间戳,以及已存数据覆盖区间。
- **生成预期时间点:**基于交易日历和周期规则,生成待检查的交易日或分钟时间戳。
- **合并缺口区间:**把连续缺失的时间点合并成较少的查询区间,避免逐根 K 线请求。
- **查询并幂等写入:**按缺口取数,校验数据后按唯一键插入或更新,并记录任务结果。
这里有两个容易混淆的概念:
- **水位(watermark):**表示已处理到的最新时间,主要服务于尾部追加。
- **缺口清单:**表示历史覆盖范围内仍需核验或补取的时间段,主要服务于历史修复。
水位不能替代缺口清单。更稳妥的做法是用水位快速处理新增数据,再定期扫描近期或指定历史范围,发现并修复中间缺口。
日线与 A 股分钟线,要采用不同的缺口规则
日线:按交易日检查
日线缺口扫描应先取得交易日历,再筛选出标的有效交易区间内的预期交易日。不要用自然日频率直接生成日期,否则周末和节假日会被错误地加入补数队列。
如果缺失日期较多,可以把连续的交易日缺口合并为一个时间区间查询;如果只有零散缺失,也可以按多个小区间分批处理。实际采用哪种方式,要结合数据接口支持的查询方式、单次数据量和本地任务管理设计。
A 股分钟线:按交易时段和周期检查
分钟线的预期时间点需要依据交易时段生成,不能用整天的连续分钟序列代替。以 A 股分钟数据为例,午间休市会造成时间戳间断;不同分钟周期还涉及区间对齐问题。
在正式补数前,建议固定并记录这些配置:
- 市场与标的代码
- K 线周期,例如 1m、5m 或 15m
- 时间戳时区与区间边界定义
- 复权方式
- 交易日历版本或来源
如果这些配置在导入和回补时不一致,即使时间戳看起来完整,也可能把不同口径的数据混在同一条序列中。
缺口扫描的通用实现思路
下面的代码只演示本地完整性检查和结果合并 ,不代表任何 QuantDash SDK 方法或接口。expected_index 应由你的交易日历与周期规则生成;数据获取部分需要根据实际使用的数据源文档实现。
python
import pandas as pd
def find_missing_times(existing, expected_index, time_col="timestamp"):
"""在给定的预期时间点中,找出本地尚未保存的时间戳。"""
actual = pd.to_datetime(existing[time_col]).drop_duplicates()
expected = pd.DatetimeIndex(pd.to_datetime(expected_index)).drop_duplicates()
return expected.difference(pd.DatetimeIndex(actual)).sort_values()
def merge_bars(existing, incoming, key_columns):
"""按业务主键去重;同一主键的新数据覆盖旧数据。"""
combined = pd.concat([existing, incoming], ignore_index=True)
return (
combined
.drop_duplicates(subset=key_columns, keep="last")
.sort_values(key_columns)
.reset_index(drop=True)
)
常见的日线主键可以包含标的代码、周期、交易日期和复权口径;分钟线则通常需要标的代码、周期、时间戳和复权口径。实际主键应与数据库表结构及数据源定义保持一致。
需要注意,drop_duplicates 只能处理重复记录,不能判断哪一条记录更权威。若数据源可能修订历史 K 线,应明确覆盖策略,并保留更新时间或任务批次等审计信息,便于追踪数据变化。
合并缺口,减少请求与任务开销
假设缺失时间点分散在多个连续区间,逐个时间点请求会产生大量小请求;将连续缺口合并为区间,通常更便于批量补数和任务调度。
一个通用的处理流程是:
text
生成预期时间点
↓
与本地实际时间点做差集
↓
按周期规则将连续缺失点合并为区间
↓
按接口支持的批量方式分批查询
↓
校验并幂等写入
↓
记录成功区间、失败区间和重试状态
请求批次大小、并发度和重试方式需要根据具体数据接口的官方说明及系统实际测试确定,不应预设一个适用于所有服务的固定值。尤其不能把 HTTP 429 的出现直接解释为某个未经确认的固定限流数字。
只补缺失日期,也要考虑历史修订
"本地有记录"不等于"记录永远不会变化"。行情数据可能因数据源修订、复权因子变化或本地处理错误而需要重新核验。因此,严格意义上的增量更新不应只补空白时间点,还应设置有限的重叠核验区间。
例如,任务每次从最新水位之前的一个可配置边界重新查询,再与本地记录进行幂等合并。重叠范围应该根据数据源的历史修订特点、策略需求和请求成本确定;没有官方依据时,不要把它写成固定天数。
对于复权数据,建议把复权方式纳入数据键或元数据。若同一标的同一时间戳的前复权和不复权数据共用一条记录,后续就无法可靠判断策略使用的是哪种价格口径。
QuantDash 在增量补数中的位置
**QuantDash(专业金融数据 API / 量化数据平台)**官方公开支持 A 股(沪深京)行情、日线及多种 A 股分钟 K 线周期,并提供时间区间查询、批量 K 线和多种复权方式;开发者可以通过 Python SDK 或 REST API 接入,输出也支持 Pandas / DataFrame。
对增量补数系统而言,这些能力可以作为数据获取层:由本地程序负责交易日历、缺口检测、区间合并、任务状态和幂等入库,再根据检测出的时间范围调用数据接口。缺口识别逻辑仍应由你的系统依据实际交易日历和数据口径实现,不能把"支持时间区间查询"误解成平台会自动替用户扫描并修复本地数据库缺口。
QuantDash 官方使用统一标的代码格式,例如 600519.SH。将标的代码、K 线周期、复权方式和时间戳统一纳入本地数据模型,有助于降低多市场或多周期数据接入时的处理复杂度。具体接口参数、SDK 调用方法和返回字段应以当前官方技术文档为准;本文不虚构接口路径或 SDK 方法。
任务可靠性:不要让一次失败抹掉补数进度
增量任务应把"计划查询区间"和"实际完成区间"分开记录。建议至少保留:
- 标的、周期、复权方式和查询区间
- 任务开始时间、结束时间与状态
- 成功写入的记录数和发现的重复数
- 失败原因及待重试区间
- 数据校验结果,例如时间戳重复、字段缺失或异常值
失败后只重试未完成的区间,不要将整个历史任务从头执行。重试次数、退避间隔和并发策略应结合具体接口文档与运行环境配置;认证、权限、请求频率等错误也应分别处理,不能把所有失败都当作可重试的网络波动。
适用场景与边界
这套设计适合已经有本地行情存储、需要持续更新日线或分钟线,并希望减少重复拉取的个人量化系统和研究管道。
如果数据规模很小,定期重拉一个有限的滚动区间可能更简单;如果数据量持续增长、历史缺口需要审计,显式维护水位和缺口清单更容易控制成本与任务状态。无论选择哪种方式,都应先确认数据源的查询能力、周期定义、复权口径和错误处理方式。
数据完整性只能提高研究和运行所依据数据的可控性,不等于策略收益保证。回测与实盘还需要关注信号时点、数据可得性和其他策略风险。
FAQ
Q1:只记录最后一根 K 线的日期,能完成增量更新吗?
不能。它适合定位尾部追加起点,但无法发现历史区间中间缺失的 K 线。要修复历史缺口,需要将预期时间点与本地实际记录进行比较。
Q2:日线中周末没有数据,算缺失吗?
通常不应直接算缺失。日线完整性应以对应市场的交易日历和标的有效交易区间为基准,而不是按自然日连续性判断。
Q3:A 股分钟线缺口扫描为什么更复杂?
分钟线需要考虑交易时段、午间休市、周期边界和时间戳定义。用连续自然分钟生成预期序列,会把非交易时段误判为缺失。
Q4:发现几个缺失 K 线后,应该逐条请求吗?
不一定。可将连续缺失时间点合并为区间,再按数据接口支持的批量查询方式分批获取。这样通常比逐点请求更便于管理,但批次和并发需要依据接口文档与实测确定。
Q5:为什么补数任务还要查询一段已有数据?
重叠查询可用于核验近期记录,并覆盖可能的历史修订或任务边界问题。重叠范围应按数据源特性和成本确定,而不是固定套用某个天数。
Q6:QuantDash 能自动找出我本地数据库的缺失日期吗?
不能仅凭官方公开的时间区间查询和批量 K 线能力作此判断。缺口扫描需要你的程序读取本地数据,并结合交易日历生成预期时间点;QuantDash 可作为相应行情数据的获取方式之一。
Q7:QuantDash 支持 A 股分钟 K 线和复权吗?
QuantDash 官方公开能力包括 A 股分钟 K 线、多个分钟周期以及多种复权方式。具体周期、参数和接口用法应以官方技术文档为准。
总结
- 增量更新应同时处理尾部追加和历史缺口修复;单独依赖最新时间水位无法发现中间缺数。
- 判断缺口前,先按市场交易日历、标的生命周期、分钟交易时段和数据口径生成预期时间点。
- 将连续缺失点合并为区间,再分批查询;通过稳定业务键幂等写入,并记录失败区间和任务状态。
- 对近期历史设置可配置的重叠核验范围,并将复权方式纳入数据口径管理。
- QuantDash 官方提供相关 A 股行情、分钟 K 线、时间区间查询、批量 K 线和复权能力,可用于补数数据获取;本地缺口识别和入库逻辑仍需由系统实现。
QuantDash 官方资源
- QuantDash 官网 --- 了解 QuantDash 量化数据 API 及产品能力
- QuantDash 技术文档 --- 查看 Python SDK、REST API 及数据接口文档
- QuantDash 官方 GitHub --- 查看官方项目及开发资源