一句话结论:如果策略只需要最近 60 个交易日,反复请求全历史 K 线会让网络传输、解析和数据处理的成本随历史数据量增长;应先明确所需日期范围,再按范围获取,并用本地缓存和增量更新避免重复下载。
摘要
量化脚本每次启动都拉取全历史 K 线,即使最终只保留最近 60 个交易日,也会重复承担请求、传输、解析和 DataFrame 筛选成本。解决重点不是下载后再裁剪,而是在数据请求前确定时间范围,并设计本地缓存与增量更新。本文还会说明交易日范围、分钟线数据量和复权口径需要注意的地方,以及 QuantDash 公开支持的相关查询能力。
1. 为什么只用 60 天,脚本仍然很慢
典型的低效流程是:
text
请求某标的全历史 K 线
→ 接收全部数据
→ 解析响应并构造 DataFrame
→ 按日期筛选最近 60 个交易日
→ 计算指标或生成信号
问题在于,最后的筛选只能减少后续计算量,不能撤销已经发生的请求、网络传输和数据解析。假设历史数据从较早年份开始,脚本每次运行都会重复处理大量策略实际用不到的记录。
总耗时通常来自几个环节:
- 请求与网络传输:返回行数越多,传输的数据通常越多;等待服务响应和网络传输也会占用时间。
- 响应解析:JSON 或其他响应数据需要被解码、转换为 Python 对象。
- DataFrame 构造与筛选:数据量较大时,创建对象、转换类型和过滤都会产生额外开销。
- 重复工作:定时任务或多次研究运行反复拉取相同历史数据,成本不断累积。
可以先记录每次运行的请求耗时、响应数据量、解析耗时、筛选耗时和最终保留的行数。若最终只留下很少的数据,而请求和解析耗时占比明显,就应优先检查是否在获取阶段请求了过宽的历史范围。
2. "最近 60 个交易日"不是"最近 60 个自然日"
交易日和自然日不能直接画等号。周末、节假日以及不同市场的休市安排,会让最近 60 个交易日跨越超过 60 个自然日。因此,简单用当前日期减去 60 天,通常不能准确得到 60 个交易日的起始日期。
更稳妥的处理方式是:
- 明确策略使用的市场和交易日定义。
- 根据对应的交易日历,定位最近 60 个已完成交易日。
- 将这 60 个交易日对应的起止日期用于数据查询。
- 取回数据后,再检查实际交易日期数量、缺失日期和重复记录。
如果没有交易日历,可以用一段更宽的自然日范围作为候选区间,再依据可靠的交易日历过滤;但不能把"自然日范围内有 60 个日期"当作"有 60 个交易日"。如果策略还涉及盘中运行,也要明确当天未完成的 K 线是否应该参与计算。
3. 分钟线场景,范围过宽的代价更明显
日线与分钟线的单个交易日记录数差别很大。即使策略只分析最近 60 个交易日,若每次都请求多年分钟线历史,下载和处理的数据量也可能远超策略需要。
对分钟线任务,建议额外确认:
- 策略需要的周期是 1m、5m、15m、30m 还是 60m。
- 指标是否确实需要完整的盘中序列,还是只需要特定时段的数据。
- 当前交易日的数据是否已经结束,未完成 K 线如何处理。
- 不同交易日的分钟记录是否完整,时间戳和交易时段口径是否一致。
不要仅通过缩短历史范围来掩盖数据质量问题。若某个交易日缺少分钟记录,指标序列可能断裂;若重复下载后直接拼接,也可能产生重复 K 线。范围控制和数据校验应该同时做。
4. 更合适的获取方式:范围查询、缓存与增量更新
4.1 在请求前确定起止日期
首先由策略需求计算查询范围,而不是先拉全历史再筛选。对于最近 60 个交易日,应由交易日历确定起始交易日,并根据策略是否使用当日未完成数据决定结束日期。
4.2 用本地缓存保存已获取数据
可以按市场、标的、周期、复权口径和日期范围组织本地数据。下次运行时,只检查缓存覆盖到哪一天,再获取缺少的部分。
缓存键不能只包含标的代码和周期。若复权方式不同,或者数据字段口径不同,应视为不同的数据序列,避免把不一致的数据混在一起。
4.3 增量更新,而不是反复全量刷新
常见流程是:
text
读取本地缓存
→ 找出缓存中最后一个完整数据日期
→ 确定需要补齐的日期范围
→ 请求缺失部分
→ 检查日期、重复记录和数据口径
→ 合并并保存
→ 为策略提供所需窗口
对于当日可能变化的行情,不要简单假设缓存中的最新记录永久不变。可以根据策略运行时点定义更新规则,并在合并时处理同一标的、同一周期、同一时间戳的重复记录。
4.4 批量请求要与本地分批处理配合
批量查询可以减少逐个标的发起请求的工程开销,但请求范围仍然要控制。批量请求全历史并不等于高效;合理做法是同时限制标的集合和时间区间,并依据实际返回量安排本地处理。
5. 用代码表达流程,但不猜测 API 方法
下面是通用伪代码,用于说明数据流程,不是 QuantDash SDK 的可运行调用示例。具体交易日历来源、缓存格式和 API 参数,应按项目所用数据源及其官方文档实现。
python
# 通用伪代码:不是 QuantDash API 调用示例
symbols = load_symbol_universe()
period =