可解释性:为什么没信号、为什么没下单(Blocking Reasons 体系)

很多量化系统的“可解释性”停留在:图上叠几条指标线、给你看一下买卖点。
但真实团队最常遇到的问题不是“为什么在这里买”,而是更致命的两句:

  • 为什么没信号?
  • 为什么没下单/没成交?

如果每次都靠翻日志、靠作者经验猜,团队协作会迅速变慢,策略交付也会变成不可规模化的“手艺活”。
结论先行:EasyQuant 把“没发生”这件事做成产品能力——用结构化原因码(Blocking Reasons)把问题归类、可统计、可视化,并给出下一步排查入口。


一、为什么“没发生”比“发生了”更难解释?

当策略产生一次买卖点,系统至少有结果可以讨论。
但当“什么都没发生”,背后可能是任意一层出了问题:

  • Universe 没解析出来(无标的可交易)
  • K 线缺失、时间戳异常、被交易时段过滤(无有效 bars)
  • 调度没跑、或者同一根 bar 被去重跳过(重复评估被跳过)
  • 指标/DSL 引擎异常(计算失败或规则错误)
  • 有信号但被风控拒绝(额度/仓位/数据质量)
  • 下单被通道拒绝(参数校验/不可交易状态)

如果这些问题都混在一坨日志里,你就无法形成“团队共识的排障路径”。
因此,交付型平台必须回答:问题属于哪一类?下一步该查哪里?


二、Blocking Reasons 的设计目标:把排障变成流程

EasyQuant 的 Blocking Reasons 体系设计目标很直接:

  • 结构化:每次阻塞给出可枚举的 eventCode(原因码),而不是一句自由文本
  • 可聚合:能按 24h/7d 统计 Top 原因,发现“系统性问题”而非个案
  • 可定位:每个原因码都能指向下一步检查(页面/API/数据表)
  • 可复用:同一套原因码既服务回测,也服务 paper,也服务准生产演进

这意味着:排障不再依赖“这个策略作者在不在”,而是任何人都能按 checklist 快速收敛问题范围。


三、原因码分层:从“现象”到“归因”

下面是一个推荐的分层理解方式(你不必一次性记全):

1)标的层:NO_SYMBOLS

含义:无可交易标的(Universe 为空/未绑定/被过滤)。
典型现象:策略运行事件里没有任何 code,或系统诊断直接提示无标的。

下一步怎么查

  • 策略是否绑定 Universe?Universe 是否有成分?
  • 筛选条件(交易时段/市场/黑白名单)是否把标的过滤掉?

2)数据层:NO_BARS / DATA_QUALITY_BLOCKED

含义:缺少 K 线数据,或数据质量不足(不新鲜、异常时间戳、缺字段)。
典型现象:回测无成交;策略长期 HOLD;bars 接口在区间内返回为空。

下一步怎么查

  • 是否能查询到最近 bars(例如 GET /api/market/bars?...&limit=10
  • 是否需要 backfill 或 seed demo 数据
  • 交易时段过滤后是否仍有数据(非交易时段 bars 会被过滤)

3)调度与去重:DUP_EVAL_SKIPPED

含义:同一根 bar 重复触发,被去重跳过(避免重复下单)。
典型现象:你看到策略“在跑”,但没有任何新评估/新订单。

下一步怎么查

  • 是否真的产生了新 bars(ts 是否变化)
  • 当前策略的执行状态是否卡在某个 lastEvaluatedBarTs

4)版本与配置:VERSION_NOT_ACTIVE

含义:策略版本未激活(你改了草稿,但执行侧仍在用旧版本/模板)。
典型现象:策略行为不符合你刚改的规则;回测结果像“没生效”。

下一步怎么查

  • 是否发布并激活了版本
  • 回测/运行快照(provenance)中记录的 versionId 是否符合预期

5)信号层:SIGNAL_NONE

含义:规则未触发(不是错误,只是当前行情不满足条件)。
典型现象:数据正常、调度正常、无风控/通道拒绝,但就是没成交。

下一步怎么查

  • 用策略图表回看触发点(买卖点标记)
  • 检查指标参数(周期过长导致信号稀少、阈值过严)

6)引擎层:ENGINE_ERROR

含义:信号引擎异常(DSL/指标/运行时错误)。
典型现象:策略运行事件出现错误码或异常栈;指标计算失败。

下一步怎么查

  • 查看运行事件(错误上下文)
  • 缩小到最小 DSL,逐步恢复组件定位问题

7)风控/通道:RISK_REJECTED / BROKER_REJECTED

含义:有信号但被风控拒绝,或被通道拒绝。
典型现象:有下单意图但订单 REJECTED / 未成交。

下一步怎么查

  • 风控命中明细(额度、仓位、数据质量等)
  • 通道校验失败原因(价格/状态/参数)

四、如何在 UI 上使用它:先看 Top 原因,再看明细

建议团队形成统一的“排障顺序”(省时间):

  1. 系统是否健康(健康检查/服务状态)
  2. 系统状态页:阻塞原因 Top(24h)
    • 如果某个原因长期 Top1/Top2,说明是系统性问题(例如缺 bars、Universe 配置问题)
  3. 策略中心:运行事件列表
    • 找到具体策略的 eventCode 与上下文
  4. 回测中心:回测详情页
    • 看是否交易了多个标的、bars 是否正常、买卖点是否符合预期

这条路径的关键是:先归类,再下钻。不要一上来就翻全量日志。


五、为什么这件事能带来“交付价值”?

Blocking Reasons 体系本质上是在把“经验”变成“产品能力”:

  • 让排障流程化:新人也能按 checklist 定位问题
  • 让问题可统计:你能看到系统最常卡在哪里,从而指导平台优先级
  • 让沟通有共识:讨论从“我感觉”变成“原因码 + 证据链”
  • 为准生产演进铺路:风控拒绝/通道拒绝等,本来就需要可解释与可审计

当一套系统能回答“为什么没发生”,它才有资格承担团队的日常运行。


六、你可以立刻做的一个验证

找一个“没成交”的回测或 paper 运行,按下面顺序走一遍:

  1. 看系统状态页的阻塞原因 Top(24h),确认原因码与中文说明
  2. 打开该策略的运行事件,确认 eventCode 是否一致
  3. 用最小化步骤复现一次(相同时间窗、相同版本)

如果这三步能让你在 5–10 分钟内把问题收敛到“标的/数据/调度/风控/通道/引擎”之一,说明这套可解释体系是有效的。


结语:可解释性不是“讲得通”,是“查得快”

量化系统的可解释性,最终衡量标准不是“能不能讲一个漂亮故事”,
而是:当策略没信号、没下单时,你能否在最短时间内定位问题类别、找到证据、形成团队共识。

下一篇(04)我们会讲“可复现性”:为什么一次回测必须有 provenance(输入快照),以及它如何支撑评审、回归与审计。