ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

量化因子库收官:从数据流到可复用因子库的工程化实践

量化因子库收官:从数据流到可复用因子库的工程化实践 量化金融里最难的不是把一个因子算出来而是让因子从原始行情数据一路走到因子库之后还能被信任、被复用、被持续更新。这篇是“365天量化金融”因子阶段的收官内容核心就两个字数据流。很多人做量化做了几个月因子能算、回测能跑但换一个数据源、换一段历史区间、换一台机器结果就对不上了。问题不出在策略代码而出在数据流和因子库没有真正建立起来。适合谁看适合正在自建量化研究和交易系统的开发者尤其是已经能拿到行情数据、能写出因子公式但还没有把因子管道化、工程化、可复用化的人。最值得关注的不是某一个因子的具体计算而是从“有数据”到“有因子库”之间究竟要完成哪些步骤、有哪些检查点、有哪些坑。1. 先搞清楚“因子阶段收官”到底在收什么因子研究做到一定程度容易产生一种“什么都跑过、什么都存了但用的时候找不到”的状态。今天说的收官不是指把所有因子计算收尾而是把因子从零散的脚本和临时文件里收口变成一套有目录、有命名、有元数据、有校验的因子库。先给三个判断标准。如果都满足说明因子阶段可以收官新增一段行情数据之后增量更新因子是自动的不需要手工重跑全部脚本。任何一个因子外部的人看命名和元数据就能说出它是什么周期、什么标的范围、什么计算参数。回测或策略代码读取因子时不依赖某个人的剪贴板、临时 Excel 或某个特定目录的绝对路径。这三条看起来简单但很多独立做量化的人一条都做不到。常见的情况是因子计算脚本散落在.py和.ipynb文件里同一个因子有多个版本数据更新之后旧结果没有清理最终策略里到底加载的是哪个文件只有当时写代码的人自己能推断。所以因子阶段收官收的不是“因子数量”而是“数据流和因子库工程化”。1.1 因子研究的最终交付物不是因子而是可复用的因子库因子本身是一种中间产物。你在研究阶段算出一个动量因子、一个波动率因子、一个量价相关性因子它们解决的是策略信号问题。但如果你想做多因子组合、因子轮动、因子衰减分析就必须把因子当作数据资产管理。这意味着每个因子至少要有唯一标识字段名比如mom_20d_close不能叫factor1。含义说明因子定义、输入字段、计算逻辑。参数记录窗口大小、是否截尾、是否中性化、中性化方式。数据版本基于哪个数据源、哪个复权方式、哪个截至日期。这些内容统一放到元数据表里比因子数据本身还重要。因为因子算错一次以后所有基于它的回测、组合、信号都可能是错的而解决这个问题的关键就是能追溯“这个因子是用什么数据算出来的”。1.2 数据流和因子库是两件事但它们必须连起来看数据流指的是从原始数据采集到清洗、对齐、计算、存储、读取的完整管道。因子库只是这条管道的终点之一。如果只管理因子库不管理数据流那么因子库会迅速变成一次性的产物。数据源一换就废日期范围一变就断字段风格一改就乱。反过来如果只关注数据采集和清洗没有把它接到因子库上那么数据质量做得再好也无法反馈到策略中。所以“从数据流到因子库”并不是两个独立阶段而是先有稳定的数据流才能有可信的因子库。下面按落地顺序拆开讲。2. 从数据流到因子库整体数据链路的 5 个层次一次完整的量化数据链路我个人习惯分成 5 层。分层的目的是把不同职责切开避免一锅烩。层次主要任务典型存储形式最容易出现的问题数据源层获取原始行情、财务、指数、行业等数据外部数据接口、第三方文件字段格式不统一、缺失、单位不一致原始数据层原样落盘不做任何修改Parquet、CSV、HDF5 按日或按标的拆分重复下载、没有校验、无法回溯清洗层去重、补全、异常识别、类型转换清洗后的标准目录把“缺失”直接填 0引入错误因子计算层根据原始字段计算时间序列特征中间特征表或内存计算参数没记录、版本混乱、重算成本高因子库层存储最终因子、元数据、校验记录Parquet 元数据表没有目录规范、无增量逻辑、无监控每一层之间用一个明确的任务边界隔开。原始数据层只能做“原样落地”清洗层才允许改值因子计算层不允许直接读取外部数据源。这样可以防止有人在某个因子里直接接了原始数据接口导致因子和清洗逻辑耦合。2.1 五层数据链路的划分把数据链路拆成 5 层最大的好处是排查问题时有明确的入口。如果因子值异常先看因子计算层参数和输入。如果输入看起来不对再往下看清洗层。如果清洗层也不对再往下看原始数据层看看原始文件是否完整。如果原始文件都丢了那就直接定位到数据源层重新增量拉取。我见过很多人做量化时跳过清洗层直接在原始数据上算因子。短期没问题时间一长原始数据里的单位、复权方式、停牌标记、缺失值规则一旦变化所有因子全部要重算。建立清洗层的意义就是把这些规则固定下来不让它们影响因子计算。2.2 每层最容易出问题的地方数据源层最典型的坑是复权方式不统一。有的数据源默认向前复权有的默认向后复权有的需要单独下载复权因子。如果因子计算层没有固定统一成一种复权口径回测结果就会受除权除息影响尤其做长期动量因子时非常明显。原始数据层最容易出现的是“以为下载成功实际文件只有一部分”。所以要给每个数据文件加一个完整性检查例如记录文件行数、日期范围、最后更新时间并在写入时生成一个校验标记。清洗层最容易出问题的是把缺失值和 0 值混为一谈。财务因子比较常遇到停牌期间成交量为 0 是正常的利润数据缺失则不能直接填 0。正确的做法是保留缺失并在因子计算时区分“停牌无交易”和“数据缺失”。3. 数据流落地的关键环节采集、清洗、对齐、复权说了分层接下来讲每个环节的具体操作。这些环节是数据流到因子库之间最影响结果的部分比因子公式本身更需要花时间。3.1 采集先按源和粒度组织目录采集阶段的目标不是把数据全部下载下来而是让后续每个环节都知道“数据在哪、是什么版本”。我建议按“数据源 / 市场 / 数据类型 / 时间粒度”组织目录。例如data/ raw/ source_a/ china_stock/ daily/ 2024-01-01.parquet 2024-01-02.parquet minute/ 2024-01-01.parquet source_b/ financials/ quarterly/ 2024Q1.parquet这样做的原因是增量更新时只需要扫描缺失日期不需要对整个目录重新遍历。如果某个数据源字段对齐有问题也只需要重拉这一层不影响其他层。采集过程中要记录两个时间数据日期和采集日期。数据日期是数据本身对应的交易日采集日期是这个文件什么时候被拉下来。这两个字段在后续排查“为什么某一天数据不完整”时非常有用。3.2 清洗不要只做缺失值填充清洗层要有顺序顺序错了结果就会偏。我的顺序通常是去重同一交易标的同一交易日出现两次的只保留一条并记录 Duplicate。格式统一日期转成统一格式代码统一成 6 位浮点数和整数分开处理。缺失标记区分 NaN、0、None、停牌、未上市、已退市。异常识别价格、成交量、收益率出现极端值或负值时给出异常标记而不是直接改值。复权处理如果后续因子需要按统一口径计算复权价。清洗层不负责“把它改成我们认为正确的值”只负责“把它标记出来”。把异常值直接改掉会让后续所有分析失去对数据真实性的信任。正确方式是保留原始值加上一个异常标志列让因子计算层决定是否剔除。3.3 对齐与复权因子计算前的最后一道关卡“对齐”这个词在量化里包含两层意思。第一层是交易日历对齐。不同数据源的交易日历可能不同有的数据源 9 月 30 日有数据有的没有。因子计算前必须统一使用同一个交易日历避免某个因子在 9 月 30 日算出的收益率是过去两天或三天的混合。第二层是财务数据的时间对齐。财务数据不能按财报期直接对齐到当前日期必须按“实际披露日期”对齐否则就用了未来数据。这里的常见做法是维护一个财报披露日期表在因子计算时只取披露日期之前的数据。复权处理则要根据因子类型选择。做短期量价因子通常用前复权价做长期动量因子要特别小心复权方式对收益率的扭曲。比较稳妥的方式是原始数据保留不复权价同时单独保存复权因子表计算时再根据因子需求决定是否复权。这样最灵活也最容易排查。4. 因子计算阶段命名、参数、版本和增量计算数据流稳定之后才进入因子计算。但因子计算阶段真正难的不是数学公式而是管理。4.1 因子命名规范字段名就是因子在数据库里的身份证我给所有因子命名都会遵守同一套规则类型_窗口_标的字段_处理方式例如mom_20d_close_ret20 日收益率动量因子。vol_60d_ret_std60 日收益率标准差因子。turn_5d_amount_avg5 日成交额均值因子。这套命名方式看起来啰嗦但读代码时非常直观。数据库字段、因子文件列名、元数据里的因子名必须完全一致否则策略代码里很容易出现“读错列”这种低级错误。不建议使用factor_01、alpha_007这种名字。因为 007 是什么逻辑一年后自己也想不起来。4.2 参数记录与版本控制因子计算一般都有窗口、阈值、中性化、去极值等参数。这些参数必须记录且最好记录成结构化格式。例如一个 20 日动量因子的元数据可以这样存{ factor_name: mom_20d_close_ret, definition: 20 个交易日前收盘价到当前收盘价的收益率, input_fields: [close], window: 20, fillna: false, winsorize: { method: mad, n: 3 }, neutralize: null, data_version: 2024-06-30, created_by: factor_pipeline_v1 }参数版本同样重要。同一个因子如果后来把 window 从 20 改成 30那么它应该是一个新因子或者在元数据里明确标识版本号。不建议直接覆盖旧文件否则回测时无法复现历史结果。4.3 增量计算与重复计算因子计算的常规流程是先确认数据日期范围再计算缺失日期最后更新元数据。增量计算的核心逻辑是读取因子库已有日期范围。计算已覆盖日期和最新数据日期之间的差集。只对差集部分重新计算。追加写入并重写索引或分区元数据。注意不要每次新增一天数据就全量重算所有因子。全量重算在股票数量少、窗口短、因子少的时候能接受一旦标的扩展到全市场几千只窗口拉到 250 日因子扩充到几十个全量重算的时间会成倍增加。另外增量计算时要小心“窗口依赖”。20 日动量因子如果要算第 T 天的值依赖 T-20 到 T 的收盘价。因此增量计算不能只从新增日期开始而要从新增日期往前回退足够的窗口长度。这个细节很容易漏一漏就会导致增量结果和全量结果不一致。5. 因子库存储与目录设计因子算完之后需要有一个统一入口读取。这就是因子库。因子库不一定要用重型数据库很多独立量化者用文件系统就能完成但目录和格式要规范。5.1 先讲目录再讲格式一个简单的因子库目录结构可以这样factor_library/ factors/ mom_20d_close_ret/ 2024-01-01_2024-06-30.parquet vol_60d_ret_std/ 2024-01-01_2024-06-30.parquet meta/ factor_meta.json data_version.json checks/ missing_rate_2024-06-30.json这样做有几点好处单个因子一个目录方便单独更新。数据按日期范围切分可以只增量追加新文件不覆盖旧文件。meta 目录集中管理元数据和数据版本不跟随数据文件散落。checks 目录放质量检查结果方便排查时回溯哪一天做过校验。5.2 parquet、hdf5、csv、数据库怎么选不同格式适合不同场景格式优点缺点适用场景Parquet列式存储读取快支持压缩单个文件不方便手工查看因子库主力格式HDF5单文件多数据集读写灵活文件损坏时恢复困难并发写容易出现锁问题小规模原型或研究过程中的中间存储CSV可直接用 Excel 打开体积大读取慢无 schema 校验临时调试、导出给同事DuckDB / SQLite支持 SQL 查询方便按条件过滤需要额外依赖SQLite 并发较弱因子查询、跨因子筛选、多条件下钻我自己的组合是因子主存储用 Parquet 文件按日期分区元数据用 JSON 或 SQLite临时查询工具用 DuckDB。这个组合在个人量化和小团队中够用而且不引入额外服务。如果团队规模变大、需要多人同时读因子库再考虑把因子数据迁移到 ClickHouse 或 PostgreSQL 等集中式存储。迁移的关键是保留字段命名和元数据不要改因子名。6. 因子库质量校验哪些指标必须盯因子库能不能被信任靠的不是感觉而是校验。以下是我每次更新因子库后都会检查的指标。6.1 数据质量指标缺失率、覆盖率、方差、极值缺失率不只是看整体缺失比例还要看缺失是否集中在特定股票、特定时间段。比如一个波动率因子在今年 3 月到 5 月缺失率突然升高很可能是这段时间的数据源出了断更问题而不只是因子本身无值。覆盖率的判断标准是因子有效值数量 / 当日全市场可交易股票数量。覆盖率明显下降时要检查是不是停牌股票过多、新股不足窗口期、或者财务数据未及时更新。方差和极值检查方面最有效的是看因子值分布。因子值出现大量极值或者某一天的标准差突然飙升大概率是输入数据有问题。可以先不急着改因子而是回看清洗层的异常标记。6.2 计算正确性校验增量对比、逻辑回归、样本外检查一个很实用的校验方法每次增量计算后取最近一个已经覆盖的日期用增量结果和旧结果对比值应该完全一致。如果不一致说明增量逻辑里窗口依赖处理错了。另一个方法是做逻辑回归或 IC 分析。因子和未来收益率之间的 IC 在合理范围内有波动是正常的但如果一个因子突然变得特别显著或者完全不显著先确认是不是数据口径变化比如换成了后复权、换了财务数据源。样本外检查适合定期做。把数据分成两段前段做因子筛选后段做验证。这个阶段不追求绝对收益追求的是因子在样本外的稳定性。如果样本内很好、样本外失效很可能是因子过拟合了参数。6.3 监控与告警长期可维护性因子库不是一次建完就完事。数据每天更新因子每天更新质量也要每天检查。最简单的做法是每天跑一个检查脚本把缺失率、覆盖率、更新延迟、新增因子数量写入一个 JSON 文件再让调度工具读它。这些告警不一定非要接入复杂的监控平台可以先从一个简单的状态文件开始再做定时邮件或消息通知。关键是把“检查”固化到流程里否则因子库过一个月就变成没人知道里面有什么的旧仓库。7. 从学习到生产排查链路和个人建议最后一段是实战里容易遇到的排查顺序以及我自己的落地建议。7.1 排查链路当发现某个因子的值异常、覆盖不全、或者回测结果和预期严重不符时我建议按照下面顺序排查先看因子本身字段名、参数、版本号是否被策略代码正确引用。再看因子计算输入用的是清洗层数据还是原始数据复权方式是否符合因子定义。然后看清洗层缺失值、异常值、停牌标记有没有被错误处理。继续看原始数据文件是否完整日期范围是否符合预期数据源有没有字段变更。最后看数据源复权因子、更新延迟、字段单位是否有变化。这个顺序的核心思路是从“离现象最近的地方”开始逐渐往源头排查。大多数人喜欢一上来就检查数据源但很多时候问题出在策略代码引用了旧文件和旧参数和数据源根本无关。7.2 个人建议如果做量化还没有到大规模生产阶段不建议一开始就设计一个巨大的分布式数据平台。先把单机文件系统 Parquet 元数据 JSON 这套最小结构跑稳再逐步加索引、加 SQL 查询、加调度。我更建议先跑通一个完整闭环日线数据 → 清洗 → 计算 3 到 5 个因子 → 存入因子库 → 做一次简单回测。这个闭环能跑通后面扩展数据源、扩展因子数量、接入实时数据就会顺畅很多。量化金融里的数据流建设本质不是技术炫技而是让每一份数据都来源清晰、口径统一、可回溯。因子库做得越稳后面的组合优化和策略研究就越省心。建议在因子阶段收官的节点上把数据链路完整走一遍而不是继续堆因子数量。
返回列表