From b22905a5f34db4a8f7f0bad3f69728a4dcdcad8a Mon Sep 17 00:00:00 2001 From: mike Date: Thu, 23 Jul 2026 21:39:22 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=A4=9A=E4=BA=A4=E6=98=93=E5=91=98?= =?UTF-8?q?=E7=BE=A4=E4=BD=93=E5=86=B3=E7=AD=96=20multi=5Ftrader=5Fdecisio?= =?UTF-8?q?n?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 【功能】同币种多交易员时,用加权群体决策替代单信号跟随 【算法】 1. 收集币种最近 20 个信号,按 trader 去重 2. 每个交易员按 strength 权重 + size 加权 3. long/short 票数比 > 1.5x 才确认群体方向 4. 反向信号总规模 > 1000 或单交易员 > 1000 触发紧急反向 5. 顺势加 10% / 反向减 50% / 无持仓开仓 1 张 【用法】python3 signal_tracker.py multi [our_side] [our_contracts] 【测试】4 场景全过: 顺势加/反向减/无持仓开/分歧观望 ✅ --- okx-auto-position/scripts/signal_tracker.py | 183 ++++++++++++++++++++ 1 file changed, 183 insertions(+) diff --git a/okx-auto-position/scripts/signal_tracker.py b/okx-auto-position/scripts/signal_tracker.py index d4590ec..c4181ef 100644 --- a/okx-auto-position/scripts/signal_tracker.py +++ b/okx-auto-position/scripts/signal_tracker.py @@ -345,3 +345,186 @@ if __name__ == '__main__': elif cmd == 'summary': print(get_all_traders_summary()) + + elif cmd == 'multi' and len(sys.argv) >= 3: + # 群体决策: multi [our_side] [our_contracts] + symbol = sys.argv[2] + our_side = sys.argv[3] if len(sys.argv) > 3 else None + our_contracts = float(sys.argv[4]) if len(sys.argv) > 4 else 0 + signals = get_recent_signals_for_symbol(symbol, limit=20) + result = multi_trader_decision(symbol, signals, our_side, our_contracts) + print(format_multi_decision(result)) + + +# ─── 多交易员群体决策 ───────────────────────────────────────────────────── + +def get_recent_signals_for_symbol(symbol, limit=20): + """获取某币种最近的信号 (含 trader)""" + conn = init_db() + rows = conn.execute(""" + SELECT trader, symbol, side, leverage, trader_size, trader_pnl, timestamp + FROM confirmed_signals + WHERE symbol = ? + ORDER BY timestamp DESC LIMIT ? + """, (symbol, limit)).fetchall() + conn.close() + return [dict(r) for r in rows] + + +def get_trader_strength(trader): + """获取交易员强度评分 (基于历史胜率)""" + stats = get_trader_stats(trader) + if isinstance(stats, list) and stats: + s = stats[0] + return s.get('strength', 1.0) + return 1.0 # 默认中等 + + +def multi_trader_decision(symbol, signals, our_side=None, our_contracts=0): + """ + 多交易员群体决策 + + signals: 最近的信号列表 [{trader, side, size, pnl, ...}] + our_side: 你当前的持仓方向 (long/short/None) + our_contracts: 你的持仓张数 + + 返回: {action, reason, group_side, confidence, urgent_reverse, suggest_amount} + """ + if not signals: + return { + 'action': 'wait', + 'reason': '无信号', + 'group_side': None, + 'confidence': 0, + 'urgent_reverse': False, + 'suggest_amount': 0 + } + + # 1. 计算群体方向 + 加权 + long_weight = 0 + short_weight = 0 + long_size = 0 + short_size = 0 + trader_votes = {} # 每个交易员最新一次信号 + + # 只取每个交易员最新一次信号 (避免重复计票) + for s in signals: + t = s.get('trader') + if t not in trader_votes: + trader_votes[t] = s + + for trader, sig in trader_votes.items(): + strength = get_trader_strength(trader) + size = float(sig.get('trader_size', 0) or 0) + side = sig.get('side', '') + if side == 'long': + long_weight += strength + long_size += size + elif side == 'short': + short_weight += strength + short_size += size + + total_weight = long_weight + short_weight + if total_weight == 0: + return { + 'action': 'wait', + 'reason': '无有效信号', + 'group_side': None, + 'confidence': 0, + 'urgent_reverse': False, + 'suggest_amount': 0 + } + + # 2. 判定群体方向 (1.5x 阈值才确认) + if long_weight > short_weight * 1.5: + group_side = 'long' + confidence = long_weight / total_weight + elif short_weight > long_weight * 1.5: + group_side = 'short' + confidence = short_weight / total_weight + else: + return { + 'action': 'wait', + 'reason': f'群体分歧 long={long_weight:.1f} short={short_weight:.1f}', + 'group_side': None, + 'confidence': 0.5, + 'urgent_reverse': False, + 'suggest_amount': 0 + } + + # 3. 紧急反向检测: 大规模反向信号 (>1000 BTC 或总反向 > 1000) + urgent_reverse = False + reverse_size = 0 + for trader, sig in trader_votes.items(): + if sig.get('side') != group_side: + sz = float(sig.get('trader_size', 0) or 0) + reverse_size += sz + if sz > 1000: # 单交易员大规模反向 + urgent_reverse = True + + if reverse_size > 1000: + urgent_reverse = True + + # 4. 决定动作 + suggest_amount = 0 + action = 'hold' + reason_parts = [ + f'群体{group_side.upper()} (置信度 {confidence*100:.0f}%)', + f'long票数={int(long_weight)}, short票数={int(short_weight)}', + f'long总规模={long_size:,.0f}, short总规模={short_size:,.0f}', + ] + + if urgent_reverse and our_contracts > 0: + action = 'reduce_50%' + suggest_amount = round(our_contracts * 0.5, 2) + reason_parts.insert(0, f'⚠️ 紧急反向: 反向总规模 {reverse_size:,.0f}') + elif our_side is None: + action = 'open' + suggest_amount = 1 # 最小开仓 + reason_parts.append('你无持仓,建议开仓') + elif our_side == group_side: + action = 'add' + # 顺势加 10% 仓位 + suggest_amount = round(our_contracts * confidence * 0.1, 2) + if suggest_amount < 0.01: + suggest_amount = 0.01 + reason_parts.append(f'顺势加仓 10% (跟群体置信度 {confidence*100:.0f}%)') + else: # our_side != group_side + action = 'reduce_50%' + suggest_amount = round(our_contracts * 0.5, 2) + reason_parts.append(f'你{our_side}与群体{group_side}反向, 减仓 50%') + + return { + 'action': action, + 'reason': ' | '.join(reason_parts), + 'group_side': group_side, + 'confidence': confidence, + 'urgent_reverse': urgent_reverse, + 'suggest_amount': suggest_amount, + 'long_weight': long_weight, + 'short_weight': short_weight, + 'long_size': long_size, + 'short_size': short_size, + 'reverse_size': reverse_size, + } + + +def format_multi_decision(result): + """格式化群体决策输出""" + if result['action'] == 'wait': + return f"⏸️ 观望: {result['reason']}" + + emoji = '🟢' if result['group_side'] == 'long' else '🔴' + urgent = '🚨 ' if result['urgent_reverse'] else '' + action_emoji = { + 'open': '🆕 开仓', + 'add': '➕ 加仓', + 'reduce_50%': '📉 减仓50%', + 'hold': '⏸️ 持有', + }.get(result['action'], result['action']) + + msg = f"""{urgent}{emoji} 群体决策: {action_emoji} {result['suggest_amount']} 张 + +📊 群体方向: {result['group_side'].upper()} (置信度 {result['confidence']*100:.0f}%) +💡 {result['reason']}""" + return msg