自动化交易系统最容易被低估的部分,不是"发出一笔下单请求",而是确认这笔订单最终发生了什么。
一次 HTTP 请求成功,可能只代表交易所已接收到请求;订单仍可能处于待成交、部分成交、完全成交或已撤销等状态。网络超时、重复提交、断线重连和批量请求部分失败,都会让"下单成功"变成一个需要持续确认的工程问题。
本文以 WEEX 现货 API 为例,梳理自动化订单管理的完整路径:创建订单、撤销订单、查询订单和成交明细,并用 REST 与 WebSocket 共同构建订单状态闭环。
一、自动化交易不是"一次请求",而是一条订单生命周期
一个可靠的订单管理流程,可以拆分为五个阶段:
生成业务订单 → 提交下单请求 → 收到受理结果(orderId / clientOrderId)→ 持续确认订单状态与成交数据 → 完成、撤销或异常恢复
其中最重要的原则是:下单接口返回成功,不等于订单已经成交;撤单请求返回成功,也不应替代后续状态核验。
在现货 API 中,单笔下单接口为 POST /api/v3/order。成功返回会带有系统生成的 orderId、客户端订单号 clientOrderId 和订单被接受的时间戳 transactTime。这些字段说明订单请求已被接收,应立即写入自建系统的订单记录中。
二、接入前:先把密钥和订单号设计好
私有交易接口需要 API Key、签名、时间戳和 Access Passphrase。密钥应只存在于受控的服务端环境中,绝不能放入网页前端、移动端应用、截图或公开代码仓库。
在订单设计上,还应为每笔业务订单生成唯一的 newClientOrderId。它不是可有可无的字段,而是处理网络异常与重复提交的重要基础。
例如:spot-btcusdt-20260721-000001
一个好的客户端订单号通常应具备:
• 全局唯一;
• 可追溯到你的业务请求;
• 不包含用户隐私或密钥信息;
• 在重试时保持不变,而不是每重试一次就重新生成。
官方文档说明:如果当前账户已经存在相同 newClientOrderId 的委托,接口会返回成功,但不会重复创建订单。也就是说,合理使用客户端订单号可以帮助系统降低重复下单风险。
三、提交订单:先明确订单类型和时效策略
限价单的 timeInForce 支持:
- GTC:订单持续有效,直至成交或撤销;
- IOC:立即成交可成交部分,剩余部分取消;
- FOK:必须立即全部成交,否则全部取消。
下面是一笔限价单的请求体示例。价格和数量仅用于展示字段格式,不代表任何交易建议:
|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| Plain Text { "symbol": "BTCUSDT", "side": "BUY", "type": "LIMIT", "timeInForce": "GTC", "quantity": "0.01", "price": "68900", "newClientOrderId": "spot-btcusdt-20260721-000001" } |
私有请求的签名需要按官方规则生成。WEEX 现货 API 使用时间戳、请求方法、接口路径、查询参数和请求体等内容构造待签名字符串,再使用 HMAC SHA256 与 Base64 编码得到签名。时间戳单位为毫秒,超出允许的服务端时间窗口可能被拒绝。
四、收到订单 ID 后,应该做什么?
假设下单接口返回:
|---------------------------------------------------------------------------------------------------------------------------------------------------|
| Plain Text { "symbol": "BTCUSDT", "orderId": 702345678901234567, "clientOrderId": "spot-btcusdt-20260721-000001", "transactTime": 1764506000456 } |
此时不要直接把业务订单标记为"已成交"。更准确的内部状态应该是:
|-----------------------------|
| Plain Text 已提交 / 已受理,等待状态确认 |
建议自建系统至少保存以下字段:
这样,无论订单后续完全成交、部分成交、撤销或因网络问题进入待核验状态,系统都有明确的追踪依据。
五、订单状态闭环:WebSocket 为主,REST 为补充
仅依赖 REST 轮询订单状态,通常会带来额外延迟和请求压力;只依赖 WebSocket 推送,也可能受到断线或本地消息处理失败的影响。
更稳妥的做法是:REST 下单 → 私有 WebSocket 订阅订单状态 → REST 查询订单详情 / 当前挂单 / 成交明细 →
本地订单记录对账与修复
WEEX 私有 WebSocket 的订单频道会推送当前账户的订单生命周期变动。连接私有频道后,可订阅:
|-----------------------------------------------------------------------|
| Plain Text { "method": "SUBSCRIBE", "params": "orders", "id": 1 } |
订单推送中可包含订单 ID、客户端订单号、订单状态、累计成交量、累计成交额、最新成交价和更新时间等信息。这样,订单系统便能在状态变化时更新本地记录,而不必高频轮询所有订单。
六、如何理解 "NEW"、"FILLED"、"CANCELED"?
订单状态不是单一结果,而是一条持续变化的链路。常见状态可以这样理解:
订单详情接口 GET /api/v3/order 可按 orderId 或 origClientOrderId 查询。返回中包括:
- origQty:原始委托数量;
- executedQty:累计已成交数量;
- cummulativeQuoteQty:累计成交额;
- status:订单状态;
- isWorking:是否仍为活动委托;
- updateTime:最近更新时间。
因此,即使系统在提交订单后发生重启,也可以通过 clientOrderId 或 orderId 恢复订单状态,而不是盲目再次下单。
七、撤单:请求成功后,仍要确认最终状态
撤单接口为:
|---------------------------------|
| Plain Text DELETE /api/v3/order |
撤单时可使用系统订单号 orderId,或使用原始客户端订单号 origClientOrderId。示例:
|----------------------------------------------------------------------------|
| Plain Text DELETE /api/v3/order?symbol=BTCUSDT&orderId=702345678901234567 |
成功响应可能如下:
|--------------------------------------------------------------------|
| Plain Text { "orderId": 702345678901234567, "status": "CANCELED" } |
这一步依然应写入本地订单事件记录,并通过订单频道或订单详情接口复核最终状态。
原因很简单:撤单请求和成交事件可能在时间上非常接近。特别是部分成交订单,撤单成功只意味着剩余未成交部分已被取消,并不意味着该订单"完全没有成交"。
因此,撤单后的正确处理顺序是:
- 记录撤单请求及响应;
- 订阅或查询订单最终状态;
- 核对 executedQty 和累计成交额;
- 查询成交明细,完成资金与手续费记录;
- 将订单标记为最终完成。
八、当前挂单、订单详情、成交明细:三种查询如何分工?
自动化系统不应只保留"订单是否成功"的模糊结果,而应使用不同查询接口完成不同核验任务。
- 查询单笔订单
|------------------------------|
| Plain Text GET /api/v3/order |
适用于订单提交后核验、网络超时恢复、按单排查异常等场景。
- 查询当前挂单
|-----------------------------------|
| Plain Text GET /api/v3/openOrders |
可按交易对筛选;不传交易对时返回当前账户全部挂单。系统启动、WebSocket 重连或定期巡检时,可以用它与本地"活动订单"列表进行比对。
如果本地记录显示订单仍在挂单,但接口已不再返回,应进一步查询订单详情或成交明细,确定它是成交、撤销还是其他终态。
- 查询成交明细
|---------------------------------|
| Plain Text GET /api/v3/myTrades |
成交明细可按交易对查询,也可通过 orderId 筛选某一笔订单的成交记录。返回信息包括成交 ID、成交价格、成交数量、成交金额、手续费、成交时间和买卖方向。
对于部分成交订单,成交明细是最终核算的基础。不要只依据订单价格推算实际成交结果,应以实际成交价、成交数量和手续费字段为准。
九、批量下单时,最容易忽略"部分成功"
批量下单接口为:
|-------------------------------------|
| Plain Text POST /api/v3/order/batch |
单次最多可提交 10 笔订单。批量请求的价值在于减少网络交互,但它并不意味着"要么全部成功,要么全部失败"。
WEEX 的批量下单返回会逐笔给出结果:成功的订单带有 orderId 和 clientOrderId,失败的订单则可能携带 errorCode 和 errorMsg。
因此,批量下单后的正确处理不是只看 HTTP 状态码,而是:
- 遍历 orderList 中的每个结果;
- 为成功订单建立独立的状态跟踪;
- 为失败订单记录错误码和错误信息;
- 不对整批请求进行不加区分的重复提交;
- 对不确定结果用原 clientOrderId 查询,而不是生成新订单号再次提交。
这类"逐笔处理"逻辑,是批量操作可靠性的核心。
十、异常恢复:遇到超时,不要立刻重复下单
下单请求超时,是自动化系统里最危险的场景之一。
超时可能表示:
- 请求根本没有到达服务端;
- 服务端已收到请求,但响应在网络中丢失;
- 订单已创建,系统却没有收到 orderId。
如果不加判断直接重试,可能造成重复订单。
推荐的恢复步骤如下:
请求超时
↓
用原 newClientOrderId 查询订单
↓
查到订单 → 恢复跟踪,不重复下单
↓
未查到订单 → 结合错误日志、时间窗口和业务规则决定是否重试
这里再次体现了 newClientOrderId 的价值:它让自建系统可以将"这次请求"与"交易所中的订单"可靠关联起来。
同时,程序应对不同错误做差异化处理:
WEEX 文档说明,REST 接口会在响应头中提供限频相关信息;超出请求限制时会返回 HTTP 429。下单接口与其他接口的限频维度可能不同,系统应读取并记录相关响应头,而不是假设固定阈值。
十一、一套可落地的订单状态管理清单
在将自动化订单管理投入生产环境前,建议逐项确认:
✅ API Key 仅保存在受控服务端,并已配置 IP 白名单
✅ 每笔订单都生成唯一且可追溯的 newClientOrderId
✅ 下单、撤单、查询请求均有签名、超时和错误处理
✅ 下单成功后不会直接标记为"已成交"
✅ 已订阅私有订单频道,持续接收订单状态变化
✅ WebSocket 重连后会重新订阅,并通过 REST 校验当前挂单
✅ 能够查询单笔订单、当前挂单与实际成交明细
✅ 批量请求逐笔解析成功或失败结果
✅ 网络超时时先查询原订单,再决定是否重试
✅ 对订单、成交、撤单和异常事件保留可审计日志
总结一下:
自动化交易的技术能力,不应仅以"能否调用下单接口"衡量,而应以"能否在网络波动、部分成交、断线重连和请求超时后仍准确恢复订单事实"来衡量。
对于 WEEX API 接入,REST 适合提交指令、查询与补偿;私有 WebSocket 适合持续接收订单状态变化。将两者结合,并以客户端订单号、订单 ID 和成交明细为核心建立状态闭环,才能让自动化订单管理具备可追溯性与可维护性。
本文仅用于介绍 API 接入、订单状态管理和系统安全实践,不构成任何投资、交易或收益建议。WEEX 现货 API 文档当前标注为 V3(BETA)。在生产部署前,请持续核对官方文档的接口参数、签名规则、订单状态和限频说明。