Edgekit 算法实现评判(v0.1.0)

针对当前已实现版本的批判性评审

Author

Edgekit Team

Published

June 13, 2026

Note📝 摘要

本文评判的是 v0.1.0 当前已实现版本src/edgekit/,对应合并到 main 的 #2–#6),不再讨论早期文档草稿。维度仍是六个:数学正确性、统计严谨性、风控逻辑、金融实务、工程一致性、实现保真度(代码是否忠实落地了 需求文档)。每条高严重性结论都用一段可复现的脚本实测验证过,不只是读代码的推断。现行规格以需求文档为准——凡是代码与 PRD 冲突的,本文按缺陷处理。

Tip✅ 本轮发现已全部修复

下列三处高severity与五处中/低severity,已逐条经 relay 流水线修复并独立实测验证:HIGH-1/HIGH-3(engine 下行保护默认开、leverage 与 contract_multiplier 不再放大止损亏损)→ PR #15、#17;HIGH-2(calibrate_h 去标准化、闭环 ARL 对齐)→ PR #18;中/低severity(敏感度报未封顶值、payoff cv 统一、parse_policy 校验闭门、binding_constraint 归因)→ PR #19。每条下方保留原始发现作为缺陷记录。其中”x_sweep 偏离 PRD”一条经核对系比对了旧版规格,已撤回。

Important总体判断

核心纯函数(solver、monitor 主判定)写得干净、与 PRD 一致;但越靠近”把数字落到钱上”的两层——engine 的换算和 tooling 的标定——缺陷越严重,其中三处直接打穿了这个库立身的两条根本承诺:「先保住本金的非对称风控」和「风险 = 止损被打掉时的损失」。(上述均已修复,见顶部横幅。)

158 个测试全绿,却没拦住这些缺陷——因为测试逐个函数对着各自的验收标准测,没有测”几个零件拼起来是否还满足 PRD 承诺的整体性质”。这正是本轮要补的盲区,见 §5


1 核心优点(值得保留)

  1. core 真的纯import edgekit 不加载 numpy(实测 'numpy' in sys.modules 为 False),随机只活在 tooling、且 seed 显式。三层边界守住了。
  2. solver 的置信下界链条正确:Wilson 单边下界 → 对数正态 RR 下界 → 下界凯利 → 半凯利 → 风险上限,数值锚点(solver(60,40,1.5)risk_fraction=0.02,证据链 0.167→0.067→0.02)精确命中 PRD 示例。
  3. monitor 主判定方向对:决策表优先级(回撤硬熔断 > CUSUM 报警 > 降档 > 样本不足 > OK)与 PRD 一致;CUSUM 在 monitor 自身口径下漂移方向正确(健康策略 E[X]=edge_0 > kS_t 趴在 0);胜率诊断改成了单边下侧、小样本走精确二项。
  4. 不可变与校验:Policy 是 frozen dataclass;solver/monitor 的”结构非法抛错、证据不足降级”两张清单基本落地。

这些是地基,下面的问题都是地基之上的管道接错了,不是地基本身塌了。


2 高严重性问题(已实测验证)

2.1 HIGH-1:rebase 默认关掉下行再基准化,反凯利 bug 原样复活

Caution

风控方向性缺陷,与 v2 设计的核心修复直接矛盾。

engine.rebase(base, equity, x, d=None)src/edgekit/engine.py:23)把下行参数 d 默认成 None,而 d is None整条下行规则被跳过——只剩”涨到 x 倍才上调”的单向棘轮。

实测:

rebase(100, 70, x=1.5, d=0.8) == 70    # 传 d:净值跌到 70,base 跟着下调
rebase(100, 70, x=1.5)       == 100    # 不传 d:base 停在 100

base 停在高点意味着净值越跌,单笔风险占当前资金的比例越大——这正是需求文档 §再基准化里写明要用”快下”规则堵掉的”越亏下注越重”。PRD 明确 d 默认 0.8,可实现把它默认成了”没有保护”。任何照函数签名直接调 rebase 的人(README 快速上手没演示 rebase,所以很容易自己直接调)都会拿到一个只升不降的 base。库最强调的非对称保护,在 API 默认值这一层被静默关掉了。

修订d 默认 0.8(与 PRD 默认表一致);要关掉下行得显式传 d=None 并在文档警示,而不是反过来。

2.2 HIGH-2:calibrate_h 把盈亏标准化,抹掉了优势、把 CUSUM 漂移做反

Caution

统计缺陷,让”必须由工具标定”的 h 系统性失真。

h(CUSUM 报警门限)在 PRD 里没有默认值,规定必须由 calibrate_hARL_0 标定后写进 policy。可这个工具坏了。

