- 行情、K 线等公共接口不需要 API Key,用 requests 发一个 GET 请求就能拿到数据。
- 私有接口要带 4 个请求头,签名 = Base64(HMAC-SHA256(时间戳 + 请求方法 + 请求路径 + 请求体, Secret Key))。
- GET 请求的查询参数要拼进请求路径一起签名,时间戳必须是 UTC 毫秒格式,与服务器相差超过 30 秒会被拒绝。
- 先用模拟盘 Key 加上 x-simulated-trading 请求头把程序跑通,确认无误后再考虑实盘。
欧易提供 REST 和 WebSocket 两套 API。本文用 Python 和 requests 库演示最常用的 REST 操作:先拿公开行情和 K 线,再按官方规则生成签名,查询余额、下单、查单和撤单。文中代码都在 2026 年 10 月实际运行过,接口和字段名以欧易 API v5 官方文档为准。
本文是编程教程,不构成任何投资建议。涉及下单的代码请一律先在模拟盘运行,程序写错一个参数,在实盘就是真金白银的损失。
准备工作
- 安装 Python 3 和
requests:
pip install requests
-
只做第一部分(公共行情)不需要任何 Key。要调用私有接口,先按 欧易 API 申请教程 创建一把模拟盘 API Key,权限勾选「读取」和「交易」即可。
-
把密钥放进环境变量,不要直接写在代码里。代码文件容易被分享、截图或上传到网上,环境变量则只存在于你的电脑中。macOS / Linux 终端:
export OKX_API_KEY="你的 API Key"
export OKX_SECRET_KEY="你的 Secret Key"
export OKX_PASSPHRASE="你的 Passphrase"
Windows PowerShell 用 $env:OKX_API_KEY="你的 API Key" 的写法,其余两项同理。
API v5 基础
- 接口地址:欧易文档当前给出的 REST 地址是
https://openapi.okx.com,实盘和模拟盘共用这个地址,靠请求头区分。如果你是在欧易美国、澳洲站(app.okx.com)注册的,要改用us.okx.com;在欧洲站(my.okx.com)注册的用eea.okx.com; - 返回格式:统一是 JSON,包含
code、msg、data三个字段。code为"0"表示成功,data是一个数组; - 数值都是字符串:价格、数量都以字符串返回,计算前要用
float()或Decimal()转换。
公共接口:获取行情和 K 线
公共接口不需要签名:
from datetime import datetime, timezone
import requests
BASE_URL = "https://openapi.okx.com"
def public_get(path, params=None):
resp = requests.get(BASE_URL + path, params=params, timeout=10)
resp.raise_for_status()
data = resp.json()
if data["code"] != "0":
raise RuntimeError(f"欧易返回错误:{data['code']} {data['msg']}")
return data["data"]
# 1. 最新行情
ticker = public_get("/api/v5/market/ticker", {"instId": "BTC-USDT"})[0]
print("最新成交价:", ticker["last"])
print("买一 / 卖一:", ticker["bidPx"], "/", ticker["askPx"])
print("24 小时最高 / 最低:", ticker["high24h"], "/", ticker["low24h"])
print("24 小时成交量(BTC):", ticker["vol24h"])
# 2. 最近 5 根 1 小时 K 线(最新的在最前面)
candles = public_get(
"/api/v5/market/candles",
{"instId": "BTC-USDT", "bar": "1H", "limit": "5"},
)
for ts, o, h, l, c, vol, vol_ccy, vol_ccy_quote, confirm in candles:
t = datetime.fromtimestamp(int(ts) / 1000, tz=timezone.utc)
state = "已收盘" if confirm == "1" else "进行中"
print(t.strftime("%m-%d %H:%M UTC"), "开", o, "高", h, "低", l, "收", c, state)
运行后会打印 BTC-USDT 的最新价、买一卖一,以及最近 5 根小时 K 线。要点:
instId是产品 ID,现货写成BTC-USDT,永续合约写成BTC-USDT-SWAP;- ticker 常用字段:
last最新成交价、bidPx/askPx买一 / 卖一价、high24h/low24h24 小时最高 / 最低价、vol24h24 小时成交量(现货以交易币计); - K 线的每一行是数组,顺序为
[ts, o, h, l, c, vol, volCcy, volCcyQuote, confirm],ts是开盘时间的毫秒时间戳,confirm为"1"表示这根 K 线已收盘; bar区分大小写:分钟用小写1m、15m,小时用大写1H、4H,日线1D;写成1h会返回 51000 参数错误;limit默认 100,最大 300,这个接口最多能取最近 1,440 根,更早的数据要用/api/v5/market/history-candles。
签名规则
私有接口的每个请求都必须带上 4 个请求头:
| 请求头 | 内容 |
|---|---|
| OK-ACCESS-KEY | 你的 API Key |
| OK-ACCESS-SIGN | 签名(见下文) |
| OK-ACCESS-TIMESTAMP | UTC 时间戳,ISO 8601 格式精确到毫秒,如 2026-10-22T08:30:15.123Z |
| OK-ACCESS-PASSPHRASE | 创建 Key 时设置的 Passphrase |
签名的生成方法:
- 拼接字符串:
时间戳 + 请求方法 + 请求路径 + 请求体; - 请求方法必须大写,如
GET、POST; - GET 请求的查询参数属于请求路径,例如
/api/v5/account/balance?ccy=BTC;GET 请求没有请求体,这部分留空; - POST 请求的请求体是 JSON 字符串,签名用的字符串必须和实际发送的一模一样;
- 用 Secret Key 对拼接结果做 HMAC-SHA256,再做 Base64 编码。
时间戳与欧易服务器相差超过 30 秒会被拒绝(错误码 50102),所以一定要用 UTC 时间,而不是本地时区。
把这些规则写成一个通用函数,保存为 okx_client.py:
import base64
import hashlib
import hmac
import json
import os
from datetime import datetime, timezone
import requests
BASE_URL = "https://openapi.okx.com"
API_KEY = os.environ["OKX_API_KEY"]
SECRET_KEY = os.environ["OKX_SECRET_KEY"]
PASSPHRASE = os.environ["OKX_PASSPHRASE"]
DEMO = True # True = 模拟盘;确认一切正常前不要改成 False
def get_timestamp():
# ISO 8601 UTC 时间,精确到毫秒,例如 2026-10-22T08:30:15.123Z
now = datetime.now(timezone.utc)
return now.isoformat(timespec="milliseconds").replace("+00:00", "Z")
def make_sign(timestamp, method, request_path, body):
prehash = timestamp + method + request_path + body
mac = hmac.new(SECRET_KEY.encode(), prehash.encode(), hashlib.sha256)
return base64.b64encode(mac.digest()).decode()
def private_request(method, path, params=None, body=None):
method = method.upper()
request_path = path
if params: # GET 参数要拼进 requestPath 一起签名
request_path += "?" + "&".join(f"{k}={v}" for k, v in params.items())
body_str = json.dumps(body, separators=(",", ":")) if body else ""
timestamp = get_timestamp()
headers = {
"OK-ACCESS-KEY": API_KEY,
"OK-ACCESS-SIGN": make_sign(timestamp, method, request_path, body_str),
"OK-ACCESS-TIMESTAMP": timestamp,
"OK-ACCESS-PASSPHRASE": PASSPHRASE,
"Content-Type": "application/json",
}
if DEMO:
headers["x-simulated-trading"] = "1"
resp = requests.request(
method, BASE_URL + request_path,
headers=headers, data=body_str or None, timeout=10,
)
data = resp.json()
if data.get("code") != "0":
raise RuntimeError(
f"欧易返回错误:code={data.get('code')} msg={data.get('msg')} data={data.get('data')}"
)
return data["data"]
注意 body_str 先生成、再同时用于签名和发送,这样能保证两者一致。
私有接口:查余额、下单、查单、撤单
在 okx_client.py 同一目录下新建一个脚本:
from okx_client import private_request
# 1. 查询交易账户余额
for acct in private_request("GET", "/api/v5/account/balance", {"ccy": "USDT,BTC"}):
print("账户总权益(美元):", acct["totalEq"])
for d in acct["details"]:
print(d["ccy"], "可用:", d["availBal"], "冻结:", d["frozenBal"])
# 2. 下一个远低于市价的限价买单(模拟盘)
order = private_request("POST", "/api/v5/trade/order", body={
"instId": "BTC-USDT",
"tdMode": "cash", # 现货非杠杆
"side": "buy",
"ordType": "limit",
"px": "50000", # 限价:USDT
"sz": "0.0001", # 数量:BTC
"clOrdId": "demo001", # 自定义订单号,可选
})[0]
print("下单结果:", order["sCode"], order["sMsg"], "订单号:", order["ordId"])
# 3. 查询订单状态
info = private_request("GET", "/api/v5/trade/order",
{"instId": "BTC-USDT", "ordId": order["ordId"]})[0]
print("状态:", info["state"], "已成交数量:", info["accFillSz"])
# 4. 撤单
cancel = private_request("POST", "/api/v5/trade/cancel-order",
body={"instId": "BTC-USDT", "ordId": order["ordId"]})[0]
print("撤单结果:", cancel["sCode"], cancel["sMsg"])
几个关键参数:
- 余额:
totalEq是以美元计的账户总权益;details里每个币种的availBal是可用余额,frozenBal是被挂单等占用的冻结余额。ccy可以不传,表示查询全部币种; - 下单:
tdMode现货非杠杆填cash;side为buy或sell;ordType为limit(限价)时必须带px;现货限价单的sz以交易币计(这里是 BTC)。最小下单量等规则可以用公共接口/api/v5/public/instruments?instType=SPOT&instId=BTC-USDT查询minSz、lotSz、tickSz; - 下单结果:每一笔订单的成败看
data里的sCode,"0"表示成功,同时返回欧易生成的ordId; - 订单状态
state:live等待成交、partially_filled部分成交、filled完全成交、canceled已撤销; - 查单和撤单都可以用
ordId或你自定义的clOrdId。
示例故意把价格设得远低于当前市价,下单后不会立即成交,方便练习查询和撤单,请根据当时行情调整。
模拟盘与实盘切换
模拟盘和实盘使用同一个接口地址和签名方式,区别只有两点:
- 使用在「交易 → 模拟交易 → 个人中心」里创建的模拟盘 API Key;
- 请求头加
x-simulated-trading: 1(上面代码里DEMO = True时自动添加)。
用实盘 Key 访问模拟盘,或者反过来,都会返回 50101。强烈建议先在模拟盘连续运行一段时间,确认下单数量、方向、异常处理都没有问题,再考虑换成实盘 Key 并把 DEMO 改为 False。
接口限速
| 接口 | 限速 | 计算依据 |
|---|---|---|
| 行情 /api/v5/market/ticker | 20 次 / 2 秒 | IP |
| K 线 /api/v5/market/candles | 40 次 / 2 秒 | IP |
| 余额 /api/v5/account/balance | 10 次 / 2 秒 | 用户 ID |
| 下单、撤单、查单 | 各 60 次 / 2 秒 | 用户 ID + 产品 ID |
超过限速会返回 50011。程序里应控制请求频率,需要实时推送行情时,官方推荐改用 WebSocket。
常见错误码
| 错误码 | 含义 | 常见原因 |
|---|---|---|
| 50011 | 超过限速 | 请求太频繁 |
| 50101 | API Key 与当前环境不匹配 | 实盘 / 模拟盘 Key 用错,或请求头漏加、多加 |
| 50102 | 时间戳过期 | 没用 UTC 时间或电脑时钟不准 |
| 50105 | Passphrase 错误 | 口令输错 |
| 50110 | IP 不在白名单内 | 换了服务器或网络 |
| 50111 / 50119 | API Key 无效 / 不存在 | Key 复制错误或已被删除 |
| 50113 | 签名无效 | 见下方 FAQ 的排查步骤 |
| 51000 | 参数错误 | 参数名或取值不对 |
| 51008 | 余额不足 | 可用余额不够下单 |
官方 SDK 与第三方库
熟悉原理后,也可以使用现成的库:欧易官方 Python SDK(pip install python-okx,官方文档的示例代码就基于它),或支持多家交易所的开源库 ccxt。无论用哪个,密钥都应当放在环境变量里。
下一步
- API Key 权限与安全设置:欧易 API 申请教程
- 不想写代码,可以试试官方的策略工具:欧易策略交易新手教程
- 合约交易前先了解保证金和强平:欧易合约新手入门
- 程序化交易更要算清成本:欧易手续费说明
常见问题
调用欧易行情接口需要 API Key 吗?
不需要。ticker、K 线、深度等公共行情接口可以直接访问,按 IP 限速。只有查询余额、下单、撤单等涉及账户的私有接口才需要 API Key 和签名。
签名一直报 50113 怎么办?
50113 表示签名无效。逐项检查:请求方法是否大写;GET 请求的查询参数是否拼进了签名用的路径;POST 请求签名用的 body 字符串是否和实际发送的完全一致;Secret Key 是否复制完整、没有多余空格。
报错 50102 是什么原因?
50102 表示时间戳过期,即请求时间和欧易服务器相差超过 30 秒。最常见的原因是用了本地时区而不是 UTC 时间,或者电脑时钟不准。可以调用 /api/v5/public/time 获取服务器时间对比。
模拟盘和实盘的代码有什么区别?
接口地址和签名方式完全一样,区别只有两点:要使用在模拟交易里创建的 API Key,并在请求头加上 x-simulated-trading 值为 1。实盘请求去掉这个请求头,换成实盘 Key。
有没有现成的 Python 库?
欧易官方提供 Python SDK(PyPI 包名 python-okx),开源库 ccxt 也支持欧易。不过先用本文的原生写法理解签名原理,排查问题时会轻松很多。
欧易的界面、费率和规则会调整,如与本文不一致,请以欧易官网最新信息为准。