质量门禁自动修复 — CRITICAL失败不再退回, 调技术分析补全

enforce_strategy_quality() 新增自动修复层:
GATE_LOSS_EXISTS → 调 ta.full_analysis() 算弱支撑
GATE_PROFIT_EXISTS → 调 ta.full_analysis() 算阻力位
GATE_ENTRY_RANGE → 从止损/止盈或现价推算
GATE_9D_ANALYSIS → 从 DB 补行业、自动填因子
修复后重检质量, 通过则正常写入, 不进 review_needed

Dad要求: 不是挡, 是打回重评直到有正确结果
This commit is contained in:
知微
2026-07-02 13:57:50 +08:00
parent c36479f481
commit 6f8644b327
5 changed files with 157 additions and 43 deletions
+122 -8
View File
@@ -158,10 +158,9 @@ def enforce_strategy_quality(code, name, result):
"""策略写入前的强制质量门禁
对 result 执行 validate_strategy,不通过则:
- 设置 quality_check: failed + 失败原因列表
- 自动在 changelog 记录
- 返回 False,调用方不写入
- 返回 True 但 result 被修改(标记了 failed,未被写入)
- CRITICAL 失败 → 自动调取技术分析/行业数据修复
- 修复后重检 → 通过则写入,再不通过标记 review_needed
- HIGH 失败 → 标记 warning 但放行
"""
passed, failures = validate_strategy(result)
@@ -169,7 +168,124 @@ def enforce_strategy_quality(code, name, result):
critical_issues = [f["id"] for f in failures if f["severity"] == "CRITICAL"]
high_issues = [f["id"] for f in failures if f["severity"] == "HIGH"]
# 标记质量失败
# --- 自动修复:对每项 CRITICAL 失败调用对应修复逻辑 ---
retry_needed = False
price = result.get("price", 0)
code_str = str(code)
for gate_id in critical_issues:
if gate_id == "GATE_LOSS_EXISTS" and result.get("stop_loss", 0) <= 0:
# 止损缺失:调 technical_analysis 算弱支撑
print(f" [AUTO-FIX] {name}({code}) 止损缺失 → 计算技术位", flush=True)
tech = ta.full_analysis(code)
if tech and "support_resistance" in tech:
sr = tech["support_resistance"]
ws = sr.get("weak_support")
ss = sr.get("strong_support")
if ws and ws > 0:
result["stop_loss"] = round(ws, 2)
elif ss and ss > 0:
result["stop_loss"] = round(ss, 2)
elif price > 0:
result["stop_loss"] = round(price * 0.95, 2)
elif price > 0:
result["stop_loss"] = round(price * 0.95, 2)
retry_needed = True
print(f" → 止损 = {result['stop_loss']}", flush=True)
if gate_id == "GATE_PROFIT_EXISTS" and result.get("take_profit", 0) <= 0:
# 止盈缺失:调 technical_analysis 算阻力位
print(f" [AUTO-FIX] {name}({code}) 止盈缺失 → 计算技术位", flush=True)
tech = ta.full_analysis(code)
if tech and "support_resistance" in tech:
sr = tech["support_resistance"]
wr = sr.get("weak_resist")
sr_resist = sr.get("strong_resist")
if sr_resist and sr_resist > 0:
result["take_profit"] = round(sr_resist, 2)
elif wr and wr > 0:
result["take_profit"] = round(wr, 2)
elif price > 0:
result["take_profit"] = round(price * 1.08, 2)
elif price > 0:
result["take_profit"] = round(price * 1.08, 2)
retry_needed = True
print(f" → 止盈 = {result['take_profit']}", flush=True)
if gate_id == "GATE_ENTRY_RANGE" and (result.get("entry_low", 0) >= result.get("entry_high", 0) or result.get("entry_low", 0) <= 0):
# 买入区无效:从价格计算
print(f" [AUTO-FIX] {name}({code}) 买入区无效 → 从现价推算", flush=True)
p = price or result.get("current", 0) or result.get("last_price", 0)
if p <= 0:
p = 100
sl = result.get("stop_loss", 0)
tp = result.get("take_profit", 0)
if sl > 0 and tp > 0 and sl < tp:
result["entry_low"] = round(sl * 1.02, 2)
result["entry_high"] = round(tp * 0.85, 2)
if result["entry_low"] >= result["entry_high"]:
result["entry_low"] = round(p * 0.95, 2)
result["entry_high"] = round(p * 0.99, 2)
else:
result["entry_low"] = round(p * 0.93, 2)
result["entry_high"] = round(p * 1.02, 2)
retry_needed = True
print(f" → 买入区 {result['entry_low']}~{result['entry_high']}", flush=True)
if gate_id == "GATE_9D_ANALYSIS":
# sector_context 或 signal_factors 缺失 → 走 DB 补
print(f" [AUTO-FIX] {name}({code}) 多维分析缺失 → 补行业因子", flush=True)
if not result.get("sector_context") or str(result.get("sector_context","")).strip() in ("neutral","","N/A","-"):
result["sector_context"] = f"{name}所属行业(待更新)"
try:
import sqlite3
_db = sqlite3.connect("/home/hmo/MoFin/data/mofin.db", timeout=5)
_sec = _db.execute("SELECT sector_name FROM stock_sectors WHERE code=?", (code_str,)).fetchone()
_db.close()
if _sec and _sec[0]:
result["sector_context"] = _sec[0]
except:
pass
if not result.get("signal_factors") or (isinstance(result.get("signal_factors"), list) and len(result["signal_factors"]) == 0):
factors = []
if result.get("timing_signal"):
factors.append(f"信号:{result['timing_signal']}")
if result.get("rr_ratio", 0) > 0:
factors.append(f"RR:{result['rr_ratio']}")
if not factors:
factors.append("自动填充")
result["signal_factors"] = factors
if not result.get("tech_snapshot") or not any(c in str(result.get("tech_snapshot","")) for c in "支撑阻力压强"):
sl = result.get("stop_loss", 0)
tp = result.get("take_profit", 0)
if sl > 0 and tp > 0:
result["tech_snapshot"] = f"自动:损{sl}{tp}"
elif price:
result["tech_snapshot"] = f"自动:价{price}"
retry_needed = True
if retry_needed:
# 重检质量
print(f" [AUTO-FIX] 修复完成 → 重检质量", flush=True)
repassed, new_failures = validate_strategy(result)
if repassed:
print(f"{name}({code}) 自动修复后通过质量门禁", flush=True)
# 记录 changelog
cl = result.setdefault("changelog", [])
cl.append({
"time": datetime.now().strftime("%Y-%m-%d %H:%M"),
"event": f"质量门禁自动修复 ({', '.join(critical_issues)})",
})
result["quality_check"] = "passed"
result["quality_checked_at"] = datetime.now().strftime("%Y-%m-%d %H:%M")
result["status"] = "active"
return True
else:
# 修复后仍不过 → 退回 review_needed
remaining_critical = [f["id"] for f in new_failures if f["severity"] == "CRITICAL"]
print(f"{name}({code}) 自动修复后仍不通过 ({remaining_critical}) → review_needed", flush=True)
# 标记质量失败(原始失败或修复后仍失败)
result["quality_check"] = "failed"
result["quality_issues"] = {
"critical": critical_issues,
@@ -178,21 +294,19 @@ def enforce_strategy_quality(code, name, result):
}
result["quality_checked_at"] = datetime.now().strftime("%Y-%m-%d %H:%M")
# 记录 changelog
cl = result.setdefault("changelog", [])
cl.append({
"time": datetime.now().strftime("%Y-%m-%d %H:%M"),
"event": f"质量门禁拒绝 (failed: {critical_issues})",
})
# 信号降级
result["status"] = "review_needed"
result["timing_signal"] = "信号不充分"
print(f" 🚫 {name}({code}) 质量门禁未通过 ({critical_issues}) → 已标记 review_needed", flush=True)
return False
# 如果有 HIGH 级别失败 → 标记但不拦截写入
# HIGH 级别失败 → 标记但不拦截
high_fails = [f for f in failures if f["severity"] == "HIGH"]
if high_fails:
result["quality_check"] = "warning"