第三章:规则引擎设计——让策略真正“活”起来

说实话,很多做策略生成的人,最后都卡在同一个地方:规则写了一大堆,跑起来却互相打架。你想想看,一个策略系统里可能有几百条规则,有的说“买入”,有的说“卖出”,有的说“观望”……到底听谁的?

这一章,我们就来解决这个问题。我会从规则的定义讲起,再到优先级、冲突检测,最后聊聊Rete算法——那个让规则引擎飞起来的核心机制。

3.1 规则的定义与优先级

规则是什么?说白了就是“如果……那么……”的逻辑。我习惯把规则拆成三部分:

  • 条件(Condition):触发规则的前提,比如“股价大于10元”
  • 动作(Action):条件满足时执行的操作,比如“买入100股”
  • 优先级(Priority):当多条规则同时满足时,谁先执行

先看一个简单的规则定义示例:

// 规则定义示例(伪代码)
Rule r1 = new Rule("股价突破买入")
    .when("stock.price > 10.0")
    .then("buy(100)")
    .priority(5);

Rule r2 = new Rule("大盘风险规避")
    .when("market.index < 3000")
    .then("cancelAllOrders()")
    .priority(10);  // 优先级更高

这里有个细节:优先级数值越大,越先执行。我在项目中遇到过,有人把优先级搞反了,结果风险规避规则永远跑在买入规则后面,差点酿成大错。嗯,这个坑我替你们踩过了。

我的建议:优先级最好用1-100的整数,留出足够的扩展空间。别用0和负数,容易跟默认值混淆。

3.2 规则冲突检测与消解

冲突,说白了就是两条规则“对着干”。比如规则A说“买入”,规则B说“卖出”,同时满足条件时,系统就懵了。

我总结了几种常见的冲突类型:

冲突类型 描述 我的处理方式
动作冲突 两条规则的动作互斥(买 vs 卖) 按优先级裁决,高优先级胜出
条件重叠 两条规则的条件有交集 细化条件,增加互斥因子
循环依赖 规则A触发规则B,规则B又触发规则A 设置最大执行深度,防止死循环

冲突消解的策略,我个人比较推荐这几种:

  • 优先级消解:谁优先级高听谁的。最简单,也最常用。
  • 最近优先消解:后定义的规则覆盖先定义的。适合动态调整的场景。
  • 特定性消解:条件更具体的规则优先。比如“股价大于10且小于20”比“股价大于10”更具体。
我曾经踩过的坑:有一次我用了“最近优先”策略,结果线上规则被一个实习生误改了顺序,导致所有交易策略都乱了。后来我强制要求:所有规则必须显式声明优先级,不允许依赖隐式顺序。

3.3 规则引擎架构与Rete算法简介

聊到规则引擎,就绕不开Rete算法。这名字听起来挺唬人,其实核心思想很简单:别重复计算

传统的做法是:每次有新数据进来,就把所有规则的条件重新算一遍。数据少还行,数据一多,性能就崩了。Rete算法的聪明之处在于:它把规则的条件拆成一个个小节点,然后缓存中间结果。只有真正变化的部分才重新计算。

下面这张图是我画的Rete网络简化结构:

Rete网络简化结构图 工作内存(事实) 规则库(规则) Alpha网络(条件匹配) Beta网络(连接匹配) 冲突集(待执行规则) 执行引擎 工作内存:存放当前所有事实(数据) Alpha网络:筛选单个条件 Beta网络:组合多个条件 冲突集:存放所有满足条件的规则

Rete算法的核心流程,我用大白话解释一下:

  1. 事实进入工作内存:比如新的股价数据来了
  2. Alpha网络过滤:每个条件节点只检查自己负责的那部分。比如“股价>10”这个节点,只关心股价,不关心成交量
  3. Beta网络连接:把多个条件组合起来。比如“股价>10且成交量>100万”
  4. 冲突集生成:所有满足条件的规则进入冲突集
  5. 执行引擎裁决:按优先级、消解策略,选出一条规则执行

为什么Rete快?

因为它只计算变化的部分。比如你有100条规则,每次只有1条规则的条件变了,Rete只重新计算那1条,而不是全部100条。这在策略生成场景下,性能提升是数量级的。

我记得有一次,一个金融客户有5000条交易规则,用传统方式跑一次要3秒。换成Rete引擎后,降到50毫秒。客户当场就愣住了。

实现建议:如果从零开始写Rete,工作量不小。我建议先用开源的Drools或EasyRules,等理解了核心思想再自己定制。别一开始就造轮子,容易翻车。

最后说一句:规则引擎的设计,本质上是在“灵活性”和“性能”之间找平衡。规则定义得越灵活,冲突检测就越复杂。我个人习惯是:先定死规则结构,再逐步放开灵活性。这样系统不容易失控。


专注资料整理