Files
Hermes-Skills/trading/okx-auto-position/scripts/cost_performance.py
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

281 lines
9.3 KiB
Python

"""
性价比检查模块
供 okx_position_advisor.py 调用
"""
import os, sys
sys.path.insert(0, os.path.dirname(__file__))
from config_loader import get as cfg
def get_okx_fee_rate(inst_type='SWAP'):
"""
从OKX API获取实际费率
返回: (maker_rate, taker_rate) 正数表示收费,负数表示返佣
"""
import requests, hmac, hashlib, base64, time, os, re
# 读取凭证
creds = {}
with open(os.path.expanduser("~/.bashrc")) as f:
for line in f:
m = re.match(r'export\s+(OKX_\w+)=(.*)', line.strip())
if m:
val = m.group(2).strip().strip('"').strip("'")
creds[m.group(1)] = val
api_key = creds.get('OKX_API_KEY', '')
secret = creds.get('OKX_SECRET', '')
passphrase = creds.get('OKX_PASSPHRASE', '')
proxies = {"http": "http://127.0.0.1:7890", "https": "http://127.0.0.1:7890"}
ts = time.strftime("%Y-%m-%dT%H:%M:%S.000Z", time.gmtime())
path = f"/api/v5/account/trade-fee?instType={inst_type}"
msg = f"{ts}GET{path}"
sig = hmac.new(secret.encode(), msg.encode(), hashlib.sha256).digest()
sig_b64 = base64.b64encode(sig).decode()
headers = {
"OK-ACCESS-KEY": api_key,
"OK-ACCESS-SIGN": sig_b64,
"OK-ACCESS-TIMESTAMP": ts,
"OK-ACCESS-PASSPHRASE": passphrase,
}
try:
r = requests.get(f"https://www.okx.com{path}", headers=headers, proxies=proxies, timeout=15)
data = r.json()
if data['code'] == '0' and data['data']:
maker = float(data['data'][0]['maker'])
taker = float(data['data'][0]['taker'])
return maker, taker
except Exception as e:
pass
# 默认费率 (fallback)
return 0.0002, 0.0005
def calc_cost_performance(entry_price, sl_price, tp_price, contracts, ct_val, leverage, fee_rate=None):
"""
计算开仓性价比
参数:
entry_price: 入场价
sl_price: 止损价
tp_price: 止盈价
contracts: 合约张数
ct_val: 合约面值 (如ETH=0.1)
leverage: 杠杆倍数
fee_rate: 单边手续费率 (默认从OKX API获取)
返回:
dict: {
'rr_ratio': 盈亏比,
'tp_distance': TP距离,
'sl_distance': SL距离,
'profit_amount': 盈利金额(USDT),
'loss_amount': 亏损金额(USDT),
'fee_cost': 手续费(USDT),
'fee_pct': 手续费占盈利百分比,
'rating': 'high'/'medium'/'low',
'rating_emoji': '✅'/'⚠️'/'❌',
'rating_text': '性价比高'/'性价比一般'/'性价比低',
'auto_execute': True/False,
'reason': 原因说明
}
"""
# 如果没有传入费率,从OKX API获取
if fee_rate is None:
maker_rate, taker_rate = get_okx_fee_rate()
# 用taker费率(市价单)- 可能是负数(返佣)
fee_rate = taker_rate
if fee_rate is None:
fee_rate = cfg('cost_performance', 'fee_rate', 0.0005)
# 计算距离
tp_distance = abs(tp_price - entry_price)
sl_distance = abs(sl_price - entry_price)
# 防止除零
if sl_distance == 0:
return {
'rr_ratio': 0,
'tp_distance': tp_distance,
'sl_distance': sl_distance,
'profit_amount': 0,
'loss_amount': 0,
'fee_cost': 0,
'fee_pct': 100,
'rating': 'low',
'rating_emoji': '❌',
'rating_text': '性价比低',
'auto_execute': False,
'reason': '止损距离为0'
}
# 盈亏比
rr_ratio = tp_distance / sl_distance
# 盈亏金额
position_size = contracts * ct_val
profit_amount = tp_distance * position_size
loss_amount = sl_distance * position_size
# 手续费 (开+平, 含杠杆)
# 注意:fee_rate可能是负数(返佣),此时fee_cost也是负数(即赚手续费)
notional_value = entry_price * position_size
fee_cost = notional_value * fee_rate * 2 # 手续费基于名义价值,不乘杠杆
# 手续费占盈利百分比(返佣时为负数,表示额外收益)
if profit_amount > 0:
fee_pct = (fee_cost / profit_amount * 100)
else:
fee_pct = 100 if fee_cost >= 0 else -100
# 性价比评级
# 注意:返佣时fee_pct为负数,表示额外收益,应该提高评级
reasons = []
# 计算净盈利(盈利 + 返佣 或 盈利 - 手续费)
net_profit = profit_amount + fee_cost # fee_cost为负时是返佣,为正时是收费
rr_high = cfg('cost_performance', 'rr_high', 2.0)
rr_medium = cfg('cost_performance', 'rr_medium', 1.5)
fee_high = cfg('cost_performance', 'fee_high_pct', 10)
fee_medium = cfg('cost_performance', 'fee_medium_pct', 5)
min_profit = cfg('position_sizing', 'min_profit_usdt', 10)
if rr_ratio >= rr_high and net_profit >= min_profit:
# 盈亏比达标 且 净盈利达标
if fee_pct < 0: # 返佣
rating = 'high'
rating_emoji = '✅'
rating_text = '性价比高'
auto_execute = True
elif fee_pct < fee_medium: # 低费率
rating = 'high'
rating_emoji = '✅'
rating_text = '性价比高'
auto_execute = True
else: # 高费率
rating = 'medium'
rating_emoji = '⚠️'
rating_text = '性价比一般'
auto_execute = False
elif rr_ratio >= rr_medium and net_profit >= min_profit:
rating = 'medium'
rating_emoji = '⚠️'
rating_text = '性价比一般'
auto_execute = False
else:
rating = 'low'
rating_emoji = '❌'
rating_text = '性价比低'
auto_execute = False
# 具体原因
if rr_ratio < rr_medium:
reasons.append(f'盈亏比{rr_ratio:.1f}:1<1.5:1')
elif rr_ratio < rr_high:
reasons.append(f'盈亏比{rr_ratio:.1f}:1偏低')
if fee_pct > fee_high:
reasons.append(f'手续费占比{fee_pct:.0f}%过高')
elif fee_pct > fee_medium:
reasons.append(f'手续费占比{fee_pct:.0f}%偏高')
elif fee_pct < 0:
reasons.append(f'返佣{abs(fee_pct):.0f}%')
if net_profit < min_profit:
reasons.append(f'净盈利{net_profit:.1f}USDT<5USDT')
reason = '; '.join(reasons) if reasons else ('盈亏比≥2:1, 手续费合理, 盈利达标' if rating == 'high' else '')
return {
'rr_ratio': round(rr_ratio, 2),
'tp_distance': round(tp_distance, 4),
'sl_distance': round(sl_distance, 4),
'profit_amount': round(profit_amount, 2),
'loss_amount': round(loss_amount, 2),
'fee_cost': round(fee_cost, 2),
'fee_pct': round(fee_pct, 2),
'net_profit': round(net_profit, 2), # 新增:净盈利
'rating': rating,
'rating_emoji': rating_emoji,
'rating_text': rating_text,
'auto_execute': auto_execute,
'reason': reason
}
def calc_min_contracts_for_profit(tp_distance, ct_val, min_profit=None):
if min_profit is None:
min_profit = cfg('position_sizing', 'min_profit_usdt', 10)
"""
计算达到最小盈利所需的合约张数
参数:
tp_distance: TP距离
ct_val: 合约面值
min_profit: 最小盈利额 (默认10USDT)
返回:
int: 需要的合约张数 (向上取整)
"""
if tp_distance <= 0 or ct_val <= 0:
return 0
# 盈利 = tp_distance * ct_val * contracts
# contracts = min_profit / (tp_distance * ct_val)
raw_contracts = min_profit / (tp_distance * ct_val)
# 向上取整到lot_sz (这里先取整,外面再处理)
import math
return math.ceil(raw_contracts)
# 测试
if __name__ == '__main__':
# 测试案例1: 性价比高
check1 = calc_cost_performance(
entry_price=1700,
sl_price=1666,
tp_price=1775,
contracts=6,
ct_val=0.1,
leverage=25
)
print("测试1 - ETH做多 (性价比高):")
print(f" 盈亏比: {check1['rr_ratio']}:1")
print(f" 盈利: {check1['profit_amount']} USDT")
print(f" 手续费: {check1['fee_cost']} USDT ({check1['fee_pct']}%)")
print(f" 评级: {check1['rating_text']}")
print(f" 自动开仓: {check1['auto_execute']}")
print()
# 测试案例2: 性价比低 (盈利<5USDT)
check2 = calc_cost_performance(
entry_price=67.21,
sl_price=70.57,
tp_price=63.85,
contracts=1,
ct_val=0.1,
leverage=10
)
print("测试2 - HYPE做空 (盈利<5USDT):")
print(f" 盈亏比: {check2['rr_ratio']}:1")
print(f" 盈利: {check2['profit_amount']} USDT")
print(f" 手续费: {check2['fee_cost']} USDT ({check2['fee_pct']}%)")
print(f" 评级: {check2['rating_text']}")
print(f" 原因: {check2['reason']}")
print(f" 自动开仓: {check2['auto_execute']}")
print()
# 测试案例3: 计算最小张数
min_contracts = calc_min_contracts_for_profit(
tp_distance=3.36,
ct_val=0.1,
min_profit=10
)
print(f"测试3 - HYPE最小张数: {min_contracts}张 (盈利={3.36*0.1*min_contracts:.1f}USDT)")