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:
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user