从交易记录重建每日资产快照:daily_summaries 历史回填实战
本文最后更新于 2026年7月12日 晚上
问题:权益曲线只有 4 个点
一个量化交易平台的 Dashboard 上,净值曲线(equity curve)只显示了 4 个点。不是数据源断了,不是前端 bug——是 daily_summaries 表只有 8 行数据,集中在短短几天内。
后端的 generate-summary 端点功能完全正常,手动调用能正确生成当日快照。问题是:从来没有定时任务调用它。 功能存在,但不运转。
这是一个典型的”数据可见性”问题:系统有能力生产数据,但缺少自动化的”数据生产调度”,导致数据积压、曲线断层、用户看到的 Dashboard 残缺不全。
现状分析
数据缺口
账户
类型
创建日期
初始资金
daily_summaries 覆盖
1
实盘 broker
06-03
100,000
06-12 ~ 06-17(仅 5 天)
2
实盘 broker
06-05
106,150
无
3
实盘 broker
06-12
10,000
无
4
模拟 simulated
06-22
100,000
无
5
模拟 simulated
06-22
100,000
无
5 个账户,从 6 月初到 7 月初整整一个月,只有账户 1 有零星几天的快照。缺失 06-18 到 07-04 的全部数据。
可用数据源
好消息是,交易记录(trades 表)是完整的。每一笔买入卖出都有记录:股票代码、方向、数量、价格、时间戳。另外,ReShare 行情服务(localhost:8200)有完整的 A 股历史价格数据。
有了交易记录 + 历史价格,理论上可以重建任意一天的持仓快照——只需要重放所有交易到目标日期,算出当天的持仓和现金,再乘以收盘价,就得到当日总资产。
回填方案
核心逻辑:交易重放 + 历史定价
对每个账户的每个历史交易日,执行以下计算:
1 | |
关键函数
持仓重建——最核心的逻辑。遍历交易记录到目标日期,按买入/卖出更新持仓:
1 | |
现金重建——从初始资金开始,按交易正向计算:
1 | |
历史定价——调用 ReShare 行情服务:
1 | |
容错设计
历史回填不可能一次跑完不报错。ReShare 某天某股可能缺数据,网络可能超时。处理策略:
1 | |
这个 fallback 不完美(成本价 ≠ 市价),但保证了脚本不中断。缺失的价格用成本近似,总比没有数据强。
执行结果
1 | |
账户
回填前
回填后
日期范围
1(主账户)
5 行
23 行
06-03 ~ 07-04
2(中信建投)
0 行
21 行
06-05 ~ 07-04
3(平安证券)
0 行
16 行
06-12 ~ 07-04
4(ETF轮动)
0 行
9 行
06-22 ~ 07-04
5(网格交易)
0 行
9 行
06-22 ~ 07-04
合计
8 行
78 行
—
equity curve 从 4 个点提升到 17 个点——Dashboard 终于能画出一条像样的曲线了。
从一次性回填到持续运转
回填解决了历史缺口,但如果不解决”为什么没有定时调用”的问题,数据又会从明天开始断档。
定时快照的部署
最初设计用 systemd timer,每个交易日 15:30 触发:
1 | |
但目标机器上 sudo 权限受限,无法注册系统级 systemd timer。退而求其次,改用用户级 crontab:
1 | |
一行 crontab,每天收盘后自动调用已有的 generate-summary 端点。不写新代码,复用已有 API。
为什么 crontab 而不是 systemd
维度
systemd timer
crontab
需要 sudo
是
否
Persistent=true(错过的补执行)
支持
不支持
日志集成
journald
邮件/stdout 重定向
部署门槛
中(需写 service+timer 两个文件)
低(一行搞定)
在这个场景下,systemd 的 Persistent 补执行能力是唯一优势,但快照是”每天收盘后生成当日”——如果 15:30 机器关了,第二天补执行也没有意义(收盘价已经更新了,但中途的持仓可能已经变了)。所以 crontab 足够。
教训:功能存在 ≠ 功能运转
这次回填最大的教训不是技术上的,而是认知上的:
generate-summary 端点一直在那里,功能完全正常。手动 curl 一下就能生成快照。但没有人定时调用它,所以数据不增长,Dashboard 是残缺的。
这不是 bug,是运维断层——开发写了功能,但没有把”谁来调用、什么时候调用、调用失败怎么办”这个闭环接上。
更广泛地说,任何有”数据生产”能力的系统,都需要回答三个问题:
谁触发——定时任务?事件回调?用户手动?
多久一次——实时?每日?按需?
失败怎么办——重试?告警?降级?
这三个问题没有答案的功能,本质上就是”僵尸端点”——活着,但不产出。
总结
步骤
方法
产出
历史回填
交易重放 + ReShare 定价
8 行 → 78 行
持续快照
crontab 每日 15:30 调用 generate-summary
数据不再断档
容错
价格缺失用成本价 fallback
脚本不中断
168 行 Python 脚本,解决了 equity curve 数据断层问题。技术不复杂——交易重放是标准会计逻辑,历史定价是 API 调用。真正有价值的是识别出”功能存在但不运转”这个隐蔽问题,然后补上定时调度的闭环。