docs: map open-source engine designs to FIDC
This commit is contained in:
@@ -0,0 +1,133 @@
|
||||
# FIDC 开源回测与交易引擎设计审查
|
||||
|
||||
## 审查范围
|
||||
|
||||
本次审查直接读取以下只读参考源码。源码位于
|
||||
`/Volumes/T7-Data/WorkSpace/reference-trading`;原计划使用的
|
||||
`/Volumes/SystemSSD` 在审查时未挂载,因此没有向本机系统盘写入参考仓库。
|
||||
|
||||
| 项目 | 审查提交 | 重点 |
|
||||
|---|---|---|
|
||||
| NautilusTrader | `ac22d5cf4a7e` | Rust 事件内核、统一回测/实盘组件、时间事件堆 |
|
||||
| QuantConnect LEAN | `23b735d99a35` | 订阅同步、TimeSlice、Universe 生命周期、惰性集合 |
|
||||
| Microsoft Qlib | `79633dd9506e` | 表达式缓存、日历切片、内存/磁盘分层 |
|
||||
| vectorbt | `34b6d5935e3e` | NumPy/Numba 密集数组仿真、紧凑状态数组 |
|
||||
| Zipline Reloaded | `943010b9da84` | Pipeline DAG、窗口预取、分段执行、复权读取 |
|
||||
| Backtrader | `b853d7c90b67` | preload/runonce 与逐 bar/live 模式分离 |
|
||||
|
||||
## 可借鉴设计
|
||||
|
||||
### 1. 时间轴和执行状态必须统一,但热路径不必经过通用消息总线
|
||||
|
||||
NautilusTrader 的 `BacktestEngine` 复用数据、执行、风险和缓存组件,并用带稳定
|
||||
序号的最小时间事件堆推进多个时钟。LEAN 用 `SubscriptionSynchronizer` 将不同订阅
|
||||
合并到同一个 frontier,再生成唯一 `TimeSlice`。两者共同证明:回测和实盘应共享
|
||||
订单、风控和时间语义,而不是共享一段策略特例代码。
|
||||
|
||||
FIDC 已经以 `decision_date`、`execution_date`、调度时间和撮合时间构成统一执行合同,
|
||||
并由同一交易核心服务于回测、模拟盘和实盘。日线全市场选股不应改为逐字段消息
|
||||
广播;这会给每个股票状态增加分配和动态分派。通用事件总线只保留在订单、成交、
|
||||
配置审计及外部集成边界。
|
||||
|
||||
### 2. 批量研究计算和事件撮合必须使用不同执行形态
|
||||
|
||||
vectorbt 把纯数值组合压入连续数组和编译循环;Backtrader 在历史批量模式使用
|
||||
`preload + runonce`,进入 live/replay 或受限内存模式后关闭该路径。Zipline 的
|
||||
Pipeline 则先生成 DAG 执行计划,按依赖顺序计算,并允许按日期 chunk 控制内存。
|
||||
|
||||
FIDC 应继续保持:
|
||||
|
||||
- 日线基础特征、rolling、横截面 rank 和因子挖掘使用 Arrow/NumPy/DuckDB/紧凑数组;
|
||||
- 订单、成交、T+1、涨跌停、停牌、公司行动和现金流使用确定性事件撮合;
|
||||
- 不把向量化收益外推到存在订单状态和路径依赖的撮合过程;
|
||||
- 不让逐 bar 实盘语义退化成预先知道整个未来数组的批量回测语义。
|
||||
|
||||
### 3. 不可变数据按内容身份共享,策略结果和可变账户状态严格隔离
|
||||
|
||||
Qlib 的缓存层、Zipline 的预取窗口和 Backtrader 的优化数据预载都说明:相同历史
|
||||
数据不应由每个策略重复解码。FIDC 当前 Source Lake 的 Parquet/Arrow、冻结 query
|
||||
scope、内容寻址 bundle、进程内 `DataSet` 和共享 result-store block 已符合这个方向。
|
||||
|
||||
共享键必须包含完整数据代际、字段投影、PIT 截止时间、复权口径和窗口。禁止共享:
|
||||
|
||||
- 选股结果、订单、仓位、账户、风控决策和策略局部变量;
|
||||
- 缺少 manifest/SHA/PIT 身份的 DataFrame 或 dict;
|
||||
- 盘中 provisional 数据与正式收盘数据混用的缓存项。
|
||||
|
||||
### 4. 字段和因子要在计划阶段冻结,运行时只物化真正需要的数据
|
||||
|
||||
LEAN 的 `TimeSliceFactory` 复用空集合,并只在收到对应数据时创建集合;Zipline 的
|
||||
Pipeline 使用执行计划和 refcount 释放中间值。FIDC 已有字段投影、
|
||||
`DailySnapshotView`、rolling requirement、Factor DAG 和 numeric bytecode VM。
|
||||
后续优化必须扩展这些类型化计划,而不是恢复宽 Python 行或每次构建完整 map。
|
||||
|
||||
## 当前性能事实
|
||||
|
||||
2026-09-06 在 177 使用同一冻结五年策略得到:
|
||||
|
||||
- restart/cold:`17.985s`;数据准备 `14.659s`;引擎 `2.627s`;
|
||||
- process-hot:总耗时中位 `3.245s`;数据准备 `0.006s`;引擎 `2.629s`;
|
||||
- 26,088 笔成交、收益 `0.9219861819172002`、canonical SHA
|
||||
`b42fea66237d06eadb24f6b8c9e2760e7319fe3699f315b99e01f433ef2aa234`
|
||||
在所有重复运行中一致;
|
||||
- 当前剖面热点为 numeric VM `12.39%`、临时股票状态构建 `10.17%`、复权均线
|
||||
`6.82%`;
|
||||
- Source Lake/进程共享缓存已经把同 bundle 数据准备从 `14.659s` 降到 `0.006s`,
|
||||
所以再把 DataFrame 改成 dict 或扩大 DuckDB 连接数不是当前热路径优化。
|
||||
|
||||
两项受开源类型化执行启发的候选已真实验证并删除:
|
||||
|
||||
1. 编译期枚举化全部 VM 标识符仅减少约 `1.44%` instructions,五年引擎中位只改善
|
||||
`0.19%`,却净增约 562 行;
|
||||
2. VM 槽位代际复用仅减少约 `0.48%` instructions,五年引擎中位回退约 `0.27%`。
|
||||
|
||||
完整证据见 `docs/evidence/numeric-vm-binding-generation-rejection-20260906.json`。
|
||||
|
||||
## FIDC 后续优化顺序
|
||||
|
||||
### P0:冷数据路径一次构建、分段发布、跨策略共享
|
||||
|
||||
以冻结 bundle 的完整身份构建 canonical base panel,并按年份或有界日期段发布
|
||||
只读 mmap/Arrow 段;父任务只扫描一次 Source Lake,worker 只映射所需段。每日增量
|
||||
只生成变化尾段,历史段保持同 SHA。必须以 process-cold `14.659s` 为基线,证明
|
||||
冷启动下降且 RSS、PIT、复权和结果 SHA 不变。
|
||||
|
||||
### P1:numeric VM 使用类型化 helper opcode 或经证据支持的 super-instruction
|
||||
|
||||
当前字符串标识符绑定不是主要成本。下一候选应在编译期把常见 rolling helper、
|
||||
比较和短路组合编译为类型化 opcode,减少解释器指令数,同时保留 helper 懒求值和
|
||||
错误信息。必须对主策略、第二种持仓数策略、通用自定义排序策略和分钟策略分别 A/B。
|
||||
|
||||
### P1:结果事件按消费者需求分层
|
||||
|
||||
撮合事实保持完整不可变;页面摘要、曲线、持仓和交易视图从 typed result store
|
||||
按需投影。禁止每次回测向 PostgreSQL 写入大矩阵,也禁止为了列表或概览解码全部
|
||||
事件。优化目标是 `resultSeconds + finalizationSeconds`,不能删减审计事实换速度。
|
||||
|
||||
### P2:分钟线有界流式窗口
|
||||
|
||||
参考 Zipline 的窗口预取和 Nautilus 的有序事件迭代器,按时间段加载分钟
|
||||
RecordBatch,保持持仓、订单和指标 ring buffer 有界;不能一次展开全市场全区间
|
||||
分钟 Python 对象,也不能在 chunk 边界丢失 corporate action、T+1 或订单队列状态。
|
||||
|
||||
## 明确不采用
|
||||
|
||||
- 不为单次策略把日循环拆成多线程;路径依赖会增加同步开销并破坏确定性。
|
||||
- 不通过增加 worker、DuckDB 槽位或扩大 HTTP 窗口掩盖单任务热点。
|
||||
- 不把全量历史数据复制成每个 worker 独占的 dict/DataFrame 缓存。
|
||||
- 不直接引入另一个框架的事件总线、账户或撮合实现;只借鉴机制并用中国市场合同验收。
|
||||
- 不缓存策略结果,也不以 request hash 返回旧结果代替真实回测。
|
||||
|
||||
## 验收矩阵
|
||||
|
||||
任何性能候选至少覆盖:
|
||||
|
||||
| 合同 | 必须保持 |
|
||||
|---|---|
|
||||
| 五年主策略 | 收益、成交数、canonical/result-store SHA、终态审计 |
|
||||
| 五年第二策略 | 不同持仓数下的同一组证据 |
|
||||
| 通用 rank 策略 | 非 market-cap 特例排序仍正确 |
|
||||
| 分钟策略 | 调度点、分钟成交价、滑点、成交量限制和 SHA |
|
||||
| 冷/热运行 | data/engine/result/finalization 分段、RSS、instructions/cycles |
|
||||
|
||||
任一合同漂移、只有微基准改善、或真实 wall/RSS 变差时,候选必须删除并保留拒绝证据。
|
||||
Reference in New Issue
Block a user