← 返回目录
p8.4动手⏱ 约 14 分钟

策略管理与版本化:改了规格就要升版本

同一个版本号下改一个参数,注册被拒——指纹 6b30f73cc9f0 vs b44970cb9ee2

🔎 最后验证 2026-08📚 来源:策略注册与版本冲突检测实测,2026-08-01 于 ECS 实测,输出见 code/outputs/stdout.txt🧰 Python 3.12.13、qsys(本课自研)
为什么学这个

p5.1 给了规格对象和指纹。这一节把它变成系统里的硬约束:

注册 ma_cross@1.0.0   指纹 6b30f73cc9f0

改了规格却不升版本 → 被拒绝:
  版本冲突:ma_cross@1.0.0 已存在且指纹不同
            (6b30f73cc9f0 vs b44970cb9ee2)
            **改了规格就要升版本** —— 这条不能绕过。

改动只有一个:fast 从 5 变成 10。

而系统的反应不是"更新一下",是拒绝注册

因为一旦允许"同版本号不同规格",所有历史结果就都失去了意义—— 你再也无法回答"上个月那条净值曲线,用的是哪个 fast"。

💡 打个比方

药品的批号不是给药厂看的,是给召回看的。

配方改了却沿用旧批号,出事时你根本不知道该召回哪一批。

策略指纹就是那个批号。

一、注册表只做三件事

@dataclass
class StrategyVersion:
    name: str
    version: str
    spec: dict
    fingerprint: str = ""      # 自动由 spec 的 sha256 生成
做什么不做什么
(name, version) → spec + fingerprint不存代码(代码在 git 里)
同版本不同指纹 → 抛错不做自动升版本(那是人的决定)
列出所有已注册版本不做"哪个更好"的判断

"不做自动升版本"是刻意的。 自动升版本会让人失去"我改了一个会影响结果的东西"这个感觉——而那个感觉正是版本号存在的理由

二、什么算"改了规格"

指纹是 spec 全字段的 sha256,所以任何字段变化都会改指纹:

改动指纹变吗结果会变吗
fast: 5 → 10
price_base: close → hlc3✅(p5.1 实测差 3.80 pp)
stop_loss: "无" → "无止损"
加一个 note 字段
⚠️ 避坑

后两行是"误报",而它们恰恰不该被消除。

改了描述文字、加了备注,指纹也会变——看起来很烦。

但如果为了"少一点误报"而把这些字段排除在指纹之外,你就必须维护一份"哪些字段算数"的名单。而这份名单本身会漂:某天有人往里加了一个真正影响结果的字段,却忘了登记。

宁可多升几次版本,也不要维护一份"哪些字段算数"的名单。

这与 p4.10 那条"任何会改变回测结果的修改,即使 API 不变也必须升次版本"是同一条纪律的两面。

🔧 动手做:偷偷改一个参数,却不升版本号——看它让不让(5 分钟)

跑策略注册的例子:先注册 [email protected],再只把 fast 从 5 改成 10,用同一个版本号 1.0.0 重新注册。

你会看到(实测):注册被拒绝,提示指纹冲突——同样是 1.0.0,指纹却从 6b30f73cc9f0 变成了 b44970cb9ee2

想明白:"版本号"靠人自觉,人会偷懒——改了参数还挂着旧版本号,以后没人知道 1.0.0 到底是哪套参数。指纹把规格(参数、逻辑)哈希成一个值,一路带到回测结果里:你改了任何东西,指纹就变,想赖都赖不掉。改了规格就必须升版本——这条规矩由代码强制,不靠自觉。

三、指纹要一路带到底

系统里每一个环节都带着它:

Signal(spec_hash=...)      →  策略发出的每个信号
OrderReq(strategy=...)     →  每一笔委托
BacktestTask(strategy_fp)  →  每一次回测(p8.5)
equity.jsonl               →  每一条净值记录

这样任何一条净值曲线、任何一笔成交,都能反查到"它是哪份规格产生的"。

反过来说:如果一个系统里的成交记录没有策略指纹,那它出了问题就只能靠猜。

四、版本号怎么升(接 p4.10 的量化库规矩)

改了什么怎么升
修 bug,不改变任何历史结果修订号 1.0.0 → 1.0.1
改了会改变回测结果的东西次版本 1.0.0 → 1.1.0
改了接口/规格结构主版本 1.0.0 → 2.0.0

第二行是量化特有的,p4.10 已经讲过理由:

使用者会拿新版本重跑旧策略。结果变了却没提示,比 API 不兼容更危险——API 不兼容会立刻报错,结果变化不会。

❓ 测验
你只是给策略 spec 加了一个 note 字段(值是一个调研笔记链接),指纹变了、注册被拒。最该怎么做?
✏️ 填空
注册表刻意不做自动升版本,因为那会让人失去「我改了一个会影响结果的东西」这个 ___。

📌 免责:本课为技术教学,策略仅为演示口径,不构成投资建议。

✅ 小结

四件事:

  1. 系统的硬约束:同版本号下改任何一个规格字段,注册直接被拒(实测 6b30f73cc9f0 vs b44970cb9ee2)。一旦允许"同版本号不同规格",所有历史结果就都失去了意义
  2. 注册表刻意不做自动升版本——自动升版本会让人失去"我改了一个会影响结果的东西"这个感觉,而那正是版本号存在的理由
  3. 宁可多升几次版本,也不要维护一份"哪些字段算数"的名单。改描述、加备注也会改指纹,看起来烦;但那份名单会漂,某天有人加了真正影响结果的字段却忘了登记
  4. 指纹一路带到底:Signal → OrderReq → BacktestTask → equity.jsonl。如果成交记录里没有策略指纹,出了问题就只能靠猜

版本号规则接 p4.10:任何会改变回测结果的修改,即使接口不变也必须升次版本——因为结果变了却没提示,比接口不兼容更危险

下一节把回测本身变成服务:一次回测 = 一条可追溯的任务记录,而前端只消费这一个契约。

下一节 → 回测服务与前端
🔎 来源与核验· 1 条,点开核对
本节每个关键论断都对应一个可追溯的来源 —— 这是本课程"靠谱、不过时"的底线。
「策略注册实测:[email protected] 指纹 6b30f73cc9f0;仅将 fast 由 5 改为 10 后再注册同版本号,被拒绝并提示指纹冲突(6b30f73cc9f0 vs b44970cb9ee2)」
📚 本节 code/p8_demo.py 与 _shared/qsys/registry.py,2026-08-01 于 ECS 实测,输出见 code/outputs/stdout.txt✓ 已核验 2026-08
指纹为 spec 全字段的 sha256 前 12 位;策略仅为演示口径。
智图软件的赞赏码
都看到这了,打个赏呗!
接下来 · p8.5
回测服务与前端:一次回测 = 一条可追溯的任务记录
继续读下一节 →