tradingSystem/scripts/run_tests.py

152 lines
11 KiB
Python
Raw Normal View History

# -*- coding: utf-8 -*-
"""
全部单测一次跑完 (零外部依赖, 不连库)
======================================
运行: docker compose run --rm pms-web python scripts/run_tests.py
包含 (例数按 2026-09-03 实跑校正; 此前写的 591 是过期数字, 当时实跑已是 608):
test_core_units.py 仓位规划器 / 安全垫与成本账 (14 )
test_batch2_units.py 命令状态机 / 方案生成器 / 回放对账纯逻辑 (35 )
test_batch3_units.py 规则闸 / 择时执行器实现B 纯逻辑 / 用户止损价只披露不拦截 (26 )
test_batch4_units.py 动作引擎 四类自主动作触发与数量口径 +
目标价到价产出清仓候选 (必定交人裁决) +
第零件甲组:PMS 目标价那条路的四个缺陷,加裁决理由服务端强制 独立审查(09-07)报的两条高严重度与两条中严重度,都在目标价这条路上,都违背 09-03 写下的「目标价必交人、系统不自动卖」。 一,跨轮减持抢在人前面卖。同轮只发一条减持只管一次扫描;下一分钟到价清仓已成在途、 按(代码,动作)被跳过,保垫减仓单独产出、不需确认、卖出方向又不走强制入队,于是 自动卖掉三分之一,人随后采纳的清仓单永远等不到可卖量。修法两处互为保险:动作引擎里 同票只要有任何一条减持在跳过集合里,本轮其余减持一律让路;提议服务的在途集合对减持侧 按代码去重,一条在途两种减持一起记。 二,到价提议挂着时高置信止损被吞。在途检查把等人拍板的到价清仓算作在途,止损整条不落。 到价提议的来源写在硬数字里,按来源区分:等人拍板的止盈不挡止损。止损落单后把还挂着的 到价提议作废并在账本记一行,免得人再点采纳对一只已清掉的票再发清仓。 三,到价当天有买入成交时清仓被整条拒。原先按总持仓报数,规则闸一句「卖出 > T+1 可卖」 拒掉再被当日去重挡住,人当天看不到。改按可卖量报数,与执行器口径对齐;可卖为零不产出。 采纳路径同样按拍板那一刻的可卖量重算。 四,挂了交易方案的票目标价永不触发。原先整只跳过,策略层又没有任何地方读目标价。 改成策略票仍评目标价,其余四类不评。目标价是用户的命令,优先级高于自动挂上的方案。 另收第五件里的一小项:裁决理由改为服务端强制,空理由直接拒绝请求。此前必填只在浏览器 里成立,任何脚本都能写出一条默认文案的裁决记录。 测试:第四批加三例(跨轮让路、策略票评目标价、按可卖量报数),第二十一批加两例 (到价提议不挡止损并被作废、非到价来源的在途仍挡),例数 655 到 660。 Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-07 11:22:05 +08:00
同轮只发一条减持 (到价清仓优先于保垫减仓) +
跨轮减持让路 / 策略票仍评目标价 / 清仓按可卖量报数 (18 )
test_batch5_units.py 决策系统信号流解析与消化口径 (8 )
test_batch6_units.py ws 通道: 测试向量/签名/公钥/水位/弃洞/DDL/逐笔入账 (68 )
test_batch7_units.py 上游选股计划: 解析/新鲜度/候选筛选/取数守卫/
候选卡五键透传与截断/仅展示在候选阶段剔除/
研究理由两键透传与不进送研判名单 (36 )
2026-07-31 13:28:10 +08:00
test_batch8_units.py 榜单变化: 名册指纹/三种语义/尾部闸/落库往返 (60 )
2026-08-03 09:53:18 +08:00
test_batch9_units.py 成本价体检 / 对账按日推进 / 行业闸 / 取整记账 (49 )
test_batch10_units.py 静默失败专项: 关键路径不许丢返回值 + 八条实例 +
页面能改的参数必须有人读 ([P] 组静态扫描) (47 )
2026-08-03 12:49:01 +08:00
test_batch11_units.py 择时实现A: 本地检查等价/应答折算/缓存冷却/退B (24 )
test_batch12_units.py 自主新建仓: 选票与滚动扣减/硬数字裁剪/漂移/预算/
转多信号插队与留痕/窗口末日只对命令/跳过原因分得开/
2026-08-25 11:09:58 +08:00
研判请求体的数据库类型 / 研判应答归一(UNAVAILABLE) (48 )
2026-08-19 10:04:20 +08:00
test_batch13_units.py 决策系统卖出采纳: 门槛分档/清场标记/确认加速/闭仓清场 (4 )
test_batch14_units.py 宏观择时: 指数计算与对齐/区域迟滞/周期与对数映射/
分方向触发/让路与冷却/宏观闸/建议采纳 (25 )
2026-08-20 10:23:33 +08:00
test_batch15_units.py 紧急清仓修复: 配额在途口径不重复扣/紧急清仓不被
2026-08-20 11:09:33 +08:00
误判配额已出完/清仓命令即时撤策略/买入提议/在途买单;
在途与配额文案拆分; 减仓不掐策略腿 (12 )
2026-08-25 11:09:58 +08:00
test_batch16_units.py 软归档 archived_at: 单表守卫/只归终态/列表默认排除 (5 )
2026-08-25 13:45:51 +08:00
test_batch17_units.py 策略自动挂载: 定性归类(子串+保守优先)/网格参数生成/
2026-08-25 14:25:45 +08:00
科创板一手200(advisor runner 两侧)/连边矩阵/
2026-08-26 10:01:30 +08:00
五阶段判定与逐票阶段返回/note 约定与冷却推导/
接力判定/clear_buypause/编排冒烟(dry_run 滴水不写/
2026-08-26 10:11:29 +08:00
名额/暂停买入/接力全链)/真跑写一跳摘要而预演不写/
判分脚本聚合与对照分组 (41 )
2026-08-26 16:51:11 +08:00
test_batch18_units.py 登录与权限: 角色判定/会话票签验/bshop 返回解析/
2026-08-27 10:16:45 +08:00
接口鉴权(维护类归管理员) (16 )
2026-08-28 13:07:27 +08:00
test_batch19_units.py 2026-08-28 审查修复回归: 科创板最小申报统一口径/
取整与部分卖/同轮买卖互斥/信号百分制契约/除权核销
缩放/日历按年降级/网格中枢与止盈闩锁/买入暂停
2026-08-28 14:34:00 +08:00
按来源分记/宏观失败路径保留留痕/双实例表名前缀 (19 )
test_batch20_units.py 选股系统候选卡判决接入 (2026-09-03): 按判决分流三态/
无判决回退/判决认不出交人/仅展示不占名额/自动执行
开关四条件与任一缺失仍入队/信号来源区分/密钥名单
含会话密钥/研判键放行与必答题/置信度进硬数字/
仅展示的跳过原因写出这一只自己的判决依据/
候选一只不剩时的说明不打印计数字典/
新建仓低把握驳回按证据不足交人/
页面静态守卫 (18 )
test_batch21_units.py 减持的自动执行边界 (2026-09-03 安全修复): 保垫减仓照旧自动/
强制入队对卖出同样有效/研判不可用的减持入队/研究证据走弱
第零件甲组:PMS 目标价那条路的四个缺陷,加裁决理由服务端强制 独立审查(09-07)报的两条高严重度与两条中严重度,都在目标价这条路上,都违背 09-03 写下的「目标价必交人、系统不自动卖」。 一,跨轮减持抢在人前面卖。同轮只发一条减持只管一次扫描;下一分钟到价清仓已成在途、 按(代码,动作)被跳过,保垫减仓单独产出、不需确认、卖出方向又不走强制入队,于是 自动卖掉三分之一,人随后采纳的清仓单永远等不到可卖量。修法两处互为保险:动作引擎里 同票只要有任何一条减持在跳过集合里,本轮其余减持一律让路;提议服务的在途集合对减持侧 按代码去重,一条在途两种减持一起记。 二,到价提议挂着时高置信止损被吞。在途检查把等人拍板的到价清仓算作在途,止损整条不落。 到价提议的来源写在硬数字里,按来源区分:等人拍板的止盈不挡止损。止损落单后把还挂着的 到价提议作废并在账本记一行,免得人再点采纳对一只已清掉的票再发清仓。 三,到价当天有买入成交时清仓被整条拒。原先按总持仓报数,规则闸一句「卖出 > T+1 可卖」 拒掉再被当日去重挡住,人当天看不到。改按可卖量报数,与执行器口径对齐;可卖为零不产出。 采纳路径同样按拍板那一刻的可卖量重算。 四,挂了交易方案的票目标价永不触发。原先整只跳过,策略层又没有任何地方读目标价。 改成策略票仍评目标价,其余四类不评。目标价是用户的命令,优先级高于自动挂上的方案。 另收第五件里的一小项:裁决理由改为服务端强制,空理由直接拒绝请求。此前必填只在浏览器 里成立,任何脚本都能写出一条默认文案的裁决记录。 测试:第四批加三例(跨轮让路、策略票评目标价、按可卖量报数),第二十一批加两例 (到价提议不挡止损并被作废、非到价来源的在途仍挡),例数 655 到 660。 Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-07 11:22:05 +08:00
来源必定交人裁决/风控高置信卖出与命令清仓不经提议分流/
到价提议挂着时止损照落并作废该提议 (17 )
PMS 文案第三批:页面枚举直出,并加一道静态守卫 页面是三千行的单文件,没有任何测试,改错了不会有东西报警。所以这一批先写守卫, 再靠守卫把问题扫出来——它比人工照清单改可靠,实际也确实多抓出了四处清单里没有的。 改掉的直出点: - 操作日志印的是函数名式的操作码(issue_command:EXIT_STOCK 这类)。冒号前是做了 什么、冒号后是对象,后半段复用页面已有的翻译组,英文码退到悬浮提示。 - 持仓表的冻结原因没走翻译,而同一份文件里另一处走了,是执行漏网。 - 提议卡两处印判决词。判决词本身不告诉人能不能买,句子主干换成原因。这里顺带 接上了 missing 字段——它一路从后端带到前端却从没被渲染过,"关注"态终于说得出 到底缺什么。 - 规则闸未通过项与裁决理由带着大写判据码。前端加一份剥前缀的实现,与后端 app/core/copy.py 同口径。 - 账本那一列原先叫「硬数字」,把整个字典打出来。改成只念人要读的几项,完整原文 退到悬浮提示。同一行的动作与研判两列也是英文原值,一起走翻译。 - 候选池落选明细、命令参数、安全垫状态、自主档位,都是直接印原值。 - 翻译兜底原先是原样返回,会让一个英文词单独立在页面上当句子。改成退到括号里。 守卫检查四件事:枚举字段直出、判据码没剥前缀、把整个对象打给交易员看、翻译兜底 裸奔。运维面板里那些有意显示原始数据的地方逐条登记放行,每条写明在哪个面板、 为什么放行。 例数 654 到 655,测试入口的例数表与哨兵位置清单同改——新增会送到页面的枚举字段 要往守卫的名单里登记一行,不登记等于不检查。 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-04 14:43:22 +08:00
test_page_enum_guard.py 页面文案守卫 (静态扫描, 不连库不起浏览器): 枚举字段不许
直接印到页面上 / 判据码显示前必须剥前缀 / 不许把整个对象
打给交易员看 / 翻译兜底不许让英文码单独当句子 (1 )
test_wiring.py 装配自检: 服务层核心落表 全链路 (内存桩) +
目标价到价必定入队 (档位 full 也不自动卖) +
用户设的止损价与目标价单独成列显示 (70 )
661
(总数按实跑逐批相加校正过两次: 曾写 649 是笔误, 实为 650; 09-03 先后加了同轮只发一条
减持与研究理由两键各一例, 652; 09-04 加了仅展示跳过原因与空候选说明各一例, 654; 又加了页面文案守卫一例, 655; 09-07 审查修复加了跨轮减持等五例, 660; 第二件低把握驳回交人一例, 现为 661)
任一子集失败即整体失败 (退出码 1)
哨兵位置清单 (2026-09-03 抄录; 改了对应的东西就得来这些地方改断言, 断言不动就是漏了):
DDL 表数 scripts/test_batch6_units.py 用例DDL 文件本身体检通过(约第 580 ),
断言 `eq(len([... k == "table"]), 19)` (约第 593 ) 加表就 +1
调度位 scripts/test_wiring.py 用例装配·调度表覆盖设计 §10 全部调度位(约第 654 ),
beat_schedule 名字集合与 pms.* 任务名两处
路由清单 scripts/test_wiring.py 用例装配·API 路由齐全 (四块页面 + 运维)(约第 634 )
need 列表 加接口就登记
批次登记 本文件 SUITES 列表 + 上面的例数表 加测试文件必须两处同改, 否则缺失
会被判失败 (没跑不许算通过)
软失败函数 scripts/test_batch10_units.py 顶部 SOFT_FAIL 名单 (约第 51 ) 与用例 [A1]
(约第 95 , AST app/ 全库) 新加失败不抛异常只回 ok=False的函数就登记
桩签名比对 scripts/test_batch10_units.py 用例 [M1] (约第 836 ): 从源码读 pms_repo 真实
签名与 test_wiring.FakeRepo 逐个比 repo 函数签名, 桩必须同改
密钥名单 scripts/test_batch6_units.py 用例密钥不进 ParamStore (协议 §10.1.1)(约第
642 ) scripts/test_batch20_units.py 用例密钥名单·会话密钥
新增密钥类配置要同时进 param_store.SECRET_KEYS 与这两处断言
死参数名单 scripts/test_batch10_units.py KNOWN_DEAD ([P] ) param_store.py 顶部
那段清点 页面能改而全库没人读的参数只能同时改这两处接通了摘掉了
或者又添了一个摆设, 都要在这里过一遍手
PMS 文案第三批:页面枚举直出,并加一道静态守卫 页面是三千行的单文件,没有任何测试,改错了不会有东西报警。所以这一批先写守卫, 再靠守卫把问题扫出来——它比人工照清单改可靠,实际也确实多抓出了四处清单里没有的。 改掉的直出点: - 操作日志印的是函数名式的操作码(issue_command:EXIT_STOCK 这类)。冒号前是做了 什么、冒号后是对象,后半段复用页面已有的翻译组,英文码退到悬浮提示。 - 持仓表的冻结原因没走翻译,而同一份文件里另一处走了,是执行漏网。 - 提议卡两处印判决词。判决词本身不告诉人能不能买,句子主干换成原因。这里顺带 接上了 missing 字段——它一路从后端带到前端却从没被渲染过,"关注"态终于说得出 到底缺什么。 - 规则闸未通过项与裁决理由带着大写判据码。前端加一份剥前缀的实现,与后端 app/core/copy.py 同口径。 - 账本那一列原先叫「硬数字」,把整个字典打出来。改成只念人要读的几项,完整原文 退到悬浮提示。同一行的动作与研判两列也是英文原值,一起走翻译。 - 候选池落选明细、命令参数、安全垫状态、自主档位,都是直接印原值。 - 翻译兜底原先是原样返回,会让一个英文词单独立在页面上当句子。改成退到括号里。 守卫检查四件事:枚举字段直出、判据码没剥前缀、把整个对象打给交易员看、翻译兜底 裸奔。运维面板里那些有意显示原始数据的地方逐条登记放行,每条写明在哪个面板、 为什么放行。 例数 654 到 655,测试入口的例数表与哨兵位置清单同改——新增会送到页面的枚举字段 要往守卫的名单里登记一行,不登记等于不检查。 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-04 14:43:22 +08:00
页面枚举名单 scripts/test_page_enum_guard.py 顶部 ENUM_FIELDS 后端新增会送到页面的
枚举字段就登记一行, 并在 index.html T 表里加对应的组不登记等于不检查
index.html 没有别的测试, 这道扫描是它唯一的护栏
动作求值器 scripts/test_batch12_units.py 用例回归·既有四类动作的判据与研判范围没被
动过 action_engine.EVALUATORS 的名单与次序钉在那里 (次序有意义: 减持排
在买入前面, 同轮买卖互斥才让得了路)
"""
import os
import subprocess
import sys
HERE = os.path.dirname(os.path.abspath(__file__))
ROOT = os.path.dirname(HERE)
SUITES = ["test_core_units.py", "test_batch2_units.py", "test_batch3_units.py",
2026-07-28 15:48:57 +08:00
"test_batch4_units.py", "test_batch5_units.py", "test_batch6_units.py",
2026-07-31 13:46:15 +08:00
"test_batch7_units.py", "test_batch8_units.py", "test_batch9_units.py",
2026-08-06 13:55:27 +08:00
"test_batch10_units.py", "test_batch11_units.py", "test_batch12_units.py",
2026-08-20 10:23:33 +08:00
"test_batch13_units.py", "test_batch14_units.py", "test_batch15_units.py",
2026-08-26 16:51:11 +08:00
"test_batch16_units.py", "test_batch17_units.py",
"test_batch18_units.py", "test_batch19_units.py", "test_batch20_units.py",
"test_batch21_units.py",
PMS 文案第三批:页面枚举直出,并加一道静态守卫 页面是三千行的单文件,没有任何测试,改错了不会有东西报警。所以这一批先写守卫, 再靠守卫把问题扫出来——它比人工照清单改可靠,实际也确实多抓出了四处清单里没有的。 改掉的直出点: - 操作日志印的是函数名式的操作码(issue_command:EXIT_STOCK 这类)。冒号前是做了 什么、冒号后是对象,后半段复用页面已有的翻译组,英文码退到悬浮提示。 - 持仓表的冻结原因没走翻译,而同一份文件里另一处走了,是执行漏网。 - 提议卡两处印判决词。判决词本身不告诉人能不能买,句子主干换成原因。这里顺带 接上了 missing 字段——它一路从后端带到前端却从没被渲染过,"关注"态终于说得出 到底缺什么。 - 规则闸未通过项与裁决理由带着大写判据码。前端加一份剥前缀的实现,与后端 app/core/copy.py 同口径。 - 账本那一列原先叫「硬数字」,把整个字典打出来。改成只念人要读的几项,完整原文 退到悬浮提示。同一行的动作与研判两列也是英文原值,一起走翻译。 - 候选池落选明细、命令参数、安全垫状态、自主档位,都是直接印原值。 - 翻译兜底原先是原样返回,会让一个英文词单独立在页面上当句子。改成退到括号里。 守卫检查四件事:枚举字段直出、判据码没剥前缀、把整个对象打给交易员看、翻译兜底 裸奔。运维面板里那些有意显示原始数据的地方逐条登记放行,每条写明在哪个面板、 为什么放行。 例数 654 到 655,测试入口的例数表与哨兵位置清单同改——新增会送到页面的枚举字段 要往守卫的名单里登记一行,不登记等于不检查。 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-04 14:43:22 +08:00
"test_page_enum_guard.py",
"test_wiring.py"]
def main():
2026-08-03 09:10:56 +08:00
# 先把「我到底在跑哪份代码」打出来。源码是打进镜像的, git pull 之后不 build 的话
# 这里跑的还是旧代码, 而它照样会 ALL SUITES PASS —— `make test` 会拿这个指纹跟
# 工作树对一遍, 对不上就喊。详见 scripts/code_fingerprint.py。
try:
from code_fingerprint import fingerprint
print(f"代码指纹 {fingerprint()} (与工作树对不上 = 容器跑的是旧镜像, 先 make deploy)")
except Exception as e:
print(f"代码指纹取不到: {type(e).__name__}: {e}")
failed = []
2026-08-03 09:10:56 +08:00
missing = []
for s in SUITES:
path = os.path.join(HERE, s)
if not os.path.exists(path):
2026-08-03 09:10:56 +08:00
# 少一个测试文件绝不能只印一行"跳过"就当没事 —— 镜像是旧的时候,
# 新增的那一批测试正好全部"不存在", 于是一条没跑却报 ALL SUITES PASS。
print(f"== 缺失 {s} —— **这一批一条都没跑**")
missing.append(s)
continue
print(f"\n=== {s} " + "=" * (52 - len(s)))
r = subprocess.run([sys.executable, path], cwd=ROOT)
if r.returncode != 0:
failed.append(s)
print("\n" + "=" * 62)
2026-08-03 09:10:56 +08:00
if missing:
print(f"SUITE MISSING: {', '.join(missing)} —— 这些测试文件不在当前代码里, "
f"一条都没跑。镜像旧了就 make deploy; 真删了就把 SUITES 清单一起改")
if failed:
print(f"SUITE FAILED: {', '.join(failed)}")
sys.exit(1)
2026-08-03 09:10:56 +08:00
if missing:
sys.exit(1) # 「没跑」不许算「通过」
print("ALL SUITES PASS")
if __name__ == "__main__":
main()