feat: 持仓锁 — 同币种只跟一个 trader
【背景】之前 BTC 同时跟多个 trader (Jasonleo/麻吉/巨鲸), 信号互相干扰 - 群体投票算法复杂,实际投出分歧 - 单一币种单一信号源更清晰 【实现】 - position_locks 表 (symbol PK, trader, side, locked_at, reason, auto_locked) - acquire_lock / release_lock / get_lock / list_locks / check_signal_lock - 72h 自动过期 (按 _hours_since 算) - 同 trader 同方向 → 续期 - 同方向不同 trader → 检查过期,过期后接管 - 不同方向 → 跳过 (允许一多一空并存) - is_real_trader 黑名单: 'X聚合社区'/'某转发' 等不参与锁 【集成】process_signal.format_message 开头调 check_signal_lock: - 锁被占用 → 推 '🔒 {reason}' (被锁,跳过) - 锁允许 → 继续推信号 【测试】6 场景全过: 同 trader 续期/反向跳过/同向跳过/聚合放行/新币自动锁 ✅ 【当前锁】BTC long → 矿工Jasonleo, HYPE short → 麻吉大哥 (手动加锁)
This commit is contained in:
@@ -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, get_last_signal
|
||||
from signal_tracker import format_comparison, record_signal as _tracker_record, record_confirmed, format_trader_rating, get_last_signal, check_signal_lock
|
||||
|
||||
# ─── 解析 ────────────────────────────────────────────────────────────────
|
||||
|
||||
@@ -241,6 +241,16 @@ def format_message(fields, rec, signal_type):
|
||||
if 'error' in rec:
|
||||
return f"⚠️ advisor错误: {rec['error']}"
|
||||
|
||||
symbol = fields.get('symbol', '?')
|
||||
side_cn = fields.get('side', '做多')
|
||||
side_en = fields.get('side_en', 'long' if side_cn == '做多' else 'short')
|
||||
trader = fields.get('trader', '?')
|
||||
|
||||
# 锁检查: 同币种只跟一个 trader
|
||||
allowed, lock_msg = check_signal_lock(symbol, trader, side_en)
|
||||
if not allowed:
|
||||
return f"🔒 {lock_msg}"
|
||||
|
||||
def _fmt(x, n=4):
|
||||
"""格式化数字: 字符串保留原样, 数字 round 到 n 位."""
|
||||
try:
|
||||
@@ -248,11 +258,8 @@ def format_message(fields, rec, signal_type):
|
||||
except (ValueError, TypeError):
|
||||
return str(x)
|
||||
|
||||
symbol = fields.get('symbol', '?')
|
||||
side_cn = fields.get('side', '做多')
|
||||
emoji = '🟩' if fields.get('side_en') == 'long' else '🟥'
|
||||
leverage = fields.get('leverage', '10')
|
||||
trader = fields.get('trader', '?')
|
||||
size = fields.get('size', '?')
|
||||
unit = fields.get('unit', '') # 仓位单位: BTC/USDT/张
|
||||
value = fields.get('value', '?')
|
||||
@@ -297,10 +304,13 @@ def format_message(fields, rec, signal_type):
|
||||
our_position = None
|
||||
our_advice = ""
|
||||
try:
|
||||
from okx_position_advisor import get_positions
|
||||
positions = get_positions(symbol=symbol)
|
||||
import ccxt
|
||||
from okx_position_advisor import load_credentials, create_exchange
|
||||
creds = load_credentials()
|
||||
ex = create_exchange(creds)
|
||||
positions = ex.fetch_positions()
|
||||
for p in positions:
|
||||
if p.get('symbol') == f"{symbol}/USDT:USDT" or p.get('symbol') == symbol:
|
||||
if symbol in p.get('symbol', '') and p.get('contracts', 0) != 0:
|
||||
our_position = p
|
||||
break
|
||||
if our_position and current_size > 0:
|
||||
|
||||
Reference in New Issue
Block a user