3. 规则引擎基础:规则定义语言(DSL)、规则解析流程、规则匹配算法(Rete/前缀树)

在高频行情灰度发布场景中,规则引擎是连接业务策略与行情数据的核心执行层。它负责将“当某只股票在 1 秒内涨幅超过 3% 且成交量大于 100 手时,触发灰度切换”这类业务描述,转化为计算机可执行的逻辑。本节将深入剖析规则引擎的三个基础支柱:规则定义语言(DSL)、规则解析流程以及规则匹配算法(Rete / 前缀树)。

3.1 规则定义语言(DSL)

DSL(Domain Specific Language)是面向特定领域的声明式语言。在行情规则中,DSL 的设计目标是让策略分析师无需编写通用编程语言(如 C++/Java),即可定义复杂的行情过滤与匹配逻辑。

3.1.1 DSL 语法设计原则

  • 声明式:用户只描述“要什么”,不描述“怎么做”。
  • 可读性:接近自然语言,降低学习成本。
  • 类型安全:字段类型(价格、成交量、时间戳)在语法层面即被约束。
  • 可扩展:支持自定义函数(如移动平均、波动率计算)。

3.1.2 典型 DSL 语法示例

以下是一个针对高频行情灰度发布的 DSL 规则片段:

RULE "HighVolSurge"
WHEN
    symbol IN ("600519.SH", "000858.SZ")
    AND price_change_pct > 2.5
    AND volume > 500000
    AND time BETWEEN "09:30:00" AND "11:30:00"
THEN
    ACTION "gray_switch" WITH priority = 8
END

3.1.3 DSL 核心元素对比

元素 说明 示例
RULE 规则标识,支持命名与元数据 RULE "VolCheck"
WHEN 条件表达式,支持逻辑组合 price > 10 AND volume < 1000
THEN 动作定义,触发灰度操作 ACTION "gray_switch"
字段引用 直接引用行情数据字段 bid_price[0], ask_volume[5]
内置函数 预置的统计与时间函数 MA(close, 5), VWAP()
实践建议:DSL 的字段引用应直接映射到行情数据的内存结构(如 L1/L2 快照),避免在解析阶段进行二次数据查找,以降低延迟。

3.2 规则解析流程

规则解析是将 DSL 文本转换为引擎可执行的内部表示(如 AST、网络结构)的过程。在高频场景下,解析流程必须在规则加载阶段完成,运行时不再进行语法分析。

3.2.1 解析流程步骤

  1. 词法分析(Lexer):将 DSL 文本拆分为 Token 流(关键字、标识符、操作符、字面量)。
  2. 语法分析(Parser):根据 DSL 文法构建抽象语法树(AST),验证结构合法性。
  3. 语义分析(Semantic Analyzer):类型检查、字段存在性校验、函数签名匹配。
  4. 中间表示生成(IR Generation):将 AST 转换为规则引擎内部的条件网络(如 Rete 网络的 Alpha/Beta 节点)。
  5. 优化与编译(Optimization & Compilation):常量折叠、条件重排、JIT 编译(可选)。

3.2.2 解析流程架构图

规则解析流程架构 DSL 文本 词法分析 语法分析 语义分析 IR 生成 优化与编译 可执行规则网络
性能关键点:在高频场景下,解析流程仅在规则部署时执行一次。运行时引擎直接使用优化后的规则网络,避免任何字符串处理或 AST 遍历。

3.3 规则匹配算法

规则匹配算法决定了引擎在接收到行情事件时,如何高效地找到所有满足条件的规则。本节重点介绍两种在金融领域广泛应用的算法:Rete 算法和前缀树(Trie)。

3.3.1 Rete 算法

Rete 是一种基于有向无环图的增量式规则匹配算法。它通过构建条件网络(Alpha 网络)和连接网络(Beta 网络),缓存中间匹配结果,避免在每次事件到来时重新计算所有条件。

Rete 网络核心组件
组件 功能 示例
Alpha 节点 对单一事实(Fact)进行原子条件测试 price > 10, volume < 1000
Beta 节点 连接两个条件,执行 Join/Not 操作 左输入:Alpha1 结果,右输入:Alpha2 结果
终端节点 当所有条件满足时,触发规则动作 执行 ACTION "gray_switch"
工作内存 存储当前所有活跃的事实与中间结果 行情快照、Alpha 内存、Beta 内存
Rete 匹配流程
  1. 行情事件(Fact)进入 Alpha 网络,依次通过各 Alpha 节点进行条件过滤。
  2. 通过 Alpha 节点的事实被传递到对应的 Beta 节点,与左侧输入进行 Join。
  3. Beta 节点维护部分匹配结果(Partial Match),避免重复计算。
  4. 当 Beta 网络中的路径全部满足时,终端节点被激活,执行规则动作。
注意:Rete 算法在规则数量较多(>1000)且条件共享度高时优势明显。但在高频行情场景下,需注意 Beta 节点内存占用和 Join 操作的延迟,必要时可引入时间窗口约束来限制匹配范围。

3.3.2 前缀树(Trie)

前缀树是一种基于字符串公共前缀的树形数据结构。在行情规则中,常用于对 symbol(股票代码) 进行高效匹配,特别是当规则按股票代码前缀分组时(如所有以 "600" 开头的股票应用同一组规则)。

前缀树匹配特点
  • 查找时间复杂度 O(k):k 为股票代码长度,与规则数量无关。
  • 空间共享:公共前缀只存储一次,适合大量以行业或板块代码开头的规则。
  • 支持通配符:可在节点上挂载通配符规则(如 "600***" 匹配所有沪市主板股票)。
前缀树在规则引擎中的典型应用
// 伪代码:使用前缀树匹配股票代码
TrieNode root = new TrieNode();
root.insert("600519", ruleSet1);
root.insert("600000", ruleSet2);
root.insert("000858", ruleSet3);

// 行情到达时
List<Rule> matchedRules = root.search("600519.SH");
// 返回 ruleSet1 中的规则

3.3.3 Rete 与前缀树对比

维度 Rete 算法 前缀树(Trie)
适用场景 多条件组合、规则间共享条件 单字段(如 symbol)前缀匹配
匹配效率 增量匹配,事件复杂度 O(条件数) O(k),k 为匹配键长度
内存占用 较高(需缓存中间结果) 较低(共享前缀)
动态更新 支持增量添加/删除规则 支持,但需重建部分路径
典型组合 价格、成交量、时间等多条件 股票代码、交易所代码

3.3.4 混合匹配策略

在实际的高频行情灰度发布系统中,通常采用 Rete + 前缀树 的混合架构:

  • 第一层(前缀树):根据行情事件的 symbol 快速定位到可能匹配的规则子集。
  • 第二层(Rete 网络):在子集内执行多条件组合匹配,输出最终触发的规则。
混合匹配策略架构 行情事件 前缀树(Symbol) Rete 网络 触发规则 混合匹配优势 1. 前缀树快速过滤:将匹配范围从全局规则集缩小到与 symbol 相关的子集。 2. Rete 精确匹配:在子集内进行多条件组合,避免无效的 Join 操作。
灰度发布实战提示:在规则灰度发布期间,混合架构允许我们仅对特定 symbol 前缀(如灰度测试组)的规则子集进行 Rete 网络的热更新,而不影响全局规则匹配,从而实现零停机规则切换。