tooling/calibrate_h.py:52_standardized_pnl 返回的是 (pnl - mean) / std——把每笔盈亏标准化成均值 0、标准差 1 的 z 分数。但 monitor 真正喂给 CUSUM 的 X_tR 倍数(止损 = −1,盈利 = 实际盈亏比),其均值就是 edge_0,不是 0。标准化这一步把要监控的优势本身减掉了。

后果是 CUSUM 漂移 k - E[X] 反号

  • monitor 真实口径:E[X]=edge_0=0.5 > k=0.25 → 漂移 −0.25,S_t 趴着,少报警(对)。
  • calibrate_h 标准化后:E[X]=0 < k=0.25 → 漂移 +0.25,S_t 一路爬,频繁报警(错)。

实测(健康基线 p_0=0.6, RR_0=1.5,2000×500):

calibrate_h 自测 同一 h 喂 monitor 真实口径
选出 h 20.0(网格上限兜底)
ARL 75(够不到目标 500) ≈450

也就是说:标准化让任何 h 都凑不够 ARL_0=500,工具退化成返回网格最大值的兜底;而它自报的 ARL=75 和 monitor 实际会看到的 ARL≈450 差了 6 倍。整条统计防线唯一的入参来源是错的。 附带:calibrate_h 还漏了 monitor 有的盈利侧 3\times RR_0 截尾(monitor.py:127 vs calibrate_h 无),两边口径进一步不一致。

修订:calibrate_h 直接喂 R 倍数 pnl(不要标准化),并加上和 monitor 一致的 3\times RR_0 截尾;加一条跨组件测试——把 calibrate_h 产出的 h 回喂 monitor,实测 ARL 必须落在 ARL_0 附近。

2.3 HIGH-3:position_sizeleverage 直接击穿”风险 = 止损损失”的定义

Caution

金融实务缺陷,违背全库第一性定义。

全库反复声明:风险 = 止损被打掉时账户亏的钱risk_fraction 就是这个。但 engine.position_sizesrc/edgekit/engine.py:52)把 leverage 乘进了名义敞口而没动 risk_budget

notional = risk_budget / stop_distance * leverage * contract_multiplier

实测 base=10万、risk_fraction=0.02、止损 5%、leverage=3:

risk_budget = 2000          # 库声称"最多亏 2000"
notional    = 120000        # 名义敞口
止损打掉真实亏损 = notional × 止损距离 = 120000 × 5% = 6000   # 实际亏 3 倍

notional 若表示市场敞口(最自然的读法),那么止损成交时的亏损就是 notional × stop_distance = risk_budget × leverageleverage=3 让”最多亏多少”变成了 risk_budget 的 3 倍——风险定义在自家 engine 里、在止损能完美成交的理想条件下就已经被击穿了(这比承重假设里那个”跳空导致止损失真”的不可控尾部更糟,因为这是确定性的换算错误)。杠杆只该影响占用的保证金/资金,不该放大止损时的亏损敞口。

修订:要么 leverage 只用于另算”占用保证金”,根本不进 notional 这条算亏损的链;要么明确 notional 是”保证金口径”并另给一个”敞口口径”字段,让 敞口 × 止损距离 == risk_budget 这条不变式恒成立,并补一条断言测试。默认 leverage=1.0 时现状没问题,所以这是杠杆/期货路径上的潜伏雷。


3 中等问题(值得修但非阻塞)

  • x_sweep / dd_check 把单笔风险写死成 1%tooling/x_sweep.py:60tooling/dd_check.py:56 都是 equity = equity * (1.0 + 0.01 * step)——0.01 是硬编码的风险比例,两个工具的入参里都没有 risk_fraction。回撤深度几乎正比于风险比例,所以选出来的 xdd_check 报的误触发概率,都是”1% 风险的策略”的结论,不是用户 policy 实际 risk_fraction 的。PRD §x-spec 写的是”按 policy 的参数生成净值”,最关键的那个参数却被写死了。修订:把 risk_fraction 作为入参传进路径推进。

  • solver 的”敏感度”在封顶时信息归零solver.py:96-124 的敏感度重走的是封顶之后risk_fraction。在 PRD 当招牌的锚点(risk_cap 生效)下实测,p±5%、RR±5% 四个方向全等于 0.02——这一节本意是”诚实报告输入敏感度”,结果恰恰在最该展示的地方一个信息都没给。修订:敏感度报未封顶的 f_{LB},让使用者看见真实的输入弹性。

  • monitor 的盈亏比诊断与 solver 口径脱节monitor.py:73_payoff_diagnosticcv_win = cv_loss = 1.0 写死,无视 solver 接受的 cv_win/cv_loss,又用 p_hat·n 反推 wins/losses。诊断不参与判定,所以定中等;但同一套不确定性,solver 和 monitor 用了两个模型。修订:cv 走 policy/入参,与 solver 统一。

  • parse_policy 的校验不完整policy.py:38-58 只校验了 8 个字段,校验 x>1gap_multiplier>=1h>0n_min>=0portfolio_capkrisk_fraction。于是一份 x=0.5gap_multiplier=0.5 的 policy 能干净载入,到了 rebase/position_size 调用点才抛错(或更糟,悄悄算错)。PRD 想让”载入即校验”成为闸门,现在闸门有缝。修订:校验覆盖所有在别处会被强制的约束。


