fix: 群体投票加规模权重 log10(size+1)

【背景】纯按票数算群体方向,1 个巨鲸 1500 BTC long = 1 票 vs Jasonleo 1400 BTC short = 1 票,2:2 误判分歧。

【修复】每票权重 = strength × log10(size+1) — 规模按对数缩放
- 1500 → 3.18 票
- 800 → 2.90 票
- 500 → 2.70 票
- 100 → 2.00 票(避免小账户=0票)

【验证】BTC 现状 4 个 trader:
- long: 巨鲸 3.18 + 麻吉 2.90 = 6.08
- short: Jasonleo 3.15 + 狙击手 2.70 = 5.85
- 比例 1.04x < 1.5x 阈值 → 真实分歧,观望 
This commit is contained in:
2026-07-23 21:42:49 +08:00
parent b22905a5f3
commit efbea5448c
+171 -174
View File
@@ -301,6 +301,177 @@ def get_all_traders_summary():
return "\n".join(lines)
# ─── 多交易员群体决策 ─────────────────────────────────────────────────────
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
}
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)
# 权重 = 强度 × log10(规模+1) — 规模按对数缩放, 避免巨鲸一票定
size_weight = max(1.0, __import__('math').log10(size + 1))
side = sig.get('side', '')
if side == 'long':
long_weight += strength * size_weight
long_size += size
elif side == 'short':
short_weight += strength * size_weight
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
}
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
}
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
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'
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:
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
# CLI
if __name__ == '__main__':
import sys
@@ -354,177 +525,3 @@ if __name__ == '__main__':
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