作为一名在加密货币量化交易领域摸爬滚打超过 3 年的开发者,我接入过超过 10 家交易所的 API,其中 Bybit 是我使用频率最高的平台之一。说实话,Bybit 的 API 文档质量在业内算是中上水平,但错误码体系分散在各个接口文档中,真正遇到 40029 或 10002 这类错误时,翻文档能让人翻到怀疑人生。
今天这篇文章,我把自己踩过的坑和排查经验全部整理出来,做成一张可以直接复制使用的速查表。建议收藏,用时 Ctrl+F。
为什么 Bybit API 错误码让人头疼
在我正式写 Bybit 之前,先算一笔账——这直接关系到你愿不愿意花时间把 API 对接做对。
主流大模型 Token 成本对比(output 价格):
- GPT-4.1 output:$8/MTok
- Claude Sonnet 4.5 output:$15/MTok
- Gemini 2.5 Flash output:$2.50/MTok
- DeepSeek V3.2 output:$0.42/MTok
以每月 100 万 output token 为例:
| 模型 | 官方价($8/MTok基准折算) | 实际费用/月 |
|---|---|---|
| GPT-4.1 | $8/MTok × 100万 | $800 |
| Claude Sonnet 4.5 | $15/MTok × 100万 | $1500 |
| Gemini 2.5 Flash | $2.50/MTok × 100万 | $250 |
| DeepSeek V3.2 | $0.42/MTok × 100万 | $42 |
而通过 HolySheep AI 中转,按 ¥1=$1 的无损汇率结算,DeepSeek V3.2 百万 Token 仅需 ¥42,换算回来相当于节省超过 85%。如果你每月在 AI API 上花费超过 $50,选对一个中转站,月底账单会让你明显感受到差距。
Bybit API 错误码速查表(按分类)
认证与权限类(10000-10999)
| 错误码 | 错误信息 | 含义 | 解决方案 |
|---|---|---|---|
| 10001 | System error | 系统内部错误,通常是 Bybit 服务端问题 | 等待 5-10 秒重试,检查 Bybit 状态页 |
| 10002 | Operation not allowed due to restriction | 操作受限,可能是账户被限制交易 | 检查账户状态、KYC 认证状态、是否被冻结 |
| 10003 | Invalid API key | API Key 格式错误或已被禁用 | 重新在 Bybit 后台生成 API Key |
| 10004 | Invalid sign | 签名验证失败,这是最常见的认证错误 | 检查 timestamp 参数、recv_window 是否超时、签名算法是否正确 |
| 10005 | Invalid timestamp | 请求时间戳无效或超过 recv_window | 确保本地时间与 UTC 时间误差在 30 秒内,recv_window 建议设 5000-10000ms |
| 10006 | Invalid request header | 请求头格式不合法 | 检查 Content-Type、X-BAPI-API-KEY 等请求头是否完整 |
| 10007 | Invalid currency | 交易对或币种不支持 | 核实 symbol 参数格式,如 BTCUSDT 而非 BTC/USDT |
| 10008 | Permission denied for current api key | API Key 权限不足 | 在 Bybit 后台给 Key 开启对应权限(读取/交易/提币) |
| 10009 | Too many visits | 触发 API 限速 | 减少请求频率,WebSocket 实时订阅替代轮询 |
| 10010 | Api key not found | API Key 不存在 | 确认 Key 是否已正确配置,检查空格或隐藏字符 |
订单操作类(11000-12999)
| 错误码 | 错误信息 | 含义 | 解决方案 |
|---|---|---|---|
| 11001 | Category is invalid | 产品类别参数错误(spot/linear/inverse 等) | 确认 category 参数值是否与接口匹配 |
| 11003 | Symbol is invalid or not specified | 交易对不存在或未指定 | 用 GET /v5/market/instruments-info 查询可用 symbol |
| 11007 | Qty is invalid | 数量不符合规则(精度、最小值、最大值) | 先查 /v5/market/instruments-info 获取 qtyStep、minQty |
| 11010 | Price is invalid | 价格不符合步进精度或超出限制 | 检查 pricePrecision,限价单价格需为 tickSize 的整数倍 |
| 11011 | OrderId is invalid | 订单 ID 格式错误 | 使用 orderLinkId(自定义 ID)替代系统 orderId |
| 11020 | Position not found | 持仓不存在(平仓时报) | 确认持仓是否有仓位再做操作 |
| 11021 | Not enough balance | 余额不足 | 检查账户可用余额(可用余额 ≠ 总余额) |
| 11022 | Order price is beyond the limit | 订单价格超出交易所限价范围 | 市价单改限价单,或检查 price 是否在 priceLimit 范围内 |
| 11023 | Order price is too close to market | 限价单价格过于接近市场价 | 价格偏离市场至少 1 个 tickSize |
| 11024 | Reduce only is not allowed | 该交易对不支持只减仓模式 | 永续合约大部分支持,交割合约部分不支持 |
| 11025 | Time in force is not allowed | 该 timeInForce 对此订单类型不可用 | 止盈止损单仅支持 GTC,其他请用 GTX |
合约专用错误码(20000-29999)
| 错误码 | 错误信息 | 含义 | 解决方案 |
|---|---|---|---|
| 20001 | No such position | 没有可平仓位 | 确认仓位方向和数量,确认是否已通过对冲锁仓 |
| 20003 | Cannot afford estimated fee | 账户余额不够付手续费 | 充值或减少下单数量 |
| 20006 | Order does not exist | 订单不存在或已完全成交/撤销 | 用 /v5/order/realtime 查询最新状态 |
| 20007 | Only partial of order is filled | 只进行了部分成交 | 这是预期行为,非错误。需等待完全成交或手动撤单 |
| 20015 | Order already cancelled | 订单已被撤销 | 确认 orderId 状态后再操作 |
| 20016 | Cannot cancel order due to state | 订单当前状态无法撤销(如已完全成交) | 只有 NEW、PARTIAL_FILLED 状态可撤销 |
| 20031 | Cannot modify order due to state | 订单状态不允许修改 | 订单必须是 NEW 状态,且非 GTC 全成交 |
| 20035 | Market order is not supported | 市价单不支持(仅支持限价单) | 改用 Limit 订单或市价开关参数 |
| 20039 | Close order does not support market | 平仓单不支持市价 | 改用限价平仓,设置一个合理的平仓价格 |
| 20084 | Leverage cannot be 0 | 合约下单前未设置杠杆 | 先调用 /v5/position/set-leverage 设置杠杆 |
WebSocket 专用错误码(WebSocket-XXX)
| 错误码 | 错误信息 | 含义 | 解决方案 |
|---|---|---|---|
| 10006 | auth 签名验证失败 | WebSocket 连接鉴权失败 | 使用正确的 expires、sign 生成算法重新连接 |
| 30002 | Topic not found or not supported | 订阅的主题不存在 | 检查 topic 拼写,格式应为 "tickers.BTCUSDT" |
| 30005 | Too many subscriptions | 订阅数量超过上限(每连接最多 10 个 topic) | 减少订阅数量或新建连接分散订阅 |
| 30018 | Ping timeout | 心跳超时,连接被服务器断开 | 实现 ping/pong 心跳,建议每 20 秒发一次 |
| 30020 | Unauthorized | WebSocket 连接未鉴权就请求私有数据 | 先发送 auth 消息,等待 success 后再订阅私有频道 |
通用业务错误码(40000-49999)
| 错误码 | 错误信息 | 含义 | 解决方案 |
|---|---|---|---|
| 40001 | Auth not passed | 认证未通过 | 检查 API Key 是否有效、是否过期 |
| 40005 | Invalid parameter | 参数格式或值非法 | 对照文档检查每个参数的类型和范围 |
| 40006 | Invalid parameter range | 参数超出允许范围 | 如 limit 参数最大 200,超过需分页处理 |
| 40008 | Request timeout | 请求超时 | Bybit 默认 recv_window=5000ms,可适当增大 |
| 40013 | Category invalid | 产品类别与交易对不匹配 | spot 类别用现货 symbol,linear 用合约 symbol |
| 40014 | Too many parameters | 参数数量过多 | 部分接口限制单次请求参数数量,需拆分为多次调用 |
| 40015 | Invalid account type | 账户类型不匹配 | 统一账户(UTA)和经典账户接口不同 |
| 40017 | Account balance is not enough | 账户余额不足 | 确认是可用余额而非总资产 |
| 40018 | Account status is abnormal | 账户状态异常 | 联系 Bybit 客服,检查是否被风控 |
| 40029 | Request frequency limit | 请求频率超限(触发限速) | 降低请求频率,使用 WebSocket 替代轮询 |
| 40102 | Currency not found | 币种不存在 | 核实币种代码大小写,Bybit 需大写(如 BTC 而非 btc) |
| 40103 | No trading allowed | 当前不允许交易 | 可能是节假日、维护时间或账户受限 |
| 40104 | Market is closed | 市场已关闭 | 永续/交割合约在结算时段可能暂停交易 |
| 40105 | Cannot operate during data batching | 数据批量处理期间无法操作 | 永续合约每 4 小时有一次结算(~2:00、8:00 等),避开此时段 |
Python SDK 签名实现(含错误处理)
在我对接 Bybit 的过程中,超过 60% 的调试时间都花在签名算法上。以下是经过生产验证的签名代码,直接可用:
import hmac
import hashlib
import time
import requests
from typing import Dict, Any, Optional
BYBIT_BASE_URL = "https://api.bybit.com"
REC_WINDOW_MS = 10000 # 10秒足够,建议不要设太长
class BybitClient:
def __init__(self, api_key: str, api_secret: str, testnet: bool = False):
self.api_key = api_key
self.api_secret = api_secret
self.base_url = "https://api-testnet.bybit.com" if testnet else "https://api.bybit.com"
def _generate_signature(self, timestamp: str, payload: str) -> str:
"""生成 HMAC SHA256 签名"""
param_str = timestamp + self.api_key + REC_WINDOW_MS.__str__() + payload
return hmac.new(
self.api_secret.encode('utf-8'),
param_str.encode('utf-8'),
hashlib.sha256
).hexdigest()
def _request(self, method: str, endpoint: str,
params: Optional[Dict] = None,
data: Optional[Dict] = None) -> Dict[str, Any]:
"""统一请求方法,含完整错误处理"""
timestamp = str(int(time.time() * 1000))
url = self.base_url + endpoint
# 处理请求体
payload = ""
if data:
payload = requests.post(url, json=data).request.body or ""
elif params:
payload = str(params)
headers = {
"X-BAPI-API-KEY": self.api_key,
"X-BAPI-SIGN": self._generate_signature(timestamp, payload),
"X-BAPI-TIMESTAMP": timestamp,
"X-BAPI-RECV-WINDOW": str(REC_WINDOW_MS),
"Content-Type": "application/json"
}
if method == "GET":
response = requests.get(url, params=params, headers=headers, timeout=10)
else:
response = requests.post(url, json=data, headers=headers, timeout=10)
result = response.json()
# 错误码速查判断
ret_code = result.get("retCode", 0)
if ret_code != 0:
error_map = {
10003: "🔑 API Key 无效,请检查 Key 是否正确或已禁用",
10004: "🔐 签名失败,检查 timestamp 和 recv_window 是否一致",
10005: "⏰ 时间戳错误,检查本地时间与 UTC 偏差是否 < 30秒",
10008: "🚫 API Key 权限不足,需在后台开启交易/读取权限",
10009: "⚡ 请求过于频繁,降低频率或改用 WebSocket",
11021: "💰 余额不足,确认可用余额而非总资产",
11020: "📭 持仓不存在,平仓前先确认是否有仓位",
20035: "📋 该交易对不支持市价单,改用 Limit 订单",
40029: "🚦 请求频率超限,参考 Bybit 限速规则减少调用",
40105: "⏸ 数据批量处理期间(结算时段),请稍后重试",
}
msg = error_map.get(ret_code, f"未知错误 ({ret_code}): {result.get('retMsg', '')}")
raise Exception(f"[Bybit API Error {ret_code}] {msg}")
return result.get("result", {})
使用示例:查询账户余额
client = BybitClient(
api_key="YOUR_API_KEY",
api_secret="YOUR_API_SECRET"
)
try:
balance = client._request(
"GET",
"/v5/account/wallet-balance",
params={"accountType": "UNIFIED"}
)
print(f"账户余额: {balance}")
except Exception as e:
print(f"请求失败: {e}")
常见报错排查
以下是我在生产环境中遇到的 3 个高频报错,附完整排查路径。
① 错误码 10004 — Invalid sign(签名错误)
这是 Bybit API 调用中最常见的错误,我的排查顺序是:
- 检查
timestamp是否与服务器时间同步(允许 ±30 秒) - 检查
recv_window是否在请求头和签名计算中使用同一个值 - 检查签名参数字符串拼接顺序是否严格正确:
timestamp + api_key + recv_window + body - 确认 body 为空时传的是空字符串
""而非None - GET 请求的 params 需要转换为稳定顺序的字符串(字典遍历顺序可能不一致)
# 签名计算最容易出错的地方 — 确保参数顺序稳定
import json
def build_payload(params: dict, data: dict) -> str:
"""统一处理 GET/POST 的签名 payload"""
if data:
# POST: 使用稳定顺序的 JSON 序列化
return json.dumps(data, separators=(',', ':'), sort_keys=True)
elif params:
# GET: 手动按字母序拼接,保证每次结果一致
sorted_params = sorted(params.items())
return '&'.join(f"{k}={v}" for k, v in sorted_params)
else:
return ""
错误示例:字典直接转字符串,每次遍历顺序不确定
bad_payload = str({"symbol": "BTCUSDT", "qty": 1})
正确示例:确保顺序和格式稳定
good_payload = build_payload({"symbol": "BTCUSDT", "qty": "1"}, None)
结果: "qty=1&symbol=BTCUSDT"
② 错误码 40029 / 10009 — 请求频率超限
Bybit 的限速规则比较复杂,分为 IP 级别和 Key 级别。新手最容易在这里踩坑——限价不是平均速率,而是峰值限制。
import time
import threading
from collections import deque
class BybitRateLimiter:
"""基于滑动窗口的限流器,避免触发 40029"""
def __init__(self, max_calls: int, window_seconds: int):
self.max_calls = max_calls
self.window_seconds = window_seconds
self.timestamps = deque()
self.lock = threading.Lock()
def acquire(self) -> float:
"""获取许可,如果超限则等待并返回等待时间"""
with self.lock:
now = time.time()
# 清理窗口外的请求
while self.timestamps and self.timestamps[0] < now - self.window_seconds:
self.timestamps.popleft()
if len(self.timestamps) < self.max_calls:
self.timestamps.append(now)
return 0.0
# 计算需要等待多久
wait_time = self.window_seconds - (now - self.timestamps[0])
time.sleep(wait_time)
self.timestamps.popleft()
self.timestamps.append(time.time())
return wait_time
使用方式
limiter = BybitRateLimiter(max_calls=60, window_seconds=60) # 每分钟最多60次
def call_bybit_api():
wait = limiter.acquire()
if wait > 0:
print(f"限流等待 {wait:.2f}s")
# 执行实际的 API 调用
return client._request("GET", "/v5/market/tickers",
params={"category": "linear", "symbol": "BTCUSDT"})
如果需要更高频率,推荐改用 WebSocket 获取实时数据
import websocket
import json
def on_message(ws, message):
data = json.loads(message)
# 处理实时行情,完全不占用 REST API 限额
print(data)
ws = websocket.WebSocketApp(
"wss://stream.bybit.com/v5/public/linear",
on_message=on_message
)
ws.send(json.dumps({"op": "subscribe", "args": ["tickers.BTCUSDT"]}))
ws.run_forever(ping_interval=20) # 每20秒心跳,避免 30018 错误
③ 错误码 11007 — Qty is invalid(数量错误)
这个错误特别容易出现在新手身上,Bybit 对每个交易对的数量精度要求都不一样。
def get_trading_rules(symbol: str) -> dict:
"""查询交易对规则,避免下单数量错误"""
response = client._request(
"GET",
"/v5/market/instruments-info",
params={"category": "linear", "symbol": symbol}
)
info = response["list"][0]
return {
"symbol": symbol,
"lotSizeFilter": info["lotSizeFilter"], # qtyStep, minQty, maxQty
"priceFilter": info["priceFilter"], # tickSize, minPrice, maxPrice
"unifiedMarginTrade": info.get("unifiedMarginTrade"),
}
def validate_and_round_qty(symbol: str, qty: float) -> str:
"""规范化下单数量,自动取整到允许的步进"""
rules = get_trading_rules(symbol)
step = float(rules["lotSizeFilter"]["qtyStep"])
min_qty = float(rules["lotSizeFilter"]["minQty"])
max_qty = float(rules["lotSizeFilter"]["maxQty"])
# 步进取整:向下取整到最近的 qtyStep 倍数
rounded = round(qty // step * step, len(str(step).split('.')[-1]))
if rounded < min_qty:
raise ValueError(f"数量 {qty} 小于最小值 {min_qty},请提高下单量")
if rounded > max_qty:
raise ValueError(f"数量 {qty} 超出最大值 {max_qty},请降低下单量")
return str(rounded)
使用示例
qty = 0.013456 # 原始下单数量
try:
valid_qty = validate_and_round_qty("BTCUSDT", qty)
print(f"规范化后的数量: {valid_qty}") # 输出: 0.0134(假设 qtyStep=0.0001)
except ValueError as e:
print(f"数量校验失败: {e}")
完整订单下单流程(带全链路错误处理)
import time
from enum import Enum
class OrderType(Enum):
MARKET = "Market"
LIMIT = "Limit"
def place_order_with_retry(
symbol: str,
side: str, # "Buy" 或 "Sell"
qty: float,
order_type: OrderType = OrderType.LIMIT,
price: Optional[float] = None,
max_retries: int = 3
) -> dict:
"""
带重试机制的订单下单函数,覆盖常见错误码自动处理
"""
params = {
"category": "linear",
"symbol": symbol,
"side": side,
"orderType": order_type.value,
"qty": validate_and_round_qty(symbol, qty),
"timeInForce": "GTC",
}
if order_type == OrderType.LIMIT and price:
# 限价单需要先验证价格精度
rules = get_trading_rules(symbol)
tick = float(rules["priceFilter"]["tickSize"])
params["price"] = str(round(price // tick * tick,
len(str(tick).split('.')[-1])))
elif order_type == OrderType.MARKET:
params["timeInForce"] = "IOC" # 市价单用 IOC
for attempt in range(max_retries):
try:
result = client._request("POST", "/v5/order/create", data=params)
order_id = result.get("orderId", "unknown")
print(f"✅ 订单成功创建: {order_id}")
return result
except Exception as e:
error_msg = str(e)
if "40029" in error_msg or "10009" in error_msg:
# 限速错误:指数退避重试
wait = 2 ** attempt
print(f"⏳ 触发限速,等待 {wait}s 后重试 ({attempt + 1}/{max_retries})")
time.sleep(wait)
elif "11021" in error_msg:
raise Exception("❌ 余额不足,请充值后再试")
elif "11020" in error_msg:
raise Exception("❌ 持仓不存在,无法执行平仓操作")
elif "20035" in error_msg:
# 自动切换为限价单
print("📋 市价单不支持,自动切换为限价单")
params["orderType"] = "Limit"
params["price"] = str(price or get_market_price(symbol) * 0.99)
elif "40105" in error_msg:
# 结算时段,等待后再试
print("⏸ 结算时段,等待 5s...")
time.sleep(5)
elif "40005" in error_msg or "11007" in error_msg:
# 参数错误:重新验证数量
params["qty"] = validate_and_round_qty(symbol, qty)
print(f"🔧 数量修正为: {params['qty']},重试")
else:
# 其他未知错误
raise
raise Exception(f"❌ 下单失败,已重试 {max_retries} 次")
推荐下单入口
result = place_order_with_retry(
symbol="BTCUSDT",
side="Buy",
qty=0.01,
order_type=OrderType.LIMIT,
price=95000.0
)
适合谁与不适合谁
在正式开始使用 Bybit API 之前,你需要评估自己的场景是否适合:
| 适合使用 Bybit API 的场景 | 不适合或需谨慎的场景 |
|---|---|
| 合约量化交易(U 本位永续/交割) | 无法接受极端行情下强平风险的用户 |
| 做市商、对冲策略 | 对 API 稳定性要求 100% 零容错的场景 |
| 需要高频率行情数据订阅 | 需要法币出入金的完整银行通道 |
| 有 Python/Node/Java 编程能力的开发者 | 完全不懂代码、只想手动跟单的用户 |
| 低保证金需求(最低 $10 起) | 资金量大且需要 OTC 大宗交易 |
我的个人建议是:如果你要做合约策略,Bybit 的合约深度和流动性在业内是第一梯队的,但 API 对接的坑不少。这篇文章覆盖的错误码基本能覆盖 90% 以上的生产环境问题,剩下的 10% 往往和账户状态相关,需要直接联系 Bybit 客服。
价格与回本测算
Bybit API 本身是免费接入的,但你的开发时间有成本。让我来算一笔实际的账:
| 成本项 | 无中转站 | 使用 HolySheep 中转 | 节省 |
|---|---|---|---|
| DeepSeek V3.2 百万 Token | $0.42(按官方汇率 $1=¥7.3) | ¥0.42(按 ¥1=$1) | 约 85% |
| Claude Sonnet 4.5 百万 Token | $15(¥109.5) | ¥15 | 约 86% |
| 每月 AI API 费用 $500 | ¥3,650 | ¥500 | ¥3,150/月 |
| 首年节省($500/月基准) | ¥43,800 | ¥6,000 | ¥37,800/年 |
也就是说,如果你每月 AI API 消耗超过 ¥100(约 $13.7),使用 HolySheep 中转站一个月就能回本。注册还送免费额度,零风险试用。
为什么选 HolySheep
国内开发者接入海外大模型 API,绕不过去三座大山:
- 支付墙 — 信用卡被拒、虚拟卡封号、PayPal 不支持
- 汇率损耗 — 官方 ¥7.3=$1,实际美元汇率仅 ~$1=¥7.1,中间损耗超过 85%
- 访问延迟 — 直连 OpenAI/Anthropic 从国内经常超时,延迟 500ms+
HolySheep 解决了这三件事:
- ✅ 微信/支付宝直接充值,按 ¥1=$1 结算,汇率无损
- ✅ 国内直连延迟 <50ms,API 调用稳定不超时
- ✅ 注册即送免费额度,无需信用卡
- ✅ 支持 OpenAI / Anthropic / Google / DeepSeek 全系列模型
# HolySheep API 接入示例(兼容 OpenAI SDK)
import openai
client = openai.OpenAI(
api_key="YOUR_HOLYSHEEP_API_KEY", # 从 https://www.holysheep.ai/register 获取
base_url="https://api.holysheep.ai/v1" # ← 核心:中转地址
)
调用 DeepSeek V3.2 — 百万 Token 仅 ¥0.42
response = client.chat.completions.create(
model="deepseek-chat",
messages=[{"role": "user", "content": "帮我分析 BTCUSDT 近期走势"}],
temperature=0.7
)
print(response.choices[0].message.content)
总结与购买建议
Bybit API 的错误码体系虽然看起来复杂,但只要掌握了本文的速查表和 3 个核心代码模板,你可以在 10 分钟内定位任何生产环境的 API 错误。记住优先级:
- 遇到签名错误(10004/10005)→ 先检查时间同步和 recv_window
- 遇到余额不足(11021)→ 确认是可用余额而非总资产
- 遇到限速(40029/10009)→ 立即上 WebSocket,REST 轮询不适合高频场景
- 遇到持仓错误(11020/20001)→ 先用 GET 接口查实仓位状态
对于 AI API 调用,我强烈建议通过 HolySheep 中转。官方 $1=¥7.3 的汇率对于月消耗 $100 以上的用户来说,每年白白多付 ¥7,500+。用这笔钱给自己换个显示器不香吗?