134 lines
4.6 KiB
Python
134 lines
4.6 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
manual_order.py - 用户手动 raw REST 下单 OKX 永续合约
|
|
|
|
用法:
|
|
python3 manual_order.py <INST_ID> <SIDE> <SZ> [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() |