第十六章:实盘交易系统

实盘交易系统,说白了就是你的策略从回测走向真金白银的最后一公里。我见过太多人,回测曲线漂亮得不行,一上实盘就崩。为什么?因为实盘要考虑的东西,回测里根本碰不到。

这一章,我们聊聊系统架构、低延迟、FIX协议、订单管理和风控。嗯,都是硬骨头,但啃下来,你就能真正把策略跑起来。

系统架构设计:别把鸡蛋放一个篮子里

我个人习惯把交易系统拆成三层:

  • 策略层:负责生成信号,不碰交易细节
  • 执行层:负责把信号变成订单,管理FIX连接
  • 风控层:独立于策略,专门做检查

为什么要拆?我在项目中遇到过一件事:策略层有个bug,循环里忘了加sleep,瞬间发了上千个订单。幸好风控层独立运行,直接截断了。要是耦合在一起,那天就爆仓了。

核心原则:策略、执行、风控必须解耦。任何一个模块挂了,不能影响其他模块。

架构上,我建议用事件驱动。每个模块只关心自己订阅的事件。比如策略层发布“买入信号”事件,执行层收到后去下单。这样改一个模块,其他模块不用动。

低延迟技术:每一微秒都很重要

做高频交易的朋友,对延迟特别敏感。但就算你不是高频,低延迟也有好处——减少滑点,提高成交率。

我常用的几个技巧:

  • 避免锁竞争:用无锁队列(比如Disruptor)传递数据。Python里可以用queue.Queue,但性能不够时,我换过multiprocessing.Queue,延迟降了一半。
  • 内存预分配:别在交易循环里动态创建对象。提前分配好,复用。
  • 减少系统调用:网络IO、磁盘IO能少就少。日志可以异步写,别阻塞主流程。

避坑指南:我曾经为了追求低延迟,把所有日志都关了。结果出问题查不了原因。后来我改成“关键日志同步写,普通日志异步写”。平衡很重要。

还有一个容易被忽略的点:CPU亲和性。把交易进程绑定到特定CPU核心,避免上下文切换。Linux下用taskset命令就能搞定。

FIX协议:交易界的通用语言

FIX协议,说白了就是交易所和你的系统之间怎么说话。它定义了一堆标签(Tag),比如55是股票代码,44是价格,38是数量。

一个简单的FIX消息长这样:

8=FIX.4.2|9=78|35=D|49=CLIENT|56=BROKER|34=1|52=20250320-10:00:00|55=600519|44=150.00|38=100|10=123|

解释一下:

  • 35=D:表示这是一条新订单(New Order Single)
  • 55=600519:股票代码,贵州茅台
  • 44=150.00:价格
  • 38=100:数量
  • 10=123:校验和

我建议用现成的FIX引擎,比如quickfix(Python有绑定)。自己手写解析?别折腾,容易出错。

注意:FIX协议版本很多,FIX.4.2、FIX.4.4、FIX.5.0……不同交易所支持的版本不一样。对接前一定要确认好。

订单管理:别让订单“飞”了

订单管理,核心就三件事:

  • 订单状态跟踪:新订单、部分成交、全部成交、已撤销……每个状态都要记录。
  • 订单生命周期:从生成到成交,中间可能经历多次修改、部分成交。
  • 异常处理:订单超时、被拒、连接断开……怎么办?

我习惯用一个订单状态机来管理。每个订单就是一个状态机实例,事件驱动它流转。

举个例子:

class Order:
    def __init__(self, order_id, symbol, side, price, qty):
        self.order_id = order_id
        self.symbol = symbol
        self.side = side
        self.price = price
        self.qty = qty
        self.filled_qty = 0
        self.status = 'NEW'  # 初始状态

    def on_fill(self, fill_qty, fill_price):
        self.filled_qty += fill_qty
        if self.filled_qty >= self.qty:
            self.status = 'FILLED'
        else:
            self.status = 'PARTIALLY_FILLED'
        # 记录成交明细
        self.fills.append({'qty': fill_qty, 'price': fill_price})

嗯,代码很简单,但实际中要考虑并发。多个线程同时修改订单状态?加锁或者用原子操作。

风控模块:最后的防线

风控模块,我把它放在执行层前面。所有订单必须先过风控,才能发出去。

常见的风控规则:

规则 说明 示例
最大持仓限制 单品种持仓不能超过某个值 茅台最多1000股
最大订单频率 每秒最多发N个订单 每秒不超过10笔
最大亏损限制 当日亏损超过阈值,停止交易 亏损超过5%自动平仓
价格偏离检查 订单价格不能偏离市场太远 不能超过最新价的±2%

我的经验:风控规则要分层。第一层是硬性规则(比如最大持仓),直接拒绝订单。第二层是软性规则(比如频率限制),可以报警但不阻断。这样既安全又灵活。

还有一个容易被忽视的点:风控模块本身也要监控。如果风控模块挂了,所有订单都发不出去。我一般会加一个“心跳检测”,风控模块每秒钟发一个心跳,如果超过3秒没收到,自动切换到备用风控。

系统架构图

下面这张图,展示了我常用的实盘系统架构。你想想看,数据从行情进来,到策略生成信号,再到执行和风控,最后到交易所,每一步都很清晰。

实盘交易系统架构图 行情数据 策略层 风控层 执行层 交易所 行情推送 信号 通过/拒绝 FIX订单 成交回报(异步) 注:虚线表示异步反馈,实线表示同步流程

这张图里,行情数据先喂给策略层,策略生成信号后发给风控层。风控层检查通过,才交给执行层。执行层通过FIX协议把订单发给交易所。成交回报走虚线路径,异步更新行情和策略状态。

一个小建议:刚开始做实盘,别追求极致的低延迟。先把功能跑通,再慢慢优化。我见过有人花三个月优化了10微秒,结果策略本身就有问题,白忙活。

好了,这一章的内容就这些。实盘交易系统是个大工程,但拆开来看,每一块都不难。关键是理解它们怎么配合,以及每个模块的边界在哪里。