20. 程序化交易系统架构:分层架构设计
做基差交易这几年,我踩过最大的坑,就是系统架构没想清楚就上手写代码。
一开始觉得,不就是拿个价差数据,算个阈值,然后下单嘛。结果呢?数据源断了不知道,策略跑飞了没察觉,交易所一卡顿,仓位全乱套。
后来我学乖了。一个靠谱的基差交易系统,必须分层。每一层各司其职,出了问题也能快速定位。说白了,就是「高内聚,低耦合」那套老话,但在实盘里,这六个字值真金白银。
20.1 数据层:地基要稳
数据层是整个系统的眼睛。基差交易对数据质量要求极高——你想想看,价差差一个tick,可能就是一笔亏损。
我个人习惯把数据层拆成三个子模块:
- 行情接入模块:对接交易所、数据商。支持多路冗余,比如同时接CTP和易盛,主路断了自动切备用。
- 数据清洗模块:去重、补全、时间戳对齐。我遇到过某数据商偶尔发重复tick,不清洗的话策略会反复开仓。
- 数据存储模块:实时数据写内存数据库(比如Redis),历史数据落盘到时序数据库(比如InfluxDB)。
这里有个避坑指南:时间戳一定要用交易所时间,别用本地时间。我曾经因为本地时钟漂移,导致回测和实盘对不上,查了三天才找到原因。
20.2 策略层:核心大脑
策略层是真正干活的地方。它从数据层拿清洗好的行情,算出基差,判断套利机会,然后生成信号。
我习惯把策略层设计成「策略引擎 + 策略实例」的模式:
- 策略引擎:负责调度、生命周期管理、风险控制。比如统一检查账户权益、持仓限制。
- 策略实例:每个具体的基差策略(比如跨期套利、期现套利)都是一个独立实例。互不干扰,可以单独启停。
举个例子,一个简单的跨期基差策略核心逻辑:
class BasisStrategy:
def __init__(self, symbol_pair, threshold):
self.symbol1 = symbol_pair[0] # 近月合约
self.symbol2 = symbol_pair[1] # 远月合约
self.threshold = threshold # 开仓阈值
def on_tick(self, tick1, tick2):
# 计算基差
basis = tick1.last_price - tick2.last_price
# 判断套利机会
if basis > self.threshold:
# 基差过大,做空基差:卖近买远
return Signal('SELL', self.symbol1, 'BUY', self.symbol2)
elif basis < -self.threshold:
# 基差过小,做多基差:买近卖远
return Signal('BUY', self.symbol1, 'SELL', self.symbol2)
else:
return None
嗯,这里要注意:阈值不能是固定值。市场波动率在变,基差的统计分布也在变。我一般用滚动窗口的动态阈值,比如过去20个tick的均值加减两倍标准差。
20.3 执行层:最后一公里
执行层负责把策略信号变成真实订单。这一层最容易被忽视,但恰恰是亏损的重灾区。
执行层要处理几个关键问题:
- 订单管理:维护订单状态机(已发送、部分成交、全部成交、已撤销)。
- 成交反馈:监听交易所的成交回报,更新持仓。
- 异常处理:撤单失败、网络断开、交易所拒绝等。
- 滑点控制:基差交易的滑点往往比单腿交易更致命。我习惯用「被动挂单 + 主动吃单」混合策略。
举个例子,执行层收到一个「卖近买远」的信号后,流程是这样的:
def execute_signal(signal):
# 1. 先挂被动单
order1 = place_limit_order(signal.symbol1, signal.side1, price=best_ask)
order2 = place_limit_order(signal.symbol2, signal.side2, price=best_bid)
# 2. 等待成交,设置超时
wait_for_fill(order1, order2, timeout=500) # 500ms
# 3. 如果有一腿没成交,撤单重发
if not order1.filled:
cancel_order(order1)
order1 = place_market_order(signal.symbol1, signal.side1)
if not order2.filled:
cancel_order(order2)
order2 = place_market_order(signal.symbol2, signal.side2)
我曾经因为没处理「一腿成交、一腿没成交」的情况,导致单边持仓过夜,第二天跳空亏了十几个点。从那以后,我强制要求执行层必须做「腿平衡」检查。
20.4 低延迟通信:别让速度拖后腿
基差交易对延迟敏感。价差机会可能只存在几十毫秒,晚一步就没了。
我常用的低延迟手段:
- 进程间通信用共享内存:数据层到策略层,策略层到执行层,都用mmap或共享内存队列。别用TCP socket,太慢。
- 避免锁竞争:用无锁队列(比如Disruptor模式)。Python里可以用
multiprocessing.Queue的底层换成共享内存。 - 网络层面:和交易所同机房托管,用FPGA网卡加速。如果条件有限,至少保证服务器和交易所之间的物理距离最短。
20.5 系统稳定性保障:不出事比赚钱更重要
做量化交易,活得久比赚得快重要。我见过太多系统跑着跑着就崩了,然后一夜回到解放前。
稳定性保障的几个要点:
| 保障措施 | 具体做法 | 我踩过的坑 |
|---|---|---|
| 冗余设计 | 数据源双路、网络双线、电源双路 | 主数据商宕机,备用没配好,裸奔了半小时 |
| 监控告警 | 延迟监控、持仓监控、资金监控 | 策略停了三天没发现,因为监控只报了warning没报error |
| 熔断机制 | 单日亏损超阈值自动平仓 | 有一次没设熔断,一天亏了两个月利润 |
| 日志审计 | 所有操作写日志,支持事后回放 | 出问题查不到原因,因为没有日志 |
我个人习惯在系统里加一个「心跳线程」,每秒检查各层是否正常。如果连续三次心跳丢失,自动发短信报警,同时启动应急平仓流程。
下面这张图是我自己画的分层架构示意图,你可以参考一下整体结构:
这张图里,三层之间用共享内存通信,每层内部又细分了子模块。你实际搭建的时候,可以根据自己的交易品种和频率做调整。但记住一点:层与层之间的接口要稳定。接口一变,上下游全得改,那代价就大了。