欧易 API Python 教程:获取行情、K 线、查询余额与下单(附签名代码)

用 Python 调用欧易(OKX)API v5 的入门教程:只依赖 requests 获取实时行情和 K 线,按官方规则生成 OK-ACCESS-SIGN 签名,查询账户余额、下限价单、查询和撤销订单,附模拟盘请求头、接口限速和常见错误码说明,代码均经过实际运行。

拾阶编辑部最后核对 约 7 分钟读完
要点速览
  1. 行情、K 线等公共接口不需要 API Key,用 requests 发一个 GET 请求就能拿到数据。
  2. 私有接口要带 4 个请求头,签名 = Base64(HMAC-SHA256(时间戳 + 请求方法 + 请求路径 + 请求体, Secret Key))。
  3. GET 请求的查询参数要拼进请求路径一起签名,时间戳必须是 UTC 毫秒格式,与服务器相差超过 30 秒会被拒绝。
  4. 先用模拟盘 Key 加上 x-simulated-trading 请求头把程序跑通,确认无误后再考虑实盘。
本文目录 11
  1. 准备工作
  2. API v5 基础
  3. 公共接口:获取行情和 K 线
  4. 签名规则
  5. 私有接口:查余额、下单、查单、撤单
  6. 模拟盘与实盘切换
  7. 接口限速
  8. 常见错误码
  9. 官方 SDK 与第三方库
  10. 下一步
  11. 常见问题

欧易提供 REST 和 WebSocket 两套 API。本文用 Python 和 requests 库演示最常用的 REST 操作:先拿公开行情和 K 线,再按官方规则生成签名,查询余额、下单、查单和撤单。文中代码都在 2026 年 10 月实际运行过,接口和字段名以欧易 API v5 官方文档为准。

本文是编程教程,不构成任何投资建议。涉及下单的代码请一律先在模拟盘运行,程序写错一个参数,在实盘就是真金白银的损失。

准备工作

  1. 安装 Python 3 和 requests:
pip install requests
  1. 只做第一部分(公共行情)不需要任何 Key。要调用私有接口,先按 欧易 API 申请教程 创建一把模拟盘 API Key,权限勾选「读取」和「交易」即可。

  2. 把密钥放进环境变量,不要直接写在代码里。代码文件容易被分享、截图或上传到网上,环境变量则只存在于你的电脑中。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 / low24h 24 小时最高 / 最低价、vol24h 24 小时成交量(现货以交易币计);
  • 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

签名的生成方法:

  1. 拼接字符串:时间戳 + 请求方法 + 请求路径 + 请求体;
  2. 请求方法必须大写,如 GET、POST;
  3. GET 请求的查询参数属于请求路径,例如 /api/v5/account/balance?ccy=BTC;GET 请求没有请求体,这部分留空;
  4. POST 请求的请求体是 JSON 字符串,签名用的字符串必须和实际发送的一模一样;
  5. 用 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。

示例故意把价格设得远低于当前市价,下单后不会立即成交,方便练习查询和撤单,请根据当时行情调整。

模拟盘与实盘切换

模拟盘和实盘使用同一个接口地址和签名方式,区别只有两点:

  1. 使用在「交易 → 模拟交易 → 个人中心」里创建的模拟盘 API Key;
  2. 请求头加 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 吗?

不需要。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 也支持欧易。不过先用本文的原生写法理解签名原理,排查问题时会轻松很多。

还没有欧易账户?注册时填写邀请码 ,可享新人福利与手续费返佣(以欧易活动页为准)。
前往欧易注册
参考资料

欧易的界面、费率和规则会调整,如与本文不一致,请以欧易官网最新信息为准。