程序员量化交易实战 32:把每日运行结果归档成 JSON
程序员量化交易实战 32:把每日运行结果归档成 JSON
古董级程序员,大厂出来后一直在创业公司,现在仍活跃在一线做 AI 相关的开发。这个专栏会把一个 A 股量化平台从 0 到 1 拆开写:数据、策略、回测、模拟盘、提醒和生产化,尽量用真实代码和真实运行结果说话。更完整的更新也会同步到微信公众号「字与码」。
第 31 篇给每日流程加了运行时间窗。
第 32 篇处理运行后的证据问题:今天到底发了什么提醒、健康状态是什么、复盘记录里写了什么,都应该落到一个稳定文件里。

为什么先归档 JSON
真实系统里,日报可能会发到群、邮件、飞书、文件,后面还可能进数据库。
但在模拟盘阶段,我更希望先有一个稳定、可 diff、可回放的文件。JSON 足够直白,也方便测试。
归档文件包含三块信息:
| 区块 | 来源 | 用途 |
|---|---|---|
alert | PaperAlertMessage | 记录当天对外发送的标题、正文和严重级别 |
health | RunHealthReport | 记录运行是否健康,以及缺价格、通知回执等状态 |
review | PaperReviewRecord | 记录权益、现金比例、风险级别和推荐动作 |
归档对象
第 32 章新增 app/report_archive.py。
@dataclass(frozen=True)
class ArchivedReport:
path: Path
trade_date: date
status: str
ArchivedReport 不保存完整内容,只返回归档路径、交易日和健康状态。完整内容通过文件读取。
写入稳定文件
文件名直接使用交易日:
path = target_dir / f"{trade_date.isoformat()}-paper-report.json"
核心写入逻辑是把已有对象压成一个稳定 payload:
payload = {
"trade_date": trade_date.isoformat(),
"alert": {
"title": alert_message.title,
"body": alert_message.body,
"severity": alert_message.severity,
},
"health": {
"status": health_report.status,
"summary": health_report.summary,
"issue_count": health_report.issue_count,
"missing_price_count": health_report.missing_price_count,
"notification_accepted": health_report.notification_accepted,
},
"review": {
"total_equity": review_record.total_equity,
"cash_ratio": review_record.cash_ratio,
"risk_severity": review_record.risk_severity,
"recommendation_action": review_record.recommendation_action,
"note": review_record.note,
},
}
写文件时使用 ensure_ascii=False 和 sort_keys=True。前者让中文日报可读,后者让文件结构稳定,后面排查问题更省心。
归档文件的角色接近“运行证据”。它不替代数据库,也不替代日志;它保存的是当天日报真正需要回看的最小闭环:对外发了什么、系统健康状态是什么、复盘记录里的关键口径是什么。
当前联动运行结果
继续运行同一条命令:
uv run python -m scripts.chapter_examples paper-ops-check
命令会在临时目录里写入两天归档,其中当天归档名为 2026-03-05-paper-report.json:

截图里可以看到归档 payload 固定包含 alert、health、review 和 trade_date。这几个键稳定以后,后面的历史统计和运维清单就不需要再理解日报正文格式。
测试归档内容
测试用临时目录,不污染工作区。
uv run pytest tests/test_report_archive.py
关键断言包括:
assert archived.path.name == "2026-01-28-paper-report.json"
assert payload["trade_date"] == "2026-01-28"
assert payload["health"]["status"] == "ok"
assert payload["review"]["recommendation_action"] == "HOLD"
这说明归档不仅写了文件,而且保留了后续统计真正需要的字段。
本章更新与代码仓库
本章更新内容:
- 新增
app/report_archive.py。 - 实现每日报告归档对象
ArchivedReport。 - 把提醒消息、健康报告和复盘记录写入稳定 JSON。
- 实现
read_archived_report(),用于后续读取归档。 - 增加
paper-ops-check联动示例,展示真实归档文件名和归档 payload 结构。 - 补充归档作为运行证据的工程定位。
- 新增
tests/test_report_archive.py,验证文件命名和关键字段。
代码仓库:
https://github.com/ax2/zi-quant-platform
本章代码:
git clone https://github.com/ax2/zi-quant-platform.git
cd zi-quant-platform
git checkout chapter-32
uv sync --extra dev
uv run pytest tests/test_report_archive.py
第 32 章提交为 8504ea9,tag 为 chapter-32。
本篇小结
归档不是为了多存一份文件,而是为了让每日运行有证据。
第 32 篇把日报、健康状态和复盘记录落成稳定 JSON。下一篇会读取这些归档文件,生成一段更长时间尺度上的运行历史摘要。