3. 信号路由:将信号分发至正确的交易执行系统
信号路由,说白了就是解决一个问题:策略产生的交易信号,到底该发给谁?
我刚开始做量化的时候,以为这事儿很简单——信号来了,直接往交易所一丢不就完了?后来发现,现实远比想象复杂。你可能有多个策略、多个账户、多个交易所,甚至还有不同的执行算法。信号发错了地方,轻则交易失败,重则造成巨大亏损。
嗯,今天我们就来聊聊这个环节。它不像策略开发那么「性感」,但绝对是整个闭环里最不能出错的一环。
3.1 信号路由的核心职责
信号路由系统,本质上是一个智能分发器。它接收来自策略层的原始信号,然后根据预设规则,把信号转发到正确的执行系统。
它的核心职责有三点:
- 信号解析:把策略输出的信号(比如「买入100股AAPL」)解析成标准格式
- 路由决策:根据信号属性(品种、账户、市场等)决定发往哪个执行系统
- 信号转发:将解析后的信号以正确的协议发送到目标系统
我在项目中遇到过最典型的场景:一个CTA策略同时交易期货和股票,期货信号要发到CTP柜台,股票信号要发到XTP柜台。如果路由逻辑写错了,期货信号跑到股票柜台,那结果就是废单满天飞。
3.2 路由规则的设计
路由规则怎么设计?我个人习惯用「路由表」的方式。说白了就是一张配置表,告诉系统:什么样的信号,走哪条路。
| 信号属性 | 条件 | 目标执行系统 |
|---|---|---|
| 品种类型 | 期货 | CTP柜台 |
| 品种类型 | 股票 | XTP柜台 |
| 账户ID | account_01 | 主交易系统 |
| 账户ID | account_02 | 备用交易系统 |
| 信号紧急度 | 高 | 快速执行通道 |
| 信号紧急度 | 低 | 普通执行通道 |
你想想看,如果不用这种配置化的方式,而是把路由逻辑硬编码在代码里,每次新增一个账户或交易所,都得改代码、重新部署。那运维成本就太高了。
3.3 路由系统的架构设计
一个典型的信号路由系统,我习惯把它分成三层:
- 接入层:接收来自各个策略的信号,做格式校验和标准化
- 路由层:根据路由表做决策,确定目标执行系统
- 转发层:将信号转换为目标系统能识别的协议,并发送出去
下面这张图,是我自己项目里用过的路由架构,你可以参考一下:
这张图展示的是最基础的路由架构。实际项目中,我还会在路由层加一个「信号缓存」模块。为什么?因为有时候目标执行系统可能暂时不可用(比如网络抖动),信号不能丢,得先缓存起来,等系统恢复后再重发。
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%就要排查
- 路由分布:各个执行系统的信号量分布,如果某个系统突然没信号了,可能是路由规则出了问题
小技巧:我习惯在路由层打日志时,把原始信号和路由结果一起记录下来。这样排查问题的时候,可以回放整个路由过程,快速定位是哪个环节出了岔子。
嗯,信号路由就讲这么多。说白了,它就是一个「聪明」的快递分拣系统——知道每个包裹该送到哪个站点。但关键在于,这个「知道」的过程,必须严谨、可配置、可监控。否则,再好的策略,信号送不到正确的地方,也是白搭。