Edgekit 需求文档 (PRD)
纯函数式交易执行与监控引擎
1 概览
1.1 项目背景
Edgekit 用数学手段管理交易资金的乘法过程,避免执行不当导致的结构性破产。项目动机见为什么做 Edgekit。
1.2 核心目标
Edgekit 由两个纯函数组成:
- solver:交易开始前回答”该不该做、单笔该冒多大险”。
- monitor:交易过程中回答”优势还在不在、要不要停”。
两者之上有一条共同原则:先保住本金。停错了,损失的是机会,可以重来;亏光了,游戏直接结束。所以宁可多停一次,也不能该停的时候没停。
1.3 分层架构
随机性是这个库最需要管住的东西。系统分成三层,边界一句话说清:core 不允许任何随机;tooling 允许随机,但必须显式给种子;policy 是连接两层的配置文件。
| 层 | 内容 | 随机性 |
|---|---|---|
| core | solver、monitor、engine 换算 | 禁止,纯确定性函数 |
| tooling | 模拟工具(选 x 和 d、标定报警线、校核熔断线) | 允许,种子必须显式传入并记录 |
| policy | JSON 配置文件,载入后冻结 | 不适用,记录 tooling 的产物和种子 |
core 在任何调用链上都不会触发模拟。tooling 算出来的东西只能写进 policy,再由 policy 进入 core。
2 核心定义
2.1 Edge(优势)
优势由两个参数描述:
- p:胜率
- RR:盈亏比(平均盈利 / 平均亏损)
优势为正的条件:p \times RR > 1 - p。
统计口径:全库的胜率、盈亏比、每笔盈亏,一律指扣除滑点和手续费之后的净结果。交易成本在数据进入系统之前就扣掉,运行过程中不再处理,监控信号也就不会被成本波动干扰。
2.2 风险 (Risk)
风险 = 止损被触发时,账户损失的金额,以 base 的百分比表示。
- 它不是仓位大小,不是保证金,也不是杠杆。
- 这个定义默认止损能在止损价附近成交。跳空、闪崩时会失真,见 Section 8。
2.3 资本基准 (base)
计算单笔风险时的分母。它不跟着净值实时变动,只按下面的再基准化规则调整。
2.4 再基准化 (x-rebase)
base 什么时候动?两条规则,方向不同:上调慢,下调快。
\text{Equity} \ge x \times \text{base} \implies \text{base} = \text{Equity} \qquad \text{(上调)}
\text{Equity} \le d \times \text{base} \implies \text{base} = \text{Equity} \qquad \text{(下调)}
上调慢,是为了避免完全复利:净值一涨就加码,对参数错误极其敏感。下调快,是为了堵住一个漏洞:如果 base 只升不降,净值下跌后,固定的风险金额占剩余资金的比例会越来越高,相当于越亏越加码。
d 的含义可以精确说出来:base 固定期间,单笔风险金额不变,这笔钱占当前资金的比例最高出现在”即将触发下调”的位置,恰好是 f/d(f 为风险比例)。所以 d 直接控制最坏时刻的相对风险膨胀倍数:d = 0.8 意味着最多膨胀到 1.25 倍。x 和 d 都由模拟联合选定,见 Section 5。
只允许这种到达阈值后的一次性调整,不允许部分调整、平滑过渡或取平均。
2.5 每笔结果的计量单位 (X_t)
把第 t 笔交易的盈亏除以这笔交易的风险额,得到的就是交易者常说的”R 倍数”:
- 止损出场:X_t \approx -1(亏掉一份风险)。有滑点或跳空时会低于 -1,按实际值记录,不截断——比预期更深的亏损正是该停手的证据。
- 盈利出场:X_t 等于这笔实际实现的盈亏比。
X_t 的长期平均值就是优势本身:\mathbb{E}[X] = p \cdot RR - (1-p)。monitor 盯的就是这个量。
进入监控统计时,盈利一侧截断到 3 \times RR_0 为止,避免一笔超大盈利把已经退化的策略拉回”看起来正常”。截断只影响检验用的统计量,不影响记账。
2.6 当前回撤 (DD)
DD_{current} = 1 - \frac{\text{当前净值}}{\text{净值历史高点}}
注意这是当前离高点有多深,净值涨回去它就变小。不要用”迄今最大回撤”——那个数字只会变大不会变小,时间足够长必然越过任何固定阈值,它反映的是时间长短,不是策略好坏。
2.7 停止与继续的双重标准
- 停:一条充分证据就停。回撤碰到熔断线停,统计检验报警也停,互相不需要确认。
- 继续:宣告”一切正常”需要足够样本(n \ge n_{min});停过之后要恢复,需要人工复核并启用新的 policy。
“多收集几个信号互相印证,免得误判”——这种谨慎只允许用在继续方向,不允许用在停止方向。
3 功能需求:solver
3.1 输入参数
solver 不接受”胜率 0.6”这样的单个数字,要的是”100 笔里赢了 60 笔”。没有样本量,就没有凯利。
| 参数 | 类型 | 说明 | 范围 |
|---|---|---|---|
wins |
int | 样本中的盈利笔数 | ≥ 0 |
losses |
int | 样本中的亏损笔数 | ≥ 0 |
RR |
float | 盈亏比估计值(净结果口径) | > 0 |
cv_win |
float | 盈利金额的变异系数,可选 | > 0,默认 1.0 |
cv_loss |
float | 亏损金额的变异系数,可选 | > 0,默认 1.0 |
kelly_fraction |
float | 凯利分数 | (0, 1],默认 0.5 |
risk_cap |
float | 风险比例硬上限 | (0, 1),默认 0.02 |
confidence |
float | 置信下限的单侧置信水平 | (0.5, 1),默认 0.90 |
胜率 \hat{p} = \text{wins} / (\text{wins} + \text{losses}) 由 solver 内部计算。
3.2 计算逻辑
思路一句话:不用”你报的胜率”算,用”按你的样本量,胜率保守地说至少有多少”算。样本越少,这个保守值离你报的数字越远,结果越保守;样本足够多,两者趋同。
记 n = \text{wins} + \text{losses},z 为置信水平对应的单侧正态分位数(90% 对应 z \approx 1.28)。
- 胜率的置信下限(Wilson 公式): p_{LB} = \frac{\hat{p} + \frac{z^2}{2n} - z\sqrt{\frac{\hat{p}(1-\hat{p})}{n} + \frac{z^2}{4n^2}}}{1 + \frac{z^2}{n}}
- 盈亏比的置信下限(对数正态近似): RR_{LB} = RR \times e^{-z \sigma}, \qquad \sigma = \sqrt{\frac{cv_{win}^2}{\text{wins}} + \frac{cv_{loss}^2}{\text{losses}}}
- 把两个下限代入凯利公式: f_{LB} = \frac{p_{LB} \times RR_{LB} - (1 - p_{LB})}{RR_{LB}}
- 交易许可:\text{edge\_gate} = (f_{LB} > 0)。按保守值算仍有正优势,才放行。
- 风险比例: \text{risk\_fraction} = \min\big(\max(0,\ f_{LB} \times \text{kelly\_fraction}),\ \text{risk\_cap}\big)
保守只靠这一个机制。我们刻意不再叠第二层(比如让凯利分数也随样本量缩小):两个保守机制叠在一起,审计时说不清各自贡献了多少。
样本 100 笔,赢 60 笔,盈亏比 1.5:
- 直接用估计值算:半凯利 = 0.167
- 用置信下限算:p_{LB} \approx 0.536,RR_{LB} \approx 1.155,半凯利 = 0.067
- 再过硬上限:最终
risk_fraction= 0.02(risk_cap生效)
从 0.167 到 0.02 这条递减链就是审计信息,报告必须把三个数都给出来。
3.3 输出结果
| 字段 | 类型 | 说明 |
|---|---|---|
edge_gate |
bool | 是否允许交易 |
risk_fraction |
float | 推荐的单笔风险比例 |
report |
object | 见下节 |
x 和 d 不是 solver 的输出,而是 policy 的输入,由 tooling 离线算好写进去(见 Section 5)。这样 core 不必碰随机数,也就不和”用蒙特卡洛选 x“冲突。
3.4 报告的要求
report 必须包含:
- 计算依据:\hat{p}、p_{LB}、RR_{LB}、估计值算出的凯利、下限算出的凯利、最终值,以及最后是哪一层约束生效(凯利分数还是
risk_cap)。 - 敏感度:p 和 RR 各变动 ±5%(相对)时,
risk_fraction会变成多少。 - 上限的代价:g(\text{risk\_fraction}) / g(f_{LB}),告诉使用者保守换走了多少理论增长。其中 g(f) = p_{LB}\ln(1 + f \cdot RR_{LB}) + (1-p_{LB})\ln(1-f)。
禁止输出”生存概率 97%“这类用估计值直接算出来的数字。所有概率性结论必须注明前提:”假设置信下限对应的参数为真”。
4 功能需求:monitor
monitor 有两道防线。第一道看钱:回撤碰到熔断线,直接停。第二道看统计:每笔结果持续累积证据,证据够了也停。两道防线彼此独立,互相不需要确认。
4.1 输入参数
基线(来自 policy,校验后冻结):
- p_0、RR_0、n_0:初始参数(净结果口径)
- \text{edge}_0 = p_0 \cdot RR_0 - (1-p_0)
- CUSUM 的参考值 k 和报警线 h(含义见下节;h 没有默认值,必须由 tooling 标定后写入 policy)
DD_hard:回撤熔断线,默认 0.30- n_{min}:宣告”正常”所需的最少样本,默认 20
运行时输入(每笔显式传入,monitor 自己不保存任何东西):
- S_{prev}:上一笔之后的累计值,第一笔传 0
- X_t:本笔的 R 倍数(Section 2.5)
- DD_{current}:当前回撤
- n:当前样本量
- \hat{p}、\hat{RR}:只用于诊断,不参与判定
函数形如 (\text{baseline}, \text{snapshot}, S_{prev}) \to (\text{state}, \text{risk\_multiplier}, S_t, \text{diagnostics})。累计值从参数进、从返回值出,monitor 本身仍是纯函数。
4.2 累积和检验(CUSUM)
CUSUM 是统计学里检测”分布是否发生了变化”的经典方法。做法:把每笔结果与一个固定参考值的差距逐笔累加。策略正常时,多数交易好于参考值,累计值贴着零;优势消失后,差距持续累积,累计值一路上升,越过报警线就报警。全程是确定的四则运算,每一步都能手工复算。
S_t = \max\big(0,\ S_{t-1} + (k - X_t^{w})\big), \qquad X_t^{w} = \min(X_t,\ 3 \times RR_0)
- 参考值 k = \text{edge}_0 / 2:取”基线优势”和”零优势”的中点。
- 报警线 h:由 tooling 模拟标定,标准是健康策略平均至少 ARL_0 笔(默认 500)才误报一次。
- 它监控的是胜率和盈亏比合在一起的总体表现(X_t 的均值就是优势本身),所以无论胜率下降、盈亏比恶化,还是两边各差一点,都反映在同一个数字里。哪怕只有单一维度恶化也能被发现,不会因为”另外两项还正常”而漏掉。
CUSUM 不是机器学习,也不是在线学习:它不修改任何参数,只是把偏差累加起来和固定的报警线比较。S_t 的完整轨迹本身就是审计记录。
4.3 决策规则
按优先级从上往下,第一条命中即返回:
| 优先级 | 条件 | 状态 | risk_multiplier |
动作 |
|---|---|---|---|---|
| 0 | 输入违反校验规则 | — | — | 抛错(见 Section 10) |
| 1 | DD_{current} \ge DD_{hard} | INVALID |
0.0 | PAUSE(回撤熔断) |
| 2 | S_t \ge h | INVALID |
0.0 | PAUSE(统计报警) |
| 3 | h/2 \le S_t < h | DEGRADED |
0.5 | REDUCE_RISK |
| 4 | n < n_{min} | INSUFFICIENT_DATA |
1.0 | HOLD,明示样本不足 |
| 5 | 其余 | OK |
1.0 | HOLD |
三个要点:
- 回撤熔断优先级最高,第 1 笔交易就生效。保护本金不需要样本量,也不接受统计信号否决。
- 统计报警不受 n_{min} 限制(表里第 2 行在第 4 行之前)。开局连亏十几笔本身就是足够的证据;n_{min} 只拦”宣告正常”,不拦”宣告失效”。
- “风险减半”是正式输出字段
risk_multiplier,引擎按risk_fraction × risk_multiplier执行,不是藏在状态表里的隐含约束。
DD_hard 默认 0.30 的依据:分数凯利下有个近似公式——净值从高点跌掉超过 1-\lambda 的终身概率约为 \lambda^{2/c-1}(c 是凯利分数)。按半凯利算,终身碰一次 30% 回撤线的概率约 34%,也就是说健康策略约有三分之一的机会经历一次”白停一次 + 人工复核”。我们接受这个代价:停错了可以恢复,该停没停就回不了头。何况实际执行被 risk_cap 压得远低于半凯利,真实的误触发率比 34% 低得多。tooling 提供按 policy 实际参数的模拟校核(?@sec-sim)。
4.4 诊断(不参与判定)
报警或降档之后,diagnostics 回答”问题出在哪个环节”,供人工复核:
- 胜率:单侧检验 \hat{p} 是否显著低于 p_0(\alpha = 0.05;样本小于 30 用精确二项检验)。
- 盈亏比:\ln \hat{RR} 的单侧置信上限是否低于 \ln RR_0。
胜率检验只看下行:胜率往上偏不是失效,不该报。
4.5 恢复规则
PAUSE 之后,monitor 不提供自动恢复。想恢复,需要人工看过诊断结果,用新的基线参数生成新版 policy:新的 p_0、RR_0、n_0,重新标定 h,累计值归零。每次恢复都留下版本记录。
5 x 和 d 怎么选(tooling)
x(上调门槛)和 d(下调门槛)由 tooling 的 x_sweep 离线联合选定,写入 policy。
先看两个参数各自控制什么:
- x 控制复利的节奏。越大,上调越少,越接近”永不加码”:安全但增长慢。越小越接近完全复利:增长快但对参数错误敏感。
- d 控制最坏时刻的相对风险。base 固定期间风险金额不变,占当前资金的比例最高为 f/d。d 越接近 1,下调越及时,相对风险越稳,但跌一点就减码,回本变慢;d 越小,风险金额在回撤里撑得越久,增长快,但最坏时刻下注越重。
两个参数都在”增长”和”安全”之间做交换,而且互相影响(下调过的 base 会改变下一次上调的位置),所以放进同一个模拟里联合选择。算法要素全部固定,照着实现,谁做结果都一样:
- 候选网格:x \in \{1.1, 1.2, \ldots, 3.0\}(步长 0.1),d \in \{0.70, 0.75, 0.80, 0.85, 0.90\},共 20 \times 5 = 100 个组合。
- 路径模拟:每个组合模拟 10,000 条路径 × 500 笔交易,按 policy 的参数和上面的双向调整规则生成净值。
- 可行条件:存活率 ≥ 95%。存活 = 整条路径的当前回撤从未碰到
DD_hard。 - 选择与并列:可行的组合里,取”终值对数的中位数”最大者。与最优值差距 1% 以内算并列;并列时先取更大的 d(最坏相对风险更稳),再取更大的 x(加码更晚)。
- 种子:随机数种子显式传入,连同数据生成方式、工具版本一起写进 policy 的
provenance(来源记录)。同一个种子,结果完全一样。
没有任何组合可行时,返回 x = \infty(永不上调)、d = 0.90(最快下调)并告警。
不跑模拟时的缺省值:d = 0.8,含义是”允许最坏相对风险膨胀到 1.25 倍”;想更紧就提高 d(一般地,允许膨胀 m 倍对应 d = 1/m)。x 没有缺省值,必须模拟产出。
模拟用什么方式生成数据(DGP)必须声明。默认:每笔独立,按 p_0 定胜负;盈利幅度服从均值 RR_0、变异系数 cv_win 的对数正态分布;亏损固定 -1。盈亏成串出现的策略(比如趋势策略),独立性假设会低估风险,应改用对真实交易记录的分块重抽样(block bootstrap)。无论用哪种,假设本身要随结果一起输出。
6 系统产出物
6.1 策略配置 (Policy)
用 JSON Schema 定义。Schema 只管字段形状;“载入后不可变”要靠实现保证(比如 frozen dataclass)。必含字段:
| 组 | 字段 |
|---|---|
| 基线 | p0, RR0, n0 |
| solver 参数 | kelly_fraction, risk_cap, confidence, risk_fraction(求解结果) |
| monitor 参数 | k, h, DD_hard, n_min |
| 再基准化 | x, d |
| engine | portfolio_cap, gap_multiplier |
| 来源记录 | sim_seed, dgp, tool_version, policy_version |
6.2 执行引擎 (Engine)
把 risk_fraction × risk_multiplier 换算成具体品种的仓位,支持现货、杠杆、期货。另有两条账户级约束:
- 持仓总量约束:所有未平仓位的风险加起来不超过
portfolio_cap(默认 0.06)。几个相关性高的头寸同时止损,是单笔风险定义覆盖不到的最大敞口。 - 跳空缓冲:容易跳空或带杠杆的品种,设置
gap_multiplierm \ge 1,单笔预算按risk_fraction/ m 执行,相当于预留 m 倍滑点的余地。
6.3 模拟工具 (Sim)
tooling 层,离线运行,种子显式传入:
x_sweep:按 Section 5 联合选定 x 和 d。calibrate_h:模拟健康策略,找出满足 ARL_0 的报警线 h。dd_check:校核DD_hard在当前 policy 参数下的误触发概率。
7 默认参数总表
| 参数 | 默认值 | 所属 | 依据 |
|---|---|---|---|
kelly_fraction |
0.5 | solver | 用约 1/4 的增长换一半波动(见 why) |
confidence |
0.90(单侧) | solver | 保守性与可用性的平衡 |
risk_cap |
0.02 | solver | 机构常规单笔风险是 0.25%–2%;0.02 落在上沿,连亏约 35 笔才腰斩 |
cv_win, cv_loss |
1.0 | solver | 没有更好证据时的保守缺省 |
| k | \text{edge}_0/2 | monitor | 基线优势与零优势的中点 |
| h | 无默认,必须标定 | monitor | 由 calibrate_h 按 ARL_0 产出 |
| ARL_0 | 500 笔 | tooling | 健康策略的平均误报间隔 |
DD_hard |
0.30 | monitor | 见 Section 4.3 |
| n_{min} | 20 | monitor | 宣告”正常”的最少样本 |
| 盈利截断 | 3 \times RR_0 | monitor | 防单笔大赚掩盖退化 |
x |
无默认,模拟产出 | policy | Section 5 |
d |
0.8(不跑模拟时的缺省) | policy | 最坏相对风险最多膨胀 1.25 倍;模拟时与 x 联合选定 |
portfolio_cap |
0.06 | engine | 持仓风险总量上限 |
gap_multiplier |
1.0 | engine | 流动性好的现货;易跳空或带杠杆的品种应大于 1 |
8 适用前提
以下四个前提是全库结论成立的条件。前提不成立,结论也不成立,所以必须写明:
- 止损能够成交。 跳空、闪崩、流动性枯竭时,实际亏损会超过
risk_fraction,杠杆进一步放大。这类风险无法消除,只能缓解:gap_multiplier预留缓冲、portfolio_cap限制总敞口、超出预期的亏损原样计入监控统计。 - 输入已扣除交易成本。 胜率、盈亏比都指扣除滑点和手续费之后的净结果。用未扣成本的数字,所有结论都会偏乐观。
- 置信下限只能应对样本不足。 回测过拟合、幸存者偏差、市场环境变化,它都应对不了。库的输出质量,上限由用户输入的质量决定——这句话要写在用户文档最显眼的位置。
- 模拟默认每笔交易相互独立。 盈亏成串出现的策略风险会被低估,应改用分块重抽样。
9 技术约束
9.1 纯函数要求
- core:同样的输入(包括显式传入的 S_{prev} 和 policy)必须得到同样的输出。不允许随机、时间依赖、外部 IO;所有状态走参数和返回值。
- tooling:同样的种子必须得到同样的输出。
9.2 禁止清单
- ❌ 在线学习:不允许在运行中自动调整 p 或 RR。
- ❌ 赌徒谬误:不允许按最近的胜负序列调整仓位。
- ❌ 逻辑蔓延:不允许加入未经数学审计的”聪明”规则。
- ❌ core 触发模拟:x、d、h 这类模拟产物只能经 policy 进入 core。
两点澄清,避免误判:CUSUM 是确定性的经典统计检验,每一步都能手工复算,不属于”在线学习”;risk_multiplier 由统计检验触发,依据的是检验结果而不是最近几笔的输赢,不属于”按胜负序列调仓”。
10 校验规则与质量标准
哪些输入直接报错、哪些输入正常返回”别交易”,分两张清单,不再含糊。
第一张:结构非法,校验期抛错。 这些值在任何语义下都不可能合法:
| 输入 | 非法条件 |
|---|---|
wins, losses, n, n0 |
负数或非整数 |
RR, RR0 |
\le 0 |
p0 |
不在 (0, 1) 开区间内(基线退化成必胜或必输,没有监控意义) |
kelly_fraction |
不在 (0, 1] 内 |
risk_cap, confidence, DD_hard, d |
超出各自声明的区间 |
| DD_{current} | 不在 [0, 1) 内 |
第二张:证据不足或优势不存在,运行期降级。 输入本身合法,结论是”别交易”或”没法判断”:
| 情形 | 行为 |
|---|---|
wins = 0 |
edge_gate = False,报告注明 |
losses = 0(盈亏比的不确定性估不出来) |
edge_gate = False,报告注明证据不足 |
| f_{LB} \le 0 | edge_gate = False |
| monitor 收到 n < n_{min} 且无报警 | INSUFFICIENT_DATA,既不宣告正常也不宣告失效 |
划分原则:构造上就非法的(类型错、范围越界)一律抛错;输入合法但证据不支持交易的,正常返回并降级。p_0 = 0 这种属于构造非法(抛错),wins = 0 这种属于证据不足(降级),两者不要混为一谈。
10.1 质量标准
- 可审计:每个输出附带完整的计算过程;CUSUM 轨迹、置信下限的推导链、哪层约束生效,都能追溯。
- 报告要求:见 Section 3.4。
- 一致性:其他文档只引用、不重复定义本文的规格;一旦出现漂移,按缺陷修复,对齐回本文。