- OKX交易自动化 (okx-auto-position, okx-crypto, okx-exchange) - 交易信号处理 (signal-confirmation-templates, trading-signal-aggregator) - 量化因子挖掘 (quant-factor-mining) - 长桥集成 (longbridge-cli, longbridge-python-sdk) - 六合彩分析 (lottery-hk) - 股息投资 (dividend-investing, dividend-scanner) - 日内交易 (intraday-trading) - 同花顺 (tonghuashun)
155 lines
6.9 KiB
Markdown
155 lines
6.9 KiB
Markdown
# OKX Raw REST API Workflow (when ccxt times out)
|
|
|
|
When ccxt's `fetch_balance()` times out over Mihomo proxy (SSL handshake on `fetch_currencies()`), use raw `curl` + `openssl` HMAC signing instead. This approach works reliably for all operations.
|
|
|
|
## Common Setup
|
|
|
|
```bash
|
|
# Read credentials from file (avoids bashrc non-interactive guard and $ expansion)
|
|
P=$(cat ~/.bashrc | grep "PASSPHRASE" | head -1 | sed 's/.*=//')
|
|
A=$(cat ~/.bashrc | grep "API_KEY" | head -1 | sed 's/.*=//')
|
|
S=$(cat ~/.bashrc | grep "OKX_SECRET" | head -1 | sed 's/.*=//')
|
|
|
|
# Generate timestamp and signature
|
|
TS=$(date -u +"%Y-%m-%dT%H:%M:%S.000Z")
|
|
MSG="${TS}GET/api/v5/account/balance"
|
|
SIG=$(echo -n "$MSG" | openssl dgst -sha256 -hmac "$S" -binary | base64)
|
|
|
|
# For POST requests, include body in signature
|
|
BODY='{"instId":"MU-USDT-SWAP","tdMode":"cross","side":"sell","posSide":"net","ordType":"market","sz":"0.39"}'
|
|
MSG="${TS}POST/api/v5/trade/order${BODY}"
|
|
SIG=$(echo -n "$MSG" | openssl dgst -sha256 -hmac "$S" -binary | base64)
|
|
```
|
|
|
|
## Operations
|
|
|
|
### 1. Check Balance (`GET /api/v5/account/balance`)
|
|
```bash
|
|
TS=$(date -u +"%Y-%m-%dT%H:%M:%S.000Z")
|
|
MSG="${TS}GET/api/v5/account/balance"
|
|
SIG=$(echo -n "$MSG" | openssl dgst -sha256 -hmac "$S" -binary | base64)
|
|
curl -s --max-time 15 --proxy http://127.0.0.1:7890 \
|
|
-H "OK-ACCESS-KEY: $A" \
|
|
-H "OK-ACCESS-SIGN: $SIG" \
|
|
-H "OK-ACCESS-TIMESTAMP: $TS" \
|
|
-H "OK-ACCESS-PASSPHRASE: $P" \
|
|
"https://www.okx.com/api/v5/account/balance"
|
|
```
|
|
|
|
### 2. Set Leverage (`POST /api/v5/account/set-leverage`)
|
|
```bash
|
|
BODY='{"instId":"MU-USDT-SWAP","mgnMode":"cross","lever":"10"}'
|
|
TS=$(date -u +"%Y-%m-%dT%H:%M:%S.000Z")
|
|
MSG="${TS}POST/api/v5/account/set-leverage${BODY}"
|
|
SIG=$(echo -n "$MSG" | openssl dgst -sha256 -hmac "$S" -binary | base64)
|
|
curl -s -X POST -H "Content-Type: application/json" --max-time 15 --proxy http://127.0.0.1:7890 \
|
|
-H "OK-ACCESS-KEY: $A" -H "OK-ACCESS-SIGN: $SIG" \
|
|
-H "OK-ACCESS-TIMESTAMP: $TS" -H "OK-ACCESS-PASSPHRASE: $P" \
|
|
-d "$BODY" "https://www.okx.com/api/v5/account/set-leverage"
|
|
```
|
|
|
|
### 3. Place Market Order (`POST /api/v5/trade/order`)
|
|
```bash
|
|
# Short
|
|
BODY='{"instId":"MU-USDT-SWAP","tdMode":"cross","side":"sell","posSide":"net","ordType":"market","sz":"0.39"}'
|
|
# Long
|
|
BODY='{"instId":"MU-USDT-SWAP","tdMode":"cross","side":"buy","posSide":"net","ordType":"market","sz":"1"}'
|
|
TS=$(date -u +"%Y-%m-%dT%H:%M:%S.000Z")
|
|
MSG="${TS}POST/api/v5/trade/order${BODY}"
|
|
SIG=$(echo -n "$MSG" | openssl dgst -sha256 -hmac "$S" -binary | base64)
|
|
curl -s -X POST -H "Content-Type: application/json" --max-time 20 --proxy http://127.0.0.1:7890 \
|
|
-H "OK-ACCESS-KEY: $A" -H "OK-ACCESS-SIGN: $SIG" \
|
|
-H "OK-ACCESS-TIMESTAMP: $TS" -H "OK-ACCESS-PASSPHRASE: $P" \
|
|
-d "$BODY" "https://www.okx.com/api/v5/trade/order"
|
|
```
|
|
|
|
### 4. Check Position (`GET /api/v5/account/positions`)
|
|
```bash
|
|
TS=$(date -u +"%Y-%m-%dT%H:%M:%S.000Z")
|
|
MSG="${TS}GET/api/v5/account/positions?instType=SWAP&instId=MU-USDT-SWAP"
|
|
SIG=$(echo -n "$MSG" | openssl dgst -sha256 -hmac "$S" -binary | base64)
|
|
curl -s --max-time 15 --proxy http://127.0.0.1:7890 \
|
|
-H "OK-ACCESS-KEY: $A" -H "OK-ACCESS-SIGN: $SIG" \
|
|
-H "OK-ACCESS-TIMESTAMP: $TS" -H "OK-ACCESS-PASSPHRASE: $P" \
|
|
"https://www.okx.com/api/v5/account/positions?instType=SWAP&instId=MU-USDT-SWAP"
|
|
```
|
|
|
|
Key fields returned:
|
|
- `pos`: negative = short, positive = long
|
|
- `avgPx`: entry price
|
|
- `markPx`: current mark price
|
|
- `liqPx`: liquidation price
|
|
- `upl`: unrealized PnL
|
|
- `imr`: initial margin (保证金)
|
|
- `mgnRatio`: margin ratio
|
|
- `closeOrderAlgo`: existing TP/SL algo orders
|
|
|
|
### 5. Set TP/SL (POST /api/v5/trade/order-algo)
|
|
|
|
**Preferred: use ordType 'oco' for paired TP+SL in one call:**
|
|
```bash
|
|
# Short: side=buy (buy to close), TP below, SL above
|
|
BODY='{"instId":"MU-USDT-SWAP","tdMode":"cross","side":"buy","posSide":"net","ordType":"oco","sz":"0.39","tpTriggerPx":"1041.83","tpOrdPx":"-1","tpTriggerPxType":"last","slTriggerPx":"1200.37","slOrdPx":"-1","slTriggerPxType":"last","reduceOnly":"true"}'
|
|
# Long: side=sell (sell to close), TP above, SL below
|
|
BODY='{"instId":"MU-USDT-SWAP","tdMode":"cross","side":"sell","posSide":"net","ordType":"oco","sz":"1","tpTriggerPx":"1300","tpOrdPx":"-1","tpTriggerPxType":"last","slTriggerPx":"1100","slOrdPx":"-1","slTriggerPxType":"last","reduceOnly":"true"}'
|
|
```
|
|
|
|
**WARNING**: When using ordType 'conditional' with BOTH tpTriggerPx and slTriggerPx in a single request, only the SL is actually created -- the TP is silently dropped (returns code 0, no error, but tpTriggerPx is empty in the response). If you must use 'conditional' for both, place TWO separate requests:
|
|
|
|
```bash
|
|
# SL only (one request)
|
|
BODY_SL='{"instId":"SOL-USDT-SWAP","tdMode":"cross","side":"buy","posSide":"net","ordType":"conditional","sz":"0.1","slTriggerPx":"84.50","slOrdPx":"-1","slTriggerPxType":"last"}'
|
|
# TP only (separate request)
|
|
BODY_TP='{"instId":"SOL-USDT-SWAP","tdMode":"cross","side":"buy","posSide":"net","ordType":"conditional","sz":"0.1","tpTriggerPx":"80.00","tpOrdPx":"-1","tpTriggerPxType":"last"}'
|
|
```
|
|
|
|
Verify pending algo orders with:
|
|
```bash
|
|
curl -s --max-time 15 --proxy http://127.0.0.1:7890 \
|
|
-H "OK-ACCESS-KEY: $A" -H "OK-ACCESS-SIGN: $SIG" \
|
|
-H "OK-ACCESS-TIMESTAMP: $TS" -H "OK-ACCESS-PASSPHRASE: $P" \
|
|
"https://www.okx.com/api/v5/trade/orders-algo-pending?instType=SWAP&instId=SOL-USDT-SWAP&ordType=conditional"
|
|
```
|
|
TS=$(date -u +"%Y-%m-%dT%H:%M:%S.000Z")
|
|
MSG="${TS}POST/api/v5/trade/order-algo${BODY}"
|
|
SIG=$(echo -n "$MSG" | openssl dgst -sha256 -hmac "$S" -binary | base64)
|
|
curl -s -X POST -H "Content-Type: application/json" --max-time 20 --proxy http://127.0.0.1:7890 \
|
|
-H "OK-ACCESS-KEY: $A" -H "OK-ACCESS-SIGN: $SIG" \
|
|
-H "OK-ACCESS-TIMESTAMP: $TS" -H "OK-ACCESS-PASSPHRASE: $P" \
|
|
-d "$BODY" "https://www.okx.com/api/v5/trade/order-algo"
|
|
```
|
|
|
|
### 6. Get Current Price (`GET /api/v5/market/ticker`)
|
|
Public endpoint — no signing needed:
|
|
```bash
|
|
curl -s --max-time 10 --proxy http://127.0.0.1:7890 \
|
|
"https://www.okx.com/api/v5/market/ticker?instId=MU-USDT-SWAP" \
|
|
| python3 -c "import sys,json; print(json.load(sys.stdin)['data'][0]['last'])"
|
|
```
|
|
|
|
### 7. Get 4H ATR (via candle data)
|
|
```bash
|
|
CANDLES=$(curl -s --max-time 15 --proxy http://127.0.0.1:7890 \
|
|
"https://www.okx.com/api/v5/market/history-candles?instId=MU-USDT-SWAP&bar=4H&limit=30")
|
|
ATR=$(echo "$CANDLES" | python3 -c "
|
|
import sys, json
|
|
d = json.load(sys.stdin)['data']
|
|
trs = []
|
|
for i in range(1, len(d)):
|
|
h = float(d[i][2]); l = float(d[i][3]); pc = float(d[i-1][4])
|
|
trs.append(max(h-l, abs(h-pc), abs(l-pc)))
|
|
print(sum(trs)/len(trs))
|
|
")
|
|
echo "ATR(4H): $ATR"
|
|
```
|
|
|
|
## Common Response Codes
|
|
|
|
| Code | Meaning | Fix |
|
|
|------|---------|-----|
|
|
| 0 | Success | — |
|
|
| 50103 | OK-ACCESS-KEY empty | Credentials not loaded; use file grep |
|
|
| 50105 | OK-ACCESS-PASSPHRASE incorrect | Passphrase contains `$` that bash expanded; read from file literally |
|
|
| 51000 | Parameter posSide error | Account is net_mode, pass `"posSide":"net"` or omit |
|
|
| exit 28 | curl timeout | Proxy or SSL issue; retry or check Mihomo |
|