跑得快:把 Rust 判了缓刑

20 阅读 1036 字 · 约 4 分钟

跑得快是四阶段最后一站。设计稿里它是最重的一站——Rust 回测核心、parquet 分区、DuckDB 查询引擎,都是为它准备的。但路线图的决策表写得很清楚:先 profiling,瓶颈在哪,优化哪。没有瓶颈,就不动。

profiling:先量化,再动手

把 pipeline 的每个环节都计时,结果出乎意料:

读数据 0.01 秒,因子 0.01 秒,信号 0.01 秒,因子 IC 0.06 秒,回测 0.3 秒,信号跟踪 0.11 秒,报告 0.23 秒。50 只股票、43K 行 bar、2478 笔成交,纯计算全流程不到 1 秒。回测摊到单票 0.006 秒——Python 的逐行撮合循环,比预想快得多。

规模外推

再往外推:沪深 300 大约 300 票,回测 1.8 秒;全市场 5000 票,回测 30 秒。当初设计 Rust 回测核心,理由之一是「不接受一次策略跑数小时」——现在有实测了:就算全市场,Python 也只要半分钟。

决策表判定:三个复杂度全部缓刑

按路线图的决策表逐个判:

  • Rust:取消。设计稿里写过一条假设——「如果 Python 回测足够快,整个 Rust 取消」。它落地了。
  • parquet 分区 + DuckDB:推迟。数据量 43K 行,全市场约 440 万行,离千万级的阈值很远。设计稿另一条假设也落地——「如果数据量远小于预估,整套存储简化」。实际上连 SQLite 都没用到。
  • tj-web:推迟。浏览报告没有手动成本痛点。

唯一的耗时项是拉数,2 分 40 秒,卡在 Tushare 的 API 限流,不是存储。全市场拉数按限流估算约 40 分钟,盘后批处理可接受;真到那天,再写增量拉数。

触发阈值:把「什么时候优化」写下来

不做的决策,也要写清楚什么时候做,否则就是拍脑袋。触发阈值记在决策文档里:

单票回测超过 0.1 秒,或全市场回测超过 1 分钟,上 Rust;数据量超过千万行,迁 parquet 分区和 DuckDB;浏览报告真的费劲了,再上 tj-web。

这些阈值是给未来的自己留的判断标准——到了那天,不是重新争论要不要优化,而是直接对照阈值做决定。

收官

四阶段走完了。回看第 1 篇,设计稿里那些最重的复杂度——Rust、七包、11 页 UI、MCP——一个都没进第一批代码。能跑阶段证明循环转得起来,跑得准把数字弄可信,跑得稳让它不用盯,跑得快用实测告诉我不需要更快。

三个大复杂度被判了缓刑,不是偷懒。是每次决策都有依据:数据契约和 A 股语义是「做了才准」,必须做对;Rust 和 UI 是「不做也能跑」,等痛点出现再上。四阶段框架的完整闭环,就是让每一类复杂度都在它该出现的时候出现。