7.4 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/性价比
- ❌ 查实际持仓数量(不准)
- ❌ 查可用余额(不准)