fix: parse_signal 兼容【仓位】字段+unit追踪+减幅计算+跟单建议

【背景】Jasonleo 9次BTC减仓信号,process_signal.py 的 size regex 只匹配【仓位大小】,不匹配【仓位】。导致推信号显示 size='?',无法算减幅,agent 推减仓建议全错位。

【修复】
1. parse_signal.py:67 - size regex 兼容【仓位(?:大小)?】
2. parse_signal.py:68 - 新增 unit 字段(BTC/USDT/张)
3. parse_signal.py:285 - 推信号时显示 size+unit
4. parse_signal.py:294 - 跟单建议: fetch 真实持仓+算大佬减幅+同比例跟单
5. signal_tracker.py:158 - format_comparison 智能显示整数/小数 + 减幅%

【测试】1450.719 BTC , 49.958(无单位), 10000 USDT 
【验证】format_comparison 减幅 -7.1% 显示正确
This commit is contained in:
2026-07-23 21:28:37 +08:00
parent fca5bfc24d
commit 1fe4509307
2 changed files with 53 additions and 9 deletions
+37 -4
View File
@@ -30,7 +30,7 @@ DEDUP_DB = Path.home() / ".hermes/trading/signal_dedup.db"
# Import signal tracker
sys.path.insert(0, str(SKILL_DIR / "scripts"))
from signal_tracker import format_comparison, record_signal as _tracker_record, record_confirmed, format_trader_rating
from signal_tracker import format_comparison, record_signal as _tracker_record, record_confirmed, format_trader_rating, get_last_signal
# ─── 解析 ────────────────────────────────────────────────────────────────
@@ -64,7 +64,8 @@ def parse_signal(text):
'symbol': r'【币种】\s*[:]?\s*(\S+)',
'side': r'【方向】\s*[:]?\s*(做多|做空)',
'leverage':r'【杠杆】\s*[:]?\s*(\d+)',
'size': r'【仓位大小\s*[:]?\s*([\d,.]+)',
'size': r'【仓位(?:大小)?\s*[:]?\s*([\d,.]+)', # 兼容 【仓位】和【仓位大小】
'unit': r'【仓位(?:大小)?】\s*[:]?\s*[\d,.]+\s+(BTC|USDT|ETH|SOL|DOGE|张|USD)\b', # 严格匹配已知单位 (必需)
'value': r'【仓位价值】\s*[:]?\s*\$?\s*([\d,.]+)',
'entry': r'【开仓价】\s*[:]?\s*([\d,.]+)',
'current': r'【当前价】\s*[:]?\s*([\d,.]+)',
@@ -75,7 +76,7 @@ def parse_signal(text):
for key, pattern in extractors.items():
m = re.search(pattern, text)
if m:
fields[key] = m.group(1).replace(',', '')
fields[key] = m.group(1).replace(',', '') if key != 'unit' else m.group(1)
# 清理symbol
if 'symbol' in fields:
@@ -253,6 +254,7 @@ def format_message(fields, rec, signal_type):
leverage = fields.get('leverage', '10')
trader = fields.get('trader', '?')
size = fields.get('size', '?')
unit = fields.get('unit', '') # 仓位单位: BTC/USDT/张
value = fields.get('value', '?')
entry_price = fields.get('entry', '?')
pnl_str = fields.get('pnl', '0')
@@ -281,15 +283,45 @@ def format_message(fields, rec, signal_type):
type_label = type_labels.get(signal_type, signal_type)
# 信号源仓位(只展示,不参与计算)
src_info = f"📊 {trader} {size} {symbol}(价值${value})← 信号源,非你的仓位"
src_info = f"📊 {trader} {size} {unit} {symbol}(价值${value})← 信号源,非你的仓位"
# 仓位变化对比
current_size = 0
try:
current_size = float(fields.get('size', '0').replace(',', ''))
comparison = format_comparison(trader, symbol, current_size)
except:
comparison = ""
# 跟单建议: fetch 真实持仓 + 算比例
our_position = None
our_advice = ""
try:
from okx_position_advisor import get_positions
positions = get_positions(symbol=symbol)
for p in positions:
if p.get('symbol') == f"{symbol}/USDT:USDT" or p.get('symbol') == symbol:
our_position = p
break
if our_position and current_size > 0:
our_contracts = float(our_position.get('contracts', 0))
# 算大佬减仓比例
last_signal = get_last_signal(trader, symbol)
last_size = (last_signal or {}).get('trader_size', 0)
if last_size and last_size > 0:
trader_delta_pct = (current_size - last_size) / last_size * 100
if trader_delta_pct < -0.5: # 大佬减仓 > 0.5%
# 跟同比例
our_reduce = our_contracts * abs(trader_delta_pct) / 100
# 取整 (BTC min=0.01张, 其他min=1张)
if symbol == 'BTC':
our_reduce = max(0.01, round(our_reduce, 2))
else:
our_reduce = max(1, round(our_reduce))
our_advice = f"\n🎯 跟单建议: 大佬减 {abs(trader_delta_pct):.1f}%, 你跟减 {our_reduce} 张 ({our_contracts}{our_contracts - our_reduce:.2f})"
except Exception as e:
our_advice = f"\n⚠️ 跟单计算跳过: {e}"
# 交易员评分
try:
trader_rating = format_trader_rating(trader)
@@ -304,6 +336,7 @@ def format_message(fields, rec, signal_type):
📊 仓位变化
{comparison}
{our_advice}
{trader_rating}
+16 -5
View File
@@ -155,17 +155,28 @@ def format_comparison(trader, symbol, current_size):
"""格式化对比信息"""
last_size, desc = compare_position(trader, symbol, current_size)
# 智能显示: 整数直接显示, 小数保留 2-3 位
def _fmt(n):
if n is None:
return "?"
if n == int(n) and abs(n) >= 10:
return f"{int(n):,}"
return f"{n:,.2f}"
if last_size is None:
return f"{trader} {symbol}: 首次出现,仓位 {current_size:,.0f}"
return f"{trader} {symbol}: 首次出现,仓位 {_fmt(current_size)}"
if "不变" in desc:
return f"{trader} {symbol}: 仓位不变 {current_size:,.0f}"
return f"{trader} {symbol}: 仓位不变 {_fmt(current_size)}"
elif "加仓" in desc:
return f"• 📈 {trader} {symbol}: {last_size:,.0f}{current_size:,.0f}{desc}"
return f"• 📈 {trader} {symbol}: {_fmt(last_size)}{_fmt(current_size)}{desc}"
elif "减仓" in desc:
return f"• 📉 {trader} {symbol}: {last_size:,.0f}{current_size:,.0f}{desc}"
delta_pct = 0
if last_size and last_size > 0:
delta_pct = (current_size - last_size) / last_size * 100
return f"• 📉 {trader} {symbol}: {_fmt(last_size)}{_fmt(current_size)}(减幅 {delta_pct:+.1f}%"
else:
return f"{trader} {symbol}: {last_size:,.0f}{current_size:,.0f}{desc}"
return f"{trader} {symbol}: {_fmt(last_size)}{_fmt(current_size)}{desc}"
# ─── 交易员统计 ──────────────────────────────────────────────────────────