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
This commit is contained in:
@@ -0,0 +1,280 @@
|
||||
"""
|
||||
性价比检查模块
|
||||
供 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)")
|
||||
Reference in New Issue
Block a user