# OKX API 关键Pitfalls ## 1. posMode=net_mode vs long_short_mode **问题**:账户可能是 `net_mode`(净头寸)而非 `long_short_mode`(多空分离)。 **检查方法**: ```python 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)**: ```python { "instId": "ETH-USDT-SWAP", "tdMode": "cross", "side": "buy", # buy=开多/平空, sell=开空/平多 "ordType": "market", "sz": "1" # 不传posSide! } ``` **设置杠杆(net_mode)**: ```python { "instId": "ETH-USDT-SWAP", "lever": "25", "mgnMode": "cross" # 不传posSide! } ``` **切换模式**(需要主账户权限): ```python POST /api/v5/account/set-position-mode {"posMode": "long_short_mode"} # 或 "net_mode" ``` --- ## 2. OCO订单合并(加仓场景) **问题**:加仓后,旧OCO只覆盖旧仓位,新OCO只覆盖新仓位,导致多个OCO并存。 **错误示例**: - 持仓5张,OCO(sell 5, SL=1660, TP=1746) - 加仓1张 → 持仓6张 - 新建OCO(sell 1, SL=1666.8, TP=1775.1) - 结果:2个OCO并存,旧OCO触发只平5张,剩1张单独走 **正确流程**: 1. 查现有OCO:`GET /api/v5/trade/orders-algo-pending?ordType=oco` 2. 找到同instId的旧OCO algoId 3. 删除旧OCO:`POST /api/v5/trade/cancel-algo` → `[{instId, algoId}]` 4. 创建新OCO覆盖全部持仓 **验证**: ```bash # 查pending OCO curl -X GET "https://www.okx.com/api/v5/trade/orders-algo-pending?ordType=oco" \ -H "OK-ACCESS-KEY: $KEY" ... # 应该只有一个OCO per instId ``` --- ## 3. 补推信号必须先查持仓 **场景**:模型断线后补推积压信号。 **错误做法**:直接用历史信号数据推送推荐(如"4,290 ETH加仓到4,455")。 **正确做法**: 1. 先查当前持仓:`GET /api/v5/account/positions` 2. 再查当前algo orders:`GET /api/v5/trade/orders-algo-pending?ordType=oco` 3. 用**当前持仓数据**而非历史信号数据生成推荐 **案例**: - 历史信号说"4,290 ETH" - 实际持仓已是5张(可能中间已有多次变动) - 用历史数据推"加仓到4,455"是错误的 --- ## 4. 遍历查询algo orders **问题**:`ordType` 参数不能组合查询,需逐个类型查。 ```python 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"] ``` **注意**:某些类型可能返回错误(如账户未开通该功能),需忽略错误继续。 --- ## 5. 密码中含特殊字符 **问题**:`OKX_PASSPHRASE` 含 `$` 等特殊字符时,bash 会尝试变量展开。 **错误**:`export OKX_PASSPHRASE=mikeOkxID$1` → `$1` 展开为空 **正确**: ```bash export OKX_PASSPHRASE='mikeOkxID$1' # 单引号 ``` **或从文件读取**: ```python with open("~/.bashrc", "r") as f: for line in f: if "OKX_PASSPHRASE" in line: passphrase = line.split("=", 1)[1].strip().strip('"').strip("'") ``` --- ## 6. 凭证变量名:OKX_SECRET(不是OKX_SECRET_KEY) bashrc里实际变量名是 `OKX_SECRET`,不是 `OKX_SECRET_KEY`。 **脚本内部**:`load_credentials()` 用 `OKX_\w+` 正则匹配,自动兼容,不受影响。 **手动ccxt初始化**(agent写临时Python时): ```python # ❌ 错误 — 会 KeyError creds['OKX_SECRET_KEY'] # ✅ 正确 creds['OKX_SECRET'] # ✅ 兼容写法 secret = creds.get('OKX_SECRET', '') or creds.get('OKX_SECRET_KEY', '') ``` **三个变量**:`OKX_API_KEY`、`OKX_SECRET`、`OKX_PASSPHRASE` --- ## 7. --execute 必须同时带 --rec-json 脚本代码 `if args.execute and args.rec_json:` 要求两个参数同时存在。 **错误**:只传 `--execute` 不传 `--rec-json` → 静默跳过执行,fall through到推荐流程 **正确两步流程**: ```bash # 第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)" ``` --- ## 8. 余额为零时ZeroDivisionError **问题**:`recommend_position()` 函数在计算 `margin_pct = total_margin / acct_info['usdt_free'] * 100` 时,如果 `usdt_free=0`(用户满仓),会抛出 `ZeroDivisionError`。 **修复**:在调用 `recommend_position()` 前检查余额: ```python if acct_info['usdt_free'] < 0.01: print("⚠️ 余额不足(可用0 USDT),无法开仓") sys.exit(0) ``` **format_signal.py 已加 try/except 处理此场景。**