#!/usr/bin/env python3 """ manual_order.py - 用户手动 raw REST 下单 OKX 永续合约 用法: python3 manual_order.py [leverage] 示例: python3 manual_order.py MSTR-USDT-SWAP sell 0.01 5 python3 manual_order.py BTC-USDT-SWAP buy 0.1 5 python3 manual_order.py ETH-USDT-SWAP sell 1 5 参数: INST_ID : OKX 合约 instId, 如 MSTR-USDT-SWAP SIDE : buy / sell SZ : 张数 (OKX 最小变动单位一般是 0.01 张) leverage : 可选, 默认 5, 自动调杠杆 ⚠ 部署位置: ~/.hermes/skills/trading/okx-auto-position/scripts/manual_order.py 调用方: process_signal 或用户手动 raw execute 反查: 调用后必须跑 check_account.py 验实际 contracts """ import json import os import sys import time import hmac import hashlib import base64 import requests from pathlib import Path def okx_get(path, t=15): """GET 请求 (用于查持仓/余额/调杠杆)""" creds = open(Path.home() / '.bashrc').read() k = s = pw = None for l in creds.splitlines(): if l.startswith('export OKX_API_KEY='): k = l.split('=', 1)[1].strip().strip('"') elif l.startswith('export OKX_SECRET='): s = l.split('=', 1)[1].strip().strip('"') elif l.startswith('export OKX_PASSPHRASE='): pw = l.split('=', 1)[1].strip().strip('"') ts = time.strftime('%Y-%m-%dT%H:%M:%S.000Z', time.gmtime()) method = 'GET' pre = ts + method + path sig = base64.b64encode(hmac.new(s.encode(), pre.encode(), hashlib.sha256).digest()).decode() h = {'OK-ACCESS-KEY': k, 'OK-ACCESS-SIGN': sig, 'OK-ACCESS-TIMESTAMP': ts, 'OK-ACCESS-PASSPHRASE': pw, 'Content-Type': 'application/json'} r = requests.get('https://www.okx.com' + path, headers=h, timeout=t, proxies={'http': 'http://127.0.0.1:7890', 'https': 'http://127.0.0.1:7890'}) return r.json() def okx_post(path, body, t=15): """POST 请求 (用于下单/撤单/调杠杆)""" creds = open(Path.home() / '.bashrc').read() k = s = pw = None for l in creds.splitlines(): if l.startswith('export OKX_API_KEY='): k = l.split('=', 1)[1].strip().strip('"') elif l.startswith('export OKX_SECRET='): s = l.split('=', 1)[1].strip().strip('"') elif l.startswith('export OKX_PASSPHRASE='): pw = l.split('=', 1)[1].strip().strip('"') ts = time.strftime('%Y-%m-%dT%H:%M:%S.000Z', time.gmtime()) method = 'POST' # ⚠ 必须是字符串, 别写 method=POST body_str = json.dumps(body) pre = ts + method + path + body_str sig = base64.b64encode(hmac.new(s.encode(), pre.encode(), hashlib.sha256).digest()).decode() h = {'OK-ACCESS-KEY': k, 'OK-ACCESS-SIGN': sig, 'OK-ACCESS-TIMESTAMP': ts, 'OK-ACCESS-PASSPHRASE': pw, 'Content-Type': 'application/json'} r = requests.post('https://www.okx.com' + path, headers=h, data=body_str, timeout=t, proxies={'http': 'http://127.0.0.1:7890', 'https': 'http://127.0.0.1:7890'}) return r.json() def set_leverage(inst_id, lever, side='net'): """调杠杆 (调杠杆后下单才能用对应张数)""" body = {'instId': inst_id, 'lever': str(lever), 'mgnMode': 'cross', 'posSide': side} return okx_post('/api/v5/account/set-leverage', body) def market_order(inst_id, side, sz, td_mode='cross', reduce_only=False): """市价单""" body = { 'instId': inst_id, 'tdMode': td_mode, 'side': side, 'posSide': 'net', 'ordType': 'market', 'sz': str(sz), } if reduce_only: body['reduceOnly'] = True return okx_post('/api/v5/trade/order', body) def main(): if len(sys.argv) < 4: print(__doc__) sys.exit(1) inst_id = sys.argv[1] side = sys.argv[2] # buy / sell sz = sys.argv[3] leverage = int(sys.argv[4]) if len(sys.argv) > 4 else 5 print(f'📊 准备下单:') print(f' 币种: {inst_id}') print(f' 方向: {side}') print(f' 张数: {sz}') print(f' 杠杆: {leverage}x') # 1. 调杠杆 print(f'\n1️⃣ 调杠杆 {leverage}x...') r = set_leverage(inst_id, leverage) print(json.dumps(r, ensure_ascii=False, indent=2)) # 2. 市价单 print(f'\n2️⃣ 市价单 {side} {sz} 张...') r = market_order(inst_id, side, sz) print(json.dumps(r, ensure_ascii=False, indent=2)) # 3. 提示反查 print(f'\n⚠ execute 后必查:') print(f' timeout 30 python3 ~/.hermes/skills/trading/okx-auto-position/scripts/check_account.py {inst_id}') if __name__ == '__main__': main()