自动交易系统开发经验分享

1. 背景

平台提供多种不同投资风格的股票买卖策略,包括短线(日内交易)、中线(2个月)。策略信号触发之后,通过多个渠道通知用户,包括邮件、手机推送、APP站内消息、IM(Telegram)等等。用户收到信号后,根据信号的买卖方向进行交易操作。经常有用户反馈,因为没留意信号或者操作不及时导致错失交易机会,希望平台提供自动交易功能,在信号触发的时候,帮助用户自动进行交易,提升交易速度,把握交易机会。因此,决定开发自动交易功能。

2. 任务

开发自动交易系统,实现策略信号触发自动买卖,确保交易的及时性、可靠性。

3. 项目难点与解决方案

3.1 难点一如何在不确定性中构建可靠系统

不确定性来源有以下几点:

|------------|--------------------------------|
| 券商连接状态不可控 | 连接随时可能因为券商风控、授权到期、用户主动终止等操作断开。 |
| 账号状态不可控 | 用户随时可以操作账号,打断系统操作。 |
| 订单状态多且不可预测 | 状态多达20+,且订单状态收到账户余额、市场流动性影响。 |

针对连接不可控场景,做了三点处理:

  1. 通过ws实时监控连接状态,中断之后立即周期性发送通知给用户,需要用户及时修复。

  2. 记录连接中断期间信号日志,待连接恢复之后,根据信号的时效性作相应的处理,对于买入信号,检查信号有效期,失效则忽略。对于卖出信号,则准备卖出。

  3. 如果中断超过指定时长,则终止跟单器,防止资源长期占用。

针对账户状态不可控场景,采用多重对账方式尽可能确保状态一致。

  1. 每次下单前和下单后都会实时获取账户最新状态,更新本地,避免基于过时数据做出决策。

  2. 周期性获取账户最新数据,校正本地数据。

针对订单状态多且复杂问题,处理办法是将状态收敛为三大类,非终态、成功终态、失败终态。对于非终态,周期性轮询更新状态,对于成功终态,无需处理,对于失败终态,细化失败场景,针对可重试场景增加重试操作。

针对以上不确定性问题,核心思路不是消除不确定性,而是构建容错的系统。使得即便出现了问题,系统也能够正常运行。

3.2 难点二如何避免信号丢失、超买或者超卖

  1. 信号可靠性有两层保证,一层是kafka消费侧手动ack,处理失败投入本地队列重试。

另一层是异步对账,定时从信号源拉取数据进行比对,检查信号源数量和本地已处理数量是否一致,不一致则同步过来。

  1. 避免超买,超买可能的场景是买入信号触发后,下单失败,失败原因可能是超时或者网络异常,实际执行成功,如果重试则出现超买。还有可能是信号重复处理。针对这些异常,做了两层处理,一个是根据信号ID做幂等处理,防止信号重复消费。

另一个是下单前先记录下单日志,如果发现该信号ID对应处理日志已存在则忽略。

  1. 避免超卖,超卖可能的场景包括用户提前清仓,此时卖出会变成做空。针对这种场景,每次下单前会读取账户最新数据进行校正,防止误操作。

整体设计方案是事件驱动+状态机管理+容错+全链路监控,如图1
图1

4. 结果

自2026年3月上线以来,数据如下,系统整体运行稳定,可靠,订单成功率高,所有失败下单均源于用户主动干预导致,比如用户提前下发止损单导致无法继续买入。

|--------|--------|
| 指标 | 表现 |
| 总跟单器创建 | 200+ |
| 总成交金额 | 1.5M+ | | 日均成交额 | 20K+ |
| 累计订单量 | 3K+ |
| 日均订单量 | 50+ |
| 最大跟单金额 | 125K | | 平均跟单金额 | 6K |
| 订单成功率 | 99.77% |

5. 收获

最大的收获是面对一个不确定性系统,该如何处理,答案就是构建一个容忍不确定性的系统。这里也就是<<DDIA>>所说的,"目标不是消除不确定性,而是构造能够容忍这些不确定性的可靠抽象"。

对于金融交易系统,交易结果本身容易受到多方面影响,链路不清晰且强依赖外部券商接口。面对这种场景,我选取的解决方案包括以下几点:

  1. 容错,针对各种异常场景,要能够容忍,必要的情况下降级处理。

  2. 对账,由于强依赖外部,自身状态不可靠,需要实时/定时对账确保数据一致性。

  3. 监控,针对关键链路,监控、告警、链路跟踪必不可少,提前发现、快速感知、定位。