9.2 Choice 结果消费与 fail-open


9.2 Choice 结果消费与 fail-open

本节摘要:拿到 typed Choice 之后的工作是消费它:approve 什么时候照单放行、reduce 的系数怎么落到订单、veto 与弃权怎么区分处理——本节给一张消费策略表和一段骨架代码。然后是本章最重要的设计问题:决策服务不可用时怎么办。QuantDinger 的默认答案是 fail-open——放行不卡死,让交易主干在决策服务故障时继续运转;本节解释这个选择的可用性逻辑,同时划出应当改为 fail-closed 的场景清单(大额、新策略首日、极端行情),并给出延迟预算的分配方法。一切的前提是一个认知:门是增强件,不是依赖件——它应该让系统更好,而不是让系统多一种死法。

学习目标

  • 设计 Choice 的消费策略:置信度阈值、弃权处理、缩量落地。
  • 说出 fail-open 的可用性逻辑与它的代价。
  • 判断哪些场景应改用 fail-closed,并配置分层策略。
  • 为下单链路制定决策门的延迟预算。

一、消费 typed Choice:三种姿态

Choice 附带置信度信息(Jev 的输出特征,详见《Jev 决策编程》第 03 章《三种问题类型》),消费策略就是"在什么置信度下执行什么动作":

Choice 置信度姿态 消费动作 说明
approve 置信度高于阈值 照单放行 低置信度的同意可按弃权处理(见下)
approve 置信度低于阈值 按弃权处理 宁可不用门,不要门硬给意见
veto 置信度高于阈值 拦截并记录 理由字段入决策日志
veto 置信度低于阈值 拦截但标记为弱否决 观察期单独统计弱否决的事后对错
reduce 任意可信置信度 按系数缩量后发出 系数设下限,防止缩到零星碎单

骨架代码(示意逻辑,接口字段以官方文档为准):

# consume_choice.py —— Choice 消费骨架(示意逻辑) from dataclasses import dataclass APPROVE_MIN_CONF = 0.7 # 同意所需的最低置信度(示意值) REDUCE_FLOOR = 0.25 # 缩量系数下限,低于则并入否决(示意值) @dataclass class Choice: decision: str # approve / veto / reduce confidence: float # 置信度 scale: float # 仅 reduce 时有效 def consume(choice: Choice, order_qty: float) -> float: """返回最终下单数量;0 表示本单不发""" if choice.decision == "approve": return order_qty if choice.confidence >= APPROVE_MIN_CONF else order_qty * 0.5 if choice.decision == "reduce": factor = max(choice.scale, REDUCE_FLOOR) return order_qty * factor if choice.confidence >= APPROVE_MIN_CONF else 0.0 return 0.0 # veto:拦截 ​

两个设计细节。其一,代码里"低置信度的同意"折半放行而不是照单全收:对拿不准的意见,折中使用比二选一更稳。其二,缩量系数设下限:把一张单缩到最小步长以下,产出的碎单只贡献手续费与对账复杂度——低于下限就该干脆否决。这些数值全是示意值,正确的取值来自你影子期(9.1 节)的分布统计,而不是抄任何人的默认值。

二、fail-open:为什么默认放行

fail-open 的定义:决策服务超时、报错、不可达时,决策门当作"通过"处理,订单照发。QuantDinger 采用这一默认设计(官方文档口径),逻辑如下:

fail-open 的可用性权衡 决策服务故障时: fail-open:订单继续发出 ──▶ 系统退化为"无门的交易系统" fail-closed:订单全部拦截 ──▶ 系统退化为"停止交易的系统" 隐含判断:对已通过第06~08章全链路验证的策略, "无门运行"的风险低于"停摆运行"的风险 ​

这段话的要点是"隐含判断"四个字:fail-open 不是说门不重要,而是说在故障瞬间,两个坏选项里选了伤害较小的那个。它的代价真实存在——故障窗口内的订单失去了语境审查,所以故障本身必须被发现:fail-open 生效时要触发告警(第 11 章)并记录故障窗口,事后把窗口内的订单拉出来人工复盘。fail-open 加告警,才是完整的默认策略;只抄 fail-open 不配告警,是把容错做成隐瞒。

故障窗口的复盘清单(告警触发后照单执行):

步骤 动作 产出
1 从告警记录确定窗口起止时间 故障窗口时间线
2 拉出窗口内发出的全部订单 订单清单与各自的 Context 快照
3 逐单补做一次语境审查(人工或重放) 哪些单在有门时可能被拦或被缩
4 复盘故障根因与恢复耗时 事件记录,供第 11 章指标复盘
5 按第 3、4 步结论调整分层配置 是否把该场景升级为 fail-closed

