diff --git a/okx-auto-position/scripts/process_signal.py b/okx-auto-position/scripts/process_signal.py index b4d374e..7c6f903 100644 --- a/okx-auto-position/scripts/process_signal.py +++ b/okx-auto-position/scripts/process_signal.py @@ -36,7 +36,7 @@ from signal_tracker import format_comparison, record_signal as _tracker_record, def parse_signal(text): """从TG信号文本提取关键字段""" - fields = {} + fields = {'_raw': text} # 存原始文本, 供 classify_signal 判定 # 交易员 - 找"【交易员】"标签, fallback "👉 跟单就选 X",再 fallback 第一个非字段名的方括号 m_trader = re.search(r'【交易员】\s*[::]?\s*([^【\n]{1,20})', text) @@ -281,6 +281,7 @@ def format_message(fields, rec, signal_type): # 信号类型标签 type_labels = { 'open': '新开仓' if not fields.get('_is_add') else 'A类加仓', + 'add': '➕ 加仓', 'reduce': 'B类减仓', 'close': '平仓', } @@ -646,7 +647,16 @@ def format_execution_result(fields, rec, exec_result): pos = exec_result.get('position', {}) algo = exec_result.get('algo', {}) - msg = f"""✅ {symbol} {side_cn} {emoji} {leverage}x 自动开仓 + # 根据 signal_type 显示动作 (open=新开仓, add=加仓, close=平仓, reduce=减仓) + action_labels = { + 'open': '新开仓', + 'add': '➕ 加仓', + 'reduce': '减仓', + 'close': '平仓', + } + action_label = action_labels.get(fields.get('signal_type', 'open'), '自动开仓') + + msg = f"""✅ {symbol} {side_cn} {emoji} {leverage}x {action_label} 📊 信号源: {trader} {size} {symbol}(价值${value}) @@ -716,6 +726,7 @@ def process_signal(text): # 分类先于去重(让 close 信号绕过2分钟去重,因为平仓是必须执行的) signal_type = classify_signal(fields) + fields['signal_type'] = signal_type # 写回 fields, 给 format_execution_result 用 # 平仓信号走独立通道 — 不看2分钟窗口,只看 raw_text hash 是否完全重复 # 修 2026-07-08 bug: 同币种同交易员的"减仓→平仓"紧跟信号被 dedup 误跳, @@ -806,6 +817,63 @@ def process_signal(text): trader_entry=float(fields.get('entry', '0').replace(',', '')), trader_pnl=float(fields.get('pnl', '0').replace(',', '')), raw_text=text, outcome='pushed_add_insufficient') + elif signal_type == 'reduce': + # 减仓信号: 锁了同方向 → 按大佬减仓比例, 自动减我们的同向持仓 + try: + import ccxt as _ccxt + from okx_position_advisor import load_credentials, create_exchange + creds = load_credentials() + ex = create_exchange(creds) + positions = ex.fetch_positions() + our_pos = next((p for p in positions if symbol in p.get('symbol', '') and p.get('contracts', 0) != 0), None) + + if not our_pos: + msg = f"⏭️ 减仓信号: 你无 {symbol} 持仓, 跳过" + else: + our_contracts = float(our_pos.get('contracts', 0)) + # 算大佬减仓比例 + last = get_last_signal(trader, symbol) + trader_before = (last or {}).get('trader_size', 0) or 0 + trader_after = float(fields.get('size', '0').replace(',', '')) + if trader_before <= 0: + msg = f"⚠️ 减仓信号: 大佬前仓位未知, 跳过 (你有 {our_contracts} 张)" + else: + delta_pct = (trader_before - trader_after) / trader_before + if delta_pct <= 0: + msg = f"⚠️ 减仓信号: 大佬实际是加仓 +{delta_pct*100:.1f}%, 跳过" + else: + # 按比例减 + reduce_amt = our_contracts * delta_pct + if symbol == 'BTC': + reduce_amt = max(0.01, round(reduce_amt, 2)) + else: + reduce_amt = max(1, round(reduce_amt)) + # 如果减完 < 0.01 张, 全平 + if symbol == 'BTC' and our_contracts - reduce_amt < 0.01: + reduce_amt = our_contracts # 全平 + # 同向减仓: long → sell, short → buy + close_side = 'sell' if our_pos.get('side') == 'long' else 'buy' + order = ex.create_order( + symbol=f'{symbol}/USDT:USDT', + type='market', + side=close_side, + amount=reduce_amt, + params={'reduceOnly': True} + ) + new_contracts = our_contracts - reduce_amt + msg = f"""✅ 减仓执行 | {symbol} {our_pos.get('side')} {delta_pct*100:.1f}% + +📊 大佬: {trader_before:,.2f} → {trader_after:,.2f} +💼 你的: {our_contracts} → {new_contracts:.2f} 张 +🔻 平 {reduce_amt} 张 (市价 {close_side})""" + except Exception as e: + msg = f"⚠️ 减仓失败: {e}" + _tracker_record(trader=trader, symbol=symbol, side=side, + leverage=int(leverage) if leverage else 10, + trader_size=float(fields.get('size', '0').replace(',', '')), + trader_entry=float(fields.get('entry', '0').replace(',', '')), + trader_pnl=float(fields.get('pnl', '0').replace(',', '')), + raw_text=text, outcome='auto_executed_reduce') else: # 需要确认或减仓信号 msg = format_message(fields, rec, signal_type)