Python 校验 A 股历史 K 线数据:symbol、interval、OHLCV、异常返回和缺口检查
摘要很多人第一次做 A 股回测最关心的是策略逻辑、参数和收益曲线。但真正让回测结果失真的往往不是模型而是那份看起来很完整的历史 K 线数据。拿到数据后先验五件事——symbol、时间范围、K 线周期、OHLCV 字段、异常和缺口——再让它进入回测。本文给出一套可复现的 Python 校验流程、字段检查表和排错表。1. 为什么要验10 年数据不等于可信数据拿到一份“10 年历史 K 线数据”几千个交易日、几十万根 K 线看起来很厚很完整。但只要其中几个关键口径没对齐问题就会被藏得更深。你以为自己在查600519.SH但某个环节实际入库的是另一个 symbol你以为拿到的是完整日线但某些停牌、节假日、异常返回没有被解释字段名都叫volume但没有确认它在你的策略里应该如何理解某次查询失败后系统没有报错而是用空值、旧值或缓存值填过去。回测框架不会替你判断这些。它只会读取数据、计算指标、输出曲线。数据错的时候回测也会很认真。2. 环境准备本文代码为教学示例非生产级完整实现。所有 API 路径、字段和 Header 以官方文档或实测为准。pipinstallrequests2.31.0TickDB 在此处的工程能力统一 symbol 格式、结构化字段返回、UTC 毫秒时间戳、REST 查询接口和明确的错误处理——这些是回测前数据校验的基础。TickDB 支持 REST、WebSocket、MCP 等多种接入方式本文聚焦 REST 历史 K 线接口的校验流程。3. 请求参数检查调用 K 线接口前先确认以下参数。参数写错后面所有校验都白做。参数核对点失败处理symbol格式为CODE.EXCHANGE如600519.SH逐字符与预期一致不匹配则阻断不依赖接口自动修正interval接口支持的周期列表1d/1w/1M等策略周期与接口周期一致不支持的 interval 会返回错误时间范围起止日期在接口支持的历史范围内停牌日、节假日是否有数据返回超范围请求验证接口是否返回明确错误Python 示例# 查询可用 interval教学伪代码实际路径以官方文档为准# resp requests.get(f{BASE_URL}/market/kline/intervals, headers{X-API-Key: API_KEY})# intervals resp.json().get(data, [])# print(intervals) # 示例[1m,3m,5m,15m,30m,1h,2h,4h,1d,1w,1M]4. 返回字段检查K 线返回体中的每个字段逐项核对类型和取值。以下字段以 TickDB REST K 线接口返回结构为示例。字段核对点失败处理symbol与请求逐字符一致不匹配则阻断time整数且非 boolUTC 毫秒类型异常则阻断open非空字符串可解析为有限 Decimal解析失败阻断不默认成 0high同上且high open, close, low不满足则标记异常low同上且low open, close, high不满足则标记异常close同上解析失败阻断volume非空字符串可解析为有限 Decimal解析失败阻断Python 示例fromdecimalimportDecimal,InvalidOperationdefvalidate_kline_bar(bar:dict,expected_symbol:str)-dict:单根 K 线的字段校验。# symbolifbar.get(symbol)!expected_symbol:return{ok:False,reason:fsymbol mismatch: expected{expected_symbol}, got{bar.get(symbol)}}# timetsbar.get(time)ifisinstance(ts,bool)ornotisinstance(ts,int):return{ok:False,reason:ftime invalid:{ts}}# OHLCVforfieldin(open,high,low,close,volume):rawbar.get(field)ifnotisinstance(raw,str)ornotraw.strip():return{ok:False,reason:f{field}missing or empty}try:valDecimal(raw)ifnotval.is_finite():return{ok:False,reason:f{field}not finite:{raw}}except(InvalidOperation,ValueError):return{ok:False,reason:f{field}unparseable:{raw}}# high low 基本逻辑ifDecimal(bar[high])Decimal(bar[low]):return{ok:False,reason:high low}return{ok:True,data:bar}5. 异常场景排错表失败场景现象处理方向symbol 不存在返回symbol not found或类似错误核对 symbol 格式确认后缀.SH/.SZinterval 不支持返回 interval 无效或空数据先查询可用 interval 列表时间范围超限返回最早可用时间限制确认接口的历史数据覆盖范围data为空正常请求但返回空数组检查 symbol 状态、权限和当前时段OHLCV 类型异常字段为null、N/A或非数字字符串阻断不默认成 0raw_snapshot未保存事后无法复盘每次请求保存原始 JSON6. 缺口检查K 线序列拿到后逐根检查时间间隔是否稳定。Python 示例defcheck_kline_gaps(klines:list,expected_interval_ms:int)-list:检查 K 线序列中的缺口。klines 已按 time 升序排列。gaps[]foriinrange(len(klines)-1):curr_tsklines[i].get(time)next_tsklines[i1].get(time)ifnotisinstance(curr_ts,int)ornotisinstance(next_ts,int):continueintervalnext_ts-curr_ts# 允许一个周期的容差如日线允许 ±1 小时ifintervalexpected_interval_ms*1.5:gaps.append({from_index:i,to_index:i1,expected_ms:expected_interval_ms,actual_ms:interval,})returngaps注意expected_interval_ms需根据 interval 换算如1d 86400000ms。停牌日和节假日会导致正常间隔变大实际使用时需结合交易日历区分“正常缺口”和“异常缺口”。7. 最小验收报告每次取数后生成一份最小验收报告defbuild_acceptance_report(symbol:str,interval:str,total_bars:int,gaps:list,errors:list,snapshot_id:str)-dict:生成 K 线数据验收报告。return{symbol:symbol,interval:interval,total_bars:total_bars,gap_count:len(gaps),gaps:gaps,error_count:len(errors),errors:errors,raw_snapshot_id:snapshot_id,checked_at:ISO8601 时间戳,verdict:通过iflen(errors)0else存疑,}8. 回测前 K 线验收清单序号检查项通过标准1symbol 格式统一带市场后缀未被自动修正2interval 明确策略周期与接口周期一致3时间范围清晰起止日期、停牌处理方式明确4OHLCV 字段稳定抽查不同年份字段含义和类型一致5异常有明确返回无效 symbol、超范围、空数据不静默6缺口已记录区分正常缺口节假日和异常缺口7raw_snapshot 已保存每次请求保留原始 JSON8验收报告已生成包含总条数、缺口数、错误数和结论回测从来不是先问“策略能不能赚钱”。更底层的问题是你喂进去的数据配不配得上这个策略。 本文 K 线校验示例以 TickDB.ai 作为候选行情入口具体字段以官方文档和实测为准⚠️ 本文为技术教程不构成任何投资建议