1. 背景
平台提供多种不同投资风格的股票买卖策略,包括短线(日内交易)、中线(2个月)。策略信号触发之后,通过多个渠道通知用户,包括邮件、手机推送、APP站内消息、IM(Telegram)等等。用户收到信号后,根据信号的买卖方向进行交易操作。经常有用户反馈,因为没留意信号或者操作不及时导致错失交易机会,希望平台提供自动交易功能,在信号触发的时候,帮助用户自动进行交易,提升交易速度,把握交易机会。因此,决定开发自动交易功能。
2. 任务
开发自动交易系统,实现策略信号触发自动买卖,确保交易的及时性、可靠性。
3. 项目难点与解决方案
3.1 难点一如何在不确定性中构建可靠系统
不确定性来源有以下几点:
|------------|--------------------------------|
| 券商连接状态不可控 | 连接随时可能因为券商风控、授权到期、用户主动终止等操作断开。 |
| 账号状态不可控 | 用户随时可以操作账号,打断系统操作。 |
| 订单状态多且不可预测 | 状态多达20+,且订单状态收到账户余额、市场流动性影响。 |
针对连接不可控场景,做了三点处理:
-
通过ws实时监控连接状态,中断之后立即周期性发送通知给用户,需要用户及时修复。
-
记录连接中断期间信号日志,待连接恢复之后,根据信号的时效性作相应的处理,对于买入信号,检查信号有效期,失效则忽略。对于卖出信号,则准备卖出。
-
如果中断超过指定时长,则终止跟单器,防止资源长期占用。
针对账户状态不可控场景,采用多重对账方式尽可能确保状态一致。
-
每次下单前和下单后都会实时获取账户最新状态,更新本地,避免基于过时数据做出决策。
-
周期性获取账户最新数据,校正本地数据。
针对订单状态多且复杂问题,处理办法是将状态收敛为三大类,非终态、成功终态、失败终态。对于非终态,周期性轮询更新状态,对于成功终态,无需处理,对于失败终态,细化失败场景,针对可重试场景增加重试操作。
针对以上不确定性问题,核心思路不是消除不确定性,而是构建容错的系统。使得即便出现了问题,系统也能够正常运行。
3.2 难点二如何避免信号丢失、超买或者超卖
- 信号可靠性有两层保证,一层是kafka消费侧手动ack,处理失败投入本地队列重试。
另一层是异步对账,定时从信号源拉取数据进行比对,检查信号源数量和本地已处理数量是否一致,不一致则同步过来。
- 避免超买,超买可能的场景是买入信号触发后,下单失败,失败原因可能是超时或者网络异常,实际执行成功,如果重试则出现超买。还有可能是信号重复处理。针对这些异常,做了两层处理,一个是根据信号ID做幂等处理,防止信号重复消费。
另一个是下单前先记录下单日志,如果发现该信号ID对应处理日志已存在则忽略。
- 避免超卖,超卖可能的场景包括用户提前清仓,此时卖出会变成做空。针对这种场景,每次下单前会读取账户最新数据进行校正,防止误操作。
整体设计方案是事件驱动+状态机管理+容错+全链路监控,如图1
图1
4. 结果
自2026年3月上线以来,数据如下,系统整体运行稳定,可靠,订单成功率高,所有失败下单均源于用户主动干预导致,比如用户提前下发止损单导致无法继续买入。
|--------|--------|
| 指标 | 表现 |
| 总跟单器创建 | 200+ |
| 总成交金额 | 1.5M+ |
| 日均成交额 | 20K+ |
| 累计订单量 | 3K+ |
| 日均订单量 | 50+ |
| 最大跟单金额 | 125K |
| 平均跟单金额 | 6K |
| 订单成功率 | 99.77% |
5. 收获
最大的收获是面对一个不确定性系统,该如何处理,答案就是构建一个容忍不确定性的系统。这里也就是<<DDIA>>所说的,"目标不是消除不确定性,而是构造能够容忍这些不确定性的可靠抽象"。
对于金融交易系统,交易结果本身容易受到多方面影响,链路不清晰且强依赖外部券商接口。面对这种场景,我选取的解决方案包括以下几点:
-
容错,针对各种异常场景,要能够容忍,必要的情况下降级处理。
-
对账,由于强依赖外部,自身状态不可靠,需要实时/定时对账确保数据一致性。
-
监控,针对关键链路,监控、告警、链路跟踪必不可少,提前发现、快速感知、定位。