okx-auto-position: 新增脚本 (whitelist.json 驱动白名单/manual_order/代理测试/执行验证)
This commit is contained in:
@@ -0,0 +1,134 @@
|
||||
#!/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()
|
||||
Reference in New Issue
Block a user