策略管理与版本化:改了规格就要升版本
同一个版本号下改一个参数,注册被拒——指纹 6b30f73cc9f0 vs b44970cb9ee2
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 不兼容会立刻报错,结果变化不会。
📌 免责:本课为技术教学,策略仅为演示口径,不构成投资建议。
四件事:
- 系统的硬约束:同版本号下改任何一个规格字段,注册直接被拒(实测
6b30f73cc9f0vsb44970cb9ee2)。一旦允许"同版本号不同规格",所有历史结果就都失去了意义 - 注册表刻意不做自动升版本——自动升版本会让人失去"我改了一个会影响结果的东西"这个感觉,而那正是版本号存在的理由
- 宁可多升几次版本,也不要维护一份"哪些字段算数"的名单。改描述、加备注也会改指纹,看起来烦;但那份名单会漂,某天有人加了真正影响结果的字段却忘了登记
- 指纹一路带到底:Signal → OrderReq → BacktestTask → equity.jsonl。如果成交记录里没有策略指纹,出了问题就只能靠猜
版本号规则接 p4.10:任何会改变回测结果的修改,即使接口不变也必须升次版本——因为结果变了却没提示,比接口不兼容更危险。
下一节把回测本身变成服务:一次回测 = 一条可追溯的任务记录,而前端只消费这一个契约。
🔎 来源与核验· 1 条,点开核对
