← 返回目录
p8.5动手⏱ 约 15 分钟

回测服务与前端:一次回测 = 一条可追溯的任务记录

三组参数跑出年化 2.99% / 10.83% / 21.07%——而每一条都带着策略指纹与数据指纹

🔎 最后验证 2026-08📚 来源:回测任务服务实测(3 个任务),2026-08-01 于 ECS 实测,输出见 code/outputs/stdout.txt🧰 Python 3.12.13、qsys(本课自研)、pandas 3.0.5
为什么学这个

一次回测不是一次函数调用,是一条任务记录。

数据指纹 d55258de9e46(数据变了 → 指纹变 → 旧结果自动作废)

任务         策略        策略指纹        累计%    年化%    回撤%   成交  被拒
bt-5-20    ma_5_20    b1cbd591ad4a    28.90   10.83  -27.40    98   108
bt-10-30   ma_10_30   cbdd0d8211fe     7.55    2.99  -31.45   101    99
bt-20-60   ma_20_60   31b0f675189d    60.29   21.07  -19.06    88   107

年化从 2.99% 到 21.07%——而这三行的区别只有均线参数。

如果没有右边那两个指纹,三个月后你无法回答:这条 21.07% 的曲线,用的是哪份规格、哪份数据。

而 p5.4 已经证明:报告最优的那一组,等于报告 N 次比较的最大值。

任务记录不能阻止你挑最好的那条,但它能让别人看见你一共跑了几条。

💡 打个比方

实验记录本上,每一次实验都要写:日期、试剂批号、参数、结果、操作人。

不是为了好看,是为了三个月后有人问"这个数据怎么来的"时,你能翻到那一页。

回测任务记录就是那一页。

一、任务记录必须有的六样

@dataclass
class BacktestTask:
    task_id: str
    strategy: str
    strategy_fp: str      # ① 策略指纹(p5.1 / p8.4)
    symbol: str
    start: str; end: str  # ② 区间
    cost: dict            # ③ 成本假设(p4.2:值 74 个百分点)
    data_fp: str          # ④ 数据指纹
    status: str
    result: dict          # ⑤ 结果
    finished_at: str      # ⑥ 完成时间

没有这六样,三个月后你无法回答"这条曲线是怎么来的"。

其中最容易被省掉的是 ④ 数据指纹:

def data_fingerprint(path):
    df = pd.read_parquet(path, columns=["symbol", "date", "close"])
    return hashlib.md5(df.to_numpy().tobytes()).hexdigest()[:12]

数据变了 → 指纹变 → 所有引用旧指纹的结果自动作废。

这正是 p5.9 那次事故需要的东西:当时如果每个结果都带数据指纹,ERC 期末倍数在 3.38~3.95 之间漂这件事,当场就会被发现

二、结果存储:前端只读一个契约

落盘 3 条任务记录(JSON Lines,追加写)
字段:task_id / strategy / strategy_fp / symbol / start / end
      cost / data_fp / status / result / finished_at

前端不需要知道回测是怎么跑的,它只消费这个契约。

而这个契约里有策略指纹和数据指纹——所以图上的每条线都能追回去

🔧 动手做:同一个策略跑三组参数,看每条结果带没带"身份证"(5 分钟)

跑回测任务服务,用三组参数各跑一次。

你会看到(实测):三条结果年化分别是 2.99% / 10.83% / 21.07%——而每一条都带着策略指纹数据指纹(数据指纹 d55258de9e46)。

想明白:一次回测不是"一个数字",是一条可追溯的记录:什么策略(指纹)、在什么数据(指纹)、什么时候跑的。三个月后你看到"年化 21%",能立刻查出它是哪套参数、哪份数据跑的——而不是一个来历不明的漂亮数字。没有指纹的回测结果,等于没有出处的传言。

三、前端图表要什么(与 p4.8 的六联图对齐)

