tradingSystem/app/core/decide_reasons.py

69 lines
3.4 KiB
Python
Raw Normal View History

# -*- coding: utf-8 -*-
"""人工裁决理由的码表与拼句 (2026-09-14 系统自决包, 台账 015)。纯逻辑, 零外部依赖, 可单测。
系统自决把绝大多数该不该做接了过去, 剩下真交人的那几张提议, 人裁决时从预设理由里选一条,
其他再补一句 不再每次手写一整段这里放两张码表 (采纳 / 驳回) 与一个拼句函数,
决定 + + 补充 + 自由文本拼成落进评审账本的一句话, 并回出用到的码 (进硬数字备查)
页面拿这两张表铺预设按钮 (GET /api/proposals/decide-reasons); 后端在裁决入口再校验一次
其他不填补充或码与决定对不上, 一律回中文错误, 不落账 (与研判不可用交人同一层的严)
旧的自由文本 reason 仍然接受 (向后兼容: 脚本旧缓存页面照发照收)
"""
from __future__ import annotations
# 驳回理由码 → 中文标签。最后一个是「其他」, 选它必须填补充。
DECLINE_REASONS = {
"DECLINE_CONSENSUS_MISMATCH": "与研判结论不符",
"DECLINE_TECH_WEAK": "技术面已转弱",
"DECLINE_POSITION_FULL": "仓位已达上限",
"DECLINE_SECTOR_CONCENTRATED": "行业集中度过高",
"DECLINE_WAIT_PULLBACK": "等待回调再议",
"DECLINE_MARKET_RISK": "大盘风险偏高",
"DECLINE_OTHER": "其他",
}
# 采纳理由码 → 中文标签。最后一个是「其他」, 选它必须填补充。
ACCEPT_REASONS = {
"ACCEPT_CONSENSUS": "合议放行,按系统建议执行",
"ACCEPT_MANUAL_JUDGE": "人工判断机会成立",
"ACCEPT_PER_COMMAND": "按既有命令执行",
"ACCEPT_RISK_CONTROL": "风控需要",
"ACCEPT_OTHER": "其他",
}
def reasons_for(decision) -> dict:
"""按决定取对应的码表。ACCEPTED 用采纳表, 其余 (DECLINED) 用驳回表。"""
return ACCEPT_REASONS if str(decision or "").upper() == "ACCEPTED" else DECLINE_REASONS
def compose(decision, reason_code=None, reason_note=None, reason=None):
"""把裁决理由拼成一句话。返回 (账本句, 用到的码或 None)。
decision: ACCEPTED / DECLINED, 决定用哪张码表
reason_code: 预设理由码 (可空)给了就必须落在对应码表里, 否则报错
reason_note: 补充说明 (其他时必填)
reason: 旧的自由文本 (向后兼容)只有它没有码时, 原样当账本句
码与自由文本都空 报错 (空理由不收)码不在表里 / 其他不填补充 报错
错误一律是能直接给人看的中文 ValueError
"""
decision = str(decision or "").upper()
table = reasons_for(decision)
code = (reason_code or "").strip() or None
note = (reason_note or "").strip()
free = (reason or "").strip()
label_side = "采纳" if decision == "ACCEPTED" else "驳回"
if not code and not free:
raise ValueError("采纳或驳回都要给理由:选一个预设理由,或写一句自由说明")
if code:
if code not in table:
raise ValueError(f"理由码 {code} 不适用于本次{label_side}")
if code.endswith("_OTHER") and not note:
raise ValueError("选「其他」时必须填补充说明")
sentence = table[code] + (f";补充:{note}" if note else "")
return sentence[:500], code
# 只有自由文本 (旧字段兼容): 原样当账本句, 不带码。
return free[:500], None