feat: 三维共振层落地(技术×资金×消息合成+signal_veto_log全程记录) + 综合评分列(收益30/胜率20/夏普20/盈亏比15/回撤15) + 普适性列(月份分布熵)

This commit is contained in:
hmo
2026-07-29 03:23:25 +08:00
parent 6ee7d348ce
commit c9e7079ee6
4 changed files with 212 additions and 2 deletions
+153
View File
@@ -0,0 +1,153 @@
#!/usr/bin/env python3
"""resonance.py — 三维共振层(技术×资金×消息)
在 v7.1 闸门通过后,对买入信号做三维合成判断:
否决(veto): 资金持续流出 + 消息利空(双重负面)
降级(downgrade): 任一单维度负面
共振(resonance): 资金强流入 + 消息利好
通过(pass): 其余
所有判断写入 signal_veto_log 供后续回测验证(2026-07-29 老爸批准)"""
import sqlite3, json, sys
from datetime import datetime, timedelta
DB = "/home/hmo/MoFin/data/mofin.db"
for p in ("/home/hmo/MoFin", "/home/hmo/web-dashboard"):
if p not in sys.path:
sys.path.insert(0, p)
NEWS_NEGATIVE = {'利空', '偏空', '中性偏空'}
NEWS_POSITIVE = {'利好', '偏多', '中性偏利好'}
FLOW_NEG_THRESHOLD = -2.5 # v6归因:持续流出胜率仅37.5%
FLOW_STRONG_POS_THRESHOLD = 4 # v6归因:加速流入胜率77.8%
def _flow_state(code):
"""资金维度: flow_5d / flow_delta → negative/neutral/positive/strong_positive"""
try:
conn = sqlite3.connect(DB)
rows = conn.execute("""
SELECT date, main_pct FROM stock_capital_flow
WHERE code=? ORDER BY date DESC LIMIT 10
""", (code,)).fetchall()
conn.close()
except sqlite3.OperationalError:
return {'state': 'unknown', 'flow_5d': None, 'flow_delta': None}
if len(rows) < 5:
return {'state': 'unknown', 'flow_5d': None, 'flow_delta': None}
recent = [r[1] for r in rows[:5] if r[1] is not None]
prior = [r[1] for r in rows[5:10] if r[1] is not None]
flow_5d = sum(recent) / len(recent) if recent else None
flow_delta = None
if recent and prior:
flow_delta = flow_5d - sum(prior) / len(prior)
state = 'neutral'
if flow_5d is not None and flow_5d < FLOW_NEG_THRESHOLD:
state = 'negative'
elif flow_delta is not None and flow_delta > FLOW_STRONG_POS_THRESHOLD:
state = 'strong_positive'
elif flow_5d is not None and flow_5d > 1:
state = 'positive'
return {'state': state, 'flow_5d': round(flow_5d, 2) if flow_5d is not None else None,
'flow_delta': round(flow_delta, 2) if flow_delta is not None else None}
def _news_state(code, name=None, sector=None):
"""消息维度: 3日内该股/该板块最新消息情绪"""
since = (datetime.now() - timedelta(days=3)).strftime('%Y-%m-%d')
try:
conn = sqlite3.connect(DB)
# 个股级优先(searched_stocks 或 summary 含代码/名称)
rows = conn.execute("""
SELECT overall_sentiment, summary, sector, created_at FROM signal_news
WHERE created_at >= ? AND (
searched_stocks LIKE ? OR summary LIKE ? OR sector = ?
)
ORDER BY id DESC LIMIT 10
""", (since, f'%{code}%', f'%{name or code}%', sector or '')).fetchall()
conn.close()
except sqlite3.OperationalError:
return {'state': 'unknown', 'sentiment': None, 'summary': None}
sentiment = None
summary = None
for s, sm, sec, ts in rows:
if s in NEWS_NEGATIVE or s in NEWS_POSITIVE:
sentiment = s
summary = (sm or '')[:120]
break
if sentiment is None:
return {'state': 'neutral', 'sentiment': None, 'summary': None}
state = 'negative' if sentiment in NEWS_NEGATIVE else 'positive'
return {'state': state, 'sentiment': sentiment, 'summary': summary}
def evaluate_resonance(code, gate, name=None):
"""三维合成判断。gate = v71_gate.check_entry_gate 的返回"""
flow = _flow_state(code)
# 板块名:从 gate factors 拿不到名字,这里用 sector_ctx 的映射
sector = None
try:
from strategy_lab import _STOCK_SECTOR
sector = _STOCK_SECTOR.get(code)
except Exception:
pass
news = _news_state(code, name, sector)
fs, ns = flow['state'], news['state']
if fs == 'negative' and ns == 'negative':
decision = 'veto'
reason = f"资金持续流出({flow['flow_5d']}) + 消息利空({news['sentiment']})"
elif fs == 'negative':
decision = 'downgrade'
reason = f"资金持续流出({flow['flow_5d']})"
elif ns == 'negative':
decision = 'downgrade'
reason = f"消息利空({news['sentiment']})"
elif fs == 'strong_positive' and ns == 'positive':
decision = 'resonance'
reason = f"三维共振: 资金加速流入({flow['flow_delta']}) + 消息{news['sentiment']}"
else:
decision = 'pass'
reason = ''
return {'decision': decision, 'reason': reason, 'flow': flow, 'news': news, 'sector': sector}
def log_resonance(code, name, price, gate, res, signal_before, signal_after):
"""完整记录三维状态 → signal_veto_log(后续回测验证的数据资产)"""
conn = sqlite3.connect(DB)
conn.execute("""
CREATE TABLE IF NOT EXISTS signal_veto_log (
id INTEGER PRIMARY KEY AUTOINCREMENT,
code TEXT, name TEXT, price REAL,
tech_score INTEGER, tech_summary TEXT,
flow_5d REAL, flow_delta REAL, flow_state TEXT,
news_sentiment TEXT, news_state TEXT, news_summary TEXT,
sector TEXT,
decision TEXT, reason TEXT,
signal_before TEXT, signal_after TEXT,
created_at TEXT
)
""")
conn.execute("""
INSERT INTO signal_veto_log
(code, name, price, tech_score, tech_summary, flow_5d, flow_delta, flow_state,
news_sentiment, news_state, news_summary, sector, decision, reason,
signal_before, signal_after, created_at)
VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)
""", (code, name, price, gate.get('score'), gate.get('summary'),
res['flow'].get('flow_5d'), res['flow'].get('flow_delta'), res['flow'].get('state'),
res['news'].get('sentiment'), res['news'].get('state'), res['news'].get('summary'),
res.get('sector'), res['decision'], res['reason'],
signal_before, signal_after,
datetime.now().strftime('%Y-%m-%d %H:%M:%S')))
conn.commit()
conn.close()
if __name__ == '__main__':
from v71_gate import check_entry_gate
for c in sys.argv[1:] or ['603599']:
g = check_entry_gate(c)
r = evaluate_resonance(c, g)
print(f"{c}: gate={g['pass']} | 资金={r['flow']['state']}({r['flow']['flow_5d']}) "
f"消息={r['news']['state']}({r['news']['sentiment']}) → {r['decision']} {r['reason']}")
@@ -1474,6 +1474,30 @@ def reassess_strategy(code, name, price, cost, shares, current_action,
except Exception as _e:
print(f" [v7.1闸门] 评估异常(放行): {_e}", flush=True)
# ----- 【三维共振层】技术×资金×消息合成判断(2026-07-29 老爸批准,全程记录) -----
_res_decision = None
if is_new_entry and any(s in timing_signal for s in ("买入", "加仓", "可追")):
try:
from resonance import evaluate_resonance, log_resonance
_res = evaluate_resonance(code, _gate if '_gate' in dir() and _gate else {'score': 0, 'summary': ''}, name)
_res_decision = _res["decision"]
_sig_before = timing_signal
if _res_decision == "veto":
timing_signal = "观望"
action_note = (action_note + " | 三维否决: " + _res["reason"]) if action_note else ("三维否决: " + _res["reason"])
elif _res_decision == "downgrade":
timing_signal = "关注"
action_note = (action_note + " | 三维降级: " + _res["reason"]) if action_note else ("三维降级: " + _res["reason"])
elif _res_decision == "resonance":
action_note = (action_note + " | " + _res["reason"]) if action_note else _res["reason"]
log_resonance(code, name, price,
_gate if '_gate' in dir() and _gate else {'score': 0, 'summary': ''},
_res, _sig_before, timing_signal)
if _res_decision != "pass":
print(f" [三维共振] {_sig_before}{timing_signal}: {_res['reason']}", flush=True)
except Exception as _e:
print(f" [三维共振] 评估异常(放行): {_e}", flush=True)
# ----- 构造 action 描述(供 cron prompt 使用) -----
action_parts = []
# 非持仓(自选股/未持有,shares=0):盈亏标签无意义——cost=0 → profit_pct=0 →