feat: 重评注入策略语义——【情势体检】(温区/黑天鹅/执行红线)+【策略定义】+【策略判断】四选一,parse解析+save落库strategy_state(老莫:每次重评先判断是否切策略)

This commit is contained in:
xxm
2026-08-18 20:20:11 +08:00
parent 9a08091193
commit 82bdd76b85
+112 -1
View File
@@ -99,6 +99,46 @@ def collect_data(code):
data["changelog_json"] = r[12] or ""
data["reassessed_at"] = r[13] or ""
data["strategy_name"] = r[15] or "" # 2026-08-18 来源策略
# 2026-08-18 策略语义注入:读 strategy_defs 定义卡 + 情势体检
try:
_sd = conn.execute("SELECT display_name, summary, entry_logic, exit_logic, review_focus, holding_style, status, retired_reason, superseded_by, regime FROM strategy_defs WHERE strategy_name=?", (data["strategy_name"],)).fetchone()
if _sd:
data["strategy_def"] = {
"display_name": _sd[0] or data["strategy_name"], "summary": _sd[1] or "",
"entry_logic": _sd[2] or "", "exit_logic": _sd[3] or "",
"review_focus": _sd[4] or "", "holding_style": _sd[5] or "",
"status": _sd[6] or "active", "retired_reason": _sd[7] or "",
"superseded_by": _sd[8] or "", "regime": _sd[9] or "",
}
else:
data["strategy_def"] = None
except Exception:
data["strategy_def"] = None
# 情势体检:温区 + 高风险消息 + 执行红线
_sit = {"regime_a": "unknown", "regime_7d_ago": "unknown", "high_risk": "", "breach_stop": "", "reach_tp": "", "out_zone": "", "over_hold": ""}
try:
_m = conn.execute("SELECT regime, date FROM market_regime WHERE market='a' ORDER BY date DESC LIMIT 1").fetchone()
if _m:
_sit["regime_a"] = _m[0] or "unknown"
_m7 = conn.execute("SELECT regime FROM market_regime WHERE market='a' AND date <= date(?, '-7 days') ORDER BY date DESC LIMIT 1", (_m[1],)).fetchone()
if _m7: _sit["regime_7d_ago"] = _m7[0] or "unknown"
except Exception:
pass
try:
_hr = conn.execute("SELECT summary FROM signal_news WHERE overall_sentiment LIKE '%HIGH%' OR summary LIKE '%高风险%' ORDER BY id DESC LIMIT 1").fetchone()
if _hr: _sit["high_risk"] = (_hr[0] or "")[:60]
except Exception:
pass
try:
_p = data.get("price") or 0
_el2, _eh2 = data.get("entry_low") or 0, data.get("entry_high") or 0
_sl2, _tp2 = data.get("stop_loss") or 0, data.get("take_profit") or 0
if _p > 0 and _sl2 > 0 and _p < _sl2: _sit["breach_stop"] = ""
if _p > 0 and _tp2 > 0 and _p >= _tp2: _sit["reach_tp"] = ""
if _eh2 > 0 and _p > _eh2 * 1.05: _sit["out_zone"] = ""
except Exception:
pass
data["situation"] = _sit
data["position_advice"] = r[14] or ""
# 持仓状态(2026-07-22 老爸要求:LLM 必须知道是否持有/成本/股数)
hr = conn.execute("SELECT shares, cost, price FROM holdings WHERE code=? AND is_active=1 AND shares>0", (code,)).fetchone()
@@ -319,7 +359,38 @@ def build_prompt(data):
pass
_position_context += _rotation_context
_orig_strategy_section = f"""来源策略: {data.get("strategy_name") or "unknown"}(按此策略选股逻辑重评,可据最新情况调整参数)\n当前策略参数: {_params_str}
# ── 策略语义注入(2026-08-18 老莫:重评按策略定义,不是裸标签)──
_sd = data.get("strategy_def")
_sit = data.get("situation") or {}
if _sd:
_retired_note = ""
if _sd.get("status") == "retired":
_retired_note = (f"\n⚠️ 该策略已被系统标记【已淘汰】:{_sd.get('retired_reason') or ''}"
f" 不要沿用其入场/出场参数作为默认锚,按当前市场环境重新评估。")
_strategy_sec = f"""【策略定义】(系统策略库 strategy_defs 提供,非LLM生成)
策略名: {_sd.get('display_name')}{data.get('strategy_name') or 'unknown'}
策略逻辑: {_sd.get('summary') or ''}
入场逻辑: {_sd.get('entry_logic') or ''}
出场规则: {_sd.get('exit_logic') or ''}
重评侧重: {_sd.get('review_focus') or ''}
持仓风格: {_sd.get('holding_style') or ''}{_retired_note}"""
else:
_strategy_sec = """【策略定义】(无策略记录)
来源策略未知。请基于技术形态/估值/资金特征判断该股当前最接近的策略画像,并在【策略判断】中说明;
若判断需要归类到某个策略,从以下候选中选择(禁止自创):
accumulation(主力建仓) / b_td1_v3(超跌原池优选) / v_mr(弱市深超跌) / hk_pe_mom(港股动量) / hk_pe_oversold(港股超卖) / p_oversold(预测超跌) / s2_panic(恐慌买强势) / leader(龙头回调)"""
_sit_sec = f"""【情势体检】(系统确定性计算,非LLM估计)
当前温区(A股): {_sit.get('regime_a','unknown')}7天前: {_sit.get('regime_7d_ago','unknown')}
温区突变: {'' if _sit.get('regime_a') != _sit.get('regime_7d_ago') and _sit.get('regime_a') not in ('unknown','') else ''}
高风险消息: {_sit.get('high_risk') or ''}
执行红线: 破止损未执行={_sit.get('breach_stop')} 达止盈未执行={_sit.get('reach_tp')} 现价超买入区上沿={_sit.get('out_zone')} 超持有期={_sit.get('over_hold')}
⚠️ 若温区突变/高风险消息/执行红线任一命中,或重评侧重检查发现原入场逻辑不成立,【策略判断】必须考虑「策略失效重定」或「更换策略归类」。"""
_orig_strategy_section = f"""{_strategy_sec}
{_sit_sec}
来源策略: {data.get("strategy_name") or "unknown"}(按此策略选股逻辑重评,可据最新情况调整参数)
当前策略参数: {_params_str}
变更记录(最近3条):
{_changelog_str}
@@ -388,6 +459,12 @@ PE={data.get('pe','?')}(最新财报) 市值={data.get('mcap','?')}亿
请严格按以下格式输出(注意节标题不可省略):
【策略判断】四选一:维持原策略 / 修改参数 / 策略失效重定 / 更换策略归类
- 默认倾向「维持原策略」;只有情势体检命中红线、或重评侧重检查发现原入场逻辑不成立时,才选后三者
- 「策略失效重定」= 原策略已不适用,按当前市场环境重新制定(新止损/止盈/持有期)
- 「更换策略归类」= 该股当前更适合另一个策略,从【策略定义】候选中选择,禁止自创
【策略失效理由】(策略判断≠维持原策略时必填,逐条对照重评侧重说明哪条不成立;维持时写"无需修改"
【更换策略归类】(仅当策略判断=更换策略归类时填,写策略名)
【维持或修改】明确二选一判断:维持原策略 / 需要修改策略
【修改点及理由】
如果维持原策略 → 写"无需修改"
@@ -513,6 +590,31 @@ def parse_response(text):
result["position"] = f"{f:.0f}%"
break
# 2026-08-18 策略判断(只认【策略判断】节行)
result["strategy_judge"] = ""
_sj = _section_line("策略判断")
if _sj:
_m = re.search(r'策略判断】\s*(维持原策略|修改参数|策略失效重定|更换策略归类)', _sj)
if _m:
result["strategy_judge"] = _m.group(1)
# 策略失效理由
result["strategy_reason"] = ""
_sr = _section_line("策略失效理由")
if _sr:
result["strategy_reason"] = re.sub(r'^\s*【策略失效理由】\s*', '', _sr).strip()[:200]
# 更换策略归类(封闭集校验:必须在候选列表内)
result["strategy_switch_to"] = ""
_CANDIDATES = ("accumulation", "b_td1_v3", "v_mr", "hk_pe_mom", "hk_pe_oversold", "p_oversold", "s2_panic", "leader")
_st = _section_line("更换策略归类")
if _st:
_body = re.sub(r'^\s*【更换策略归类】\s*', '', _st).strip().lower()
for c in _CANDIDATES:
if c in _body:
result["strategy_switch_to"] = c
break
if not result["strategy_switch_to"] and result["strategy_judge"] == "更换策略归类":
result["strategy_judge"] = "策略失效重定" # 归因无效→降级
return result
def save_result(code, full_text, parsed, ta_levels=None):
@@ -639,6 +741,15 @@ def save_result(code, full_text, parsed, ta_levels=None):
conn.execute(sql, params)
conn.commit()
# 2026-08-18 策略状态落库(strategy_judge/switch_to,老莫:策略切换决策)
_j = parsed.get("strategy_judge", "")
if _j == "策略失效重定":
conn.execute("UPDATE holding_strategies SET strategy_state='invalidated' WHERE code=? AND status='active'", (code,))
conn.commit()
elif _j == "更换策略归类" and parsed.get("strategy_switch_to"):
conn.execute("UPDATE holding_strategies SET strategy_attributed=?, strategy_state='switched', strategy_provenance='llm_attributed' WHERE code=? AND status='active'", (parsed["strategy_switch_to"], code))
conn.commit()
# ── 信号以分析为唯一事实源(防信号/分析脱节)──
from mofin_db import reconcile_signal_from_analysis
final_sig = reconcile_signal_from_analysis(conn, code)