feat: 信号溯源系统——signal_ledger记录每次推送(策略/版本/温区/原因), 多策略同推检测共振标记(🔥加关注); stale_push_wlin推送行带[策略][温区]标记+共振标记

This commit is contained in:
hmo
2026-08-13 18:19:17 +08:00
parent 41f0d43cb5
commit e27a074710
2 changed files with 197 additions and 2 deletions
+143
View File
@@ -0,0 +1,143 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""signal_ledger.py — 信号溯源系统(2026-08-13 老莫要求)
核心:记录每次股票被推荐的原因(策略/版本/时间/温区),多策略同推标记共振加关注。
表 signal_ledger
id, code, name, strategy, version, regime, temp_band, pushed_at, reason,
resonance_count, resonance_strategies, source_module, updated_at
共振检测:同一股票 24h 内被多个激活策略推 → resonance_count>1,标记"多策略共振"(加关注)
用法:
from signal_ledger import record_signal, get_resonance
record_signal(code="300750", name="宁德时代", strategy="v_oversold", version="v_oversold",
regime="trend_down", temp_band="panic", reason="进买入区+重评买入", source_module="stale_push_wlin")
res = get_resonance("300750") # 返回该股票近24h共振信息
"""
import json
import sqlite3
from pathlib import Path
from datetime import datetime, timedelta
DB = "/home/hmo/MoFin/data/mofin.db"
RESONANCE_WINDOW_H = 24 # 共振窗口(小时)
def _conn():
conn = sqlite3.connect(DB, timeout=30)
conn.execute("PRAGMA busy_timeout=30000")
return conn
def init_table():
conn = _conn()
conn.execute("""
CREATE TABLE IF NOT EXISTS signal_ledger (
id INTEGER PRIMARY KEY AUTOINCREMENT,
code TEXT NOT NULL,
name TEXT,
strategy TEXT,
version TEXT,
regime TEXT,
temp_band TEXT,
pushed_at TIMESTAMP,
reason TEXT,
resonance_count INTEGER DEFAULT 1,
resonance_strategies TEXT,
source_module TEXT,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)
""")
conn.execute("CREATE INDEX IF NOT EXISTS idx_signal_ledger_code ON signal_ledger(code)")
conn.execute("CREATE INDEX IF NOT EXISTS idx_signal_ledger_pushed ON signal_ledger(pushed_at)")
conn.commit()
conn.close()
def record_signal(code, name="", strategy="", version="", regime="", temp_band="",
reason="", source_module=""):
"""记录一次信号推送,并检测多策略共振"""
init_table()
now = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
conn = _conn()
try:
# 共振检测:近 24h 内同一股票被其他策略推过
cutoff = (datetime.now() - timedelta(hours=RESONANCE_WINDOW_H)).strftime("%Y-%m-%d %H:%M:%S")
rows = conn.execute(
"""SELECT DISTINCT strategy FROM signal_ledger
WHERE code=? AND pushed_at >= ? AND strategy != ? AND strategy != ''""",
(code, cutoff, strategy)
).fetchall()
other_strats = [r[0] for r in rows if r[0]]
resonance_count = len(other_strats) + (1 if strategy else 0)
resonance_strategies = ",".join(sorted(set([strategy] + other_strats))) if strategy else ""
conn.execute(
"""INSERT INTO signal_ledger
(code, name, strategy, version, regime, temp_band, pushed_at, reason,
resonance_count, resonance_strategies, source_module)
VALUES (?,?,?,?,?,?,?,?,?,?,?)""",
(code, name, strategy, version, regime, temp_band, now, reason,
resonance_count, resonance_strategies, source_module)
)
conn.commit()
return {"resonance_count": resonance_count, "resonance_strategies": resonance_strategies}
finally:
conn.close()
def get_resonance(code, hours=RESONANCE_WINDOW_H):
"""查某股票近 N 小时的共振信息"""
init_table()
cutoff = (datetime.now() - timedelta(hours=hours)).strftime("%Y-%m-%d %H:%M:%S")
conn = _conn()
try:
rows = conn.execute(
"""SELECT strategy, version, regime, pushed_at, reason FROM signal_ledger
WHERE code=? AND pushed_at >= ? ORDER BY pushed_at DESC""",
(code, cutoff)
).fetchall()
if not rows:
return None
strats = sorted({r[0] for r in rows if r[0]})
return {
"code": code,
"count": len(strats),
"strategies": strats,
"latest": rows[0][3],
"signals": [{"strategy": r[0], "version": r[1], "regime": r[2], "at": r[3], "reason": r[4]} for r in rows],
}
finally:
conn.close()
def get_recent_signals(limit=50):
"""查最近推送的信号(供评估)"""
init_table()
conn = _conn()
try:
rows = conn.execute(
"""SELECT code, name, strategy, regime, temp_band, pushed_at, reason,
resonance_count, resonance_strategies, source_module
FROM signal_ledger ORDER BY pushed_at DESC LIMIT ?""",
(limit,)
).fetchall()
return [
{"code": r[0], "name": r[1], "strategy": r[2], "regime": r[3], "temp": r[4],
"at": r[5], "reason": r[6], "resonance": r[7], "res_strats": r[8], "source": r[9]}
for r in rows
]
finally:
conn.close()
if __name__ == "__main__":
init_table()
# 自测
r1 = record_signal("300750", "宁德时代", "v_oversold", "v_oversold", "trend_down", "panic", "进买入区", "test")
print("单策略:", r1)
r2 = record_signal("300750", "宁德时代", "v_mr_sel", "v_mr_sel", "trend_down", "panic", "超跌信号", "test")
print("多策略共振:", r2)
print("共振查询:", get_resonance("300750"))
+54 -2
View File
@@ -366,6 +366,28 @@ def main():
except Exception as _e:
print(f"[DB_LOAD FAIL] {_e}", file=sys.stderr)
# 2026-08-13 温区自适应:当前温区激活策略集合(regime_weights.active
_active_strats = None
try:
from regime_gate import _load_weights
_w = _load_weights()
if _w and _w.get("weights"):
_active_strats = {k for k, v in _w["weights"].items() if v.get("matched")}
except Exception:
pass # 无温区数据 → 不过滤(兼容旧逻辑)
def _is_active(code):
"""该自选股的策略是否在当前温区激活"""
if _active_strats is None:
return True
e = code_data.get(code, {})
for key in ("tag", "version", "strategy_type", "decision_type"):
v = e.get(key)
if v and str(v) in _active_strats:
return True
# 不在激活集(或无标识)→ 视为非激活(保守不推)
return False
cash = load_cash()
stocks = []
stale_list = []
@@ -912,10 +934,40 @@ def main():
)
action_tag = "🛒" if (lots > 0 or swap_text) else "⚠️"
# 2026-08-13 信号溯源:记录策略/版本/温区 + 共振标记(多策略同推加关注)
strat_id = d.get("tag") or d.get("version") or d.get("strategy_type") or d.get("decision_type") or ""
regime_now = ""
temp_now = ""
try:
from regime_gate import get_current_regime
_rg = get_current_regime()
regime_now = _rg.get("regime", "")
from temp_band import get_market_temp
_tp = get_market_temp()
temp_now = _tp.get("band", "")
except Exception:
pass
# 溯源记录 + 共振检测
resonance_tag = ""
try:
from signal_ledger import record_signal
_res = record_signal(
code=code, name=name, strategy=strat_id, version=strat_id,
regime=regime_now, temp_band=temp_now,
reason=f"进买入区{buy_low}~{buy_high} + 重评{sig}",
source_module="stale_push_wlin",
)
if _res and _res.get("resonance_count", 1) > 1:
resonance_tag = f" 🔥多策略共振({_res['resonance_count']}策略: {_res['resonance_strategies']})"
except Exception:
pass
strat_tag = f" [{strat_id}]" if strat_id else ""
regime_tag = f"[{regime_now}]" if regime_now else ""
lines.append(
f" {action_tag} {name}({code}) {pfx}{price:.2f} 买区{buy_low}~{buy_high} | "
f"1手{lot:,.0f}元 RR={rr:.1f}{sl}{tp}\n"
f"1手{lot:,.0f}元 RR={rr:.1f}{sl}{tp}{strat_tag}{regime_tag}{resonance_tag}\n"
f" {analysis}\n"
f" 技术{ss['强撑']}{ss['弱撑']}{ss['弱压']}{ss['强压']} | 信号{sig}\n"
f" 仓位:理论{theo_pct}%×总资产 | 建议{actual_pct}%{details}"