Files

255 lines
7.4 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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
}
```
---
## 2. OCO订单合并(加仓场景)
**问题**:加仓后,旧OCO只覆盖旧仓位,新OCO只覆盖新仓位,导致多个OCO并存。
**正确流程**
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覆盖全部持仓
---
## 3. 条件单API参数(2026-07-05新增)
**Trigger订单(做T用)**
```python
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
```python
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:` 要求两个参数同时存在。
**正确两步流程**
```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)"
```
---
## 7. 余额为零时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)
```
---
## 8. 密码中含特殊字符
**问题**`OKX_PASSPHRASE``$` 等特殊字符时,bash 会尝试变量展开。
**正确**:从文件读取:
```python
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` 参数不能组合查询,需逐个类型查。
```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"]
```
---
## 10. raw API posSide显示"net"
**问题**net_mode下,`/api/v5/account/positions` 返回的 `posSide` 字段是 `"net"` 而非 `"long"`/`"short"`
**解决**:用 `pos` 字段判断方向:
```python
side = "long" if float(pos) >= 0 else "short"
```
---
## 11. --close是全平,部分平仓需手动下单(2026-07-06新增)
**问题**advisor脚本的 `--close` 参数会平掉该币种**全部仓位**,无法指定平仓数量。
**部分平仓方法**
```python
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'}
)
```
**减仓百分比计算**
```python
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标准用法**
```python
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直接查询:
```python
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/性价比
- ❌ 查实际持仓数量(不准)
- ❌ 查可用余额(不准)