diff --git a/deploy/profile-scripts/per_stock_reassess.py b/deploy/profile-scripts/per_stock_reassess.py index 31f1e687..d4747d8a 100644 --- a/deploy/profile-scripts/per_stock_reassess.py +++ b/deploy/profile-scripts/per_stock_reassess.py @@ -66,18 +66,10 @@ def _build_full_analysis(code, entry, result): eh = result.get("entry_high") sl = result.get("stop_loss") or entry.get("stop_loss", 0) tp = result.get("take_profit") or entry.get("take_profit", 0) - # 2026-08-18 修复 rr_ratio:统一用 entry/stop/tp 算(与 promote 同口径 rr = (tp-mid)/(mid-sl)) - # strategy_lifecycle 的 rr_ratio 可能算错(6.5 vs 实际 8.64),统一用 entry/stop/tp 算最准 - _el_rr = result.get("entry_low") if result.get("entry_low") is not None else entry.get("entry_low", 0) - _eh_rr = result.get("entry_high") if result.get("entry_high") is not None else entry.get("entry_high", 0) - _sl_rr = result.get("stop_loss") or entry.get("stop_loss", 0) - _tp_rr = result.get("take_profit") or entry.get("take_profit", 0) - if _el_rr > 0 and _eh_rr > _el_rr and _sl_rr > 0 and _tp_rr > 0: - _mid_rr = (_el_rr + _eh_rr) / 2 - rr = round((_tp_rr - _mid_rr) / (_mid_rr - _sl_rr), 2) if _mid_rr > _sl_rr else 0 - else: - # 无有效区间/止损/止盈时,用 holding 的旧 rr_ratio 或 0 - rr = entry.get("rr_ratio", 0) + # 2026-08-18 修复:删除 rr_ratio 覆盖——RR 由 scanner 定(candidates.rr=holding.rr_ratio), + # per_stock_reassess 不该重算/覆盖(它该用 holding 的 rr_ratio,不重算)。 + # 正确架构:RR 是 scanner 定义的单一事实来源,per_stock_reassess 只读不重算。 + rr = entry.get("rr_ratio", 0) act = result.get("action", "") # ── 从DB拉取大盘、基本面、资金流 ── diff --git a/deploy/profile-scripts/s2_panic_v2_scanner.py b/deploy/profile-scripts/s2_panic_v2_scanner.py index 2783f75f..052bce2b 100644 --- a/deploy/profile-scripts/s2_panic_v2_scanner.py +++ b/deploy/profile-scripts/s2_panic_v2_scanner.py @@ -159,14 +159,17 @@ def main(): inserted = 0 for code, name, sig in hits: reasons = (f"rsi={sig['rsi']} mcap_q={sig['mcap_q']} score={sig['score']}") + # 2026-08-18 补 rr 列(断链 bug:promote 的 _rr is None 全跳过,与 b_td1_v3 同口径) + _mid_v = (sig['price'] * 0.98 + sig['price']) / 2 + _rr_v = round((sig["target"] - _mid_v) / (_mid_v - sig["stop_loss"]), 2) if _mid_v > sig["stop_loss"] > 0 else 0 conn.execute( - "INSERT INTO candidates (code, name, sector, reason, entry_range, stop_loss, target, source_strategy, created_at) " - "VALUES (?,?,?,?,?,?,?,?,datetime('now','localtime')) " + "INSERT INTO candidates (code, name, sector, reason, entry_range, stop_loss, target, rr, source_strategy, created_at) " + "VALUES (?,?,?,?,?,?,?,?,?,datetime('now','localtime')) " "ON CONFLICT(code) DO UPDATE SET " "name=excluded.name, sector=excluded.sector, reason=excluded.reason, " - "entry_range=excluded.entry_range, stop_loss=excluded.stop_loss, target=excluded.target, source_strategy=excluded.source_strategy", + "entry_range=excluded.entry_range, stop_loss=excluded.stop_loss, target=excluded.target, rr=excluded.rr, source_strategy=excluded.source_strategy", (code, code, "s2_panic_v2", reasons, - f"{sig['price']*0.98:.2f}~{sig['price']:.2f}", sig["stop_loss"], sig["target"], "s2_panic_v2")) + f"{sig['price']*0.98:.2f}~{sig['price']:.2f}", sig["stop_loss"], sig["target"], _rr_v, "s2_panic_v2")) inserted += 1 print(f" 🟢 {code} {name} 价{sig['price']} score={sig['score']} {reasons}", flush=True) conn.commit() diff --git a/deploy/profile-scripts/strategy_lifecycle.py b/deploy/profile-scripts/strategy_lifecycle.py index 8e1d0e45..4237f5e4 100644 --- a/deploy/profile-scripts/strategy_lifecycle.py +++ b/deploy/profile-scripts/strategy_lifecycle.py @@ -1179,15 +1179,11 @@ def reassess_strategy(code, name, price, cost, shares, current_action, found = True break - # ----- 风险回报比最终计算 ----- - # 2026-08-18 修复:买入价用买入区中值(entry_mid)算 risk,不是现价 - # 现价不是买入价——实盘按 entry_mid 买入,risk=entry_mid-stop,reward=tp-entry_mid - _el = result.get("entry_low") or 0 - _eh = result.get("entry_high") or 0 - _entry_mid = (_el + _eh) / 2 if (_el > 0 and _eh > _el) else price - risk = max(_entry_mid - new_stop, _entry_mid * 0.01) - reward = max(new_target - _entry_mid, 0) - rr_ratio = reward / risk if risk > 0 else 0 + # ----- 风险回报比(不重算,RR 由 scanner 定)----- + # 2026-08-18 修复:RR 由 scanner 定(candidates.rr=holding.rr_ratio), + # strategy_lifecycle 管持仓状态(信号/止损移动),不重算/重写 RR。 + # 正确架构:RR 是 scanner 定义的单一事实来源,lifecycle 只读不重算。 + rr_ratio = result.get("rr_ratio") or 0 # ----- 状态判断 ----- if is_deep_loss: