用事件推送接收成交回报的程式,普遍藏着一个危险的问题:订单在交易所侧早已成交完成,程式却一无所知,直到几小时后对账查询才对上账。相比连不上这种会报错的故障,「连接看起来正常但消息没到齐」更危险,因为它安静。要理解为什么安静,先看交易所私有事件流的结构:程式先通过接口申请一个带有效期的会话凭证,再用长连接订阅账户与订单频道,服务端在事件发生时把下单、成交、撤单等消息推过来,中间穿插心跳维持会话。
第一个漏点在连接的生命周期。长连接普遍有服务端侧的最大存活时长与闲置断开策略,会话凭证同样会到期;不少平台会在心跳时自动续凭证,却不保证替你重连已经断掉的连接。程式如果只建立一次订阅就默认它永远活着,网络抖动之后到自我察觉断开之前,存在一段窗口:断连期间的事件不会在重连后补发,需要程式自己用查询接口把差额拉回来。没有补拉逻辑的订阅,等于默许任何一次闪断都能吞掉若干回报。
第二个漏点在消息的顺序与完整性。一笔订单的事件会被拆成多条消息:提交、部分成交、再次部分成交、终态,各带订单号与序号。如果程式只按「状态等于已成交」的消息更新缓存,会漏掉部分成交的账;如果按消息到达顺序直接覆盖状态,晚到的旧消息可能覆盖新状态。稳的做法是以订单号为主键、按序号合并事件做状态机,乱序和重复都变成幂等操作,而不是事故。
第三个漏点在订阅范围。同一条连接上常并列多个频道,账户余额、订单回报、仓位更新可能各自是独立的订阅地址或独立凭证域;订了现货的频道却以为合约的成交也会推过来,是对账日才会爆掉的错误。同样常见的是把推送消息与查询接口返回当同一套编号体系使用——两边经常是两套编号,对账只能锚定在交易所内部的订单号上,而不是消息到达次序。
可靠的自建做法是推送加查询双通道:推送负责快,定期用查询接口拉未完成订单与近期成交负责全,查询窗口比预期的最大事件延迟略宽,漏掉的事件在下一轮自然被补齐。低频程式甚至可以只用查询,代价是延迟与接口配额。无论哪种,程式的状态应当允许「暂时未知」这个中间态,而不是一断推送就假设没发生。
值得加监控的失败信号有几种:申请会话凭证失败但进程没有退出;连接活着、只收心跳、没有任何业务事件——多半是订阅参数写错却没人报错;推送事件里的交易所时间戳与查询结果的时间持续拉大,说明推送链路的延迟在漂移。
最后一层提醒:事件流解决的是「多快知道」,不提供任何执行层面的保证。订单成不成交、按什么价格成交,和推送链路的好坏没有任何关系,不要把回报的及时误读为市场的可靠。
心跳机制本身也值得一层理解。事件流通常要求在固定间隔内回复服务端的心跳消息,超时不回,服务端会主动断开会话;程式若把心跳处理放在容易被业务逻辑阻塞的线程里,高峰期就会出现「明明在收发数据却被判定闲置」的假死断开。另一种常见配置是允许程式不发心跳、由服务端单向下发保活帧,这种情况下断连的判断完全依赖本地对连接状态的感知,而传输层半开连接——一侧已断、另一侧仍以为相连——恰恰是本地最难即时察觉的状态,只有靠读超时的主动判断才能暴露。这也是双通道方案在工程上更稳的原因之一:定时查询本身就是一次强制的连接健康检查。
风险提示:本文仅讨论行情与事件推送的机制与工程处理,不构成投资建议。事件流协议、凭证有效期、频道划分与序号定义因平台与版本而异,请以目标平台官方接口文档为准。
发表评论
还没有评论,来说两句吧。
评论区为展示样式,提交不会被处理。