diff --git a/api.py b/api.py index b32a49d..cb01f7e 100644 --- a/api.py +++ b/api.py @@ -20,6 +20,7 @@ import pandas as pd from fastapi import FastAPI, HTTPException, Request from fastapi.responses import PlainTextResponse +import card import config import db import logic_state_daily @@ -185,4 +186,14 @@ def logic_state_lookup(codes: str | None = None, code: str | None = None, out.append({"input": c, "code": k, "date": d, "source": "computed", "verdict": None, "card_rank": None, "plan_version": None, **(plan._state_out(computed.get(k)) or {})}) # noqa: SLF001 + # 安全边际三情景随状态一起回(第四件;2026-09-07 拍板持仓页显示中性情景作参考目标价,只显示不触发)。 + # 逐票日频表里不存它,每次按此刻现算;算失败整批不带这两键,状态照回。 + try: + vals = plan.valuations_for(list(keyed), d) + for item in out: + v = vals.get(item["code"]) + item["valuation"] = v + item["valuation_text"] = card.valuation_view(v) + except Exception as e: # noqa: BLE001 —— 估值算不出不该拖垮状态查询 + _access.warning("logic_state valuation failed date=%s err=%r", d, e) return {"date": d, "count": len(out) + len(bad), "states": out + bad} diff --git a/plan.py b/plan.py index 6ca6e1f..b5ae734 100644 --- a/plan.py +++ b/plan.py @@ -259,6 +259,18 @@ def logic_states_for(codes: list, ds: str) -> dict: return out +def valuations_for(codes: list, ds: str) -> dict: + """给定几只票的安全边际三情景(第四件),按前缀码索引。给接口 /logic_state 用:持仓页要显示 + 参考目标价(中性情景,2026-09-07 拍板:只显示不触发,不替代用户手设的目标价),而持仓票 + 不在计划里。取数与计划装配同一处:券商研报原始行、前复权收盘价。""" + codes = [str(c).strip() for c in (codes or []) if str(c).strip()] + if not codes: + return {} + rows = sources.broker_reports(codes, ds) + prices = sources.close_prices(ds) + return sources.valuation_scenarios(codes, ds, prices, rows=rows) + + def _assemble_cards(ds: str, codes: list, ev: dict, upside: pd.Series, mkt_days: set, risk: set | None = None) -> tuple[dict, list]: """候选卡装配(2026-09-02 方案第 2.2 节第三项):对档位表里的全部票(主榜与观察档,