Files
Hermes-Skills/okx-auto-position/scripts/signal_tracker.py
T
mike 657dc41c46 Initial commit: Trading skills collection
- OKX交易自动化 (okx-auto-position, okx-crypto, okx-exchange)
- 交易信号处理 (signal-confirmation-templates, trading-signal-aggregator)
- 量化因子挖掘 (quant-factor-mining)
- 长桥集成 (longbridge-cli, longbridge-python-sdk)
- 六合彩分析 (lottery-hk)
- 股息投资 (dividend-investing, dividend-scanner)
- 日内交易 (intraday-trading)
- 同花顺 (tonghuashun)
2026-07-05 02:39:41 -04:00

337 lines
11 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env python3
"""
信号历史跟踪DB
记录每次确认的信号,用于对比加仓/减仓趋势。
表结构:
- confirmed_signals: 已确认的信号(用户回复Y后记录)
- position_history: 仓位变化历史
"""
import sqlite3
from pathlib import Path
from datetime import datetime
DB_PATH = Path.home() / ".hermes/trading/signal_history.db"
def get_conn():
DB_PATH.parent.mkdir(parents=True, exist_ok=True)
conn = sqlite3.connect(str(DB_PATH))
conn.row_factory = sqlite3.Row
return conn
def init_db():
conn = get_conn()
conn.executescript("""
CREATE TABLE IF NOT EXISTS confirmed_signals (
id INTEGER PRIMARY KEY AUTOINCREMENT,
timestamp TEXT NOT NULL,
trader TEXT NOT NULL,
symbol TEXT NOT NULL,
side TEXT NOT NULL,
leverage INTEGER,
trader_size REAL,
trader_entry REAL,
trader_pnl REAL,
our_contracts REAL,
our_margin REAL,
our_entry REAL,
outcome TEXT DEFAULT 'confirmed',
raw_text TEXT
);
CREATE TABLE IF NOT EXISTS position_history (
id INTEGER PRIMARY KEY AUTOINCREMENT,
timestamp TEXT NOT NULL,
trader TEXT NOT NULL,
symbol TEXT NOT NULL,
size REAL NOT NULL,
entry_price REAL,
pnl REAL,
signal_type TEXT
);
CREATE INDEX IF NOT EXISTS idx_confirmed_trader_symbol
ON confirmed_signals(trader, symbol, timestamp);
CREATE INDEX IF NOT EXISTS idx_history_trader_symbol
ON position_history(trader, symbol, timestamp);
""")
conn.commit()
return conn
def record_confirmed(trader, symbol, side, leverage, trader_size, trader_entry, trader_pnl, our_contracts, our_margin, our_entry, raw_text=""):
"""记录已确认的信号"""
conn = init_db()
conn.execute("""
INSERT INTO confirmed_signals
(timestamp, trader, symbol, side, leverage, trader_size, trader_entry, trader_pnl, our_contracts, our_margin, our_entry, raw_text)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
""", (datetime.now().isoformat(), trader, symbol, side, leverage,
trader_size, trader_entry, trader_pnl, our_contracts, our_margin, our_entry, raw_text[:2000]))
conn.execute("""
INSERT INTO position_history
(timestamp, trader, symbol, size, entry_price, pnl, signal_type)
VALUES (?, ?, ?, ?, ?, ?, ?)
""", (datetime.now().isoformat(), trader, symbol, trader_size, trader_entry, trader_pnl, 'confirmed'))
conn.commit()
conn.close()
def record_signal(trader, symbol, side, leverage, trader_size, trader_entry, trader_pnl, raw_text="", outcome="pushed"):
"""记录推送的信号(不管是否确认)"""
conn = init_db()
conn.execute("""
INSERT INTO confirmed_signals
(timestamp, trader, symbol, side, leverage, trader_size, trader_entry, trader_pnl, outcome, raw_text)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
""", (datetime.now().isoformat(), trader, symbol, side, leverage,
trader_size, trader_entry, trader_pnl, outcome, raw_text[:2000]))
conn.execute("""
INSERT INTO position_history
(timestamp, trader, symbol, size, entry_price, pnl, signal_type)
VALUES (?, ?, ?, ?, ?, ?, ?)
""", (datetime.now().isoformat(), trader, symbol, trader_size, trader_entry, trader_pnl, outcome))
conn.commit()
conn.close()
def get_last_confirmed(trader, symbol):
"""获取上次确认的信号"""
conn = init_db()
row = conn.execute("""
SELECT * FROM confirmed_signals
WHERE trader = ? AND symbol = ? AND outcome = 'confirmed'
ORDER BY timestamp DESC LIMIT 1
""", (trader, symbol)).fetchone()
conn.close()
return dict(row) if row else None
def get_last_signal(trader, symbol):
"""获取上次推送的信号(不管是否确认)"""
conn = init_db()
row = conn.execute("""
SELECT * FROM confirmed_signals
WHERE trader = ? AND symbol = ?
ORDER BY timestamp DESC LIMIT 1
""", (trader, symbol)).fetchone()
conn.close()
return dict(row) if row else None
def get_position_trend(trader, symbol, limit=5):
"""获取仓位变化趋势"""
conn = init_db()
rows = conn.execute("""
SELECT * FROM position_history
WHERE trader = ? AND symbol = ?
ORDER BY timestamp DESC LIMIT ?
""", (trader, symbol, limit)).fetchall()
conn.close()
return [dict(r) for r in rows]
def compare_position(trader, symbol, current_size):
"""对比当前仓位与上次,返回变化描述"""
last = get_last_signal(trader, symbol)
if not last:
return None, "首次出现"
last_size = last.get('trader_size', 0)
if not last_size or last_size == 0:
return None, "上次仓位未知"
change = current_size - last_size
change_pct = (change / last_size) * 100
if abs(change_pct) < 1:
return last_size, "仓位不变"
elif change > 0:
return last_size, f"加仓 +{change_pct:.1f}%"
else:
return last_size, f"减仓 {change_pct:.1f}%"
def format_comparison(trader, symbol, current_size):
"""格式化对比信息"""
last_size, desc = compare_position(trader, symbol, current_size)
if last_size is None:
return f"• {trader} {symbol}: 首次出现,仓位 {current_size:,.0f}"
if "不变" in desc:
return f"• {trader} {symbol}: 仓位不变 {current_size:,.0f}"
elif "加仓" in desc:
return f"• 📈 {trader} {symbol}: {last_size:,.0f}{current_size:,.0f}{desc}"
elif "减仓" in desc:
return f"• 📉 {trader} {symbol}: {last_size:,.0f}{current_size:,.0f}{desc}"
else:
return f"• {trader} {symbol}: {last_size:,.0f}{current_size:,.0f}{desc}"
# ─── 交易员统计 ──────────────────────────────────────────────────────────
def get_trader_stats(trader=None):
"""获取交易员统计数据"""
conn = init_db()
if trader:
rows = conn.execute("""
SELECT trader, symbol, side, outcome, trader_pnl, timestamp
FROM confirmed_signals
WHERE trader = ?
ORDER BY timestamp DESC
""", (trader,)).fetchall()
else:
rows = conn.execute("""
SELECT trader, symbol, side, outcome, trader_pnl, timestamp
FROM confirmed_signals
ORDER BY trader, timestamp DESC
""").fetchall()
conn.close()
# 按交易员分组
stats = {}
for row in rows:
r = dict(row)
t = r['trader']
if t not in stats:
stats[t] = {
'trader': t,
'total': 0,
'pushed': 0,
'confirmed': 0,
'auto_executed': 0,
'cancelled': 0,
'wins': 0,
'losses': 0,
'total_pnl': 0,
'trades': [],
}
s = stats[t]
s['total'] += 1
outcome = r.get('outcome', 'pushed')
if outcome in s:
s[outcome] += 1
pnl = r.get('trader_pnl', 0) or 0
s['total_pnl'] += pnl
if pnl > 0:
s['wins'] += 1
elif pnl < 0:
s['losses'] += 1
s['trades'].append({
'symbol': r['symbol'],
'side': r['side'],
'pnl': pnl,
'outcome': outcome,
'time': r['timestamp'],
})
# 计算胜率
for t in stats:
s = stats[t]
decided = s['wins'] + s['losses']
s['win_rate'] = (s['wins'] / decided * 100) if decided > 0 else 0
s['avg_pnl'] = (s['total_pnl'] / s['total']) if s['total'] > 0 else 0
return stats
def format_trader_rating(trader):
"""格式化交易员评分(用于推送模板)"""
stats = get_trader_stats(trader)
if trader not in stats or stats[trader]['total'] < 2:
return f"📊 {trader}: 数据不足(信号<2条)"
s = stats[trader]
win_rate = s['win_rate']
total = s['total']
total_pnl = s['total_pnl']
# 评分等级
if win_rate >= 70:
rating = "⭐⭐⭐⭐⭐ 精准"
elif win_rate >= 60:
rating = "⭐⭐⭐⭐ 可靠"
elif win_rate >= 50:
rating = "⭐⭐⭐ 一般"
elif win_rate >= 40:
rating = "⭐⭐ 谨慎"
else:
rating = "⭐ 高风险"
# 最近3笔
recent = s['trades'][:3]
recent_str = " → ".join([
f"{t['symbol']}{'+' if t['pnl']>0 else ''}{t['pnl']:.0f}"
for t in recent
])
return f"""📊 {trader} 胜率评级: {rating}
• 胜率: {win_rate:.0f}%{s['wins']}胜/{s['losses']}负/{total}总)
• 总盈亏: {'+' if total_pnl>0 else ''}{total_pnl:.0f} USDT
• 最近: {recent_str}"""
def get_all_traders_summary():
"""获取所有交易员的汇总表"""
stats = get_trader_stats()
if not stats:
return "暂无交易员数据"
lines = ["| 交易员 | 胜率 | 总盈亏 | 信号数 |",
"|--------|------|--------|--------|"]
for t, s in sorted(stats.items(), key=lambda x: x[1]['win_rate'], reverse=True):
win_rate = s['win_rate']
total_pnl = s['total_pnl']
emoji = "⭐" * min(5, max(1, int(win_rate / 20)))
lines.append(
f"| {t} | {emoji} {win_rate:.0f}% | {'+' if total_pnl>0 else ''}{total_pnl:.0f} | {s['total']} |"
)
return "\n".join(lines)
# CLI
if __name__ == '__main__':
import sys
if len(sys.argv) < 2:
print("用法:")
print(" python3 signal_tracker.py compare 麻吉大哥 HYPE 12000")
print(" python3 signal_tracker.py history 麻吉大哥 HYPE")
print(" python3 signal_tracker.py record 麻吉大哥 HYPE long 10 12000 70.8 -3500")
print(" python3 signal_tracker.py rating 麻吉大哥")
print(" python3 signal_tracker.py summary")
sys.exit(0)
cmd = sys.argv[1]
if cmd == 'compare' and len(sys.argv) >= 5:
trader = sys.argv[2]
symbol = sys.argv[3]
size = float(sys.argv[4])
print(format_comparison(trader, symbol, size))
elif cmd == 'history' and len(sys.argv) >= 4:
trader = sys.argv[2]
symbol = sys.argv[3]
trend = get_position_trend(trader, symbol)
for t in trend:
print(f" {t['timestamp'][:16]} | {t['size']:,.0f} | {t.get('pnl', 0):+.0f} | {t['signal_type']}")
elif cmd == 'record' and len(sys.argv) >= 8:
trader = sys.argv[2]
symbol = sys.argv[3]
side = sys.argv[4]
leverage = int(sys.argv[5])
size = float(sys.argv[6])
entry = float(sys.argv[7])
pnl = float(sys.argv[8]) if len(sys.argv) > 8 else 0
record_signal(trader, symbol, side, leverage, size, entry, pnl)
print(f"✅ 已记录: {trader} {symbol} {side} {leverage}x {size:,.0f} @{entry}")
elif cmd == 'rating' and len(sys.argv) >= 3:
trader = sys.argv[2]
print(format_trader_rating(trader))
elif cmd == 'summary':
print(get_all_traders_summary())