处理现有问题
This commit is contained in:
parent
d0398b9331
commit
cf99e1446c
|
|
@ -66,7 +66,7 @@ scripts/
|
||||||
test_batch6_units.py ws 通道: 测试向量/签名/公钥/水位/DDL/逐笔入账 65 例
|
test_batch6_units.py ws 通道: 测试向量/签名/公钥/水位/DDL/逐笔入账 65 例
|
||||||
test_batch7_units.py 上游选股计划: 解析/新鲜度/候选筛选/取数守卫 32 例
|
test_batch7_units.py 上游选股计划: 解析/新鲜度/候选筛选/取数守卫 32 例
|
||||||
test_batch8_units.py 榜单变化: 名册指纹/三种语义/尾部闸/落库往返 60 例
|
test_batch8_units.py 榜单变化: 名册指纹/三种语义/尾部闸/落库往返 60 例
|
||||||
test_batch9_units.py 成本价体检 / 对账按日推进 / 行业闸 / 取整记账 47 例
|
test_batch9_units.py 成本价体检 / 对账按日推进 / 行业闸 / 取整记账 49 例
|
||||||
test_batch10_units.py 静默失败专项: 关键路径不许丢返回值 + 八条实例 40 例
|
test_batch10_units.py 静默失败专项: 关键路径不许丢返回值 + 八条实例 40 例
|
||||||
test_wiring.py 装配自检: 服务层→核心→落表 全链路 (内存桩) 58 例
|
test_wiring.py 装配自检: 服务层→核心→落表 全链路 (内存桩) 58 例
|
||||||
init_db.py 建表 (应用 ddl_pms_v1.sql, 幂等, 默认演练; 含 DDL 体检)
|
init_db.py 建表 (应用 ddl_pms_v1.sql, 幂等, 默认演练; 含 DDL 体检)
|
||||||
|
|
@ -302,7 +302,7 @@ make t-gate # 随时: 规则闸/研判闸拒了什么、为什么
|
||||||
|
|
||||||
## 已实现 / 待开发
|
## 已实现 / 待开发
|
||||||
|
|
||||||
**已实现**:建表 DDL 与建表脚本;配置与运行参数中心;仓位规划器与安全垫账;命令系统(27 类命令全目录 + 双状态机 + 冲突识别);方案生成器(降仓凑额四档、升仓、建仓分批、清仓/减至、行业清仓与限额、暂停买入撤单);账本回放与对账引擎(成交认领、外部成交并入 BASE 告警、以下游为准修正、除权检测、T+1 可用量、连续不一致升级);规则闸终检;择时执行器实现 B(分日配额、分笔、VWAP/回踩/不追高、14:45 兜底、停牌一字板顺延、窗口耗尽收口、挂单有效期);动作引擎四类自主动作 + 研判闸客户端 + 提议分流;决策系统信号消化(两条流独立消费组订阅、置信度分档转清仓指令或提议);管理页面四块 + 运维/日报抽屉;调度器九个调度位;**上游选股计划接口接入**(`/plan` 取候选池、交易日龄硬校验、`theme` 灌行业映射表、页面预览抽屉与不可用横幅);**榜单变化提示**(名册快照 + 新进/掉榜/档位升降/覆盖翻转/名次跳变,持仓票单列,榜尾截断噪音闸);**ws 直连通道的连接层**(常驻进程 + 出口队列 + 签名 + seq 水位与累积确认,见下);**单测 391 例**。
|
**已实现**:建表 DDL 与建表脚本;配置与运行参数中心;仓位规划器与安全垫账;命令系统(27 类命令全目录 + 双状态机 + 冲突识别);方案生成器(降仓凑额四档、升仓、建仓分批、清仓/减至、行业清仓与限额、暂停买入撤单);账本回放与对账引擎(成交认领、外部成交并入 BASE 告警、以下游为准修正、除权检测、T+1 可用量、连续不一致升级);规则闸终检;择时执行器实现 B(分日配额、分笔、VWAP/回踩/不追高、14:45 兜底、停牌一字板顺延、窗口耗尽收口、挂单有效期);动作引擎四类自主动作 + 研判闸客户端 + 提议分流;决策系统信号消化(两条流独立消费组订阅、置信度分档转清仓指令或提议);管理页面四块 + 运维/日报抽屉;调度器九个调度位;**上游选股计划接口接入**(`/plan` 取候选池、交易日龄硬校验、`theme` 灌行业映射表、页面预览抽屉与不可用横幅);**榜单变化提示**(名册快照 + 新进/掉榜/档位升降/覆盖翻转/名次跳变,持仓票单列,榜尾截断噪音闸);**ws 直连通道的连接层**(常驻进程 + 出口队列 + 签名 + seq 水位与累积确认,见下);**单测 393 例**。
|
||||||
|
|
||||||
### 静默失败专项(2026-07-31,八条已修)
|
### 静默失败专项(2026-07-31,八条已修)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,8 @@
|
||||||
"""
|
"""
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import re
|
||||||
|
|
||||||
from app.core.sizer import LOT, lot_qty, split_batches, check_caps
|
from app.core.sizer import LOT, lot_qty, split_batches, check_caps
|
||||||
from app.core.cushion import sell_allocation # noqa: F401 (供调用方做核销预览)
|
from app.core.cushion import sell_allocation # noqa: F401 (供调用方做核销预览)
|
||||||
|
|
||||||
|
|
@ -631,6 +633,26 @@ def _open_batch_items(ts_code, sp, price, reason_prefix="建仓"):
|
||||||
# ================================================================
|
# ================================================================
|
||||||
# 四、拒绝原因聚合
|
# 四、拒绝原因聚合
|
||||||
# ================================================================
|
# ================================================================
|
||||||
|
_REJ_CODE = re.compile(r"^([A-Z][A-Z0-9_]{2,}):")
|
||||||
|
_REJ_NUM = re.compile(r"\d+(?:[,\d]*)(?:\.\d+)?%?")
|
||||||
|
|
||||||
|
|
||||||
|
def _reason_key(text: str) -> str:
|
||||||
|
"""把一条拒绝原因归一成"同类原因同一个键"。
|
||||||
|
|
||||||
|
有判据码 (`SECTOR_RATIO: …`) 就用码; 没有码的把**数字抹成 …** 再当键 —— 价格、金额、
|
||||||
|
只数每只票都不一样, 不抹掉就永远聚不到一起。括号里的尾注 (「(已尝试: CUSTOM → …)」)
|
||||||
|
也去掉, 它是排查细节不是分类依据, 原始 rejects 清单里一个字没少。
|
||||||
|
"""
|
||||||
|
s = str(text or "").strip()
|
||||||
|
m = _REJ_CODE.match(s)
|
||||||
|
if m:
|
||||||
|
return m.group(1)
|
||||||
|
s = s.split("(")[0].split("(")[0].strip()
|
||||||
|
s = _REJ_NUM.sub("…", s)
|
||||||
|
return s[:40].strip() or "(无原因)"
|
||||||
|
|
||||||
|
|
||||||
def summarize_rejects(rejects) -> str:
|
def summarize_rejects(rejects) -> str:
|
||||||
"""把 rejects 清单聚合成一行人话。
|
"""把 rejects 清单聚合成一行人话。
|
||||||
|
|
||||||
|
|
@ -640,7 +662,13 @@ def summarize_rejects(rejects) -> str:
|
||||||
端到面上。**"拒了 10 只"和"没有候选"是两件完全不同的事, 长得却一样。**
|
端到面上。**"拒了 10 只"和"没有候选"是两件完全不同的事, 长得却一样。**
|
||||||
|
|
||||||
输出按拒绝码归类计数, 多的排前面:
|
输出按拒绝码归类计数, 多的排前面:
|
||||||
"SECTOR_RATIO 10 只 (600000.SH…); STOCK_CAP 2 只 (600519.SH…)"
|
"SECTOR_RATIO 10 只 (600000.SH…); 目标金额 … 元按现价 … 买不足一手 8 只 (600764.SH…)"
|
||||||
|
|
||||||
|
**没有判据码的原因必须先把数字抹掉再归类** (2026-08-03 实机修): 原来对无码原因是
|
||||||
|
`text[:20]` 硬截 20 个字, 结果两件事同时坏掉 —— 一是价格在截断范围内, 8 只票 8 个价,
|
||||||
|
于是聚成 8 组、一组一只, 聚合等于没做, 输出比原始清单还长; 二是截断落在词中间, 印出
|
||||||
|
"…按现价 18.68 买" / "…按现价 6.47 买不" 这种半句话。聚合本来是为了让人一眼看懂
|
||||||
|
"为什么这么少", 结果自己成了噪音源。
|
||||||
"""
|
"""
|
||||||
if not rejects:
|
if not rejects:
|
||||||
return ""
|
return ""
|
||||||
|
|
@ -648,10 +676,7 @@ def summarize_rejects(rejects) -> str:
|
||||||
for r in rejects or []:
|
for r in rejects or []:
|
||||||
code = r.get("ts_code")
|
code = r.get("ts_code")
|
||||||
for reason in (r.get("reasons") or []):
|
for reason in (r.get("reasons") or []):
|
||||||
# 判据码是冒号前那一段 (SECTOR_RATIO / STOCK_CAP / MAX_NAMES …);
|
key = _reason_key(str(reason))
|
||||||
# 一手不可行那类没有码, 整句截断当分类名
|
|
||||||
text = str(reason)
|
|
||||||
key = text.split(":", 1)[0].strip() if ":" in text[:24] else text[:20]
|
|
||||||
slot = by.setdefault(key, {"n": 0, "codes": []})
|
slot = by.setdefault(key, {"n": 0, "codes": []})
|
||||||
slot["n"] += 1
|
slot["n"] += 1
|
||||||
if code and code not in slot["codes"]:
|
if code and code not in slot["codes"]:
|
||||||
|
|
|
||||||
|
|
@ -13,10 +13,10 @@
|
||||||
test_batch6_units.py ws 通道: 测试向量/签名/公钥/水位/DDL/逐笔入账 (65 例)
|
test_batch6_units.py ws 通道: 测试向量/签名/公钥/水位/DDL/逐笔入账 (65 例)
|
||||||
test_batch7_units.py 上游选股计划: 解析/新鲜度/候选筛选/取数守卫 (32 例)
|
test_batch7_units.py 上游选股计划: 解析/新鲜度/候选筛选/取数守卫 (32 例)
|
||||||
test_batch8_units.py 榜单变化: 名册指纹/三种语义/尾部闸/落库往返 (60 例)
|
test_batch8_units.py 榜单变化: 名册指纹/三种语义/尾部闸/落库往返 (60 例)
|
||||||
test_batch9_units.py 成本价体检 / 对账按日推进 / 行业闸 / 取整记账 (47 例)
|
test_batch9_units.py 成本价体检 / 对账按日推进 / 行业闸 / 取整记账 (49 例)
|
||||||
test_batch10_units.py 静默失败专项: 关键路径不许丢返回值 + 八条实例 (40 例)
|
test_batch10_units.py 静默失败专项: 关键路径不许丢返回值 + 八条实例 (40 例)
|
||||||
test_wiring.py 装配自检: 服务层→核心→落表 全链路 (内存桩) (58 例)
|
test_wiring.py 装配自检: 服务层→核心→落表 全链路 (内存桩) (58 例)
|
||||||
共 391 例
|
共 393 例
|
||||||
任一子集失败即整体失败 (退出码 1)。
|
任一子集失败即整体失败 (退出码 1)。
|
||||||
"""
|
"""
|
||||||
import os
|
import os
|
||||||
|
|
|
||||||
|
|
@ -426,6 +426,35 @@ def t_g4():
|
||||||
assert "1 只" in s and "股A" in s
|
assert "1 只" in s and "股A" in s
|
||||||
|
|
||||||
|
|
||||||
|
@case("[G5] 无码原因**先抹掉数字再归类** —— 价格各不相同也要聚成一组")
|
||||||
|
def t_g5():
|
||||||
|
# 2026-08-03 实机: 8 只票同一个原因、8 个不同价格, 原来按 text[:20] 硬截,
|
||||||
|
# 价格落在截断范围内 → 聚成 8 组一组一只 (聚合等于没做), 而且截断落在词中间,
|
||||||
|
# 印出「…按现价 18.68 买」「…按现价 6.47 买不」这种半句话。
|
||||||
|
rej = [{"ts_code": c, "reasons": [f"目标金额 60 元按现价 {p} 买不足一手 "
|
||||||
|
f"(已尝试: CUSTOM → BASE/FILL → BASE)"]}
|
||||||
|
for c, p in (("600764.SH", "18.68"), ("000800.SZ", "6.47"),
|
||||||
|
("600104.SH", "11.22"), ("002594.SZ", "95.77"))]
|
||||||
|
s = summarize_rejects(rej)
|
||||||
|
assert s.count("只") == 1, f"没聚成一组: {s}"
|
||||||
|
assert "4 只" in s, s
|
||||||
|
assert "买不足一手" in s, f"截断落在词中间了: {s}"
|
||||||
|
assert "18.68" not in s and "95.77" not in s, f"具体价格不该出现在聚合标签里: {s}"
|
||||||
|
assert "已尝试" not in s, f"括号尾注是排查细节, 不该进分类标签: {s}"
|
||||||
|
|
||||||
|
|
||||||
|
@case("[G6] 有码与无码混在一起时各归各的, 仍按条数降序")
|
||||||
|
def t_g6():
|
||||||
|
rej = [{"ts_code": "A", "reasons": ["目标金额 60 元按现价 1.00 买不足一手"]},
|
||||||
|
{"ts_code": "B", "reasons": ["目标金额 60 元按现价 2.00 买不足一手"]},
|
||||||
|
{"ts_code": "C", "reasons": ["目标金额 60 元按现价 3.00 买不足一手"]},
|
||||||
|
{"ts_code": "D", "reasons": ["SECTOR_RATIO: 行业[白酒]占组合将达 55.0% > 上限 40%"]},
|
||||||
|
{"ts_code": "E", "reasons": ["STOCK_CAP: 单股占规模将达 9.1% > 上限 8%"]}]
|
||||||
|
s = summarize_rejects(rej)
|
||||||
|
assert s.index("3 只") < s.index("SECTOR_RATIO"), f"没按条数降序: {s}"
|
||||||
|
assert "SECTOR_RATIO 1 只" in s and "STOCK_CAP 1 只" in s, s
|
||||||
|
|
||||||
|
|
||||||
# ================================================================ [H] 一手取整的记账口径
|
# ================================================================ [H] 一手取整的记账口径
|
||||||
# 2026-07-31 实机: 命令进度写着 planned_amount=600,000 / gap=0 ("完全满足"), 而 15 条方案
|
# 2026-07-31 实机: 命令进度写着 planned_amount=600,000 / gap=0 ("完全满足"), 而 15 条方案
|
||||||
# 的金额合计只有 573,883 —— 少买 26,117 元 (4.4%) 且账面上完全看不出来。
|
# 的金额合计只有 573,883 —— 少买 26,117 元 (4.4%) 且账面上完全看不出来。
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue