Files
Hermes-Skills/trading/okx-crypto/references/raw-rest-api-workflow.md
T
Hermes Skills Manager 6770bc9b9d Initial commit: Hermes Agent skills collection
- Trading skills (OKX, dividend, lottery, quantitative)
- Creative skills (ASCII art, diagrams, video)
- Development skills (GitHub, debugging, TDD)
- Research skills (arXiv, blog monitoring)
- Productivity skills (email, documents, notes)
- MCP integration skills
- Custom user skills
2026-07-05 02:31:15 -04:00

6.9 KiB

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

# 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)

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)

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)

# 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)

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:

# 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:

# 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:

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)

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