3. 信号路由:将信号分发至正确的交易执行系统

信号路由,说白了就是解决一个问题:策略产生的交易信号,到底该发给谁?

我刚开始做量化的时候,以为这事儿很简单——信号来了,直接往交易所一丢不就完了?后来发现,现实远比想象复杂。你可能有多个策略、多个账户、多个交易所,甚至还有不同的执行算法。信号发错了地方,轻则交易失败,重则造成巨大亏损。

嗯,今天我们就来聊聊这个环节。它不像策略开发那么「性感」,但绝对是整个闭环里最不能出错的一环。

3.1 信号路由的核心职责

信号路由系统,本质上是一个智能分发器。它接收来自策略层的原始信号,然后根据预设规则,把信号转发到正确的执行系统。

它的核心职责有三点:

  • 信号解析:把策略输出的信号(比如「买入100股AAPL」)解析成标准格式
  • 路由决策:根据信号属性(品种、账户、市场等)决定发往哪个执行系统
  • 信号转发:将解析后的信号以正确的协议发送到目标系统

我在项目中遇到过最典型的场景:一个CTA策略同时交易期货和股票,期货信号要发到CTP柜台,股票信号要发到XTP柜台。如果路由逻辑写错了,期货信号跑到股票柜台,那结果就是废单满天飞。

3.2 路由规则的设计

路由规则怎么设计?我个人习惯用「路由表」的方式。说白了就是一张配置表,告诉系统:什么样的信号,走哪条路。

信号属性 条件 目标执行系统
品种类型 期货 CTP柜台
品种类型 股票 XTP柜台
账户ID account_01 主交易系统
账户ID account_02 备用交易系统
信号紧急度 快速执行通道
信号紧急度 普通执行通道

你想想看,如果不用这种配置化的方式,而是把路由逻辑硬编码在代码里,每次新增一个账户或交易所,都得改代码、重新部署。那运维成本就太高了。

3.3 路由系统的架构设计

一个典型的信号路由系统,我习惯把它分成三层:

  • 接入层:接收来自各个策略的信号,做格式校验和标准化
  • 路由层:根据路由表做决策,确定目标执行系统
  • 转发层:将信号转换为目标系统能识别的协议,并发送出去

下面这张图,是我自己项目里用过的路由架构,你可以参考一下:

策略A 策略B 策略C 接入层:信号解析与标准化 路由层:路由表决策 转发层:协议转换与发送 信号流

这张图展示的是最基础的路由架构。实际项目中,我还会在路由层加一个「信号缓存」模块。为什么?因为有时候目标执行系统可能暂时不可用(比如网络抖动),信号不能丢,得先缓存起来,等系统恢复后再重发。

3.4 信号路由的代码实现

光说不练假把式。我写一个简单的路由引擎核心代码,你感受一下:

class SignalRouter:
    def __init__(self, route_table):
        self.route_table = route_table  # 路由表
        self.executors = {}  # 执行系统实例

    def register_executor(self, name, executor):
        self.executors[name] = executor

    def route(self, signal):
        # 1. 解析信号
        parsed = self._parse(signal)

        # 2. 匹配路由规则
        target = self._match_rule(parsed)

        if not target:
            raise Exception(f"未找到匹配的路由规则: {parsed}")

        # 3. 转发信号
        executor = self.executors.get(target)
        if not executor:
            raise Exception(f"执行系统未注册: {target}")

        return executor.execute(parsed)

    def _parse(self, signal):
        # 标准化信号格式
        return {
            'symbol': signal.get('symbol'),
            'action': signal.get('action'),
            'quantity': signal.get('quantity'),
            'account': signal.get('account'),
            'type': signal.get('type')
        }

    def _match_rule(self, parsed):
        for rule in self.route_table:
            if all(parsed.get(k) == v for k, v in rule['conditions'].items()):
                return rule['target']
        return None

这段代码看起来简单,但核心逻辑都在里面了。你想想看,路由表是配置化的,新增一条规则只需要往表里加一条记录,完全不需要改代码。

核心要点:路由引擎的设计原则是「配置驱动,代码稳定」。路由表可以频繁变更,但路由引擎的代码尽量不改。

3.5 避坑指南

我曾经踩过一个坑,说出来你可能不信——路由规则写成了「如果品种是期货,则发往CTP」,结果忘了考虑股指期货。股指期货也是期货,但它需要发到中金所的专用通道。那次事故导致一批股指期货信号发到了商品期货柜台,全部废单。

从那以后,我养成了一个习惯:路由规则一定要做「全量覆盖测试」。把所有可能的信号属性组合都列出来,确保每一条都有对应的路由规则。

警告:路由规则不要有「默认兜底」逻辑。我曾经见过有人写「如果都不匹配,就发到主交易系统」,结果新策略的信号因为配置遗漏,全部发到了主交易系统,差点把主系统的订单通道打爆。

3.6 信号路由的监控

路由系统跑起来之后,怎么知道它工作正常?我个人习惯加三个监控指标:

  • 路由延迟:从信号到达路由层到转发出去的时间,超过阈值就报警
  • 路由成功率:成功转发的信号占比,低于99.9%就要排查
  • 路由分布:各个执行系统的信号量分布,如果某个系统突然没信号了,可能是路由规则出了问题

小技巧:我习惯在路由层打日志时,把原始信号和路由结果一起记录下来。这样排查问题的时候,可以回放整个路由过程,快速定位是哪个环节出了岔子。

嗯,信号路由就讲这么多。说白了,它就是一个「聪明」的快递分拣系统——知道每个包裹该送到哪个站点。但关键在于,这个「知道」的过程,必须严谨、可配置、可监控。否则,再好的策略,信号送不到正确的地方,也是白搭。


专注资料整理