原创 · 约 7 分钟阅读 · 阅读 --
Last updated on
程序员量化交易实战 14:把策略候选保存成实验记录
程序员量化交易实战 14:把策略候选保存成实验记录
古董级程序员,大厂出来后一直在创业公司,现在仍活跃在一线做 AI 相关的开发。这个专栏会把一个 A 股量化平台从 0 到 1 拆开写:数据、策略、回测、模拟盘、提醒和生产化,尽量用真实代码和真实运行结果说话。更完整的更新也会同步到微信公众号「字与码」。
第 13 篇能产生参数候选。但候选如果只停留在终端输出里,很快就会丢失上下文。
第 14 篇做实验记录:候选参数是什么,基准是谁,指标差异是多少,系统给出的状态和决策是什么。这些都要结构化保存,后面才能复盘。

终端输出不是实验记录
策略研究经常会出现这种情况:今天跑出一个候选,看起来不错;明天换了数据区间或参数,已经不记得它为什么好。
所以候选结果必须带着证据一起保存。

实验记录对象
第 14 章新增 app/experiment_records.py。
@dataclass(frozen=True)
class ExperimentRecord:
experiment_id: str
name: str
status: str
candidate: dict[str, Any]
baseline: dict[str, Any] | None
decision: str
created_at: str
它暂时还是纯 Python 对象,但结构已经接近后续写入数据库的 payload。
候选必须和基准比
比较函数是:
def compare_candidate_to_baseline(candidate: dict[str, Any], baseline: dict[str, Any] | None = None) -> dict[str, Any]:
candidate_metrics = dict(candidate.get("metrics") or {})
baseline_metrics = dict((baseline or {}).get("metrics") or {})
if not baseline_metrics:
return {"status": "no_baseline", "deltas": {}, "passed": False}
没有基准时,不直接通过,只标成 no_baseline。
有基准时计算差异:
deltas = {
"total_return": candidate_return - baseline_return,
"max_drawdown": candidate_drawdown - baseline_drawdown,
"trade_count": candidate_trade_count - baseline_trade_count,
}
当前通过条件很克制:收益要改善,回撤不能明显恶化。
记录状态
build_experiment_record() 会给出三个状态:
candidate:缺少基准,等待比较。passed:相对基准通过。rejected:没有清过基准。
这一步让实验记录能进入下一章的策略晋升门禁。
本章更新与代码仓库
本章更新内容:
- 新增
app/experiment_records.py。 - 实现候选与基准比较、实验记录构建、状态决策和 payload 序列化。
- 新增
tests/test_experiment_records.py,覆盖缺少基准、通过基准、拒绝候选和 payload 输出。
代码仓库:
https://github.com/ax2/zi-quant-platform
本章代码:
git clone https://github.com/ax2/zi-quant-platform.git
cd zi-quant-platform
git checkout chapter-14
uv sync --extra dev
uv run pytest tests/test_experiment_records.py
第 14 章全量测试通过:183 passed,仍只有既有 FastAPI deprecation warning。
本篇小结
策略研究必须留下证据。
第 14 篇把参数搜索结果变成结构化实验记录。下一篇继续往前走:哪些实验记录可以进入模拟盘观察,哪些必须被拒绝。