3. 量化实盘服务架构

量化实盘服务不是“把回测代码搬到服务器上跑”这么简单。回测里一次buy()调用只是账本上的一行记录,而实盘中它意味着:行情可能延迟、订单可能被拒、持仓可能与预期不符、网络可能中断、程序可能崩溃。因此,一个可上线的量化实盘服务,必须把行情接入、策略引擎、风控模块、订单执行、监控告警五个部分拆成职责清晰的模块,并用统一的数据契约把它们串起来。本节先建立整体架构认知,后续章节再逐个深入。

一、核心原理:五模块的职责边界

这五个模块的关系可以概括为“一条主链路 + 两条旁路”。主链路是:行情接入 → 策略引擎 → 风控模块 → 订单执行。两条旁路分别是:监控告警贯穿全链路采集指标,风控模块同时旁路监听行情与持仓状态。理解这个结构,比记住模块名字更重要。

  • 行情接入:负责把交易所或数据商的原始行情(WebSocket/TCP/FIX)转换为内部统一的Tick或Bar结构,处理断线重连、心跳、时间戳对齐、去重与乱序。
  • 策略引擎:消费标准化行情,按策略逻辑计算目标仓位或信号,输出“意图订单”(Intent Order),而不是直接下单。
  • 风控模块:对意图订单做前置校验——资金、持仓、单笔限额、频率、价格偏离、黑名单等,通过后才生成可执行订单。
  • 订单执行:负责报单、撤单、查询、成交回报处理,维护本地订单状态机,并与交易所状态对账。
  • 监控告警:采集延迟、吞吐、PnL、持仓、订单成功率等指标,异常时通过钉钉、邮件、电话等方式通知。

关键设计原则是单向数据流 + 状态可重建。策略引擎不直接触碰交易接口,风控不修改策略逻辑,执行层不判断策略对错。每个模块只做自己的事,通过消息队列或内存总线传递结构化事件。这样当出现问题时,可以快速定位是行情、策略、风控还是执行层的问题。

量化实盘服务五模块数据流 行情接入 WS/TCP/FIX 策略引擎 信号/意图订单 风控模块 限额/频率/持仓 订单执行 报单/撤单/回报 监控告警(贯穿全链路) 延迟 / PnL / 持仓 / 订单成功率 / 异常事件 状态存储 持仓 / 订单 / 成交 / 资金快照 Redis + PostgreSQL 消息总线 行情事件 / 信号事件 / 订单事件 Redis Stream / NATS / Kafka

二、方法流程:从行情到成交的完整链路

下面以一段简化的伪代码展示主链路的事件流转。真实系统中每个环节都会异步化,但数据契约保持一致。

# 1. 行情接入层:标准化 Tick
tick = {
    "symbol": "BTCUSDT",
    "price": 64250.5,
    "volume": 0.12,
    "ts_exchange": 1718000000123,
    "ts_local": 1718000000135
}
bus.publish("market.tick", tick)

# 2. 策略引擎:消费行情,输出意图订单
def on_tick(tick):
    signal = strategy.compute(tick)
    if signal.action != "HOLD":
        intent = {
            "symbol": tick.symbol,
            "side": signal.action,
            "qty": signal.qty,
            "price": tick.price,
            "strategy_id": "ma_cross_01"
        }
        bus.publish("strategy.intent", intent)

# 3. 风控模块:前置校验
def on_intent(intent):
    if not risk.check(intent):
        log.warning("risk rejected: %s", intent)
        return
    order = build_order(intent)
    bus.publish("order.new", order)

# 4. 订单执行:报单并维护状态机
def on_order(order):
    resp = exchange.place_order(order)
    order.status = resp.status
    store.save(order)
    bus.publish("order.update", order)

这条链路中有三个容易被忽视的细节:第一,时间戳必须双记录,交易所时间和本地时间都要保留,否则无法计算端到端延迟;第二,意图订单与执行订单要分离,风控可能修改数量或拒绝,策略层不应感知执行细节;第三,所有事件必须可持久化,崩溃后能从消息总线或数据库中重建状态。

三、案例场景:一次行情断线引发的连锁反应

某实盘服务在运行中遇到交易所 WebSocket 断线 8 秒。由于行情接入层没有做重连后的快照对齐,策略引擎在恢复后收到了一批乱序 Tick,误判为趋势反转,连续发出 5 笔买入意图。风控模块只检查了单笔限额,没有检查“短时间内同方向累计仓位”,全部放行。订单执行层正常报单,最终持仓超出策略上限 3 倍。监控告警只在 PnL 回撤超过阈值时才触发,此时已经亏损。

复盘后做了三处改进:行情接入层增加断线重连后的快照同步与乱序丢弃;风控模块增加时间窗口内的累计仓位与频率限制;监控告警增加行情延迟与订单频率的实时指标。这个案例说明,五个模块不是孤立存在的,任何一个模块的薄弱都会让整条链路失效。

常见误区:把风控逻辑写在策略引擎里,或者让策略直接调用交易所 API。这样做的后果是:策略代码与交易接口耦合,无法独立测试风控规则;一旦策略出错,可能绕过所有限制直接下单。正确做法是风控作为独立模块,策略只能输出意图订单。

四、模块职责与关键指标清单

下表总结了五个模块的核心职责、关键输出和必须监控的指标,可作为架构设计时的检查清单。

模块 核心职责 关键输出 必须监控的指标
行情接入 连接交易所、标准化数据、断线重连 标准化 Tick/Bar 事件 行情延迟、丢包率、重连次数
策略引擎 计算信号、生成意图订单 意图订单事件 信号频率、计算耗时、异常次数
风控模块 前置校验、限额、频率、持仓检查 通过/拒绝的订单 拒绝率、触发规则分布、累计仓位
订单执行 报单、撤单、成交回报、状态对账 订单状态更新事件 报单成功率、成交延迟、状态不一致数
监控告警 采集指标、异常检测、通知 告警事件、仪表盘 告警响应时间、误报率、漏报率
设计建议:在项目初期,可以用 Redis Stream 作为消息总线,PostgreSQL 存储订单与持仓快照,Prometheus + Grafana 做指标采集与展示。这套组合部署简单、生态成熟,适合 Docker 化部署。等业务量增长后,再考虑替换为 Kafka 或 NATS。

最后强调一点:架构设计的目标不是“模块越多越好”,而是每个模块的失败边界清晰、可观测、可恢复。行情断了,策略不应收到脏数据;风控拒了,策略应能收到明确原因;执行层状态不一致,监控应能立即发现。把这五块拼成一个整体,量化实盘服务才具备长期稳定运行的基础。