9.7 KiB
OKX API 关键Pitfalls
1. posMode=net_mode vs long_short_mode
问题:账户可能是 net_mode(净头寸)而非 long_short_mode(多空分离)。
检查方法:
GET /api/v5/account/config
# 响应中 "posMode": "net_mode" 或 "long_short_mode"
影响:
net_mode:禁止传posSide参数,否则报错sCode=51000 "Parameter posSide error"long_short_mode:必须传posSide(long/short)
下单示例(net_mode):
{
"instId": "ETH-USDT-SWAP",
"tdMode": "cross",
"side": "buy", # buy=开多/平空, sell=开空/平多
"ordType": "market",
"sz": "1" # 不传posSide!
}
设置杠杆(net_mode):
{
"instId": "ETH-USDT-SWAP",
"lever": "25",
"mgnMode": "cross" # 不传posSide!
}
2. OCO订单合并(加仓场景)
问题:加仓后,旧OCO只覆盖旧仓位,新OCO只覆盖新仓位,导致多个OCO并存。
正确流程:
- 查现有OCO:
GET /api/v5/trade/orders-algo-pending?ordType=oco - 找到同instId的旧OCO algoId
- 删除旧OCO:
POST /api/v5/trade/cancel-algo→[{instId, algoId}] - 创建新OCO覆盖全部持仓
3. 条件单API参数(2026-07-05新增)
Trigger订单(做T用):
okx_post('/api/v5/trade/order-algo', {
"instId": "ETH-USDT-SWAP",
"tdMode": "cross",
"side": "buy",
"ordType": "trigger", # 用trigger不是conditional
"sz": "4",
"triggerPx": "1770",
"triggerPxType": "last",
"orderPx": "-1" # 参数名是orderPx不是ordPx
})
关键错误:
- ❌
"ordPx": "-1"→ 报错50014: Parameter orderPx can not be empty - ❌ 加
"reduceOnly": "true"→ 报错51205: Reduce Only is not available - ❌ 用
conditional类型 → SL触发价不能低于当前价
Trigger vs Conditional vs OCO:
| 类型 | 用途 | 触发方向 |
|---|---|---|
| trigger | 价格到任意方向触发 | 任意 |
| conditional | 止损/止盈 | SL不能低于现价 |
| oco | 同时设TP+SL | 双腿 |
详见 references/okx-trigger-orders.md
4. OCO的sz必须是lot_sz的整数倍
问题:加仓后position=14.77张,但OCO设置sz=14.77时报错 "Order quantity must be a multiple of the lot size"。
解决:OCO的sz向下取整到lot_sz:
oco_sz = int(position_contracts) # 14.77 → 14
5. 凭证变量名:OKX_SECRET(不是OKX_SECRET_KEY)
bashrc里实际变量名是 OKX_SECRET,不是 OKX_SECRET_KEY。
三个变量:OKX_API_KEY、OKX_SECRET、OKX_PASSPHRASE
6. --execute 必须同时带 --rec-json
脚本代码 if args.execute and args.rec_json: 要求两个参数同时存在。
正确两步流程:
# 第1步:获取推荐JSON
python3 okx_position_advisor.py --symbol HYPE --side long --leverage 10 --json > /tmp/rec.json
# 第2步:执行下单(必须同时带 --execute 和 --rec-json)
python3 okx_position_advisor.py --symbol HYPE --side long --leverage 10 --execute --json --rec-json "$(cat /tmp/rec.json)"
7. 余额为零时ZeroDivisionError
问题:recommend_position() 函数在计算 margin_pct = total_margin / acct_info['usdt_free'] * 100 时,如果 usdt_free=0(用户满仓),会抛出 ZeroDivisionError。
修复:在调用 recommend_position() 前检查余额:
if acct_info['usdt_free'] < 0.01:
print("⚠️ 余额不足(可用0 USDT),无法开仓")
sys.exit(0)
8. 密码中含特殊字符
问题:OKX_PASSPHRASE 含 $ 等特殊字符时,bash 会尝试变量展开。
正确:从文件读取:
with open("~/.bashrc", "r") as f:
for line in f:
if "OKX_PASSPHRASE" in line:
passphrase = line.split("=", 1)[1].strip().strip('"').strip("'")
9. 遍历查询algo orders
问题:ordType 参数不能组合查询,需逐个类型查。
for algo_type in ["oco", "conditional", "trigger", "move_order_stop"]:
result = okx_get(f"/api/v5/trade/orders-algo-pending?ordType={algo_type}")
# 处理 result["data"]
10. raw API posSide显示"net"
问题:net_mode下,/api/v5/account/positions 返回的 posSide 字段是 "net" 而非 "long"/"short"。
解决:用 pos 字段判断方向:
side = "long" if float(pos) >= 0 else "short"
11. --close是全平,部分平仓需手动下单(2026-07-06新增)
问题:advisor脚本的 --close 参数会平掉该币种全部仓位,无法指定平仓数量。
部分平仓方法:
from okx_position_advisor import load_credentials, create_exchange
creds = load_credentials()
exchange = create_exchange(creds)
# 卖出指定张数(平多)
order = exchange.create_order(
symbol='ETH/USDT:USDT',
type='market',
side='sell', # sell=平多, buy=平空
amount=4, # 指定张数
params={'tdMode': 'cross'}
)
减仓百分比计算:
import math
current_contracts = 14.09
reduce_pct = 0.30 # 减三成
close_contracts = math.floor(current_contracts * reduce_pct) # 4张
⚠️ 注意:ccxt的create_order直接下单,不会自动清理OCO。部分平仓后,旧OCO可能覆盖已不存在的仓位(OKX会自动处理,但最好手动检查)。
12. 直接curl调OKX API返回403但ccxt正常(2026-07-06新增)
问题:用curl+proxy直接调OKX REST API返回403 Forbidden,但通过ccxt(同样走proxy)正常工作。
可能原因:
- OKX API key绑定了IP白名单,ccxt的请求头与curl不同
- ccxt自动处理了某些认证细节(如nonce、签名格式)
解决:所有API操作统一用ccxt,不要手写curl。只有ccxt超时时才回退到curl。
ccxt标准用法:
from okx_position_advisor import load_credentials, create_exchange
creds = load_credentials()
exchange = create_exchange(creds)
exchange.timeout = 30000 # 30s超时
# 查持仓
positions = exchange.fetch_positions(['ETH/USDT:USDT'])
active = [p for p in positions if abs(float(p.get('contracts', 0))) > 0]
# 查余额
bal = exchange.fetch_balance()
free_usdt = bal.get('free', {}).get('USDT', 0)
# 下单
order = exchange.create_order('ETH/USDT:USDT', 'market', 'buy', 4, {'tdMode': 'cross'})
13. advisor脚本的acct_free和contracts可能不准(2026-07-06新增)
问题:okx_position_advisor.py --json 返回的 acct_free 和 contracts 字段可能与实际不符。
实测案例:
- advisor返回:
acct_free=0.97, contracts=0.04 - 实际ccxt查:持仓14.09张ETH多,可用0 USDT(满仓)
原因:advisor的recommend_position()内部计算逻辑可能截断或取整异常,且acct_free只反映当时快照(可能已过时)。
解决:查持仓和余额必须用ccxt直接查询:
positions = exchange.fetch_positions()
bal = exchange.fetch_balance()
active = [p for p in positions if abs(float(p.get('contracts', 0))) > 0]
for p in active:
side = '多' if float(p['contracts']) > 0 else '空'
print(f"{p['symbol']}: {p['contracts']}张 {side} | 均价: {p.get('entryPrice','-')} | 浮盈: {p.get('unrealizedPnl','-')}")
print(f"可用: {bal.get('free',{}).get('USDT',0)} USDT")
advisor脚本用途:
- ✅ 算TP/SL/ATR/性价比
- ❌ 查实际持仓数量(不准)
- ❌ 查可用余额(不准)
14. /api/v5/trade/orders-history 必须传 instType=SWAP + state=filled(2026-09-19 新增)
问题: 查历史平仓订单拿真实 pnl 时,两个参数都错会拉不到数据。
踩坑:
- ❌ 漏传
instType→ code 50014 "Parameter instType can not be empty" - ❌
state='2'(数字) → code 51600 "Status not found" - ✅
state='filled'(字符串) +instType='SWAP'→ code 0, 返回正常
正确调用:
resp = _okx_raw_request('GET', '/api/v5/trade/orders-history', {
'instId': 'ZEC-USDT-SWAP',
'instType': 'SWAP', # ← 必须!
'state': 'filled', # ← 字符串, 数字 2 不行
'limit': '30',
})
# 响应每条订单都有 pnl 字段 = 真实已实现盈亏 (含手续费/funding)
# info.pnl, info.avgPx, info.fillTime, info.fee
注: /api/v5/trade/orders-history-archive (更久之前) 同样要 instType=SWAP + state=filled。
实战: 用 info.pnl 拿 ZEC 平仓真实盈亏 = +$15.40,比 info.avgPx × 数量 × 方向 算的准(已扣手续费)。
用途: 平仓推送带真实 pnl(2026-09-19 新增功能)—— process_signal.py 的 format_close_message 调 _fetch_close_pnl_details 拿 info.pnl 直接拼推送。
15. 平仓推送带 pnl + 持仓时长(2026-09-19 新增)
问题: 平仓推送只说"已市价全平",没 pnl,没持仓时长,要去 OKX app 查。
修复 (process_signal.py::format_close_message):
- 用
_fetch_close_pnl_details拉 OKX/api/v5/trade/orders-history拿真实 pnl (含手续费/funding) - 用
pos['cTime'](开仓时间戳) +info.fillTime(平仓时间戳) 算持仓时长 - 格式:
3天5小时/5小时22分/22分(根据大小自动选)
推送示例:
✅ ZEC 做多 🟩 10x 已市价全平 | 熬鹰
long 157张 @ 1247.50 → 平仓价 $1248.19
⏱ 持仓: 1天3小时
💰 实际盈亏: **+$15.40** (OKX 实测, 含手续费/funding)
💡 OKX 历史订单实数据
前置: pos_before 必须包含 cTime 字段 (持仓开仓时间戳)—— _okx_raw_request('GET', '/api/v5/account/positions', {'instId': inst_id}) 返回里就有,直接加进 pos_before dict。
坑: info.pnl 字段在 OKX swap 已经扣手续费 + funding,不需要再扣一次 fee 字段。