4 低优先(实现卫生)

  • binding_constraint 误归因solver.py:115 在负优势(f_{LB}\le0 → risk=0)时把约束方报成 kelly_fraction,但真正让风险归零的是”取 max(0,·) 的零下限”,不是半凯利。退化情形归因错。
  • Baseline.n0 是死入参monitor.py 要求并校验 n0,但判定逻辑全程没用它(只做溯源)。函数层面是冗余输入。
  • Policy.h 类型标成 intpolicy.py:18 标注 h: int,而 calibrate_h 产出的是 float 门限。标注与现实不符(Python 不强制,纯卫生问题)。
  • tooling 模拟没向量化x_sweep._simulate_combo 是 path×trade 的纯 Python 双层循环(只有随机数用了 numpy)。按 PRD 规格的 10000×500×20×5,这是 5 亿次纯 Python 迭代,实际跑不动;测试只能用缩小参数。是性能/可用性问题,不是正确性问题,但 PRD 写的规模目前不可达。

5 一个系统性观察:测试全绿为什么没拦住

158 个测试逐函数对着各自 issue 的验收标准测,每个零件单独看都对。但上面三个高severity 全是跨零件的一致性问题:

  • calibrate_h 产出的 h,回喂 monitor 是否真给到 ARL_0?——没有这条测试。
  • x_sweep / dd_check 用的风险比例,是否就是 policy 的 risk_fraction?——没有这条测试。
  • rebase 默认是否保住下行保护、position_size 的 leverage 是否保住”敞口×止损距离 == risk_budget”?——没有这条不变式测试。

逐件验收挡不住接口口径错位。建议补一层”性质测试/契约测试”:直接断言 PRD 承诺的整体性质(ARL 闭环、风险不变式、再基准化的相对风险上界 f/d、x 选择的可复现性),而不只是断言每个函数在锚点上的返回值。


6 维度评分(v0.1.0 当前实现)

维度 评分 一句话理由
数学正确性 强(4/5) solver 置信下界链、monitor 主判定的代数都对,锚点精确命中。
统计严谨性 中(3/5) monitor 自身的 CUSUM/诊断对,但 calibrate_h 标准化把漂移做反(HIGH-2),门限标定整体失真。
风控逻辑 中偏弱(2.5/5) 决策表非对称性落地得好,却被 rebase 默认关下行(HIGH-1)抵消了核心保护。
金融实务 弱偏中(2.5/5) 净口径、gap_multiplier、portfolio_cap 都在,但 leverage 击穿风险定义(HIGH-3),tooling 风险写死(中-1)。
工程一致性 中(3/5) 三层边界、纯函数、不可变都守住了,但 parse_policy 校验只覆盖部分字段、有缝。
实现保真度 中偏弱(2.5/5) core 主路径忠实,tooling 与 engine 的默认值/口径多处和 PRD 对不上,且无跨组件契约测试兜底。

7 三大行动建议

Important

1. 把被默认关掉/写死的风控参数接回真实 policy(修 HIGH-1、中-1)

rebased 默认 0.8;x_sweep / dd_check 接收并使用真实 risk_fraction。让”非对称下行保护”和”按真实风险标定”成为默认行为,而不是要用户记得显式打开。

Important

2. 修好 calibrate_h,并用闭环测试钉死它(修 HIGH-2)

去掉标准化、补上 3\times RR_0 截尾,让 calibrate_h 的 CUSUM 口径和 monitor 逐字一致;加一条”h 回喂 monitor 实测 ARL ≈ ARL_0“的跨组件测试,杜绝再次漂移反号。

Important

3. 守住”风险 = 止损损失”这条不变式(修 HIGH-3、中-5)

重新厘定 position_size 的 leverage/contract_multiplier 语义,保证”敞口 × 止损距离 == risk_budget”在任何杠杆下恒成立;parse_policy 把所有在别处会强制的约束在载入期一次校验掉。配一层断言 PRD 整体性质的契约测试(见 §5)。


地基(凯利纪律、非对称风控哲学、三层纯函数边界)是真材实料,core 主路径也忠实落地了。问题集中在”把数字变成钱”的最后两层:engine 的换算和 tooling 的标定,有三处默认值/口径错位,悄悄抵消了库立身的两条承诺。都是定位明确、可单点修复的工程缺陷,不动数学骨架。本文留作 v0.1.0 的实现体检记录。