第四章:做市策略系统架构
做市系统,说白了就是一个精密的「印钞机」——前提是你得把它搭对。
我见过太多团队,策略逻辑写得天花乱坠,结果一上线就崩。为什么?因为架构没想清楚。今天我就把做市系统的五层架构掰开揉碎了讲给你听。
4.1 整体架构概览
做市系统不是单打独斗,它需要五个层次协同工作:
- 数据层:喂给系统「眼睛」
- 策略层:负责「大脑」决策
- 执行层:充当「手脚」干活
- 风控层:扮演「安全带」
- 监控层:做好「仪表盘」
这五层缺一不可。我早期做的一个项目,就是忽略了风控层,结果一次极端行情直接爆仓。嗯,从那以后我再也不敢偷懒了。
核心原则:每一层都要独立部署、独立升级。千万别搞成「大泥球」架构。
4.2 数据层:系统的「眼睛」
数据层是所有决策的基础。你想想看,如果行情数据都拿不准,策略再牛也是白搭。
4.2.1 核心数据源
| 数据类型 | 来源 | 更新频率 | 用途 |
|---|---|---|---|
| Level 2 行情 | 交易所API | 毫秒级 | 实时定价 |
| 成交数据 | WebSocket | 实时 | 成交确认 |
| 历史数据 | 数据库 | 离线 | 回测分析 |
| 账户信息 | REST API | 秒级 | 仓位管理 |
我的经验:数据层一定要做「双路备份」。主链路用WebSocket,备用链路用REST轮询。我曾经因为WebSocket断连,整整5分钟没有收到行情更新,差点酿成大祸。
4.2.2 数据清洗与对齐
原始数据不能直接用。你需要做三件事:
- 去重:同一个tick重复推送,只保留最新
- 排序:按时间戳严格排序,乱序数据要丢弃
- 对齐:不同交易所的数据要统一时间基准
// 数据清洗示例(伪代码)
function cleanTick(rawTick) {
// 去重:检查sequence是否已处理
if (processedSequences.has(rawTick.sequence)) {
return null; // 已处理过,丢弃
}
// 排序:确保时间戳递增
if (rawTick.timestamp < lastTimestamp) {
log.warn("乱序数据,丢弃");
return null;
}
// 对齐:统一到毫秒级时间戳
rawTick.timestamp = normalizeTimestamp(rawTick.timestamp);
processedSequences.add(rawTick.sequence);
lastTimestamp = rawTick.timestamp;
return rawTick;
}
4.3 策略层:系统的「大脑」
策略层是核心中的核心。这里我建议你保持「策略引擎」和「策略逻辑」分离。
4.3.1 策略引擎架构
策略引擎负责:
- 加载策略配置
- 管理策略生命周期(启动/暂停/停止)
- 分发行情数据到各个策略实例
- 收集策略输出的报价信号
关键设计:策略引擎要支持「热加载」。也就是说,修改策略参数后不用重启系统。我见过太多团队,改个参数要停掉整个系统,这在做市场景下是致命的。
4.3.2 核心策略模块
一个典型的做市策略包含:
- 定价模型:计算合理价格区间
- 库存管理器:控制持仓风险
- 报价生成器:生成买卖报价
- 信号过滤器:过滤无效信号
// 策略层核心逻辑示例
class MarketMakingStrategy {
constructor(config) {
this.pricingModel = new PricingModel(config.pricing);
this.inventoryManager = new InventoryManager(config.inventory);
this.quoteGenerator = new QuoteGenerator(config.quote);
}
onTick(tick) {
// 1. 计算合理价格
const fairPrice = this.pricingModel.calculate(tick);
// 2. 检查库存状态
const inventorySignal = this.inventoryManager.evaluate();
// 3. 生成报价
const quotes = this.quoteGenerator.generate(fairPrice, inventorySignal);
// 4. 过滤无效报价
return this.filterQuotes(quotes);
}
}
4.4 执行层:系统的「手脚」
执行层负责把策略的报价信号变成真实的订单。这里有个坑——交易所API不是100%可靠的。
4.4.1 订单管理
执行层需要处理:
- 订单生命周期:新单→已提交→部分成交→完全成交→已撤销
- 撤单重发:超时未成交的订单要撤销并重新报价
- 订单簿同步:确保本地订单簿和交易所一致
避坑指南:我曾经遇到过一个bug——撤单请求发送成功了,但交易所返回了超时错误。结果系统以为撤单失败,又发了一次撤单。最后同一个订单被撤了两次,导致报价出现空窗期。解决方案是:给每个订单一个唯一ID,并维护一个「订单状态机」。
4.4.2 延迟优化
做市是毫秒级的竞争。执行层必须做到:
| 优化项 | 方法 | 预期效果 |
|---|---|---|
| 网络延迟 | 托管服务器、专线连接 | 降低50-80% |
| 序列化延迟 | 使用Protobuf替代JSON | 降低60-70% |
| 业务逻辑延迟 | 预计算、缓存热点数据 | 降低30-40% |
4.5 风控层:系统的「安全带」
风控层是最后一道防线。我见过太多人,策略赚钱的时候觉得风控是累赘,爆仓了才后悔没装。
4.5.1 核心风控规则
- 仓位限制:单边持仓不超过总资金的20%
- 最大亏损:单日亏损超过5%自动暂停
- 报价偏离:报价偏离市场中间价超过阈值则拦截
- 频率限制:每秒报价次数不超过10次
// 风控检查示例
function riskCheck(order) {
// 仓位检查
if (currentPosition + order.quantity > positionLimit) {
return { passed: false, reason: "仓位超限" };
}
// 报价偏离检查
if (Math.abs(order.price - midPrice) > maxSpread) {
return { passed: false, reason: "报价偏离过大" };
}
// 频率检查
if (orderCountInSecond >= maxOrderPerSecond) {
return { passed: false, reason: "报价频率超限" };
}
return { passed: true };
}
我的建议:风控规则要分「硬风控」和「软风控」。硬风控直接拦截订单,软风控只发告警。刚开始做的时候,建议全部用硬风控,等系统稳定了再逐步放开。
4.6 监控层:系统的「仪表盘」
监控层让你知道系统「活得好不好」。没有监控,你就像在开一辆没有仪表盘的车。
4.6.1 核心监控指标
| 指标类别 | 具体指标 | 告警阈值 |
|---|---|---|
| 性能指标 | 订单处理延迟、行情延迟 | >100ms告警 |
| 业务指标 | 成交率、库存周转率 | 成交率<30%告警 |
| 风控指标 | 当前仓位、当日盈亏 | 仓位>80%告警 |
| 系统指标 | CPU、内存、网络 | CPU>80%告警 |
4.6.2 告警分级
我习惯把告警分成三级:
- P0(致命):系统宕机、资金异常 → 电话通知
- P1(严重):策略暂停、风控触发 → 即时消息
- P2(警告):延迟升高、成交率下降 → 邮件通知
核心观点:监控不是为了看数据,而是为了「发现问题」。所以告警一定要 actionable——收到告警后,你知道该做什么。
4.7 五层协作流程
最后,我们看看这五层是怎么配合的:
- 数据层收到行情tick,推送给策略层
- 策略层计算报价,发送给执行层
- 执行层在发单前,先经过风控层检查
- 风控层通过后,执行层向交易所发单
- 所有环节的数据都上报给监控层
这个流程看起来简单,但每个环节都可能出问题。我建议你从第一天就把监控做起来,别等到出事了再补。
最后说一句:架构设计没有银弹。这五层架构是我在多个项目中打磨出来的,但它不一定适合所有场景。关键是要理解每一层「为什么存在」,然后根据你的实际情况做调整。
公众号:蓝海资料掘金营,微信deep3321