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