欧易 · API 实操
欧易 API 常见报错与排查清单:照着这张表修
写量化脚本时,可能还没轮到琢磨策略,就先卡在一行红字报错上:脚本跑起来,终端弹出一串看不懂的 code 和英文,人就懵了。好消息是,欧易 API 的报错就那么几种高频款,搞清楚每种长什么样、对应哪个原因,排查会快得多。
这篇把欧易 API 的常见报错整理成一份清单,核心是一张错误码对照表——出了问题,先到表里按错误码或关键词定位,再看下面对应的逐条排查。读完你应该能把大多数报错在几分钟内自己解决,而不是对着红字干瞪眼。
先学会读报错:code + msg
排错的第一步,是看懂报错本身。欧易接口出错时,会返回三样信息:
- HTTP 状态码:最粗的一层,比如 401(鉴权问题)、429(请求太多)、400(请求本身有问题)。
- code:欧易自己的数字错误码,最精确地指向问题类型。这是你排查的主要依据。
- msg:一段文字说明,帮你确认方向,有时还会直接点出哪个字段出错。
如果你用 ccxt,它会把这些原始报错包装成更易读的异常类型,比如鉴权问题抛 AuthenticationError、限频抛 RateLimitExceeded、余额不够抛 InsufficientFunds。异常信息里一般也带着欧易的原始 code 和 msg。一个好习惯是用 try/except 把它们打印清楚:
import ccxt
okx = ccxt.okx({
'apiKey': '你的_apiKey',
'secret': '你的_secret',
'password': '你的_Passphrase',
'enableRateLimit': True,
})
try:
balance = okx.fetch_balance()
print('USDT 可用:', balance['USDT']['free'])
except ccxt.InvalidNonce as e:
print('时间戳过期(50102),先校准本机时间再重试:', e)
except ccxt.AuthenticationError as e:
print('鉴权失败,先看报错里的 code 再决定查什么:', e)
except ccxt.RateLimitExceeded as e:
print('触发限频,放慢请求:', e)
except ccxt.BaseError as e:
print('其他报错,看原始 code/msg:', e)
把异常里那串 code 和 msg 原样记下来,对照下面这张表,基本就能定位到底是哪一类问题。
高频报错对照表
下面这张表覆盖了新手容易撞上的几种报错。表里的错误码含义核于 2026-10,以欧易 API v5 文档 Error Code 一节为准(欧易偶尔会调整码值,遇到对不上的就以 msg 文字和官方文档为准),重点是把「原因→怎么修」对上号。鉴权这一类尤其要按 code 分支走:同样是「连不上」,50113 和 50105 要查的是完全不同的东西。
| 报错(code / 关键词) | 大概率原因 | 怎么修 |
|---|---|---|
| 50011 Too Many Requests / 限频 |
请求太密集,触发接口限频保护 | 打开 enableRateLimit;还撞就加指数退避重试,别用无延时的 while 循环 |
| 50113 Invalid signature |
签名本身没通过校验,问题出在签名这一步,不一定是凭证填错 | 核对签名原文的拼接方式(请求方法、请求路径、请求体)、用来做 HMAC 的是不是 secret、有没有按 HMAC-SHA256 算完再 Base64;用 ccxt 一般不用自己签,先确认 secret 没复制错 |
| 50111 Invalid OK-ACCESS-KEY |
apiKey 本身无效:复制错(首尾带空格、漏字符)或已被删 | 重新从后台复制 apiKey;确认这个 Key 还在、没被删 |
| 50101 APIKey does not match current environment |
Key 与环境对不上:模拟盘 Key 没开 set_sandbox_mode(True),或实盘 Key 开了它 |
让 Key 与开关对齐:模拟盘 Key 配 set_sandbox_mode(True),实盘 Key 不开;自检 okx.headers.get('x-simulated-trading'),模拟盘下应打印 1 |
| 50105 OK-ACCESS-PASSPHRASE incorrect (50104 = 这一项为空) |
口令对不上:ccxt 的 password 字段填成了登录密码,而不是建 Key 时设的 Passphrase |
把 password 改成你建那个 Key 时设的 Passphrase;记不清只能删 Key 重建 |
| 余额不足 Insufficient balance |
下单金额超过账户可用余额,或资金在别的账户(如资金账户 vs 交易账户) | 减小下单量;确认资金在用于交易的账户里;模拟盘可重置虚拟资金 |
| 50102 Timestamp request expired 50112 Invalid OK-ACCESS-TIMESTAMP |
请求里的时间戳和服务器时间对不上:按官方文档,相差超过 30 秒就直接拒绝(50112 则是这个字段本身格式不对) | 开启 NTP 自动校时,把系统时间同步准;云服务器一般默认已开 |
| 50120 API key 没有该功能的权限 |
这个 Key 没有所需权限(常见:只勾了读取,没勾交易);它返回的是 HTTP 200,不像别的鉴权错那样报 401,要读 body 里的 code 才看得见 | 回 API 管理给该 Key 补勾「交易」权限,不用重建 |
| 50110 Your IP is not included in your API key's IP whitelist |
给 Key 设了 IP 白名单,但当前出口 IP 不在名单里(换网络/开代理/宽带 IP 变了) | 先查清自己当前的出口 IP,再把它加进该 Key 的白名单(每把 Key 最多绑 20 个 IP,支持网段);出口 IP 不稳定的处置见下文,别拿「取消白名单」当常规解法 |
| 交易对不存在 instrument does not exist |
symbol 格式写错,或用了合约格式去下现货单 | 现货写成 BTC/USDT(大写带斜杠);先 load_markets() 看可用交易对 |
| 数量/精度不符 size too small |
下单量小于该交易对最小下单量,或精度位数不对 | 用 load_markets() 查该交易对的 limits 和 precision,按要求取整 |
逐条排查怎么修
表格给方向,这一节给细节。挑你撞上的那条往下看。
鉴权类(50101 / 50102 / 50105 / 50110 / 50111 / 50112 / 50113)与权限类(50120)
鉴权失败在 HTTP 层统统是 401,ccxt 里多数抛 AuthenticationError(50110 抛的 PermissionDenied 也是它的子类)。例外是 50102「时间戳过期」:按 ccxt 主分支 2026-09 的源码,它被映射成 InvalidNonce,归在 NetworkError 那一支,只写 except AuthenticationError 接不住它,得单独写一个分支。所以别照着「最常见的原因」挨个试——先把 code 抠出来,再按 code 走对应的那一条。50120「权限不足」要单独摘出来:它不在 401 那一堆里,按官方错误码表走的是 HTTP 200、靠 body 里的 code 判断,请求在协议层是「成功」的,只凭状态码你根本看不出它出了错。
- 50113 Invalid signature:查签名这一步。签名原文的拼接(请求方法、请求路径、请求体)、用来做 HMAC 的密钥是不是 secret、有没有按 HMAC-SHA256 算完再 Base64。用 ccxt 的话签名是库做的,这时先怀疑 secret 复制错了。
- 50105 Passphrase 不对(50104 是没填):只查口令。ccxt 里的
password字段填的必须是你建 Key 时设的那串 Passphrase,不是登录密码,也不是 secret;记不清了只能删 Key 重建。 - 50102 / 50112 时间戳问题:只查时间。按官方文档,请求时间戳与服务器时间相差超过 30 秒就会被拒。开 NTP 自动校时,长期跑的机器定期确认它真的在同步。用 ccxt 的话,50102 抛的是
InvalidNonce,在它的分支里校完时间再重试;最后留一个BaseError兜底。 - 50111 Invalid OK-ACCESS-KEY:查 apiKey 本身——复制时首尾带了空格、漏了字符,或者 Key 已被删。
- 50101 APIKey 与当前环境不匹配:Key 本身没错,是环境对不上——模拟盘 Key 没开
set_sandbox_mode(True)(请求不带模拟盘标记头,按实盘处理),或实盘 Key 开了它。把 Key 和开关对齐,再打印okx.headers.get('x-simulated-trading')自检,模拟盘下应为 1。ccxt 里它抛的也是AuthenticationError。 - 50110 IP 不在白名单:查网络出口,不是查凭证。见下面「IP 白名单」一节。
- 50120 权限不够(HTTP 200):凭证都对,只是这个 Key 没勾上对应权限。它不会以 401 的形式出现,只能读 body 里的 code——所以判断逻辑别只写
if status == 401。见下面「权限不足」。
顺带说一句:欧易的 msg 文字有时会直接点名是哪个 header 出的问题,把它和 code 一起原样记下来,比凭印象猜快得多。
权限不足
典型症状是「能查余额,不能下单」。查余额走读取权限,下单走交易权限,两者分开授权。回欧易后台 API 管理,找到这个 Key,补勾「交易」即可,不用重建。如果你顺手勾过提现——别留着,跑量化的 Key 永远别开提现权限。另外别把「交易权限」想得太窄:按欧易文档的定义,它除了下单撤单,还包含资金划转和一部分账户设置,所以为了做账户间划转去补勾提现是没必要的。
IP 白名单(50110)
这条报错说的是「你现在这个出口 IP 不在这把 Key 的名单里」,所以排查从网络出口开始,而不是从凭证开始:
- 先把当前出口 IP 查出来,和你在后台给这把 Key 填的名单逐个对一遍。注意是出口 IP:开了代理、走了公司网关、家里宽带重拨过,出口都可能和你以为的不是同一个。
- 出口 IP 不稳定,解决方向是把出口固定下来,而不是把锁拆掉:把策略挪到一台 IP 固定的机器上跑,或者在出口确实会变时有节奏地更新白名单。欧易这边每把 Key 最多绑 20 个 IP,支持 IPv4/IPv6 和网段,留出余量并不难。
- 调试阶段用只读 Key 或模拟盘 Key,别拿那把有交易权限的主力 Key 反复试。
- 不建议把「取消白名单」当常规排障动作。官方对绑 IP 的说法是强烈建议而非强制,但有一条规则要知道:没有绑定 IP、又带 trade 或 withdraw 权限的 Key,闲置 14 天后会被自动删除——所以不绑 IP 的 Key 除了风险更大,还可能哪天被删掉。模拟盘 Key 不受这条删除规则约束,这也是拿它做调试的一个理由。
余额不足
除了字面意义的钱不够,还有一个隐蔽原因:欧易有不同的账户(资金账户、交易账户等),你的钱可能不在用于下单的那个账户里。确认资金已经划转到交易账户。模拟盘的话,虚拟资金用完了在模拟盘界面重置即可——这也是 先在模拟盘练手 的好处,试错不花真钱。
symbol / 精度类
现货交易对在 ccxt 里统一是 大写/大写 带正斜杠,比如 BTC/USDT。写成 BTCUSDT、小写、或用合约格式都会报错。数量太小或精度不对,就用 load_markets() 查这个交易对的最小下单量和精度,按它要求来。这块在我们的 API 量化入门 里有完整的下单示例。
限频与退避重试
限频(50011 / Too Many Requests)是脚本党容易撞上、也最该提前防的一类。触发它通常不代表代码写错了,只是请求太密集,碰到了交易所的保护。处理它分两层:
第一层:打开 ccxt 自带的限速。初始化时加 'enableRateLimit': True,ccxt 会自动在请求之间插入合理间隔,大多数场景这一步就够了:
okx = ccxt.okx({
'apiKey': '你的_apiKey',
'secret': '你的_secret',
'password': '你的_Passphrase',
'enableRateLimit': True, # 自动限速,先开上
})
第二层:加指数退避重试。如果你的策略在某些时刻请求确实很密(比如同时刷很多交易对),光靠自动限速可能还会偶尔撞。这时给关键请求包一层重试:撞一次等 1 秒再试,再撞等 2 秒、4 秒,逐步拉长,撞够几次就放弃报错。
import ccxt, time
def with_retry(fn, max_tries=5):
delay = 1
for i in range(max_tries):
try:
return fn()
except ccxt.RateLimitExceeded:
print(f'限频,第 {i+1} 次,等 {delay}s 再试')
time.sleep(delay)
delay *= 2 # 指数退避:1 → 2 → 4 → 8 ...
raise RuntimeError('重试多次仍被限频,放弃')
# 用法:把会被限频的调用丢进去
ticker = with_retry(lambda: okx.fetch_ticker('BTC/USDT'))
print(ticker['last'])
一个反面教材:别写 while True: 里面紧接着打接口、中间不加任何延时。这种循环会在一秒内打出几十上百个请求,瞬间撞限频。任何高频循环里都要留间隔。
一个高频踩坑场景
fetch_ticker,中间没加任何延时,也忘了开 enableRateLimit。这种写法会持续触发接口的限频保护,终端开始刷 RateLimitExceeded(对应 50011),后面的请求大批失败。修法分两步:先在初始化加上 'enableRateLimit': True,再把每个交易对的请求包进上面那个 with_retry 退避函数。改完再跑,请求之间就有了间隔,偶尔撞上限频也会按退避重试而不是直接报废。教训很直接:循环里打接口,限速和退避是标配,不是可选项。
常见问题
欧易 API 报错怎么读?
欧易的接口出错时会返回一个 code(数字错误码)和一段 msg(文字说明),还有 HTTP 状态码。先看 code,它最精确地指向问题类型;msg 帮你确认方向。在 ccxt 里,报错通常被包成对应的异常类型(比如 AuthenticationError、RateLimitExceeded、InsufficientFunds),异常信息里一般也带着欧易原始的 code 和 msg。把这串信息原样搜一下,基本就能定位。
为什么一直报鉴权失败 / Invalid sign?
别把鉴权失败当成一种错。它们在 HTTP 层都是 401,在 ccxt 里多数抛 AuthenticationError,只有 50102 时间戳过期抛的是 InvalidNonce(属于 NetworkError),要单独接;欧易返回的 code 指向的是不同的东西:50113 是签名没通过,查签名原文的拼接方式和 secret;50105 是 Passphrase 不对,ccxt 的 password 字段填的必须是建 Key 时设的那串口令;50102 与 50112 是时间戳问题,请求时间与服务器时间相差超过 30 秒就会被拒,开 NTP 校时;50111 是 apiKey 本身无效;50101 是 Key 与环境不匹配,模拟盘 Key 没开 set_sandbox_mode,或实盘 Key 开了它;50110 是当前出口 IP 不在白名单里。50120「权限不足」要单独摘出来:它不在 401 那一堆里,按官方错误码表走 HTTP 200、靠 body 里的 code 判断,凭证都没问题,只是这把 Key 没勾上对应权限。先把 code 抠出来,再决定查哪一样,比按经验挨个试快。
触发限频(50011 / Too Many Requests)怎么办?
限频是请求太密集触发的保护。最省事的办法是在初始化 ccxt 时打开 enableRateLimit,它会自动在请求间插入间隔。如果还撞,就在代码里加指数退避重试——撞一次等 1 秒,再撞等 2 秒、4 秒,逐步拉长。别用 while True 不加延时去打接口。
能查余额但下单报权限不足,为什么?
查余额属于读取权限,下单属于交易权限,两者在建 Key 时是分开授权的。能查不能下,基本就是只勾了读取没勾交易。回欧易后台 API 管理里给这个 Key 补勾交易权限即可,不用重建。
时间戳过期(timestamp expired)是什么意思?
欧易对请求里的时间戳有容差,如果你的电脑或服务器系统时间不准、偏差超过容差,签名就会因为时间戳过期被拒。解决办法是把系统时间同步到准确的网络时间,开启 NTP 自动校时。云服务器一般默认开了,本地机器偶尔会漂。
报「交易对不存在」是怎么回事?
先看报错里的 code 和 msg,这类报错指向的是 symbol 这个字段。ccxt 里现货交易对是大写带正斜杠,比如 BTC/USDT,写成 BTCUSDT、btc/usdt、或者用了合约格式去下现货单,都会报交易对不存在。msg 有时会直接点出哪个字段不合法,按它点的字段查最省事;拿不准就先 print(okx.load_markets().keys()) 看一眼可用的交易对到底长什么样。
把报错排查这关过了,你的脚本就不会动不动卡死了。接下来值得花时间的是把 风控写进代码——比报错更可怕的是脚本没报错却一直在下错单。也别忘了新代码先丢 模拟盘 里跑,排错不花真钱。