量化系统的每日运转是一条定时流水线:行情落地 → 校验 → 因子计算 → 信号 → 订单 → 归档。「几点跑什么」的可靠性直接等于策略的可靠性——本篇讲流水线的组织方式(调度)与底下的存储选型(数据库)。
调度:从 cron 到 DAG
cron 的问题:用「猜时间」表达依赖——「17:05 跑因子计算」隐含假设「数据 17:00 前一定就绪」,一旦数据迟到,因子计算用的是残缺数据(且无人知晓)。DAG 调度(Airflow/Prefect/Dagster)的正确姿势:声明依赖而不是猜时间——
| |
每个任务的触发条件是「上游任务成功」,而不是「到了某个钟点」。数据的迟到不再是事故(流水线自动顺延),而是可告警的异常(SLA 超时告警,见日志与监控 )。
DAG 的四个实务要点:
- 每个任务幂等且可重跑:任务失败重跑不能产生重复数据(写临时表 + 原子切换,见因子存储 )——幂等是「放心重跑」的前提,而重跑是流水线的日常;
- 质量校验是 DAG 的显式节点:校验失败 → 下游全部短路 + 告警——「宁可今天不交易,不可用坏数据交易」(检查项见数据清洗 );
- 回填(backfill)是一等公民:数据源修复后「重算某区间的因子」是高频操作——DAG 支持按日期区间重跑(前提是任务幂等);
- 个人量化的最小版:一个带依赖检查与失败告警的 Python 调度脚本(
检查上游文件时间戳 → 失败则通知并退出)就能覆盖 90% 需求——cron + 防御式脚本,晚于需要再上 Airflow。
存储选型:按访问模式选库
量化数据的核心访问模式是「按日期截面读全部股票」+「按股票读全部历史」,两类负载的最佳载体不同:
| 存储 | 适合 | 特点 |
|---|---|---|
| Parquet 文件 + 本地盘 | 个人/小团队的一切 | 按日期分区的宽表,Pandas/Polars 直读,零运维;文件即快照,版本管理容易 |
| ClickHouse / DuckDB | 因子数多、需要切片查询 | 列存 OLAP,长表(date, code, factor, value)亿行级秒查;DuckDB 单机零运维,ClickHouse 需要服务化 |
| Arctic/ArcticDB(Mongo 系) | 时序面板为主 | tick/分钟级高频面板的专用形态,版本化内置 |
| PostgreSQL / MySQL | 元数据、订单、账户 | 事务型账本(订单流、持仓、审计记录)——交易账本不用 OLAP 库,要事务保证 |
| Redis / 内存 | 盘中实时状态 | 行情缓存、实时信号状态、秒级刷新的监控指标 |
选型的两条经验:先文件后数据库(Parquet 阶段撑到因子数过百、数据量过 TB 再上 OLAP——大多数个人研究者永远停在 Parquet 就够了);账本与行情分离(订单/持仓/资金是法律意义上的记录,进事务库并备份,与行情库的故障域隔离)。
每日流水线参考蓝图
| |
三个设计细节:所有节点写「运行指纹」(输入数据版本 + 代码版本 + 输出版本,见Git )——三个月后任何一天的信号可完整追溯;人工干预的入口预设计(「今日跳过交易」「手动覆盖目标持仓」要有一键且留痕的通道——总有引擎判断不了的日子);盘后流水线的总时长预算(从收盘到日报 < 2 小时),超时即优化信号(流水线太长 = 数据迟到时的应对余量太小)。