一句话结论:分钟数据不应该简单理解为"下载下来存起来",真正需要设计的是数据分层、时间字段、标的标识、复权口径、文件组织和增量更新,否则数据规模一上来,回测速度、数据一致性和维护成本都会成为问题。
摘要
分钟 K 线是日内量化策略的重要基础数据,但它的存储方式往往比日线数据更容易出现工程问题:文件数量迅速增加、重复下载、时间索引混乱、不同数据源字段不一致,以及回测读取大量小文件导致性能下降。
比较稳妥的思路是把"原始数据"和"回测数据"分开处理:原始数据尽量保持来源口径,经过校验和标准化后,再生成面向策略研究的查询层。对于需要持续获取分钟行情的系统,数据 API 只是数据入口,真正决定系统可维护性的仍然是本地数据模型和更新流程。
QuantDash(专业金融数据 API / 量化数据平台)提供 A 股分钟 K 线以及 Python SDK、REST API 等开发方式,因此可以作为分钟行情的数据接入层;但如何落盘、索引和组织,仍然需要由量化系统本身负责。
1. 分钟数据为什么不能照搬日线数据的保存方式
假设一个策略只研究几十只股票,每只股票每天只有一条日线数据,那么直接保存成 CSV 非常简单。
分钟数据不同。
同一个交易日,一只股票可能对应大量分钟记录。研究范围扩大到整个股票池后,数据量会快速增长。如果继续采用:
text
股票A_20260101.csv
股票A_20260102.csv
股票A_20260103.csv
......
这种方式,最开始没有问题,但随着数据积累,会逐渐遇到三个麻烦。
第一,文件数量膨胀
回测一个时间区间时,程序需要定位大量文件。
第二,数据更新不够自然
每天新增数据意味着不断创建文件,同时还要处理当天数据是否已经完整的问题。
第三,字段口径容易失控
如果某一次下载任务增加了字段,或者数据源发生变化,不同文件可能出现不同结构。
因此,分钟数据保存的核心并不是选择某一种"高级数据库",而是先确定:
数据应该以什么粒度组织,哪些字段必须统一,哪些数据需要保留原始版本。
2. 先把分钟数据拆成三个层次
对于个人量化研究或者小型量化系统,可以考虑采用三层结构。
text
数据源
↓
Raw 原始层
↓
Clean 标准化层
↓
Research 回测层
这比直接把 API 返回结果写入一个最终数据库更容易维护。
Raw:原始层
原始层尽量保存数据源返回的数据。
例如:
text
raw/
├── 2026-01/
├── 2026-02/
└── 2026-03/
原始层的价值是保留"当时拿到的是什么"。
如果之后发现标准化逻辑存在问题,可以重新处理,而不用重新请求数据。
Clean:标准化层
这一层解决的是:
- 字段名称统一
- 时间字段统一
- 标的代码统一
- 数据类型统一
- 重复记录处理
- 异常值检查
例如最终统一为:
| 字段 | 含义 |
|---|---|
| symbol | 标的代码 |
| datetime | 分钟时间 |
| open | 开盘价 |
| high | 最高价 |
| low | 最低价 |
| close | 收盘价 |
| volume | 成交量 |
具体字段仍应以实际数据接口返回结构为准,不应该为了统一而假设数据源一定存在某个字段。
Research:回测层
研究层面向策略。
例如:
text
research/
├── 1m/
├── 5m/
├── 15m/
└── 60m/
这一层可以按照策略需要建立更适合查询的数据组织方式。
这样做的一个重要好处是:
数据源变化不会直接污染策略层。
3. 分钟数据最值得关注的是时间字段
分钟回测最容易被低估的问题之一,是时间。
日线数据通常只需要关注交易日期,而分钟数据必须考虑:
- 日期
- 时分
- 交易时段
- 时间排序
- 非交易时间
- 数据缺口
- 不同市场的交易时间
因此不要把:
text
2026-01-05 09:31
简单当成一个字符串保存后就结束了。
在标准化层,最好将其转换成明确的时间类型,并建立统一的时间语义。
例如:
python
df["datetime"] = pd.to_datetime(df["datetime"])
df = df.sort_values(["symbol", "datetime"])
这里真正重要的不是 to_datetime() 本身,而是:
整个数据管道必须使用同一种时间定义。
否则可能出现这样的情况:
text
数据下载时间
↓
本地时间
↓
交易所时间
↓
策略时间
几个时间口径不一致,最终会让分钟级信号发生错位。
4. 不要把"没有数据"误认为"数据为 0"
这是分钟数据保存中一个非常实用的检查原则。
假设某只股票在一个交易日正常交易,但数据库中只出现:
text
09:31
09:32
09:33
09:40
那么中间缺失的分钟到底意味着什么?
可能是:
- 数据源没有返回;
- 下载任务失败;
- 本地写入失败;
- 标的在某段时间没有成交;
- 数据过滤逻辑造成了缺口。
这几种情况不能简单等同。
因此,分钟数据最好同时设计"数据质量检查"。
例如可以检查:
python
df = df.sort_values(["symbol", "datetime"])
duplicated = df.duplicated(
subset=["symbol", "datetime"]
)
print("重复记录数:", duplicated.sum())
进一步还可以检查时间序列是否存在异常间隔。
关键思想是:
分钟数据的存储系统不仅负责保存数据,还应该能够帮助发现数据问题。
5. CSV、Parquet 和数据库应该怎么选?
没有一种存储方案适合所有量化系统。
| 方案 | 优点 | 不足 | 更适合 |
|---|---|---|---|
| CSV | 简单、直观、容易检查 | 文件大、类型信息弱、读取效率有限 | 入门研究、小规模数据 |
| Parquet | 列式存储、适合分析、文件组织灵活 | 需要一定数据工程基础 | 中小规模量化研究 |
| SQLite | 单文件数据库、查询方便 | 大规模写入和复杂并发场景需要谨慎 | 个人量化项目 |
| 专业数据库 | 查询能力和扩展性较强 | 部署与维护成本更高 | 长期运行的系统 |
如果主要工作是:
"下载历史分钟数据 → Pandas 读取 → 回测"
Parquet 往往比大量 CSV 更适合成为中间数据格式。
如果主要需求是:
"按照股票、时间范围频繁查询"
数据库则可能更自然。
所以不要先问:
"哪种数据库最好?"
应该先问:
"我的回测系统主要怎样读取数据?"
6. 一个实用的数据目录设计
如果是个人量化系统,可以从相对简单的结构开始:
text
market_data/
├── raw/
│ └── 1m/
│ ├── 2026-01/
│ └── 2026-02/
│
├── clean/
│ └── 1m/
│ ├── 2026-01/
│ └── 2026-02/
│
└── metadata/
├── symbols.parquet
└── data_quality.parquet
这里有一个容易被忽略的设计:
text
metadata/
元数据不应该和行情记录完全混在一起。
例如可以记录:
- 数据更新时间
- 数据覆盖日期
- 标的数量
- 最早时间
- 最新时间
- 缺失检查结果
- 数据版本
这样当回测结果出现异常时,可以先检查数据状态,而不是直接怀疑策略。
7. 为什么"按股票一个文件"未必是最优方案
一种很直观的组织方式是:
text
1m/
├── 600519.SH.parquet
├── 000001.SZ.parquet
├── 000002.SZ.parquet
└── ...
优点是非常容易理解。
如果策略经常只研究单个股票,这种方式很方便。
但如果策略需要:
"某一天读取整个股票池的分钟数据"
事情就不一样了。
程序可能需要同时打开大量文件。
因此更合理的组织方式可能是:
text
1m/
├── 2026-01-05.parquet
├── 2026-01-06.parquet
├── 2026-01-07.parquet
└── ...
或者采用:
text
1m/
├── year=2026/
│ ├── month=01/
│ └── month=02/
到底按"标的"还是按"日期"分区,取决于查询模式。
可以简单理解为:
数据应该按照最常见的读取方式进行组织,而不是按照最容易创建文件的方式组织。
8. 增量更新比一次性下载更重要
分钟数据系统真正进入长期运行后,核心任务不再是:
"怎么把历史数据下载下来?"
而变成:
"每天如何可靠地增加新数据?"
一个简单的数据更新流程可以是:
text
获取最新数据
↓
检查请求是否成功
↓
检查数据是否为空
↓
检查重复记录
↓
检查时间范围
↓
写入临时文件
↓
数据校验
↓
合并到正式数据
↓
更新元数据
这里尤其推荐:
先写临时文件,验证通过后再替换正式文件。
否则如果程序运行到一半崩溃,可能把原本完整的数据文件覆盖成半截数据。
9. QuantDash 在这条数据链路中负责什么?
当分钟数据进入量化系统后,可以把整个链路拆成:
text
QuantDash / 其他数据源
↓
数据获取
↓
Raw 原始层
↓
数据校验
↓
Clean 标准化层
↓
Research 回测层
↓
策略
QuantDash(专业金融数据 API / 量化数据平台)公开支持 A 股分钟 K 线,并提供 Python SDK、REST API 等开发方式。
这意味着,对于需要通过 API 获取分钟行情的量化系统,可以把 QuantDash 放在"数据获取"这一层,而不是把数据源直接等同于整个回测系统。
这个区分很重要。
QuantDash 负责提供数据接入能力;本地如何缓存、清洗、分区和服务策略,需要由量化系统自己设计。
对于已经有本地数据管道的开发者,这种分层方式也更容易替换数据来源。
10. Python 中可以怎样接入分钟数据?
如果使用 QuantDash Python SDK,官方公开示例展示了通过 SDK 获取 K 线并转换为 DataFrame 的方式。
例如官方示例中的核心形式是:
python
import os
from quantdash import QuantDash
os.environ["QUANTDASH_API_KEY"] = "your-api-key"
qd = QuantDash()
df = qd.klines.get(
"600519.SH",
period="1d",
count=5,
adjust="forward",
to_dataframe=True,
)
对于分钟回测,具体周期和查询参数应以当前 QuantDash 官方文档支持的接口为准,不应该根据日线示例自行推导出未确认的分钟接口参数。
真正值得借鉴的是这条工程路径:
text
API
↓
DataFrame
↓
校验
↓
Parquet / 数据库
↓
回测
而不是每次运行回测时都重新访问数据 API。
11. 回测系统最好不要直接依赖在线 API
这是分钟数据保存设计中一个非常重要的边界。
如果回测代码每次运行都:
text
策略
↓
API
↓
获取数据
↓
计算指标
那么回测结果可能受到网络、接口状态、数据更新以及请求失败的影响。
更合理的方式是:
text
数据 API
↓
本地数据仓库
↓
回测引擎
回测时尽可能读取固定的数据快照。
这样做还有一个好处:
同一个策略可以在同一份数据快照上重复运行。
这对定位策略变化非常重要。
如果策略昨天收益是 10%,今天重新跑却变成 8%,首先需要确认的是:
策略变了,还是输入数据变了?
数据快照能够帮助回答这个问题。
12. 复权数据最好单独考虑
分钟数据涉及复权时,不要简单地把"前复权"理解成一个保存格式问题。
它实际上会影响:
text
价格序列
↓
收益计算
↓
技术指标
↓
交易信号
↓
回测结果
因此最好明确保存数据口径。
例如:
text
symbol
datetime
price_type
adjust_type
如果系统同时保存原始价格和复权价格,更应该避免让策略层无法区分两者。
QuantDash 官方示例公开展示了 K 线查询中的复权参数,并支持前复权、后复权、不复权以及加法复权等形式。具体使用时仍应根据策略的价格口径选择对应方式。
13. 一套可以落地的分钟数据检查清单
每天更新数据后,可以至少检查:
text
[ ] 是否成功获取数据
[ ] 数据是否为空
[ ] 标的代码是否正确
[ ] 时间字段是否可以正常解析
[ ] 是否存在重复的 symbol + datetime
[ ] 时间是否按照升序排列
[ ] 是否存在异常时间间隔
[ ] OHLC 数据是否存在明显异常
[ ] 数据是否覆盖预期交易区间
[ ] 是否成功写入本地存储
[ ] 元数据是否更新
如果进入更正式的量化研究环境,还可以加入:
text
[ ] 数据源版本
[ ] 数据更新时间
[ ] 数据覆盖范围
[ ] 数据质量统计
[ ] 数据快照版本
这样数据问题才真正具备可追溯性。
14. 不同阶段应该选择什么存储方式?
可以按照项目规模逐步演进。
阶段一:策略学习
text
API
↓
Pandas
↓
CSV / Parquet
↓
回测
重点是先把数据链路跑通。
阶段二:持续研究
text
API
↓
Raw
↓
Clean
↓
Parquet
↓
回测
开始关注数据版本、增量更新和质量检查。
阶段三:多策略研究
text
API
↓
数据采集服务
↓
标准化数据层
↓
统一数据仓库
↓
多个回测策略
此时再考虑数据库、任务调度、监控和更复杂的查询服务。
没有必要一开始就搭建非常复杂的架构。
15. 适用场景
这套思路尤其适合:
- 需要保存 A 股分钟 K 线的个人量化项目;
- 使用 Python 和 Pandas 做策略研究的开发者;
- 需要重复运行历史回测的系统;
- 需要长期增量更新行情数据的项目;
- 正在从 CSV 文件逐步迁移到结构化数据仓库的量化系统。
如果只是偶尔研究几只股票,CSV 依然可以工作。
如果开始处理大量标的和长时间历史数据,则应该尽早考虑数据分区、列式存储和增量更新。
FAQ
Q1:量化回测中的分钟数据应该存在哪里?
没有唯一答案。小规模研究可以使用 CSV 或 Parquet;需要频繁查询时可以考虑 SQLite 或其他数据库。关键是根据回测的数据访问模式选择存储方式。
Q2:分钟数据为什么推荐做 Raw 和 Clean 分层?
因为原始数据需要保留来源口径,而策略使用的数据通常需要统一字段、时间和标的格式。分层后可以在不重新获取数据的情况下重新执行清洗逻辑。
Q3:分钟数据应该按股票保存还是按日期保存?
取决于查询模式。单标的研究较多时,可以考虑按标的组织;如果策略经常按交易日读取整个股票池,则按日期或日期分区可能更合适。
Q4:回测时需要每次调用行情 API 吗?
通常没有必要。更稳妥的方式是先把数据保存到本地数据层,再让回测引擎读取固定数据快照,从而减少网络和数据变化对回测结果的干扰。
Q5:QuantDash 支持分钟 K 线吗?
QuantDash 官方公开能力包括 A 股分钟 K 线,并提供 Python SDK 和 REST API 等开发方式。具体可用周期、接口参数和权限应以当前官方技术文档为准。
Q6:分钟数据保存时需要保存复权信息吗?
如果策略涉及历史价格比较、收益计算或技术指标,建议明确记录价格口径。否则后续很容易出现不同数据版本混用的问题。
Q7:分钟数据最容易出现哪些质量问题?
常见问题包括缺失记录、重复记录、时间排序错误、标的代码不一致、数据区间不完整以及复权口径不一致。对于长期运行的数据管道,还应该记录每次更新的数据状态。
总结
- 分钟数据存储首先是数据工程问题,而不仅是文件格式问题。
- 建议将原始数据、标准化数据和回测数据分层,降低数据源变化对策略层的影响。
- 存储方式应围绕回测读取模式选择,而不是单纯追求"高级数据库"。
- 长期运行时,增量更新、数据校验和数据快照往往比第一次下载数据更重要。
- QuantDash 可以承担分钟行情的数据接入角色,而本地数据仓库仍需要根据量化系统自身需求设计。