|
|
@@ -197,30 +197,85 @@ def _order_sections(rows: Sequence[_View]) -> list[str]:
|
|
|
|
|
|
|
|
|
def _link_summary(rows: Sequence[_View]) -> str:
|
|
|
- """每单链路不列表。按主单汇总执行事实和对冲执行根。"""
|
|
|
+ """每单链路写两段说明,不列表。只根据已经算出的执行事实和对冲执行根,不补未核对的票据和已撤单。"""
|
|
|
|
|
|
if not rows:
|
|
|
return "本轮没有执行事实或对冲执行根结论。"
|
|
|
- orders: dict[str, str] = {}
|
|
|
- for view in rows:
|
|
|
- key = view.master_order_no or view.formula_text
|
|
|
- orders[key] = _worse(orders.get(key), view.verdict)
|
|
|
+ facts = [view for view in rows if view.formula_text.startswith("执行事实")]
|
|
|
+ hedges = [view for view in rows if "执行根" in view.formula_text]
|
|
|
+ parts: list[str] = []
|
|
|
+ if facts:
|
|
|
+ parts.append(_fact_paragraph(facts))
|
|
|
+ if hedges:
|
|
|
+ parts.append(_hedge_paragraph(hedges))
|
|
|
+ covered = {id(view) for view in facts}
|
|
|
+ covered.update(id(view) for view in hedges)
|
|
|
+ rest = [view for view in rows if id(view) not in covered]
|
|
|
+ if rest:
|
|
|
+ parts.append(_plain_count("其余每单链路节点", rest))
|
|
|
+ if not parts:
|
|
|
+ parts.append(_plain_count("每单链路", rows))
|
|
|
+ return "\n\n".join(parts)
|
|
|
+
|
|
|
+
|
|
|
+def _fact_paragraph(rows: Sequence[_View]) -> str:
|
|
|
+ """执行事实:input 的成交名义本金、订单成交均价与实时源表。"""
|
|
|
+
|
|
|
+ orders = _worst_orders(rows)
|
|
|
+ ok = sum(1 for verdict in orders.values() if verdict == VERDICT_CORRECT)
|
|
|
+ text = (
|
|
|
+ f"已完成或已结算的 ORD 主单 {len(orders)} 笔。"
|
|
|
+ "执行事实核对 input 封存的成交名义本金、订单成交均价与实时源表是否一致:"
|
|
|
+ f"{ok} 笔一致。"
|
|
|
+ )
|
|
|
+ return _with_failures(text, rows)
|
|
|
+
|
|
|
+
|
|
|
+def _hedge_paragraph(rows: Sequence[_View]) -> str:
|
|
|
+ """对冲执行根:成交股数对照。自动单走 ext_ref,补录走委托号或母单号。"""
|
|
|
+
|
|
|
+ ok = sum(1 for view in rows if view.verdict == VERDICT_CORRECT)
|
|
|
+ text = (
|
|
|
+ f"需要 TRS 执行根的对冲 {len(rows)} 条,成交股数与执行根对照:{ok} 条一致。"
|
|
|
+ "自动单按 ext_ref(HR 加对冲主键)定位,补录按委托号或算法母单号。"
|
|
|
+ "自定义补录和从未建单不要求执行根。"
|
|
|
+ )
|
|
|
+ return _with_failures(text, rows)
|
|
|
+
|
|
|
+
|
|
|
+def _plain_count(title: str, rows: Sequence[_View]) -> str:
|
|
|
+ orders = _worst_orders(rows)
|
|
|
counts = {VERDICT_CORRECT: 0, VERDICT_ERROR: 0, VERDICT_SUSPECT: 0}
|
|
|
for verdict in orders.values():
|
|
|
counts[verdict] += 1
|
|
|
- text = (
|
|
|
- f"本轮主单 {len(orders)} 笔。执行事实与对冲执行根:"
|
|
|
+ return (
|
|
|
+ f"{title} {len(orders)} 笔:"
|
|
|
f"正确 {counts[VERDICT_CORRECT]} 笔,"
|
|
|
f"错误 {counts[VERDICT_ERROR]} 笔,"
|
|
|
f"可疑 {counts[VERDICT_SUSPECT]} 笔。"
|
|
|
)
|
|
|
- bad = [key for key, verdict in orders.items() if verdict != VERDICT_CORRECT]
|
|
|
- if bad:
|
|
|
- text += "未通过:" + "、".join(bad[:8])
|
|
|
- if len(bad) > 8:
|
|
|
- text += f"…等共 {len(bad)} 笔"
|
|
|
- text += "。"
|
|
|
- return text
|
|
|
+
|
|
|
+
|
|
|
+def _worst_orders(rows: Sequence[_View]) -> dict[str, str]:
|
|
|
+ orders: dict[str, str] = {}
|
|
|
+ for view in rows:
|
|
|
+ key = view.master_order_no or view.formula_text
|
|
|
+ orders[key] = _worse(orders.get(key), view.verdict)
|
|
|
+ return orders
|
|
|
+
|
|
|
+
|
|
|
+def _with_failures(text: str, rows: Sequence[_View]) -> str:
|
|
|
+ """未通过的单另起一行,写主单号和代入,不把原因再用分号粘在句末。"""
|
|
|
+
|
|
|
+ failed = [view for view in rows if view.verdict != VERDICT_CORRECT]
|
|
|
+ if not failed:
|
|
|
+ return text
|
|
|
+ lines = [text, "未通过:"]
|
|
|
+ for view in failed:
|
|
|
+ who = view.master_order_no or "主单"
|
|
|
+ detail = view.calc_text or view.check_text or view.verdict
|
|
|
+ lines.append(f"{who}:{detail}")
|
|
|
+ return "\n".join(lines)
|
|
|
|
|
|
|
|
|
def _field_blocks(rows: Sequence[_View]) -> list[str]:
|
|
|
@@ -278,28 +333,33 @@ def _report_block(rows: Sequence[_View], business_date: date) -> str:
|
|
|
for view in rows:
|
|
|
verdict = _worse(verdict, view.verdict)
|
|
|
names = _formula_names(rows)
|
|
|
- expect = ";".join(names[:6])
|
|
|
- if len(names) > 6:
|
|
|
- expect += f";其余 {len(names) - 6} 列见依据"
|
|
|
correct = sum(1 for view in rows if view.verdict == VERDICT_CORRECT)
|
|
|
bases = _unique(view.basis for view in rows if view.basis)
|
|
|
- basis = ";".join(bases[:4])
|
|
|
- if len(bases) > 4:
|
|
|
- basis += f";另有 {len(bases) - 4} 条依据"
|
|
|
label_text = {VERDICT_CORRECT: "正确", VERDICT_ERROR: "错误", VERDICT_SUSPECT: "可疑"}[verdict]
|
|
|
actual = f"{len(rows)} 列无差异" if verdict == VERDICT_CORRECT else f"{correct}/{len(rows)} 列无差异,其余见下方清单"
|
|
|
return _lines(
|
|
|
(
|
|
|
f"报表:{label}",
|
|
|
f"筛选:{screen}",
|
|
|
- f"期望:{expect}",
|
|
|
+ _stacked("期望", names),
|
|
|
f"实据:{actual}",
|
|
|
- f"依据:{basis or '见各列登记'}",
|
|
|
+ _stacked("依据", bases or ["见各列登记"]),
|
|
|
f"结论:{label_text}",
|
|
|
)
|
|
|
)
|
|
|
|
|
|
|
|
|
+def _stacked(label: str, items: Sequence[str]) -> str:
|
|
|
+ """多条依据或期望各占一行。用分号拼在一起时,原型路径会挤成一行。"""
|
|
|
+
|
|
|
+ kept = [item for item in items if item]
|
|
|
+ if not kept:
|
|
|
+ return f"{label}:"
|
|
|
+ if len(kept) == 1:
|
|
|
+ return f"{label}:{kept[0]}"
|
|
|
+ return f"{label}:{kept[0]}\n" + "\n".join(kept[1:])
|
|
|
+
|
|
|
+
|
|
|
def _formula_names(rows: Sequence[_View]) -> list[str]:
|
|
|
found: list[str] = []
|
|
|
for view in rows:
|