Files

7.4 KiB
Raw Permalink Blame History

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并存。

正确流程

  1. 查现有OCOGET /api/v5/trade/orders-algo-pending?ordType=oco
  2. 找到同instId的旧OCO algoId
  3. 删除旧OCOPOST /api/v5/trade/cancel-algo[{instId, algoId}]
  4. 创建新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_KEYOKX_SECRETOKX_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_freecontracts 字段可能与实际不符。

实测案例

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