错误消息封装
{
"err": {
"error_code": "RateLimited",
"message": "You have exceeded the allowed message rate.",
"incoming_message": {
"type": "add_order",
"params": {
"symbol": "AAPL",
"side": "BUY",
"order_type": "LIMIT",
"order_time_in_force": "GTC",
"quantity": 10.0,
"price": 200.0,
"client_order_id": "my-order-001",
"take_profit": null,
"stop_loss": null
}
}
}
}
字段
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
error_code | string | 是 | 机器可读错误代码,见下表;与服务端枚举变体完全一致。 |
message | string/null | 否 | 便于阅读的上下文信息,可以为 null。 |
incoming_message | object/null | 否 | 若可用,回传导致错误的原始请求消息。 |
incoming_message使用与请求相同的带标签格式:{ "type": "<snake_case>", "params": { ... } }。
错误代码
以下值与服务端枚举一一对应:| 代码 | 发生情况 | 建议的客户端处理 |
|---|---|---|
RateLimited | 超过允许的消息速率,按消息类型加权计算。 | 退避后重试,考虑批量处理并遵守公布限额。 |
InvalidJSONFormat | 消息不是有效 JSON,或不符合预期模式。 | 发送前验证 JSON 和模式,包含必填字段及正确类型、枚举值。 |
AlreadyAuthenticated | 在同一连接已成功认证后再次认证。 | 不要在已认证连接上重复认证。 |
InvalidParameter | 一个或多个参数超范围、缺失或不适用于该消息。 | 修正参数后重试。 |
PermissionDenied | API 密钥或用户无权执行请求操作。 | 检查密钥范围及角色,必要时联系支持团队。 |
ServerError | 意外的内部错误。 | 短暂等待后重试;持续发生时,向支持团队提供时间戳和 incoming_message。 |
InvalidOrder | 订单无效或格式错误。 | 检查并修正订单参数与格式后重试。 |
InvalidOrderId | 提供的订单 ID 无效。 | 检查其是否为有效订单 UUID。 |
KycRequired | 用户需要先完成 KYC。 | 完成 KYC 后重试。 |
TncRequired | 用户需接受最新条款。 | 接受最新条款。 |
限流说明
服务器根据消息"type"(例如 "add_order"、"cancel_order")计算消息权重,并在滑动窗口上应用限制。部分消息权重更高。遇到 RateLimited 时,应延迟并降低频率;可行时优先合并为数量更少、内容更集中的请求。
示例
1. JSON 无效
{
"err": {
"error_code": "InvalidJSONFormat",
"message": "Expected field `symbol` (string).",
"incoming_message": {
"type": "add_order",
"params": {
"side": "BUY",
"order_type": "LIMIT",
"order_time_in_force": "GTC",
"quantity": 10.0,
"price": 200.0
}
}
}
}
2. 已经完成认证
{
"err": {
"error_code": "AlreadyAuthenticated",
"message": "Connection is already authenticated.",
"incoming_message": { "type": "auth", "params": {} }
}
}
3. 参数无效
{
"err": {
"error_code": "InvalidParameter",
"message": "price must be >= min_tick and within price bands.",
"incoming_message": {
"type": "modify_order",
"params": {
"symbol": "AAPL",
"order_id": "cd1a2b0c-...-f3",
"quantity": 10.0,
"price": 0.0001,
"take_profit": 0.0,
"stop_loss": 0.0,
"side": "BUY"
}
}
}
}
4. 权限不足
{
"err": {
"error_code": "PermissionDenied",
"message": "API key does not allow trading on this account.",
"incoming_message": {
"type": "transfer_balance",
"params": { "amount": 100.0, "to_api_key": "****" }
}
}
}
5. 触发限流
{
"err": {
"error_code": "RateLimited",
"message": "Too many requests. Please retry after 200ms.",
"incoming_message": { "type": "cancel_all_orders", "params": {} }
}
}
6. 服务端错误
{
"err": {
"error_code": "ServerError",
"message": "Unexpected error processing request.",
"incoming_message": { "type": "get_user_trades", "params": { "limit": 50 } }
}
}
客户端处理模式
Python
Python
import json
import websocket
def on_message(ws, message):
msg = json.loads(message)
if msg.get("type") == "error":
code = msg.get("error_code")
print(f"[ERROR] {code}: {msg.get('message')}")
# basic strategy
if code == "RateLimited":
# implement backoff before retrying your last request
return
elif code in ("InvalidJSONFormat", "InvalidParameter"):
# fix your payload; don't auto-retry
return
elif code in ("PermissionDenied", "AlreadyAuthenticated"):
# adjust flow; no retry
return
else:
# ServerError or unknown: small backoff and optional retry
return
else:
print("OK:", msg)
Node
Node
import WebSocket from "ws";
const ws = new WebSocket("wss://trade.qfex.com?api_key=YOUR_API_KEY");
ws.on("message", (raw) => {
const msg = JSON.parse(raw.toString());
if (msg.type === "error") {
const { error_code, message } = msg;
console.error(`[ERROR] ${error_code}: ${message || ""}`);
switch (error_code) {
case "RateLimited":
// schedule retry w/ backoff
break;
case "InvalidJSONFormat":
case "InvalidParameter":
// fix payload, do not blind-retry
break;
case "PermissionDenied":
case "AlreadyAuthenticated":
// update client state/flow
break;
default:
// transient server error: optional retry with jitter
break;
}
return;
}
// handle normal messages…
});
Go
Go
type ErrorFrame struct {
Type string `json:"type"`
ErrorCode string `json:"error_code"`
Message *string `json:"message,omitempty"`
IncomingMessage json.RawMessage `json:"incoming_message,omitempty"`
}
func handleMessage(raw []byte) {
// peek at "type"
var meta struct{ Type string `json:"type"` }
if err := json.Unmarshal(raw, &meta); err != nil { return }
if meta.Type == "error" {
var ef ErrorFrame
if err := json.Unmarshal(raw, &ef); err == nil {
log.Printf("[ERROR] %s: %s", ef.ErrorCode, deref(ef.Message))
// handle per code...
}
return
}
// normal flow...
}
认证与错误
trade.qfex.com要求在连接建立后的 1 分钟内完成认证。- 对已经认证的连接再次认证,会返回
AlreadyAuthenticated。
排查清单
- 验证 JSON,包括类型、枚举及必填字段。
- 遵守速率限制,降低突发流量并尽可能批量处理。
- 确保 API 密钥拥有正确权限。
- 记录错误帧中的
incoming_message,便于重现问题。 ServerError持续发生时,向 support@qfex.com 提供时间戳及引发错误的incoming_message。