第 3 步是这套流程的灵魂:fail-open 放行的单不能放任不管,事后补审把"故障期间失去的审查"补回来一部分;更重要的是,它是分层配置的数据来源——哪些场景在故障时真的出过事、哪些从没出过,靠一次次的窗口复盘积累,第 5 步的调整才会越来越有据,而不是拍脑袋把阈值越调越紧。

三、什么场景应改为 fail-closed

fail-open 的隐含判断对"已充分验证的策略"成立,以下场景它不成立,应改为 fail-closed(订单拦截):

场景 为什么不该 fail-open 配置建议
单笔金额大 一单失控的代价高于停摆代价 按单笔规模分层:超阈值即 fail-closed
新策略首日 "已验证"前提不成立 新接入策略默认 fail-closed
极端行情 正是门存在理由的时段 波动指标越限联动切换
决策门近期高否决率 门在拼命拦,说明语境恶化 连续多单否决后自动升级

实践形态是分层配置:默认 fail-open,按单笔规模、策略接入时长、市场状态三层条件局部升级为 fail-closed(配置能力以官方文档为准)。这比全局 fail-closed 可用,比全局 fail-open 安全。

一句话记住分界:fail-open 赌的是"门坏了没关系",fail-closed 赌的是"门拦住的一切都该拦"——你的系统该在哪一段下什么注,取决于那段订单的失配成本。

四、延迟预算:门不能吃掉信号价值

决策门在下单路径上,它的耗时直接叠加进 7.2 节的信号滑出。预算方法:先测量无门链路的下单延迟分布,再给门分配一个不超过总预算某个比例的份额(示意做法:十分之一到五分之一),超时即按 fail 策略处理:

环节 预算份额(示意) 说明
信号到订单构造 大头留给原有链路 决策门不该是延迟主因
决策门调用(含 Context 组装) 约一成到两成总预算 timeout_ms 据此设定
超时处理 触发 fail 策略 fail-open 放行+告警,或分层升级

两个配套动作。其一,timeout_ms 一旦设定就进监控:门的 P99 耗时逼近超时值之前就要告警,而不是等超时雪崩。其二,影子期同时测延迟分布,把 9.1 节"影子模式跑一至两周"的观察项加上延迟这一条——分布不稳的引擎配置(例如过大的上下文字段)先修再上线。

五、常见问题与排查

问题 排查方向 要点
影子期数据怎么变成阈值 统计各 choice 的置信度分布 阈值取分布的稳定分界,不抄默认值
timeout_ms 设多少合适 无门链路延迟分布加预算份额 见本节预算表;P99 逼近超时前告警
fail-open 窗口内的单要不要撤 逐单补审结果 有据地留或撤,不搞一刀切全撤
弱否决统计有什么用 观察期单独统计事后对错 弱否决的正确率是门校准的输入
缩量后订单仍被风控拦 缩量系数与最小下单量关系 下限要高于最小步长,防碎单
分层条件越加越多记不住 分层逻辑成文 写进配置注释与值班手册,评审变更

这张表的第二行常被反过来做:先设一个"看起来合理"的超时值,再让链路去适应它。正确方向是无门链路的实测分布在前、预算份额在后——超时值是推导出来的,不是设定的。第六行则是分层策略的长期隐患:四类 fail-closed 场景叠加三层条件之后,没人能凭记忆说清"这笔单此刻走的是哪条 fail 路径",所以分层逻辑必须成文并随配置一起评审,排查时才能按图索骥。

本节要点回顾

  • 消费策略三姿态:高置信执行、低置信折半或弃权、缩量设下限防碎单;数值来自自己的影子期统计。
  • fail-open 是"两个坏选项里选伤害小的":必须配告警与故障窗口复盘,否则容错变隐瞒。
  • 大额、新策略、极端行情、高否决率四类场景应 fail-closed;分层配置比全局二选一更合理。
  • 门只占延迟预算的小头,超时即走 fail 策略,P99 逼近超时前就要告警。

门装好了、消费策略定了、故障预案有了,最后一个问题是:门后面的引擎用谁的。下一节把选项摆开——默认的 Jev 托管最省事,协议同构的 Kev 与 Laya 可以自托管换来自主与数据不出域,选型表与切换验证清单都在那里。


作者与出处
原作者: 灏天文库
整理: 灏天文库整理
本站整理收录,版权归原作者/开源协议所有;欢迎通过原文链接访问源仓库。
发布者: 作者: 灏天文库 转发
评论区 (0)
U