面板回答什么数据来自
净值 + 基准(对数轴)跑赢了没有equity_curve
水下图我要熬多久equity_curve 计算
月度收益热力收益是否均匀equity_curve 计算
成交点标注在什么位置进出fills.jsonl
被拒委托时间轴向量化回测画不出来rejects.jsonl
血缘水印这张图是怎么来的strategy_fp + data_fp + cost
⚠️ 避坑

最后一行是很多系统会省掉的。

图会被截屏、转发、三个月后翻出来。没有水印的图,三天后就是一张不可追溯的图片(p1.8)。

而回测报告的水印比行情图多两项:引擎名与成本假设——因为 p4.2 已经证明,成本假设值 74 个百分点

另外注意倒数第二行:被拒委托时间轴

本节三个任务的被拒笔数是 108 / 99 / 107——和成交笔数几乎一样多。一张不画被拒的报告图,会让人以为这些委托都成交了。

四、任务化带来的三个能力

能力怎么来的
批量与对比任务是数据,可以一次提交一批(本节 3 个)并排比
幂等重跑(strategy_fp, data_fp, cost, 区间) 相同 → 直接返回旧结果,不重算
自动作废数据指纹变了 → 引用旧指纹的结果标记为过期

第三条是任务化最大的收益:它把"数据更新了要不要重跑"这个靠人记的事,变成了系统能判断的事

❓ 测验
你的回测服务只记录了策略参数和结果,没记数据指纹。三个月后数据源修订了历史值,会发生什么?
✏️ 填空
任务记录的六样里,最容易被省掉的是 ___ 指纹——它让「数据变了旧结果自动作废」成为可能。

📌 免责:本课为技术教学,回测参数与结果均为演示口径,不构成投资建议。

✅ 小结

四件事:

  1. 一次回测 = 一条任务记录,而不是一次函数调用。六样必备:策略指纹 · 区间 · 成本假设 · 数据指纹 · 结果 · 完成时间。实测三组参数年化 2.99% / 10.83% / 21.07%——区别只有均线参数
  2. 最容易被省掉的是数据指纹。有了它,数据变了 → 指纹变 → 旧结果自动作废;p5.9 那次事故如果每个结果都带数据指纹,当场就会被发现
  3. 前端只消费一个契约,不需要知道回测怎么跑。而契约里有两个指纹,所以图上的每条线都能追回去
  4. 图表六联与 p4.8 对齐,其中两项最常被省:被拒委托时间轴(本节三个任务被拒 108/99/107,和成交笔数几乎一样多)与血缘水印(比行情图多引擎名与成本假设两项,因为 p4.2 证明成本值 74 个百分点)

任务化带来三个能力:批量对比 · 幂等重跑 · 自动作废第三条把"数据更新了要不要重跑"从靠人记的事,变成了系统能判断的事。

下一节接实时行情:限频、抖动、断线——以及一个用 300 步模拟量化出来的结论。

下一节 → 实时行情接入
🔎 来源与核验· 1 条,点开核对
本节每个关键论断都对应一个可追溯的来源 —— 这是本课程"靠谱、不过时"的底线。
「回测任务服务实测(sh.600000,2024-01~2026-07,数据指纹 d55258de9e46):ma_5_20 累计 28.90%/年化 10.83%/回撤 -27.40%/成交 98/被拒 108;ma_10_30 7.55%/2.99%/-31.45%/101/99;ma_20_60 60.29%/21.07%/-19.06%/88/107」
📚 本节 code/p85_backtest_service.py,2026-08-01 于 ECS 实测,输出见 code/outputs/stdout.txt✓ 已核验 2026-08
三组参数仅为演示,不构成任何参数推荐;p5.4 已说明报告最优组等于报告 N 次比较的最大值。
智图软件的赞赏码
都看到这了,打个赏呗!
接下来 · p8.6
实时行情接入:限频、抖动、断线
继续读下一节 →