一套实盘量化系统的工程架构
这不是一篇教学文章,也不是 Demo 展示。下面这套系统真实运行在实盘环境中:每天定时生成信号,经一条完整链路落进券商账户。写它,是因为”从零到实盘”这段路上踩过的坑、做过的权衡,值得被记录。
系统全景
整个系统由五个层次和一个横切面组成:
1 | |
关键设计决策
每一个决策背后都是真实教训,不是教科书上的标准答案。
1. 信号是”意图”,不是”指令”
策略端只发送”希望持有某个标的多少比例”的目标状态,消费端拿到后自行查价、查仓、计算实际买卖量(多退少补)。为什么?策略端无法感知实盘账户的真实状态——有没有停牌、有没有除权、资金够不够。把”算量”下放到执行端,信号端就能保持纯粹,不会被账户状态污染。
2. 架构性单向:只出站,永不入站
策略平台出于安全设计,入站流量是被拦截的(黑洞)。这意味着策略端永远收不到任何回执——没有 ACK,没有确认。一开始这是让我们很头痛的限制,后来反而变成了设计原则:发送端 fire-and-forget(发完即走),所有确认、登记、对账全部落在接收端与执行端。单向模型让整条链路简单到没有”等待回执”这种死锁的可能。
3. 幂等与去重
同一笔信号不能重复执行两次。消费端对所有已处理的信号做持久化登记,重复到达的(例如网络重发)直接丢弃。另一个容易踩的坑是”入金去重”:同一天的资金入账可能触发多笔补仓信号,如果不去重,会重复买入。
4. 队列持久化 + 丢信号回退
消费端用阻塞弹出(BRPOP)消费队列。极端情况下(消费端被强制重启)信号可能已经弹出但未处理完,这时需要把未终态的信号重新推回队列。终态登记(成功/失败/跳过)是幂等的基础——只有到达终态的信号才允许被安全丢弃。
5. 状态机与熔断
系统运行中会碰到”今天不适合交易”的种种情况:极端行情、系统异常、账户异常。一个显式的状态机(正常 / 暂停 / 恢复 / 止损名单)把”要不要交易”这个判断从代码逻辑里抽离出来,变成可人工干预的开关。止损后的标的进入名单,防止反弹时被误买回。
6. 可观测性是第一需求
交易系统的日志不是给自己看的,是给”出事之后凌晨两点爬起来的人”看的。账本记录每一笔决策与成交,日报定时推送账户状态与链路健康度,健康检查暴露每个环节的延迟与积压。宁可多打十行日志,不可少打一行。
踩坑实录
开发环境污染实盘队列。 回测与模拟共用同一队列,一度让实盘收到测试信号。教训:环境隔离不是”应该做”,是”必须做”。
平台热重载杀进程。 代码编辑器保存即触发重载,进程被杀,信号丢失。教训:交易系统必须手动重启,任何自动重载都是隐患。
交易 SDK 的调用顺序。 初始化、连接、订阅账户,三步缺一不可,缺任何一步所有交易接口静默失败或阻塞。这种”文档不会写、报错不会报”的坑,只能靠断点排查。
Windows 中文编码。 系统默认 GBK,写入文件的中文 JSON 全部乱码。此后所有文件 I/O 显式指定 UTF-8。
过度设计的教训。 早期给消费端加了离线标记、自动重连、轮询巡检一大堆”保护”功能,后来发现全是多余的——它们制造的复杂度比解决的问题多。复杂功能先在脑子里跑三遍再写代码,能用简单方案解决的问题,复杂方案就是一种负债。
结尾
这套系统教会我的最重要一件事:量化系统的价值不在于策略有多聪明,而在于整个链路有多可靠。 策略负责方向,工程负责活着。只要系统不停机、不重复、不丢单、不裸奔,长期站在牌桌上,就已经赢过了大多数对手。
—— 松鸦,2026 年 8 月